[Pkg-octave-commit] [matlab2tikz] 02/04: Imported Upstream version 1.0.0
Sébastien Villemot
sebastien at debian.org
Fri Feb 19 21:39:15 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastien pushed a commit to branch master
in repository matlab2tikz.
commit 9beb4576baf0b73fcbc5dea8fe95b715c43dc29b
Author: Sébastien Villemot <sebastien at debian.org>
Date: Fri Feb 19 22:35:19 2016 +0100
Imported Upstream version 1.0.0
---
.travis.yml | 10 +
AUTHORS | 1 -
AUTHORS.md | 42 +
CHANGELOG.md | 383 +
ChangeLog | 350 -
COPYING => LICENSE.md | 38 +-
Makefile | 21 -
README.md | 119 +-
THANKS | 44 -
gallery/matlab2tikz-snapshot.png | Bin 34048 -> 0 bytes
logos/matlab2tikz.svg | 94 +
{tools => src}/cleanfigure.m | 181 +-
{tools => src}/figure2dot.m | 2 +-
src/{matlab2tikzInputParser.m => m2tInputParser.m} | 4 +-
src/matlab2tikz.m | 10320 ++++++++++---------
src/private/isAxis3D.m | 5 +
src/private/m2tUpdater.m | 242 +
src/updater.m | 142 -
test/README | 19 -
test/README.md | 91 +
test/codeReport.m | 268 +
test/data/converted/Makefile | 23 +
test/data/reference/Makefile | 19 +
test/makeLatexReport.m | 211 +
test/makeTravisReport.m | 73 +
test/matlab2tikz_acidtest.m | 376 -
test/myCount.dat | 24 -
test/pointReductionTest.m | 34 -
test/private/OSVersion.m | 16 +
test/private/VersionControlIdentifier.m | 46 +
test/private/calculateMD5Hash.m | 34 +
test/private/cleanFiles.m | 17 +
test/private/countNumberOfErrors.m | 16 +
test/private/emptyStage.m | 4 +
test/private/emptyStatus.m | 17 +
test/private/errorHandler.m | 41 +
test/private/errorHasOccurred.m | 16 +
test/private/execute_hash_stage.m | 34 +
test/private/execute_plot_stage.m | 45 +
test/private/execute_save_stage.m | 61 +
test/private/execute_tikz_stage.m | 26 +
test/private/execute_type_stage.m | 14 +
test/private/fillStruct.m | 10 +
test/private/getEnvironment.m | 25 +
test/private/getStagesFromStatus.m | 5 +
test/private/hashTableName.m | 55 +
test/private/initializeWorkingDirectory.m | 23 +
test/private/loadHashTable.m | 19 +
test/private/m2tstrjoin.m | 16 +
test/private/splitUnreliableTests.m | 7 +
test/private/testMatlab2tikz.m | 159 +
test/runMatlab2TikzTests.m | 32 +
test/saveHashTable.m | 37 +
test/suites/ACID.MATLAB.8.3.md5 | 98 +
test/suites/ACID.MATLAB.8.4.md5 | 98 +
test/suites/ACID.Octave.3.8.0.md5 | 76 +
test/{testfunctions.m => suites/ACID.m} | 1964 ++--
test/suites/issues.m | 43 +
test/suites/private/getEnvironment.m | 25 +
test/{ => suites/private}/herrorbar.m | 23 +-
test/suites/private/isEnvironment.m | 46 +
test/suites/private/isMATLAB.m | 4 +
test/suites/private/isOctave.m | 5 +
test/suites/private/isVersionBelow.m | 38 +
test/suites/private/versionCompare.m | 20 +
test/suites/testPatches.m | 121 +
test/suites/testSurfshader.m | 102 +
test/testGraphical.m | 47 +
test/testHeadless.m | 62 +
test/tex/Makefile | 22 +-
version-0.4.7 | 6 -
71 files changed, 9921 insertions(+), 6790 deletions(-)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..461be05
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: c++
+# command to install dependencies
+before_install:
+ - sudo add-apt-repository -y ppa:octave/stable
+ - sudo apt-get update -qq
+ - sudo apt-get install octave
+script:
+ - cd test && /usr/bin/octave -q --eval runMatlab2TikzTests.m
+notifications:
+ hipchat: f4c2c5f87adc85025545e5b59b3fbe at Matlab2tikz
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 2558a19..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1 +0,0 @@
-Nico Schlömer designed and implemented the intial version and is the current maintainer.
diff --git a/AUTHORS.md b/AUTHORS.md
new file mode 100644
index 0000000..da5cfe0
--- /dev/null
+++ b/AUTHORS.md
@@ -0,0 +1,42 @@
+Nico Schlömer designed and implemented the intial version and is the current maintainer.
+
+Thanks for patches, suggestions, and other contributions go to:
+
+ * Martijn Aben (The MathWorks)
+ * Ben Abbott
+ * Eike Blechschmidt
+ * Klaus Broelemann
+ * Katherine Elkington
+ * Andreas Gäb
+ * Egon Geerardyn
+ * Roman Gesenhues
+ * Michael Glasser (The MathWorks)
+ * David Haberthür
+ * Patrick Häcker
+ * David Horsley
+ * Burkart Lingner
+ * Oleg Komarov
+ * Mykel Kochenderfer
+ * Henk Kortier
+ * Theo Markettos
+ * Dragan Mitrevski
+ * Francesco Montorsi
+ * Peter Pablo
+ * Ricardo Santiago Mozos
+ * Johannes Mueller-Roemer
+ * Julien Ridoux
+ * Christoph Rüdiger
+ * Carlos Russo
+ * Johannes Schmitz
+ * Michael Schoeberl
+ * Donghua Wang
+ * Robert Whittlesey
+ * Pooya Ziraksaz
+ * Bastiaan Zuurendonk (The MathWorks)
+ * _and many more!_
+
+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 for the [Pgfplots](http://pgfplots.sourceforge.net) package which forms the basis for the matlab2tikz output on the LaTeX side.
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a140da2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,383 @@
+# 2015-06-15 Version 1.0.0 [Egon Geerardyn](egon.geerardyn at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at gmail.com)
+
+ * Improved Octave compatibility.
+ * Several bugfixes:
+ - input parser
+
+# 2011-01-31 Version 0.1.0 [Nico Schlömer](nico.schloemer at 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 at 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 at 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 at 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 at 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 at 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 at 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 at ua.ac.be)
+
+ * Initial version
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index e3a2a5c..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,350 +0,0 @@
-2014-03-07 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.7.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.6.
- * Set externalData default to false.
- * Properly check for required Pgfplots version.
- * Marker scaling in scatter plots.
-
-2014-02-02 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.5.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.4.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.3.
- * Support for 3D quiver plots.
- * Extended support for colorbar axis options.
- * New logo!
- * Bugfixes:
- - text generation
- - extraCode option
- - join strings
- - ...
-
-2013-09-12 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.2.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.1.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.4.0.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.3.3.
- * Support for:
- - pictures in LaTeX subfloats
- * Bugfixes:
- - axes labels
- - extra* options
- - logscaled axes
- - ...
-
-2013-03-14 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.3.2.
- * Support for:
- - waterfall plots
- * Bugfixes:
- - axis locations
- - color handling
- - stacked bars
- - ...
-
-2013-02-15 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.3.1.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.3.0.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.2.3.
- * Multiline text for all entities.
- * Support for logical images.
- * Support for multiple legends (legends in subplots).
- * Fixed version check bug.
- * Fix minumumPointsDistance.
-
-2012-07-19 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.2.2.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.2.1.
- * 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 Infs/NaNs.
- * Better info text.
- * matlab2tikz version checking.
- * Line plotting code cleanup.
- * About 10 bugfixes.
-
-2012-03-17 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.2.0.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.1.4.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.1.3.
- * Greatly overhauled text handling.
- * Better Octave compatibility.
- * Several bugfixes:
- - subplot order
- - environment detection
-
-
-2011-06-02 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.1.2.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.1.1.
- * Improved Octave compatibility.
- * Several bugfixes:
- - input parser
-
-2011-01-31 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.1.0.
- * Basic Octave compatibility.
- * Several bugfixes:
- - bar plots fix (thanks to Christoph Rüdiger)
- - fix legends with split graphs
-
-2010-09-10 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.0.7.
- * 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 Nico Schlömer <nico.schloemer at gmail.com>
-
- * Version 0.0.6.
- * 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 Nico Schlömer <nico.schloemer at ua.ac.be>
-
- * Version 0.0.5.
- * Improvements in axis handling:
- - colored axes
- - allow different left and right ordinates
- * Improvements for line plots:
-
- * Version 0.0.5.
- * 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 Dragen Mitrevski)
- - transparent patches (thanks Carlos Russo)
- * Added support for:
- - background color
- - Bode plots
- - zplane plots
- - freqz plots
-
-
-2009-06-09 Nico Schlömer <nico.schloemer at ua.ac.be>
-
- * Version 0.0.4.
- * 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 Nico Schlömer <nico.schloemer at ua.ac.be>
-
- * Version 0.0.3.
- * Added support for:
- - subplots
- - reverse axes
- * Completed support for:
- - images
-
-2009-01-08 Nico Schlömer <nico.schloemer at ua.ac.be>
-
- * Version 0.0.2.
- * 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 Nico Schlömer <nico.schloemer at ua.ac.be>
-
- * Initial version 0.0.1.
diff --git a/COPYING b/LICENSE.md
similarity index 61%
rename from COPYING
rename to LICENSE.md
index 15dc183..bd63256 100644
--- a/COPYING
+++ b/LICENSE.md
@@ -1,24 +1,24 @@
-Copyright (c) 2008--2012 Nico Schlömer
+Copyright (c) 2008--2015 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
+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
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 1d9d6e9..0000000
--- a/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-# This makefile creates a release tarball.
-
-MATLAB2TIKZ_DIR=.
-VERSION=0.4.7
-
-default: release
-
-release:
- # The license is automatically added by
- # MathWorks after the upload.
- @zip -r matlab2tikz_${VERSION}.zip \
- ${MATLAB2TIKZ_DIR}/AUTHORS \
- ${MATLAB2TIKZ_DIR}/ChangeLog \
- ${MATLAB2TIKZ_DIR}/README.md \
- ${MATLAB2TIKZ_DIR}/THANKS \
- ${MATLAB2TIKZ_DIR}/version-${VERSION} \
- ${MATLAB2TIKZ_DIR}/tools/ \
- ${MATLAB2TIKZ_DIR}/src/
-
-clean:
- rm -f matlab2tikz_${VERSION}.zip
diff --git a/README.md b/README.md
index 129872e..aa7a6e9 100644
--- a/README.md
+++ b/README.md
@@ -1,65 +1,86 @@
-This is matlab2tikz, a MATLAB(R) script for converting MATLAB(R) figures into
-native TikZ/Pgfplots figures.
+[![Build Status](https://travis-ci.org/matlab2tikz/matlab2tikz.svg?branch=master)](https://travis-ci.org/matlab2tikz/matlab2tikz)
+![matlab2tikz](https://raw.githubusercontent.com/wiki/matlab2tikz/matlab2tikz/matlab2tikz.png)
-To download and rate matlab2tikz, go to its page on MathWorks
-http://www.mathworks.com/matlabcentral/fileexchange/22022.
+`matlab2tikz` is a MATLAB(R) script to convert native MATLAB(R) figures to TikZ/Pgfplots figures that integrate seamlessly in LaTeX documents.
-matlab2tikz supports the conversion of most MATLAB figures,
-including 2D and 3D plots. For plots constructed with third-
-party packages, your mileage may vary.
+To download the official releases and rate `matlab2tikz`, please visit its page on [FileExchange](http://www.mathworks.com/matlabcentral/fileexchange/22022).
-The workflow is as follows.
+`matlab2tikz` converts most MATLAB(R) figures, including 2D and 3D plots.
+For plots constructed with third-party packages, however, your mileage may vary.
-0. a. Place the matlab2tikz scripts (contents of src/ folder) in a directory
- where MATLAB can find it (the current directory, for example).
- b. Make sure that your LaTeX installation includes the packages
- * TikZ (aka PGF, >=2.00) and
- * Pgfplots (>=1.3).
+Installation
+============
-1. Generate your plot in MATLAB.
+Place the `matlab2tikz` functions (contents of `src/` folder) somewhere where MATLAB(R) can find them: either your working directory or add `src/` to the path using `addpath`.
+
+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.12.1 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
-2. Invoke matlab2tikz by
-```matlab
->> matlab2tikz();
-```
- or
-```matlab
->> matlab2tikz('myfile.tex');
-```
- The script accepts numerous options; check them out by invoking the help,
-```matlab
->> help matlab2tikz
-```
-Sometimes, MATLAB makes it hard to create matching LaTeX plots by keeping
-invisible objects around or stretches the plots too far beyond the bounding box.
-Use
```matlab
->> cleanfigure;
->> matlab2tikz('myfile.tex');
+matlab2tikz('myfile.tex');
```
-to first clean the figure of unwanted entities, and then convert it to TeX.
-3. Add the contents of `myfile.tex` into your LaTeX source code; a
- convenient way of doing so is to use `\input{/path/to/myfile.tex}`.
- Also make sure that at the header of your document the Pgfplots package
- is included:
+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}
-% and optionally (as of Pgfplots 1.3):
-\pgfplotsset{compat=newest}
-\pgfplotsset{plot coordinates/math parser=false}
-\newlength\figureheight
-\newlength\figurewidth
+
+ \usepackage{pgfplots}
+ \pgfplotsset{compat=newest}
+ %% the following commands are sometimes needed
+ \usetikzlibrary{plotmarks}
+ \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}
+ \input{myfile.tex}
\end{document}
```
-There are reported incompatibilities with the following LaTeX packages:
- * signalflowdiagram <http://www.texample.net/tikz/examples/signal-flow-building-blocks/>
- (Check out <http://sourceforge.net/tracker/?func=detail&aid=3312653&group_id=224188&atid=1060656>.)
+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
+================
-If you experience bugs, have nice examples of what matlab2tikz can do, or if
-you are just looking for more information, please visit the web page of
-matlab2tikz <https://github.com/nschloe/matlab2tikz>.
+* For more information about `matlab2tikz`, have a look at our [GitHub repository](https://github.com/matlab2tikz/matlab2tikz). If you are a good MATLAB(R) programmer or LaTeX writer, you are always welcome to help improving `matlab2tikz`!
+* Some common problems and pit-falls are documented in our [wiki](https://github.com/matlab2tikz/matlab2tikz/wiki/Common-problems).
+* If you experience (other) bugs or would like to request a feature, please visit our [issue tracker](https://github.com/matlab2tikz/matlab2tikz/issues).
diff --git a/THANKS b/THANKS
deleted file mode 100644
index a8c210d..0000000
--- a/THANKS
+++ /dev/null
@@ -1,44 +0,0 @@
-matlab2tikz has once greatly profited from its ancestor, Matfig2PGF,
-
- http://www.mathworks.com/matlabcentral/fileexchange/12962
-
-Matfig2PGF is written by Paul Wagenaars.
-
-Also, the author would like to thank Christian Feuersänger package Pgfplots
-without which the structure of matlab2tikz would hardly be possible.
-
-Thanks for patches, suggestions, and other contributions to
-
- Katherine Elkington
- Andreas Gäb
- Roman Gesenhues
- David Haberthür
- Patrick Häcker
- Mykel Kochenderfer
- Henk Kortier
- Theo Markettos
- Dragan Mitrevski
- Carlos Russo
- Robert Whittlesey
- Pooya Ziraksaz
- Johannes Schmitz
- Christoph Rüdiger
- Ben Abbott
- Johannes Mueller-Roemer
- Julien Ridoux
- Burkart Lingner
- Francesco Montorsi
- Ricardo Santiago Mozos
- Eike Blechschmidt
- Michael Schoeberl
- Patrick Häcker
- Egon Geerardyn
- ...
-
-and MathWorks' very own
-
- Martijn Aben
- Bastiaan Zuurendonk
- Michael Glasser
-
-and many more!
diff --git a/gallery/matlab2tikz-snapshot.png b/gallery/matlab2tikz-snapshot.png
deleted file mode 100644
index 2c86bb9..0000000
Binary files a/gallery/matlab2tikz-snapshot.png and /dev/null differ
diff --git a/logos/matlab2tikz.svg b/logos/matlab2tikz.svg
new file mode 100644
index 0000000..ea2a8f7
--- /dev/null
+++ b/logos/matlab2tikz.svg
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg3051"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="m2t.svg">
+ <defs
+ id="defs3053" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.25"
+ inkscape:cx="696.59128"
+ inkscape:cy="461.26734"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1280"
+ inkscape:window-height="738"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata3056">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#ef8200;fill-opacity:1;stroke:none"
+ d="m 229.79347,889.91953 c -51.20296,-69.53548 -110.45905,-150.5284 -117.8477,-161.07762 -3.66043,-5.22621 -4.15923,-6.45766 -2.93039,-7.23468 0.81449,-0.51503 2.1559,-0.94763 2.9809,-0.96134 4.45183,-0.074 21.84491,-16.50352 39.70528,-37.5057 64.93246,-76.3547 212.14442,-292.5117 313.27925,-460 28.45805,-47.12908 55.23448,-94.70724 59.01417,-107.81682 2.21373,-7.67817 3.30364,-4.58186 5.54982,7.259 0.84717,4.46595 9.42069,39.94343 19.05225,78.83886 61.4356,248.09709 88.96885,376. [...]
+ id="path3072" />
+ <path
+ style="fill:#ffd912;fill-opacity:1"
+ d="M 828.49628,790.87316 C 751.45425,746.41551 656.62349,689.46978 647.33146,682.08395 c -2.47911,-1.97053 -2.52321,-2.17947 -1.05805,-5.01277 3.18772,-6.16438 4.02557,-14.85566 3.44538,-35.74002 -0.80529,-28.98647 -5.98761,-65.55929 -17.38517,-122.69097 -18.80756,-94.27528 -55.9766,-241.89492 -91.4729,-363.29152 -4.95189,-16.93533 -13.8484,-44.15875 -13.64905,-44.7568 0.19935,-0.59804 7.77507,16.91106 10.71396,23.16944 14.72516,31.35732 169.10504,368.5638 262.04653,572.37888 18.8 [...]
+ id="path3070" />
+ <path
+ style="fill:#00b0cf;fill-opacity:1"
+ d="M 98.496283,712.93087 C 76.224153,691.69469 25.659453,651.42885 -55.133796,590.59166 l -36.630081,-27.58239 14.630081,-4.80606 C 1.4604828e-5,532.86436 84.848253,489.14509 155.49628,438.33721 c 90.71369,-65.23848 211.18904,-171.14032 339.75,-298.65155 14.7125,-14.59237 30.24771,-31.09621 30.24771,-30.65137 0,1.46965 -5.56006,9.74411 -33.50167,53.6059 -117.938,185.13504 -236.74752,364.94776 -300.3065,454.5 -22.45559,31.6391 -46.86362,64.24839 -58.75719,78.5 -10.15154,12.16419 -2 [...]
+ id="path2987" />
+ <text
+ xml:space="preserve"
+ style="font-size:256.08959961px;font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ef8200;fill-opacity:1;stroke:none;font-family:Monotype Corsiva;-inkscape-font-specification:Monotype Corsiva Bold Italic"
+ x="835.56165"
+ y="469.78217"
+ id="text3863"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3865"
+ x="835.56165"
+ y="469.78217"
+ style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans Italic"><tspan
+ style="font-style:normal;-inkscape-font-specification:Liberation Sans"
+ id="tspan2999">MATLAB</tspan><tspan
+ style="font-style:italic;font-weight:bold;-inkscape-font-specification:Liberation Sans Italic"
+ id="tspan2997">2</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="835.56165"
+ y="789.89417"
+ id="tspan3867"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#ef8200;fill-opacity:1;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ id="tspan3875">Ti</tspan><tspan
+ style="font-style:italic;-inkscape-font-specification:Liberation Sans Italic"
+ id="tspan2995">k</tspan><tspan
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"
+ id="tspan3873">Z</tspan></tspan></text>
+ </g>
+</svg>
diff --git a/tools/cleanfigure.m b/src/cleanfigure.m
similarity index 72%
rename from tools/cleanfigure.m
rename to src/cleanfigure.m
index 237a463..be188ad 100644
--- a/tools/cleanfigure.m
+++ b/src/cleanfigure.m
@@ -49,9 +49,9 @@ function cleanfigure(varargin)
meta.gca = [];
% Set up command line options.
- m2t.cmdOpts = matlab2tikzInputParser;
+ m2t.cmdOpts = m2tInputParser;
m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'minimumPointsDistance', 1.0e-10, @isnumeric);
- m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'handle', gcf, @isnumeric);
+ m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'handle', gcf, @ishandle);
% Finally parse all the elements.
m2t.cmdOpts = m2t.cmdOpts.parse(m2t.cmdOpts, varargin{:});
@@ -114,16 +114,49 @@ function indent = recursiveCleanup(meta, h, minimumPointsDistance, indent)
movePointsCloser(meta, h);
% Don't be too precise.
coarsenLine(meta, h, minimumPointsDistance);
+ elseif strcmpi(type, 'stair')
+ pruneOutsideBox(meta, h);
elseif strcmp(type, 'text')
- % Check if text is inside bounds by checking if the Extent rectangle
- % and the axes box overlap.
- xlim = get(meta.gca, 'XLim');
- ylim = get(meta.gca, 'YLim');
+ % Ensure units of type 'data' (default) and restore the setting later
+ units_original = get(h, 'Units');
+ set(h, '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.
+ x_lim = get(meta.gca, 'XLim');
+ y_lim = get(meta.gca, 'YLim');
+ z_lim = get(meta.gca, 'ZLim');
+ axLim = [x_lim; y_lim; z_lim];
+
+ pos = get(h, 'Position');
+ % If the axis is 2D, ignore the z component for the checks
+ if ~isAxis3D(meta.gca)
+ pos(3) = 0;
+ end
+ bPosInsideLim = ( pos' >= axLim(:,1) ) & ( pos' <= axLim(:,2) );
+
+ % 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(h, 'Extent');
- extent(3:4) = extent(1:2) + extent(3:4);
- overlap = xlim(1) < extent(3) && xlim(2) > extent(1) ...
- && ylim(1) < extent(4) && ylim(2) > extent(2);
- if ~overlap
+
+ % Restore original units (after reading all dimensions)
+ set(h, 'Units', units_original);
+
+ % This check makes sure the extent is only considered if it contains
+ % valid values. The 3D case returns a vector of NaNs.
+ if all(~isnan(extent))
+ % Extend the actual axis limits by the extent of the textbox so that
+ % the textbox is not discarded, if it overlaps the axis.
+ x_lim(1) = x_lim(1) - extent(3); % x-limit is extended by width
+ y_lim(1) = y_lim(1) - extent(4); % y-limit is extended by height
+ axLim = [x_lim; y_lim; z_lim];
+
+ bPosInsideLimExt = ( pos' >= axLim(:,1) ) & ( pos' <= axLim(:,2) );
+ bPosInsideLim = bPosInsideLim | bPosInsideLimExt;
+ end
+
+ if ~all(bPosInsideLim)
% Artificially disable visibility. m2t will check and skip.
set(h, 'Visible', 'off');
end
@@ -139,7 +172,13 @@ function pruneOutsideBox(meta, handle)
xData = get(handle, 'XData');
yData = get(handle, 'YData');
- zData = get(handle, 'ZData');
+
+ % Obtain zData, if available
+ if isprop(handle, 'ZData')
+ zData = get(handle, 'ZData');
+ else
+ zData = [];
+ end
if isempty(zData)
data = [xData(:), yData(:)];
@@ -234,33 +273,50 @@ function pruneOutsideBox(meta, handle)
return;
end
+% ==========================================================================
+function [bottomLeft, topLeft, bottomRight, topRight] = corners(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 out = segmentVisible(data, dataIsInBox, xLim, yLim)
% Given a bounding box {x,y}Lim, loop through all pairs of subsequent nodes
% in p and determine whether the line between the pair crosses the box.
-
n = size(data, 1);
out = false(n-1, 1);
+
+ [bottomLeft, topLeft, bottomRight, topRight] = corners(xLim, yLim);
+
for k = 1:n-1
- out(k) = (dataIsInBox(k) && all(isfinite(data(k+1,:)))) ... % one of the neighbors is inside the box
- || (dataIsInBox(k+1) && all(isfinite(data(k,:)))) ... % and the other is finite
- || segmentsIntersect(data(k,:), data(k+1,:), ...
- [xLim(1);yLim(1)], [xLim(1);yLim(2)]) ... % left border
- || segmentsIntersect(data(k,:), data(k+1,:), ...
- [xLim(1);yLim(1)], [xLim(2);yLim(1)]) ... % bottom border
- || segmentsIntersect(data(k,:), data(k+1,:), ...
- [xLim(2);yLim(1)], [xLim(2);yLim(2)]) ... % right border
- || segmentsIntersect(data(k,:), data(k+1,:), ...
- [xLim(1);yLim(2)], [xLim(2);yLim(2)]); % top border
+ this = data(k , :);
+ next = data(k+1, :);
+
+ % One of the neighbors is inside the box and the other is finite
+ nextVisible = (dataIsInBox(k+1) && all(isfinite(this)));
+ thisVisible = (dataIsInBox(k) && all(isfinite(next)));
+
+ % Check whether the line connecting this point and the next one
+ % intersects with any of the borders of the drawn axis
+ left = segmentsIntersect(this, next, bottomLeft , topLeft);
+ right = segmentsIntersect(this, next, bottomRight, topRight);
+ bottom = segmentsIntersect(this, next, bottomLeft , bottomRight);
+ top = segmentsIntersect(this, next, topLeft , topRight);
+
+ % The segment is visible when any of the following hold:
+ % - this point is visible in the axis and the next is finite
+ % - this point is finite and the next is visible in the axis
+ % - the segment connecting this and the next point crosses a border
+ out(k) = thisVisible || nextVisible || left || right || top || bottom;
end
-
end
% =========================================================================
function out = segmentsIntersect(X1, X2, X3, X4)
% Checks whether the segments X1--X2 and X3--X4 intersect.
lambda = crossLines(X1, X2, X3, X4);
out = all(lambda > 0.0) && all(lambda < 1.0);
- return
end
% =========================================================================
function coarsenLine(meta, handle, minimumPointsDistance)
@@ -348,6 +404,7 @@ function movePointsCloser(meta, handle)
return;
end
+ numberOfPoints = length(xData);
data = [xData(:), yData(:)];
xlim = get(meta.gca, 'XLim');
@@ -372,6 +429,7 @@ function movePointsCloser(meta, handle)
% Loop through all points which are to be included in the plot yet do not
% fit into the extended box, and gather the points by which they are to be
% replaced.
+
replaceIndices = find(~dataIsInLargeBox)';
m = length(replaceIndices);
r = cell(m, 1);
@@ -417,23 +475,47 @@ function movePointsCloser(meta, handle)
else
rep = r{k};
end
- if isempty(d) && ~isempty(rep) && lastEntryIsReplacement
- % The last entry was a replacment, and the first one now is.
- % Prepend a NaN.
- rep = [NaN(1, size(r{k}, 2)); ...
- rep];
+
+ % Don't draw line, if connecting line would be completely outside axis.
+ % We can check this using a line clipping algorithm.
+ % Illustration of the problem:
+ % http://www.cc.gatech.edu/grads/h/Hao-wei.Hsieh/Haowei.Hsieh/sec1_example.html
+ % This boils down to a line intersects line test, where all four lines of
+ % the axis rectangle need to be considered.
+ %
+ % First consider two easy cases:
+ % 1. This can't be the case, if last point was not replaced, because it is
+ % inside the axis limits ('lastEntryIsReplacement == 0').
+ % 2. This can't be the case, if the current point will not be replace,
+ % because it is inside the axis limits.
+ % ( (isempty(d) && ~isempty(rep) == 0 ).
+ if lastEntryIsReplacement && (isempty(d) && ~isempty(rep))
+ % Now check if the connecting line goes through the axis rectangle.
+ % OR: Only do this, if the original segment was not visible either
+ bLineOutsideAxis = ~segmentVisible(...
+ data([lastReplIndex,replaceIndices(k)],:), ...
+ [false;false], xlim, ylim);
+
+ % If line is completly outside the axis, don't draw the line. This is
+ % achieved by adding a NaN and necessary, because the two points are
+ % moved close to the axis limits and thus would afterwards show a
+ % connecting line in the axis.
+ if bLineOutsideAxis
+ rep = [NaN(1, size(r{k}, 2)); rep];
+ end
end
- % Add the data.
- if ~isempty(d)
- dataNew = [dataNew; ...
- d];
+
+ % Add the data, depending if it is a valid point or a replacement
+ if ~isempty(d) % Add current point from valid point 'd'
+ dataNew = [dataNew; d];
lastEntryIsReplacement = false;
end
- if ~isempty(rep)
- dataNew = [dataNew; ...
- rep];
+ if ~isempty(rep) % Add current point from replacement point 'rep'
+ dataNew = [dataNew; rep];
lastEntryIsReplacement = true;
end
+
+ % Store last replacement index
lastReplIndex = replaceIndices(k);
end
dataNew = [dataNew; ...
@@ -451,7 +533,7 @@ function movePointsCloser(meta, handle)
return;
end
% =========================================================================
-function xNew = moveToBox(x, xRef, xlim, ylim)
+function xNew = moveToBox(x, xRef, xLim, yLim)
% Takes a box defined by xlim, ylim, one point x and a reference point
% xRef.
% Returns the point xNew that sits on the line segment between x and xRef
@@ -462,26 +544,29 @@ function xNew = moveToBox(x, xRef, xlim, ylim)
% the smallest parameter alpha such that x + alpha*(xRef-x)
% sits on the boundary.
minAlpha = inf;
+ [bottomLeft, topLeft, bottomRight, topRight] = corners(xLim, yLim);
+ %TODO: clean up duplicate code below, possibly store lambda in matrix
+
% left boundary:
- lambda = crossLines(x, xRef, [xlim(1);ylim(1)], [xlim(1);ylim(2)]);
+ lambda = crossLines(x, xRef, bottomLeft, topLeft);
if 0.0 < lambda(2) && lambda(2) < 1.0 && abs(minAlpha) > abs(lambda(1))
minAlpha = lambda(1);
end
% bottom boundary:
- lambda = crossLines(x, xRef, [xlim(1);ylim(1)], [xlim(2);ylim(1)]);
+ lambda = crossLines(x, xRef, bottomLeft, bottomRight);
if 0.0 < lambda(2) && lambda(2) < 1.0 && abs(minAlpha) > abs(lambda(1))
minAlpha = lambda(1);
end
% right boundary:
- lambda = crossLines(x, xRef, [xlim(2);ylim(1)], [xlim(2);ylim(2)]);
+ lambda = crossLines(x, xRef, bottomRight, topRight);
if 0.0 < lambda(2) && lambda(2) < 1.0 && abs(minAlpha) > abs(lambda(1))
minAlpha = lambda(1);
end
% top boundary:
- lambda = crossLines(x, xRef, [xlim(1);ylim(2)], [xlim(2);ylim(2)]);
+ lambda = crossLines(x, xRef, topLeft, topRight);
if 0.0 < lambda(2) && lambda(2) < 1.0 && abs(minAlpha) > abs(lambda(1))
minAlpha = lambda(1);
end
@@ -512,12 +597,20 @@ function lambda = crossLines(X1, X2, X3, X4)
%
% for lambda and mu.
+ %TODO: why don't we use `\` instead of Cramer's rule?
+
rhs = X3(:) - X1(:);
- % Divide by det even if it's 0: Infs are returned.
+ % Don't divide by det(erminant), if it is zero. Directly return 'inf'.
+ % Otherwise this yields "warning: division by zero" in octave. See #664.
% A = [X2-X1, -(X4-X3)];
detA = -(X2(1)-X1(1))*(X4(2)-X3(2)) + (X2(2)-X1(2))*(X4(1)-X3(1));
- invA = [-(X4(2)-X3(2)), X4(1)-X3(1);...
- -(X2(2)-X1(2)), X2(1)-X1(1)] / detA;
+
+ if detA == 0
+ invA = inf;
+ else
+ invA = [-(X4(2)-X3(2)), X4(1)-X3(1);...
+ -(X2(2)-X1(2)), X2(1)-X1(1)] / detA;
+ end
lambda = invA * rhs;
end
diff --git a/tools/figure2dot.m b/src/figure2dot.m
similarity index 98%
rename from tools/figure2dot.m
rename to src/figure2dot.m
index 73df420..205872e 100644
--- a/tools/figure2dot.m
+++ b/src/figure2dot.m
@@ -35,6 +35,7 @@ function figure2dot(filename)
set(0, 'ShowHiddenHandles', 'on');
filehandle = fopen(filename, 'w');
+ finally_fclose_filehandle = onCleanup(@() fclose(filehandle));
% start printing
fprintf(filehandle, 'digraph simple_hierarchy {\n\n');
@@ -53,7 +54,6 @@ function figure2dot(filename)
% finish off
fprintf(filehandle, '}');
- fclose(filehandle);
set(0, 'ShowHiddenHandles', 'off');
end
diff --git a/src/matlab2tikzInputParser.m b/src/m2tInputParser.m
similarity index 98%
rename from src/matlab2tikzInputParser.m
rename to src/m2tInputParser.m
index 2c32ab8..30147cd 100644
--- a/src/matlab2tikzInputParser.m
+++ b/src/m2tInputParser.m
@@ -1,4 +1,4 @@
-function parser = matlab2tikzInputParser()
+function parser = m2tInputParser()
%MATLAB2TIKZINPUTPARSER Input parsing for matlab2tikz..
% This implementation exists because Octave is lacking one.
@@ -93,7 +93,7 @@ function p = deprecateParam (p, name, alternatives)
elseif ischar(alternatives)
alternatives = {alternatives}; % make cellstr
elseif ~iscellstr(alternatives)
- error('matlab2tikzInputParser:BadAlternatives',...
+ error('m2tInputParser:BadAlternatives',...
'Alternatives for a deprecated parameter must be a char or cellstr');
end
p.DeprecatedParameters.(name) = alternatives;
diff --git a/src/matlab2tikz.m b/src/matlab2tikz.m
index 33a40a4..a4fe22a 100644
--- a/src/matlab2tikz.m
+++ b/src/matlab2tikz.m
@@ -25,7 +25,7 @@ function matlab2tikz(varargin)
% MATLAB2TIKZ('showInfo',BOOL,...) turns informational output on or off.
% (default: true)
%
-% MATLAB2TIKZ('showWarning',BOOL,...) turns warnings on or off.
+% MATLAB2TIKZ('showWarnings',BOOL,...) turns warnings on or off.
% (default: true)
%
% MATLAB2TIKZ('imagesAsPng',BOOL,...) stores MATLAB(R) images as (lossless)
@@ -35,10 +35,16 @@ function matlab2tikz(varargin)
% 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. If LaTeX
-% source and the external files will reside in the same directory, this can
-% be set to '.'. (default: [])
+% 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
@@ -47,9 +53,16 @@ function matlab2tikz(varargin)
% 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: [])
%
@@ -106,7 +119,7 @@ function matlab2tikz(varargin)
% matlab2tikz('myfile.tex');
%
-% Copyright (c) 2008--2014, Nico Schlömer <nico.schloemer at gmail.com>
+% Copyright (c) 2008--2015, Nico Schlömer <nico.schloemer at gmail.com>
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
@@ -132,974 +145,1023 @@ function matlab2tikz(varargin)
% POSSIBILITY OF SUCH DAMAGE.
% Note:
-% This program is originally based on Paul Wagenaars' Matfig2PGF which itself
-% uses pure PGF as output format <paul at wagenaars.org>, see
-%
-% http://www.mathworks.com/matlabcentral/fileexchange/12962
+% This program was based on Paul Wagenaars' Matfig2PGF that can be
+% found on http://www.mathworks.com/matlabcentral/fileexchange/12962 .
+
+%% 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.cmdOpts = [];
+m2t.currentHandles = [];
+
+m2t.transform = []; % For hgtransform groups
+m2t.pgfplotsVersion = [1,3];
+m2t.name = 'matlab2tikz';
+m2t.version = '1.0.0';
+m2t.author = 'Nico Schlömer';
+m2t.authorEmail = 'nico.schloemer at gmail.com';
+m2t.years = '2008--2015';
+m2t.website = 'http://www.mathworks.com/matlabcentral/fileexchange/22022-matlab2tikz-matlab2tikz';
+VCID = VersionControlIdentifier();
+m2t.versionFull = strtrim(sprintf('v%s %s', m2t.version, VCID));
+
+m2t.tol = 1.0e-15; % numerical tolerance (e.g. used to test equality of doubles)
+m2t.imageAsPngNo = 0;
+m2t.dataFileNo = 0;
+m2t.quiverId = 0; % identification flag for quiver plot styles
+m2t.automaticLabelIndex = 0;
+
+% definition of color depth
+m2t.colorDepth = 48; %[bit] RGB color depth (typical values: 24, 30, 48)
+m2t.colorPrecision = 2^(-m2t.colorDepth/3);
+m2t.colorFormat = sprintf('%%0.%df',ceil(-log10(m2t.colorPrecision)));
+
+% 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', ...
+ '\\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', true, @islogical);
+
+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, '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);
+
+% 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.
%
-% In an attempt to simplify and extend things, the idea for matlab2tikz has
-% emerged. The goal is to provide the user with a clean interface between the
-% very handy figure creation in MATLAB and the powerful means that TikZ with
-% Pgfplots has to offer.
-%
-% =========================================================================
- % Check if we are in MATLAB or Octave.
- [m2t.env, m2t.envVersion] = getEnvironment();
-
- minimalVersion = struct('MATLAB', struct('name','2008b', 'num',[7 7]), ...
- 'Octave', struct('name','3.4.0', 'num',[3 4 0]));
- checkDeprecatedEnvironment(m2t, minimalVersion);
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- m2t.cmdOpts = [];
-
- m2t.currentHandles = [];
-
- % For hgtransform groups.
- m2t.transform = [];
- m2t.pgfplotsVersion = [1,3];
- m2t.name = 'matlab2tikz';
- m2t.version = '0.4.7';
- m2t.author = 'Nico Schlömer';
- m2t.authorEmail = 'nico.schloemer at gmail.com';
- m2t.years = '2008--2014';
- m2t.website = 'http://www.mathworks.com/matlabcentral/fileexchange/22022-matlab2tikz';
- VCID = VersionControlIdentifier();
- m2t.versionFull = strtrim(sprintf('v%s %s', m2t.version, VCID));
-
- m2t.tol = 1.0e-15; % global round-off tolerance;
- % used, for example, in equality test for doubles
- m2t.imageAsPngNo = 0;
- m2t.dataFileNo = 0;
-
- % definition of color depth
- m2t.colorDepth = 48; %[bit] RGB color depth (typical values: 24, 30, 48)
- m2t.colorPrecision = 2^(-m2t.colorDepth/3);
- m2t.colorFormat = sprintf('%%0.%df',ceil(-log10(m2t.colorPrecision)));
-
- % the actual contents of the TikZ file go here
- m2t.content = struct('name', [], ...
- 'comment', [], ...
- 'options', {cell(0,2)}, ...
- 'content', {cell(0)}, ...
- 'children', {cell(0)} ...
- );
- m2t.preamble = sprintf(['\\usepackage{pgfplots}\n', ...
- '\\usepackage{grffile}\n', ...
- '\\pgfplotsset{compat=newest}\n', ...
- '\\usetikzlibrary{plotmarks}\n', ...
- '\\usepackage{amsmath}\n']);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % scan the options
- ipp = matlab2tikzInputParser;
-
- 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', true, @islogical);
-
- 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, '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, '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, 'relativeDataPath', [], @ischar);
-
- % 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 plots, and this
- % function does the job.
- %
- % What is a "large" array?
- % TeX parser buffer is buf_size=200000 char on Mac TeXLive, let's say
- % 100000 to be on the safe side.
- % 1 point is represented by 25 characters (estimation): 2 coordinates (10
- % char), 2 brackets, commma 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');
-
- % Finally parse all the elements.
- ipp = ipp.parse(ipp, varargin{:});
-
- m2t.cmdOpts = ipp; % store the input parser back into the m2t data struct
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % inform users of potentially dangerous options
- if m2t.cmdOpts.Results.parseStringsAsMath
- userInfo(m2t, ['\n==========================================================================\n', ...
- 'You are using the parameter ''parseStringsAsMath''.\n', ...
- 'This may produce undesirable string output. For full control over output\n', ...
- 'strings please set the parameter ''parseStrings'' to false.\n', ...
- '==========================================================================']);
- end
-
-
- % The following color RGB-values which will need to be defined.
- % 'extraRgbColorNames' contains their designated names, 'extraRgbColorSpecs'
- % their specifications.
- [m2t.extraRgbColorNames, m2t.extraRgbColorSpecs] = ...
- dealColorDefinitions(m2t.cmdOpts.Results.extraColors);
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % shortcut
- m2t.ff = m2t.cmdOpts.Results.floatFormat;
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % add global elements
- if isempty(m2t.cmdOpts.Results.figurehandle)
+% 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');
+
+%% Finally parse all the arguments
+ipp = ipp.parse(ipp, varargin{:});
+m2t.cmdOpts = ipp; % store the input parser 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.']);
+
+% The following color RGB-values which will need to be defined.
+% 'extraRgbColorNames' contains their designated names, 'extraRgbColorSpecs'
+% their specifications.
+[m2t.extraRgbColorNames, m2t.extraRgbColorSpecs] = ...
+ dealColorDefinitions(m2t.cmdOpts.Results.extraColors);
+
+%% shortcut
+m2t.ff = m2t.cmdOpts.Results.floatFormat;
+
+%% add global elements
+if isempty(m2t.cmdOpts.Results.figurehandle)
error('matlab2tikz:figureNotFound','MATLAB figure not found.');
- end
- m2t.currentHandles.gcf = m2t.cmdOpts.Results.figurehandle;
- if m2t.cmdOpts.Results.colormap
- m2t.currentHandles.colormap = m2t.cmdOpts.Results.colormap;
- else
- m2t.currentHandles.colormap = get(m2t.currentHandles.gcf, 'colormap');
- end
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % handle output file handle/file name
- if ~isempty(m2t.cmdOpts.Results.filehandle)
- fid = m2t.cmdOpts.Results.filehandle;
- fileWasOpen = true;
- if ~isempty(m2t.cmdOpts.Results.filename)
- userWarning(m2t, ...
- 'File handle AND file name for output given. File handle used, file name discarded.')
- end
- else
- fileWasOpen = false;
- % set filename
- if ~isempty(m2t.cmdOpts.Results.filename)
- filename = m2t.cmdOpts.Results.filename;
- else
- [filename, pathname] = uiputfile({'*.tex;*.tikz'; '*.*'}, 'Save File');
- filename = fullfile(pathname, filename);
- end
+end
+m2t.currentHandles.gcf = m2t.cmdOpts.Results.figurehandle;
+if m2t.cmdOpts.Results.colormap
+ m2t.currentHandles.colormap = m2t.cmdOpts.Results.colormap;
+else
+ m2t.currentHandles.colormap = get(m2t.currentHandles.gcf, 'colormap');
+end
- % open the file for writing
- fid = fileOpenForWrite(m2t, filename);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- m2t.tikzFileName = fopen(fid);
+%% 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.cmdOpts.Results.relativeDataPath)
+ if ~isempty(m2t.cmdOpts.Results.relativePngPath)
+ %NOTE: eventually break backwards compatibility of relative PNG path
+ m2t.relativeDataPath = m2t.cmdOpts.Results.relativePngPath;
+ userWarning(m2t, ['Using "relativePngPath" for "relativeDataPath".', ...
+ ' This will stop working in a future release.']);
+ else
+ m2t.relativeDataPath = m2t.cmdOpts.Results.relativeDataPath;
+ end
+else
+ m2t.relativeDataPath = m2t.cmdOpts.Results.relativeDataPath;
+end
+if isempty(m2t.cmdOpts.Results.dataPath)
+ m2t.dataPath = fileparts(m2t.tikzFileName);
+else
+ m2t.dataPath = m2t.cmdOpts.Results.dataPath;
+end
- if m2t.cmdOpts.Results.automaticLabels
- m2t.automaticLabelIndex = 0;
- end
- % 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.cmdOpts.Results.relativeDataPath)
- if ~isempty(m2t.cmdOpts.Results.relativePngPath)
- %NOTE: eventually break backwards compatibility of relative PNG path
- m2t.relativeDataPath = m2t.cmdOpts.Results.relativePngPath;
- userWarning(m2t, ['Using "relativePngPath" for "relativeDataPath".', ...
- ' This will stop working in a future release.']);
- else
- m2t.relativeDataPath = fileparts(m2t.tikzFileName);
- end
- else
- m2t.relativeDataPath = m2t.cmdOpts.Results.relativeDataPath;
- end
+userInfo(m2t, ['(To disable info messages, pass [''showInfo'', false] to matlab2tikz.)\n', ...
+ '(For all other options, type ''help matlab2tikz''.)\n']);
- 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.name, m2t.versionFull)
- userInfo(m2t, '\nThis is %s %s.\n', m2t.name, m2t.versionFull)
+%% Check for a new matlab2tikz version outside version control
+if m2t.cmdOpts.Results.checkForUpdates && isempty(VCID)
+ isUpdateInstalled = m2tUpdater(...
+ m2t.name, ...
+ m2t.website, ...
+ m2t.version, ...
+ m2t.cmdOpts.Results.showInfo, ...
+ getEnvironment...
+ );
+ % Terminate conversion if update was successful (the user is notified
+ % by the updater)
+ if isUpdateInstalled, return, end
+end
- % Conditionally check for a new matlab2tikz version outside version control
- if m2t.cmdOpts.Results.checkForUpdates && isempty(VCID)
- updater(m2t.name, ...
- m2t.website, ...
- m2t.version, ...
- m2t.cmdOpts.Results.showInfo, ...
- m2t.env)
- end
+%% print some version info to the screen
+versionInfo = ['The latest updates can be retrieved from\n' ,...
+ ' %s\n' ,...
+ 'where you can also make suggestions and rate %s.\n' ,...
+ 'For usage instructions, bug reports, the latest ' ,...
+ 'development versions and more, see\n' ,...
+ ' https://github.com/matlab2tikz/matlab2tikz,\n' ,...
+ ' https://github.com/matlab2tikz/matlab2tikz/wiki,\n' ,...
+ ' https://github.com/matlab2tikz/matlab2tikz/issues.\n'];
+userInfo(m2t, versionInfo, m2t.website, m2t.name);
+
+%% Save the figure as TikZ to file
+saveToFile(m2t, fid, fileWasOpen);
+end
+% ==============================================================================
+function [m2t, fid, fileWasOpen] = openFileForOutput(m2t)
+% opens the output file and/or show a dialog to select one
+if ~isempty(m2t.cmdOpts.Results.filehandle)
+ fid = m2t.cmdOpts.Results.filehandle;
+ fileWasOpen = true;
+ if ~isempty(m2t.cmdOpts.Results.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.cmdOpts.Results.filename)
+ filename = m2t.cmdOpts.Results.filename;
+ else
+ [filename, pathname] = uiputfile({'*.tex;*.tikz'; '*.*'}, 'Save File');
+ filename = fullfile(pathname, filename);
+ end
+ m2t.tikzFileName = filename;
+end
- % print some version info to the screen
- versionInfo = ['The latest updates can be retrieved from\n' ,...
- ' %s\n' ,...
- 'where you can also make suggestions and rate %s.\n' ,...
- 'For usage instructions, bug reports, the latest ' ,...
- 'development versions and more, see\n' ,...
- ' https://github.com/nschloe/matlab2tikz,\n' ,...
- ' https://github.com/nschloe/matlab2tikz/wiki,\n' ,...
- ' https://github.com/nschloe/matlab2tikz/issues.\n'
- ];
- userInfo(m2t, versionInfo, m2t.website, m2t.name);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Save the figure as pgf to file -- here's where the work happens
- saveToFile(m2t, fid, fileWasOpen);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-end
-% =========================================================================
+end
+% ==============================================================================
function l = filenameValidation(x, p)
% is the filename argument NOT another keyword?
- l = ischar(x) && ~any(strcmp(x,p.Parameters));
+ 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?
+% is the filehandle the handle to an opened file?
l = isnumeric(x) && any(x==fopen('all'));
end
-% =========================================================================
-function l = isCellOrChar(x)
- l = iscell(x) || ischar(x);
+% ==============================================================================
+function bool = isCellOrChar(x)
+ bool = iscell(x) || ischar(x);
end
-% =========================================================================
-function isValid = iscolordefinitions(colors)
- isRGBTuple = @(c)( numel(c) == 3 && all(0<=c & c<=1) );
+% ==============================================================================
+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}) );
- isValid = iscell(colors) && all(cellfun(isValidEntry, colors));
+ bool = iscell(colors) && all(cellfun(isValidEntry, colors));
end
-%==========================================================================
+% ==============================================================================
function fid = fileOpenForWrite(m2t, filename)
- switch m2t.env
- case 'MATLAB'
- fid = fopen(filename, ...
- 'w', ...
- 'native', ...
- m2t.cmdOpts.Results.encoding ...
- );
- case 'Octave'
- fid = fopen(filename, 'w');
- otherwise
- errorUnknownEnvironment();
- end
+ encoding = switchMatOct({'native', m2t.cmdOpts.Results.encoding}, {});
+ fid = fopen(filename, 'w', encoding{:});
if fid == -1
error('matlab2tikz:fileOpenError', ...
- 'Unable to open file ''%s'' for writing.', ...
- filename);
+ '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
+% 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.
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % enter plot recursion --
- % It is important to turn hidden handles on, as visible lines (such as the
- % axes in polar plots, for example), are otherwise hidden from their
- % parental handles (and can hence not be discovered by matlab2tikz).
- % With ShowHiddenHandles 'on', there is no escape. :)
- set(0, 'ShowHiddenHandles', 'on');
-
- % get all axes handles
- fh = m2t.currentHandles.gcf;
- axesHandles = findobj(fh, 'type', 'axes');
-
- tagKeyword = switchMatOct(m2t, 'Tag', 'tag');
- if ~isempty(axesHandles)
- % Find all legend handles. This is MATLAB-only.
- legendHandleIdx = strcmp(get(axesHandles, tagKeyword), 'legend');
- m2t.legendHandles = axesHandles(legendHandleIdx);
- % Remove all legend handles as they are treated separately.
- axesHandles = axesHandles(~legendHandleIdx);
- end
+% Save the figure as TikZ to a file. All other routines are called from here.
+
+ % It is important to turn hidden handles on, as visible lines (such as the
+ % axes in polar plots, for example), are otherwise hidden from their
+ % parental handles (and can hence not be discovered by matlab2tikz).
+ % With ShowHiddenHandles 'on', there is no escape. :)
+ set(0, 'ShowHiddenHandles', 'on');
+
+ % get all axes handles
+ [m2t, axesHandles] = findPlotAxes(m2t, m2t.currentHandles.gcf);
+
+ % Turn around the handles vector to make sure that plots that appeared
+ % first also appear first in the vector. This has effects on the alignment
+ % and the order in which the plots appear in the final TikZ file.
+ % In fact, this is not really important but makes things more 'natural'.
+ axesHandles = axesHandles(end:-1:1);
+
+ % Alternative Positioning of axes.
+ % Select relevant Axes and draw them.
+ [m2t, axesBoundingBox] = getRelevantAxes(m2t, axesHandles);
+
+ m2t.axesBoundingBox = axesBoundingBox;
+ m2t.axesContainers = {};
+ for relevantAxesHandle = m2t.relevantAxesHandles(:)'
+ m2t = drawAxes(m2t, relevantAxesHandle);
+ end
- colorbarKeyword = switchMatOct(m2t, 'Colorbar', 'colorbar');
- if ~isempty(axesHandles)
- % Find all colorbar handles. This is MATLAB-only.
- cbarHandleIdx = strcmp(get(axesHandles, tagKeyword), colorbarKeyword);
- m2t.cbarHandles = axesHandles(cbarHandleIdx);
- % Remove all legend handles as they are treated separately.
- axesHandles = axesHandles(~cbarHandleIdx);
- else
- m2t.cbarHandles = [];
- end
+ % Handle color bars.
+ for cbar = m2t.cbarHandles(:)'
+ m2t = handleColorbar(m2t, cbar);
+ end
- % Turn around the handles vector to make sure that plots that appeared
- % first also appear first in the vector. This has effects on the alignment
- % and the order in which the plots appear in the final TikZ file.
- % In fact, this is not really important but makes things more 'natural'.
- axesHandles = axesHandles(end:-1:1);
-
- % find alignments
- [relevantAxesHandles, alignmentOptions, IX] = alignSubPlots(m2t, axesHandles);
- m2t.axesContainers = {};
- for ix = IX(:)'
- m2t = drawAxes(m2t, relevantAxesHandles(ix), alignmentOptions(ix));
- end
+ % Draw annotations
+ m2t = drawAnnotations(m2t);
- % Handle color bars.
- for cbar = m2t.cbarHandles(:)'
- m2t = handleColorbar(m2t, cbar);
- end
+ % Add all axes containers to the file contents.
+ for axesContainer = m2t.axesContainers
+ m2t.content = addChildren(m2t.content, axesContainer);
+ end
- % Add all axes containers to the file contents.
- for axesContainer = m2t.axesContainers
- m2t.content = addChildren(m2t.content, axesContainer);
- end
+ set(0, 'ShowHiddenHandles', 'off');
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % actually print the stuff
+ minimalPgfplotsVersion = formatPgfplotsVersion(m2t.pgfplotsVersion);
+
+ m2t.content.comment = sprintf('This file was created by %s.\n', m2t.name);
+
+ if m2t.cmdOpts.Results.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.website, m2t.name ) ...
+ ];
+ end
- set(0, 'ShowHiddenHandles', 'off');
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % actually print the stuff
- minimalPgfplotsVersion = formatPgfplotsVersion(m2t, m2t.pgfplotsVersion);
-
- environment = sprintf('%s %s',m2t.env, m2t.envVersion);
- m2t.content.comment = sprintf(['This file was created by %s %s running on %s.\n', ...
- 'Copyright (c) %s, %s <%s>\n', ...
- 'All rights reserved.\n',...
- 'Minimal pgfplots version: %s\n'], ...
- m2t.name, m2t.versionFull, environment, ...
- m2t.years, m2t.author, m2t.authorEmail,...
- minimalPgfplotsVersion);
-
- if m2t.cmdOpts.Results.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.website, m2t.name ) ...
- ];
- end
+ userInfo(m2t, 'You will need pgfplots version %s or newer to compile the TikZ output.',...
+ minimalPgfplotsVersion);
- userInfo(m2t, 'You will need pgfplots version %s or newer to compile the TikZ output.',...
- minimalPgfplotsVersion);
+ % Add custom comment.
+ if ~isempty(m2t.cmdOpts.Results.tikzFileComment)
+ m2t.content.comment = [m2t.content.comment, ...
+ sprintf('\n%s\n', m2t.cmdOpts.Results.tikzFileComment)
+ ];
+ end
- % Add custom comment.
- if ~isempty(m2t.cmdOpts.Results.tikzFileComment)
- m2t.content.comment = [m2t.content.comment, ...
- sprintf('\n%s\n', m2t.cmdOpts.Results.tikzFileComment)
- ];
- end
+ m2t.content.name = 'tikzpicture';
- m2t.content.name = 'tikzpicture';
+ % Add custom TikZ options if any given.
+ m2t.content.options = opts_append_userdefined(m2t.content.options, ...
+ m2t.cmdOpts.Results.extraTikzpictureOptions);
- % Add custom TikZ options if any given.
- if ~isempty(m2t.cmdOpts.Results.extraTikzpictureOptions)
- if ischar(m2t.cmdOpts.Results.extraTikzpictureOptions)
- m2t.cmdOpts.Results.extraTikzpictureOptions = ...
- {m2t.cmdOpts.Results.extraTikzpictureOptions};
- end
- for k = 1:length(m2t.cmdOpts.Results.extraTikzpictureOptions)
- m2t.content.options = ...
- addToOptions(m2t.content.options, ...
- m2t.cmdOpts.Results.extraTikzpictureOptions{k}, []);
- end
- end
+ m2t.content.colors = generateColorDefinitions(m2t.extraRgbColorNames, ...
+ m2t.extraRgbColorSpecs, m2t.colorFormat);
- % Don't forget to define the colors.
- if ~isempty(m2t.extraRgbColorNames)
- m2t.content.colors = sprintf('%%\n%% defining custom colors\n');
- for k = 1:length(m2t.extraRgbColorNames)
- % make sure to append with '%' to avoid spacing woes
- ff = m2t.colorFormat;
- m2t.content.colors = [m2t.content.colors, ...
- sprintf(['\\definecolor{%s}{rgb}{', ff, ',', ff, ',', ff,'}%%\n'], ...
- m2t.extraRgbColorNames{k}', m2t.extraRgbColorSpecs{k})];
- end
- m2t.content.colors = [m2t.content.colors sprintf('%%\n')];
- end
+ % 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,
- if ~isempty(m2t.content.comment)
- fprintf(fid, '%% %s\n', ...
- strrep(m2t.content.comment, sprintf('\n'), sprintf('\n%% ')));
- end
+ % Finally print it to the file
+ addComments(fid, m2t.content.comment);
+ addStandalone(m2t, fid, 'preamble');
+ addCustomCode(fid, '', m2t.cmdOpts.Results.extraCode, '');
+ addStandalone(m2t, fid, 'begin');
- if m2t.cmdOpts.Results.standalone
- fprintf(fid, '\\documentclass[tikz]{standalone}\n%s\n', m2t.preamble);
- end
+ printAll(m2t, m2t.content, fid); % actual plotting happens here
+
+ addCustomCode(fid, '\n', m2t.cmdOpts.Results.extraCodeAtEnd, '');
- % Add custom code.
- if ~isempty(m2t.cmdOpts.Results.extraCode)
- if ischar(m2t.cmdOpts.Results.extraCode)
- fprintf(fid, '%s\n', m2t.cmdOpts.Results.extraCode);
- elseif iscellstr(m2t.cmdOpts.Results.extraCode)
- for str = m2t.cmdOpts.Results.extraCode(:)'
- fprintf(fid, '%s\n', str{1});
+ addStandalone(m2t, fid, 'end');
+end
+% ==============================================================================
+function addStandalone(m2t, fid, part)
+% writes a part of a standalone LaTeX file definition
+if m2t.cmdOpts.Results.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(names, specs, colorFormat)
+% output the color definitions to LaTeX
+ str = '';
+ ff = colorFormat;
+ if ~isempty(names)
+ m2t.content.colors = sprintf('%%\n%% defining custom colors\n');
+ for k = 1:length(names)
+ % make sure to append with '%' to avoid spacing woes
+ str = [str, ...
+ sprintf(['\\definecolor{%s}{rgb}{', ff, ',', ff, ',', ff,'}%%\n'], ...
+ names{k}', specs{k})];
+ end
+ str = [str 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 = findobj(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 = findobj(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 = findobj(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
- else
- error('matlab2tikz:saveToFile', 'Need str or cellstr.');
- end
- end
+ end
+ m2t.cbarHandles = m2t.cbarHandles(:)';
+ idx = ~ismember(axesHandles, m2t.cbarHandles);
+ axesHandles = axesHandles(idx);
+ else
+ m2t.cbarHandles = [];
+ end
- if m2t.cmdOpts.Results.standalone
- fprintf(fid, '\\begin{document}\n');
- end
- % printAll() handles the actual figure plotting.
- printAll(m2t, m2t.content, fid);
- if m2t.cmdOpts.Results.standalone
- fprintf(fid, '\n\\end{document}');
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % Remove scribe layer holding annotations (MATLAB < R2014b)
+ m2t.scribeLayer = findobj(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 = get(h, 'Children');
+
+ % prepare cell array of pgfEnvironments
+ pgfEnvironments = cell(0);
+
+ % 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)'
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [m2t, legendString, interpreter] = findLegendInformation(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 {'uitoolbar', 'uimenu', 'uicontextmenu', 'uitoggletool',...
+ 'uitogglesplittool', 'uipushtool', 'hgjavacomponent'}
+ % don't to anything for these handles and its children
+ str = '';
+
+ case ''
+ warning('matlab2tikz:NoChildren',...
+ ['No children found for handle %d. ',...
+ 'Carrying on as if nothing happened'], double(h));
+
+ otherwise
+ error('matlab2tikz:handleAllChildren', ...
+ 'I don''t know how to handle this object: %s\n', ...
+ get(child, 'Type'));
- % close the file if necessary
- if ~fileWasOpen
- fclose(fid);
- end
+ end
+
+ str = addLegendInformation(m2t, str, legendString, interpreter);
+
+ % append the environment
+ pgfEnvironments{end+1} = str;
+ end
end
-% =========================================================================
-function [m2t, pgfEnvironments] = handleAllChildren(m2t, handle)
- % Draw all children of a graphics object (if they need to be drawn).
- children = get(handle, 'Children');
-
- % prepare cell array of pgfEnvironments
- pgfEnvironments = cell(0);
-
- % 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)'
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % First of all, check if 'child' is referenced in a legend.
- % If yes, some plot types may want to add stuff (e.g. 'forget plot').
- % Add '\addlegendentry{...}' then after the plot.
- legendString = [];
- m2t.currentHandleHasLegend = false;
-
- % Check if current handle is referenced in a legend.
- switch m2t.env
- case 'MATLAB'
- if ~isempty(m2t.legendHandles)
- % Make sure that m2t.legendHandles is a row vector.
- for legendHandle = m2t.legendHandles(:)'
- ud = get(legendHandle, 'UserData');
- % In Octave, the field name is 'handle'. Well, whatever.
- % fieldName = switchMatOct(m2t, 'handles', 'handle');
- k = find(child == ud.handles);
- if isempty(k)
- % Lines of error bar plots are not referenced
- % directly in legends as an error bars plot contains
- % two "lines": the data and the deviations. Here, the
- % legends refer to the specgraph.errorbarseries
- % handle which is 'Parent' to the line handle.
- k = find(get(child,'Parent') == ud.handles);
- end
- if ~isempty(k)
- % Legend entry found. Add it to the plot.
- m2t.currentHandleHasLegend = true;
- interpreter = get(legendHandle, 'Interpreter');
- legendString = ud.lstrings(k);
- end
- end
- end
- case 'Octave'
- % Octave associates legends with axes, not with (line) plot.
- % The variable m2t.gcaHasLegend is set in drawAxes().
- m2t.currentHandleHasLegend = ~isempty(m2t.gcaAssociatedLegend);
- interpreter = get(m2t.gcaAssociatedLegend, 'interpreter');
- if isfield(get(child), 'displayname')
- legendString = get(child, 'displayname');
- end
- otherwise
- errorUnknownEnvironment();
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- switch get(child, 'Type')
- % 'axes' environments are treated separately.
+% ==============================================================================
+function [m2t, legendString, interpreter] = findLegendInformation(m2t, child)
+% Check if 'child' is referenced in a legend.
+% If yes, some plot types may want to add stuff (e.g. 'forget plot').
+% Add '\addlegendentry{...}' then after the plot.
+legendString = '';
+interpreter = '';
+hasLegend = false;
+
+if isempty(child)
+ return; % an empty (i.e. non-existent) child cannot have a legend entry
+end
+
+% Check if current handle is referenced in a legend.
+switch getEnvironment
+ case 'MATLAB'
+ [legendString, interpreter, hasLegend] = findLegendInfoMATLAB(m2t, child);
+
+ case 'Octave'
+ % Octave does not store a reference to the legend entry in the
+ % plotted objects. It references the plotted objects in reverse,
+ % in the legend's 'deletefcn' property.
+ % The variable m2t.gcaAssociatedLegend is set in drawAxes().
+ if ~isempty(m2t.gcaAssociatedLegend)
+ delfun = get(m2t.gcaAssociatedLegend,'deletefcn');
+ legendEntryPeers = delfun{6}; % See set(hlegend, "deletefcn", {@deletelegend2, ca, [], [], t1, hplots}); in legend.m
+ hasLegend = ismember(child, legendEntryPeers);
+ interpreter = get(m2t.gcaAssociatedLegend, 'interpreter');
+ legendString = getOrDefault(child,'displayname','');
+ end
- case 'line'
- [m2t, str] = drawLine(m2t, child);
+ otherwise
+ errorUnknownEnvironment();
+end
- case 'patch'
- [m2t, str] = drawPatch(m2t, child);
+% split string to cell, if newline character '\n' (ASCII 10) is present
+delimeter = sprintf('\n');
+legendString = regexp(legendString,delimeter,'split');
- case 'image'
- [m2t, str] = drawImage(m2t, child);
+m2t.currentHandleHasLegend = hasLegend && ~isempty(legendString);
+end
+% ==============================================================================
+function [legendString, interpreter, hasLegend] = findLegendInfoMATLAB(m2t, child)
+% finds the legend info in MATLAB (only!). Eventually this could be merged back
+% into `findLegendInformation`
+ legendString = '';
+ interpreter = '';
+ hasLegend = false;
+ %FIXME: this part (e.g. fall back objects) should be restructured.
+ for legendHandle = m2t.legendHandles(:)'
+ ud = get(legendHandle, 'UserData');
+ if isfield(ud, 'handles')
+ plotChildren = ud.handles;
+ else
+ plotChildren = getOrDefault(legendHandle, 'PlotChildren', []);
+ end
+ k = find(child == plotChildren);
+ if isempty(k)
+ % Lines of error bar plots are not referenced
+ % directly in legends as an error bars plot contains
+ % two "lines": the data and the deviations. Here, the
+ % legends refer to the specgraph.errorbarseries
+ % handle which is 'Parent' to the line handle.
+ k = find(get(child,'Parent') == plotChildren);
+ end
+ if ~isempty(k)
+ % Legend entry found. Add it to the plot.
+ hasLegend = true;
+ interpreter = get(legendHandle, 'Interpreter');
+ if ~isempty(ud) && isfield(ud,'strings')
+ legendString = ud.lstrings(k);
+ else
+ legendString = get(child, 'DisplayName');
+ end
+ end
+ end
+end
+% ==============================================================================
+function str = addLegendInformation(m2t, str, legendString, interpreter)
+% Add legend after the plot data.
+% The test for ischar(str) && ~isempty(str) is a workaround for hggroups;
+% the output might not necessarily be a string, but a cellstr.
+ if ischar(str) && ~isempty(str) && m2t.currentHandleHasLegend
+ c = prettyPrint(m2t, legendString, interpreter);
+ % We also need a legend alignment option to make multiline
+ % legend entries work. This is added by default in getLegendOpts().
+ str = [str, sprintf('\\addlegendentry{%s};\n\n', join(m2t, c, '\\'))];
+ end
+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.
- case 'hggroup'
- [m2t, str] = drawHggroup(m2t, child);
+ assertRegularAxes(handle);
- 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 = [];
+ % 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.axesContainers{end+1} = struct('handle', handle, ...
+ 'name', '', ...
+ 'comment', [], ...
+ 'options', {opts_new()}, ...
+ 'content', {cell(0)}, ...
+ 'children', {cell(0)});
- case 'surface'
- [m2t, str] = drawSurface(m2t, child);
+ % update gca
+ m2t.currentHandles.gca = handle;
- case 'text'
- [m2t, str] = drawText(m2t, child);
+ % 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.axesContainers{end}.is3D = isAxis3D(handle);
- case 'rectangle'
- [m2t, str] = drawRectangle(m2t, child);
+ % Flag if axis contains barplot
+ m2t.axesContainers{end}.barAddedAxisOption = false;
- case {'uitoolbar', 'uimenu', 'uicontextmenu', 'uitoggletool',...
- 'uitogglesplittool', 'uipushtool', 'hgjavacomponent'}
- % don't to anything for these handles and its children
- str = [];
+ m2t.gcaAssociatedLegend = getAssociatedLegend(m2t, handle);
- otherwise
- error('matlab2tikz:handleAllChildren', ...
- 'I don''t know how to handle this object: %s\n', ...
- get(child, 'Type'));
+ m2t = retrievePositionOfAxes(m2t, handle);
- end
+ m2t = addAspectRatioOptionsOfAxes(m2t, handle);
- % Add legend after the plot data.
- % The test for ischar(str) && ~isempty(str) is a workaround for hggroups;
- % the output might not necessarily be a string, but a cellstr.
- if ischar(str) && ~isempty(str) ...
- && m2t.currentHandleHasLegend && ~isempty(legendString)
- c = prettyPrint(m2t, legendString, interpreter);
- % We also need a legend alignment option to make multiline
- % legend entries work. This is added by default in getLegendOpts().
- str = [str, ...
- sprintf('\\addlegendentry{%s};\n\n', join(m2t, c, '\\'))]; %#ok
- end
+ % 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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'point meta min', sprintf(m2t.ff, clim(1)));
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'point meta max', sprintf(m2t.ff, clim(2)));
+ end
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % Recurse into the children of this environment.
+ [m2t, childrenEnvs] = handleAllChildren(m2t, handle);
+ m2t.axesContainers{end} = addChildren(m2t.axesContainers{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.axesContainers{end}.options = opts_merge(m2t.axesContainers{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. Well.
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'hide axis', []);
+ % % An invisible axes container *can* have visible children, so don't
+ % % immediately bail out here.
+ % children = get(handle, 'Children');
+ % for child = children(:)'
+ % if isVisible(child)
+ % % If the axes contain something that's visible, add an invisible
+ % % axes pair.
+ % m2t.axesContainers{end}.name = 'axis';
+ % m2t.axesContainers{end}.options = {m2t.axesContainers{end}.options{:}, ...
+ % 'hide x axis', 'hide y axis'};
+ % m2t.axesContainers{end}.comment = getTag(handle);
+ % break;
+ % end
+ % end
+ % % recurse into the children of this environment
+ % [m2t, childrenEnvs] = handleAllChildren(m2t, handle);
+ % m2t.axesContainers{end} = addChildren(m2t.axesContainers{end}, childrenEnvs);
+ % return
+ end
+ m2t.axesContainers{end}.name = 'axis';
- % append the environment
- pgfEnvironments{end+1} = str;
- end
+ m2t = drawBackgroundOfAxes(m2t, handle);
+ m2t = drawTitleOfAxes(m2t, handle);
+ m2t = drawBoxAndLineLocationsOfAxes(m2t, handle);
+ m2t = drawGridOfAxes(m2t, handle);
+ m2t = drawLegendOptionsOfAxes(m2t, handle);
+ m2t.axesContainers{end}.options = opts_append_userdefined(...
+ m2t.axesContainers{end}.options, m2t.cmdOpts.Results.extraAxisOptions);
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
+% ==============================================================================
+function m2t = drawGridOfAxes(m2t, handle)
+% draws the grids of an axes
+%TODO: has{XYZ}Grid is always false without a good reason
+ hasXGrid = false;
+ hasYGrid = false;
+ hasZGrid = false;
+ if hasXGrid || hasYGrid || hasZGrid
+ matlabGridLineStyle = get(handle, 'GridLineStyle');
+ % Take over the grid line style in any case when in strict mode.
+ % If not, don't add anything in case of default line grid line style
+ % and effectively take Pgfplots' default.
+ defaultMatlabGridLineStyle = ':';
+ if m2t.cmdOpts.Results.strict ...
+ || ~strcmpi(matlabGridLineStyle,defaultMatlabGridLineStyle)
+ gls = translateLineStyle(matlabGridLineStyle);
+ axisGridOpts = {'grid style', sprintf('{%s}', gls)};
+ m2t.axesContainers{end}.options = cat(1, ...
+ m2t.axesContainers{end}.options,...
+ axisGridOpts);
+ end
+ else
+ % 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.cmdOpts.Results.strict
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'axis on top', []);
+ end
+ % FIXME: axis background, axis grid, main, axis ticks, axis lines, axis tick labels, axis descriptions, axis foreground
+ end
end
-% =========================================================================
-function m2t = drawAxes(m2t, handle, alignmentOptions)
- % Input arguments:
- % handle.................The axes environment handle.
- % alignmentOptions.......The alignment options as defined in the
- % function 'alignSubPlots()'.
- % This argument is optional.
-
- % Handle special cases.
- % MATLAB(R) uses 'Tag', Octave 'tag' for their tags. :/
- tagKeyword = switchMatOct(m2t, 'Tag', 'tag');
- colorbarKeyword = switchMatOct(m2t, 'Colorbar', 'colorbar');
- switch get(handle, tagKeyword)
- case colorbarKeyword
- % Handle a colorbar separately.
- m2t = handleColorbar(m2t, handle);
- return
- case 'legend'
- % Don't handle the legend here, but further below in the 'axis'
- % environment.
- % In MATLAB, an axes environment and its corresponding legend are
- % children of the same figure (siblings), while in Pgfplots, the
- % \legend (or \addlegendentry) command must appear within the axis
- % environment.
- return
- otherwise
- % continue as usual
- end
+% ==============================================================================
+function m2t = add3DOptionsOfAxes(m2t, handle)
+% adds 3D specific options of an axes object
+ if isAxis3D(handle)
+ [m2t, zopts] = getAxisOptions(m2t, handle, 'z');
+ m2t.axesContainers{end}.options = opts_merge(...
+ m2t.axesContainers{end}.options, zopts);
+
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'view', sprintf(['{', m2t.ff, '}{', m2t.ff, '}'], get(handle, 'View')));
+ end
+end
+% ==============================================================================
+function legendhandle = getAssociatedLegend(m2t, handle)
+% Check if the axis is referenced by a legend (only necessary for Octave)
+ legendhandle = [];
+ switch getEnvironment
+ case 'Octave'
+ % Make sure that m2t.legendHandles is a row vector.
+ for lhandle = m2t.legendHandles(:)'
+ ud = get(lhandle, 'UserData');
+ if isVisibleContainer(lhandle) && any(handle == ud.handle)
+ legendhandle = lhandle;
+ break;
+ end
+ end
+ case 'MATLAB'
+ % no action needed
+ otherwise
+ errorUnknownEnvironment();
+ end
+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.cmdOpts.Results.width, ...
+ m2t.cmdOpts.Results.height, m2t.axesBoundingBox);
+ % set the width
+ if (~m2t.cmdOpts.Results.noSize)
+ % optionally prevents setting the width and height of the axis
+ m2t = setDimensionOfAxes(m2t, 'width', pos.w);
+ m2t = setDimensionOfAxes(m2t, 'height', pos.h);
- % Initialize empty enviroment.
- % 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.axesContainers{end+1} = struct('handle', handle, ...
- 'name', [], ...
- 'comment', [], ...
- 'options', {cell(0,2)}, ...
- 'content', {cell(0)}, ...
- 'children', {cell(0)}, ...
- 'stackedBarsPresent', false, ...
- 'nonbarPlotsPresent', false ...
- );
-
- % update gca
- m2t.currentHandles.gca = handle;
-
- % This is an ugly workaround for bar plots.
- % Bar plots need to have several values counted on a per-axes basis, e.g.,
- % the number of bars. Setting m2t.barplotId to [] here makes sure those
- % values are recomputed in drawBarseries().
- m2t.barplotId = [];
-
- % Octave:
- % Check if this axis environment is referenced by a legend.
- m2t.gcaAssociatedLegend = [];
- switch m2t.env
- case 'Octave'
- if ~isempty(m2t.legendHandles)
- % Make sure that m2t.legendHandles is a row vector.
- for lhandle = m2t.legendHandles(:)'
- ud = get(lhandle, 'UserData');
- if any(handle == ud.handle)
- m2t.gcaAssociatedLegend = lhandle;
- break;
- end
- end
- end
- case 'MATLAB'
- % no action needed
- otherwise
- errorUnknownEnvironment();
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % get the axes dimensions
- dim = getAxesDimensions(handle, ...
- m2t.cmdOpts.Results.width, ...
- m2t.cmdOpts.Results.height);
- % set the width
- if dim.x.unit(1)=='\' && dim.x.value==1.0
- % only return \figurewidth instead of 1.0\figurewidth
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'width', dim.x.unit);
- else
- if strcmp(dim.x.unit, 'px')
- % TikZ doesn't know pixels. -- Convert to inches.
- dpi = get(0, 'ScreenPixelsPerInch');
- dim.x.value = dim.x.value / dpi;
- dim.x.unit = 'in';
- end
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'width', ...
- sprintf([m2t.ff, '%s'], dim.x.value, dim.x.unit));
- end
- if dim.y.unit(1)=='\' && dim.y.value==1.0
- % only return \figureheight instead of 1.0\figureheight
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'height', dim.y.unit);
- else
- if strcmp(dim.y.unit, 'px')
- % TikZ doesn't know pixels. -- Convert to inches.
- dpi = get(0, 'ScreenPixelsPerInch');
- dim.y.value = dim.y.value / dpi;
- dim.y.unit = 'in';
- end
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'height', ...
- sprintf([m2t.ff, '%s'], dim.y.value, dim.y.unit));
- end
- % Add the physical dimension of one unit of length in the coordinate system.
- % This is used later on to translate lenghts to physical units where
- % necessary (e.g., in bar plots).
- m2t.unitlength.x.unit = dim.x.unit;
- xLim = get(m2t.currentHandles.gca, 'XLim');
- m2t.unitlength.x.value = dim.x.value / (xLim(2)-xLim(1));
- m2t.unitlength.y.unit = dim.y.unit;
- yLim = get(m2t.currentHandles.gca, 'YLim');
- m2t.unitlength.y.value = dim.y.value / (yLim(2)-yLim(1));
- for axis = 'xyz'
- m2t.([axis 'AxisReversed']) = ...
- strcmp(get(handle,[upper(axis),'Dir']), 'reverse');
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % In MATLAB, all plots are treated as 3D plots; it's just the view that
- % makes 2D plots appear like 2D.
- % Recurse into the children of this environment. Do this here to give the
- % contained plots the chance to set m2t.currentAxesContain3dData to true.
- m2t.currentAxesContain3dData = false;
- [m2t, childrenEnvs] = handleAllChildren(m2t, handle);
- m2t.axesContainers{end} = addChildren(m2t.axesContainers{end}, childrenEnvs);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if m2t.axesContainers{end}.stackedBarsPresent ...
- && m2t.axesContainers{end}.nonbarPlotsPresent
- userWarning(m2t, ['Pgfplots can''t deal with stacked bar plots', ...
- ' and non-bar plots in one axis environment.', ...
- ' The LaTeX file will probably not compile.']);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % The rest of this is handling axes options.
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Set view for 3D plots.
- if m2t.currentAxesContain3dData
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'view', sprintf(['{', m2t.ff, '}{', m2t.ff, '}'], get(handle, 'View')));
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % the following is general MATLAB behavior
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'scale only axis', []);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % 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.axesContainers{end}.options = merge(m2t.axesContainers{end}.options, ...
- getAxisOptions(m2t, handle, 'x') ...
- );
- m2t.axesContainers{end}.options = merge(m2t.axesContainers{end}.options, ...
- getAxisOptions(m2t, handle, 'y') ...
- );
- %[m2t, hasXGrid] = getAxisOptions(m2t, handle, 'x');
- %[m2t, hasYGrid] = getAxisOptions(m2t, handle, 'y');
- if m2t.currentAxesContain3dData
- %[m2t, hasZGrid] = getAxisOptions(m2t, handle, 'z');
- m2t.axesContainers{end}.options = ...
- merge(m2t.axesContainers{end}.options, ...
- getAxisOptions(m2t, handle, 'z') ...
- );
- end
- hasXGrid = false;
- hasYGrid = false;
- hasZGrid = false;
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if ~isVisible(handle)
- % Setting hide{x,y} axis also hides the axis labels in Pgfplots whereas
- % in MATLAB, they may still be visible. Well.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'hide axis', []);
- end
- %if ~isVisible(handle)
- % % An invisible axes container *can* have visible children, so don't
- % % immediately bail out here.
- % children = get(handle, 'Children');
- % for child = children(:)'
- % if isVisible(child)
- % % If the axes contain something that's visible, add an invisible
- % % axes pair.
- % m2t.axesContainers{end}.name = 'axis';
- % m2t.axesContainers{end}.options = {m2t.axesContainers{end}.options{:}, ...
- % 'hide x axis', 'hide y axis'};
- % m2t.axesContainers{end}.comment = getTag(handle);
- % break;
- % end
- % end
- % % recurse into the children of this environment
- % [m2t, childrenEnvs] = handleAllChildren(m2t, handle);
- % m2t.axesContainers{end} = addChildren(m2t.axesContainers{end}, childrenEnvs);
- % return
- %end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % This used to discriminate between semilog{x,y}axis, loglog, and axis.
- % Now, the log-scale is handled by the {x,y,z}mode argument, so just go for
- % axis.
- m2t.axesContainers{end}.name = 'axis';
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % set alignment options
- if ~isempty(alignmentOptions.opts)
- m2t.axesContainers{end}.options = cat(1,m2t.axesContainers{end}.options,...
- alignmentOptions.opts);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % background color
- backgroundColor = get(handle, 'Color');
- if ~strcmp(backgroundColor, 'none')
- [m2t, col] = getColor(m2t, handle, backgroundColor, 'patch');
- if ~strcmp(col, 'white')
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis background/.style', sprintf('{fill=%s}', col));
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % title
- title = get(get(handle, 'Title'), 'String');
- if ~isempty(title)
- titleInterpreter = get(get(handle, 'Title'), 'Interpreter');
- title = prettyPrint(m2t, title, titleInterpreter);
- titleStyle = getFontStyle(m2t, get(handle,'Title'));
- if length(title) > 1
- titleStyle = addToOptions(titleStyle, 'align', 'center');
- end
- if ~isempty(titleStyle)
- m2t.axesContainers{end}.options = addToOptions(...
- m2t.axesContainers{end}.options, 'title style', ...
- sprintf('{%s}', prettyprintOpts(m2t, titleStyle, ',')));
- end
- title = join(m2t, title, '\\[1ex]');
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'title', sprintf('{%s}', title));
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % axes locations
- boxOn = strcmp(get(handle, 'box'), 'on');
- xloc = get(handle, 'XAxisLocation');
- if boxOn
- if strcmp(xloc, 'bottom')
- % default; nothing added
- elseif strcmp(xloc, 'top')
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis x line*', 'top');
- else
- error('matlab2tikz:drawAxes', ...
- 'Illegal axis location ''%s''.', xloc);
- end
- else % box off
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis x line*', xloc);
- end
- yloc = get(handle, 'YAxisLocation');
- if boxOn
- if strcmp(yloc, 'left')
- % default; nothing added
- elseif strcmp(yloc, 'right')
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis y line*', 'right');
- else
- error('matlab2tikz:drawAxes', ...
- 'Illegal axis location ''%s''.', yloc);
- end
- else % box off
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis y line*', yloc);
- end
- if m2t.currentAxesContain3dData
- % There's no such attribute as 'ZAxisLocation'.
- % Instead, the default seems to be 'left'.
- if ~boxOn
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis z line*', 'left');
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % grid line style
- if hasXGrid || hasYGrid || (exist('hasZGrid','var') && hasZGrid)
- matlabGridLineStyle = get(handle, 'GridLineStyle');
- % Take over the grid line style in any case when in strict mode.
- % If not, don't add anything in case of default line grid line style
- % and effectively take Pgfplots' default.
- defaultMatlabGridLineStyle = ':';
- if m2t.cmdOpts.Results.strict ...
- || ~strcmp(matlabGridLineStyle,defaultMatlabGridLineStyle)
- gls = translateLineStyle(matlabGridLineStyle);
- axisGridOpts = {'grid style', sprintf('{%s}', gls)};
- m2t.axesContainers{end}.options = cat(1, ...
- m2t.axesContainers{end}.options,...
- axisGridOpts);
- end
- else
- % 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).
- % To date (Dec 12, 2009) Pgfplots is not able to handle those things
- % separately.
- % See also http://sourceforge.net/tracker/index.php?func=detail&aid=3510455&group_id=224188&atid=1060657
- % As a prelimary compromise, only pull this option if no grid is in use.
- if m2t.cmdOpts.Results.strict
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis on top', []);
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % See if there are any legends that need to be plotted.
- % Since the legends are at the same level as axes in the hierarchy,
- % we can't work out which relates to which using the tree
- % so we have to do it by looking for a plot inside which the legend sits.
- % This could be done better with a heuristic of finding
- % the nearest legend to a plot, which would cope with legends outside
- % plot boundaries.
- switch m2t.env
- case 'MATLAB'
- legendHandle = legend(handle);
- if ~isempty(legendHandle)
- [m2t, key, legendOpts] = getLegendOpts(m2t, legendHandle);
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- key, ...
- ['{', legendOpts, '}']);
- end
- case 'Octave'
- % TODO: How to uniquely connect a legend with a pair of axes in Octave?
- axisDims = pos2dims(get(handle,'Position')); %#ok
- % siblings of this handle:
- siblings = get(get(handle,'Parent'), 'Children');
- % "siblings" always(?) is a column vector. Iterating over the column
- % with the for statement below wouldn't return the individual vector
- % elements but the same column vector, resulting in no legends exported.
- % So let's make sure "siblings" is a row vector by reshaping it:
- siblings = reshape(siblings, 1, []);
- for sibling = siblings
- if sibling && strcmp(get(sibling,'Type'), 'axes') && strcmp(get(sibling,'Tag'), 'legend')
- legDims = pos2dims(get(sibling, 'Position')); %#ok
-
- % TODO The following logic does not work for 3D plots.
- % => Commented out.
- % This creates problems though for stacked plots with legends.
- % if ( legDims.left > axisDims.left ...
- % && legDims.bottom > axisDims.bottom ...
- % && legDims.left + legDims.width < axisDims.left + axisDims.width ...
- % && legDims.bottom + legDims.height < axisDims.bottom + axisDims.height)
- [m2t, key, legendOpts] = getLegendOpts(m2t, sibling);
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- key, ...
- ['{', legendOpts, '}']);
- % end
- end
- end
- otherwise
- errorUnknownEnvironment();
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % add manually given extra axis options
- if ~isempty(m2t.cmdOpts.Results.extraAxisOptions)
- if ischar(m2t.cmdOpts.Results.extraAxisOptions)
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- m2t.cmdOpts.Results.extraAxisOptions, []);
- elseif iscellstr(m2t.cmdOpts.Results.extraAxisOptions)
- for k = 1:length(m2t.cmdOpts.Results.extraAxisOptions)
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- m2t.cmdOpts.Results.extraAxisOptions{k}, []);
- end
- else
- error('matlab2tikz:illegalExtraAxisOptions',...
- 'Illegal extraAxisOptions (need string or cell string).')
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'at', ...
+ ['{(' formatDim(pos.x.value, pos.x.unit) ','...
+ formatDim(pos.y.value, pos.y.unit) ')}']);
+ % the following is general MATLAB behavior:
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'scale only axis', []);
+ end
end
-% =========================================================================
-function m2t = handleColorbar(m2t, handle)
+% ==============================================================================
+function m2t = setDimensionOfAxes(m2t, widthOrHeight, dimension)
+% sets the dimension "name" of the current axes to the struct "dim"
+ m2t.axesContainers{end}.options = opts_add(...
+ m2t.axesContainers{end}.options, 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.axesContainers{end}.is3D
+ % Note: set 'plot box ratio' for 3D axes to avoid bug with
+ % 'scale mode = uniformly' (see #560)
+ aspectRatio = getPlotBoxAspectRatio(handle);
+ m2t.axesContainers{end}.options = opts_add(...
+ m2t.axesContainers{end}.options, '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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'axis background/.style', sprintf('{fill=%s}', col));
+ end
+end
+% ==============================================================================
+function m2t = drawTitleOfAxes(m2t, handle)
+% processes the title of an axes object
+ [m2t, m2t.axesContainers{end}.options] = getTitle(m2t, handle, ...
+ m2t.axesContainers{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);
+ style = getFontStyle(m2t, object);
+ if length(str) > 1 %multiline
+ style = opts_add(style, 'align', 'center');
+ end
+ if ~isempty(style)
+ opts = opts_add(opts, [tikzKeyword ' style'], ...
+ sprintf('{%s}', opts_print(m2t, 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 = strcmpi(get(h, 'box'), 'on');
+ 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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'xticklabel pos','right');
+ end
+ if ~isYaxisLeft
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'yticklabel pos','right');
+ end
+ % Position axes lines (strips the box)
+ else
+ m2t.axesContainers{end}.options = ...
+ opts_append(m2t.axesContainers{end}.options, ...
+ 'axis x line*', xLoc);
+ m2t.axesContainers{end}.options = ...
+ opts_append(m2t.axesContainers{end}.options, ...
+ 'axis y line*', yLoc);
+ if m2t.axesContainers{end}.is3D
+ % There's no such attribute as 'ZAxisLocation'.
+ % Instead, the default seems to be 'left'.
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'axis z line*', 'left');
+ end
+ end
+end
+% ==============================================================================
+function m2t = drawLegendOptionsOfAxes(m2t, handle)
+ % See if there are any legends that need to be plotted.
+ % Since the legends are at the same level as axes in the hierarchy,
+ % we can't work out which relates to which using the tree
+ % so we have to do it by looking for a plot inside which the legend sits.
+ % This could be done better with a heuristic of finding
+ % the nearest legend to a plot, which would cope with legends outside
+ % plot boundaries.
+ switch getEnvironment
+ case 'MATLAB'
+ legendHandle = legend(handle);
+ if ~isempty(legendHandle)
+ [m2t, key, legendOpts] = getLegendOpts(m2t, legendHandle);
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ key, ...
+ ['{', legendOpts, '}']);
+ end
+ case 'Octave'
+ % TODO: How to uniquely connect a legend with a pair of axes in Octave?
+ axisDims = pos2dims(get(handle,'Position')); %#ok
+ % siblings of this handle:
+ siblings = get(get(handle,'Parent'), 'Children');
+ % "siblings" always(?) is a column vector. Iterating over the column
+ % with the for statement below wouldn't return the individual vector
+ % elements but the same column vector, resulting in no legends exported.
+ % So let's make sure "siblings" is a row vector by reshaping it:
+ siblings = reshape(siblings, 1, []);
+ for sibling = siblings
+ if sibling && strcmpi(get(sibling,'Type'), 'axes') && strcmpi(get(sibling,'Tag'), 'legend')
+ legDims = pos2dims(get(sibling, 'Position')); %#ok
+
+ % TODO The following logic does not work for 3D plots.
+ % => Commented out.
+ % This creates problems though for stacked plots with legends.
+ % if ( legDims.left > axisDims.left ...
+ % && legDims.bottom > axisDims.bottom ...
+ % && legDims.left + legDims.width < axisDims.left + axisDims.width ...
+ % && legDims.bottom + legDims.height < axisDims.bottom + axisDims.height)
+ [m2t, key, legendOpts] = getLegendOpts(m2t, sibling);
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ key, ...
+ ['{', legendOpts, '}']);
+ % end
+ end
+ end
+ otherwise
+ errorUnknownEnvironment();
+ end
+end
+% ==============================================================================
+function m2t = handleColorbar(m2t, handle)
if isempty(handle)
return;
end
@@ -1108,28 +1170,27 @@ function m2t = handleColorbar(m2t, handle)
parentAxesHandle = double(get(handle,'axes'));
parentFound = false;
for k = 1:length(m2t.axesContainers)
- if m2t.axesContainers{k}.handle == parentAxesHandle
- k0 = k;
- parentFound = true;
- break;
- end
+ if m2t.axesContainers{k}.handle == parentAxesHandle
+ k0 = k;
+ parentFound = true;
+ break;
+ end
end
if parentFound
- m2t.axesContainers{k0}.options = ...
- addToOptions(m2t.axesContainers{k0}.options, ...
- matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
- % Append cell string.
- m2t.axesContainers{k0}.options = cat(1,...
- m2t.axesContainers{k0}.options, ...
- getColorbarOptions(m2t, handle));
+ m2t.axesContainers{k0}.options = ...
+ opts_append(m2t.axesContainers{k0}.options, ...
+ matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
+ % Append cell string.
+ m2t.axesContainers{k0}.options = cat(1,...
+ m2t.axesContainers{k0}.options, ...
+ getColorbarOptions(m2t, handle));
else
- warning('matlab2tikz:parentAxesOfColorBarNotFound',...
- 'Could not find parent axes for color bar. Skipping.');
+ warning('matlab2tikz:parentAxesOfColorBarNotFound',...
+ 'Could not find parent axes for color bar. Skipping.');
end
end
-% =========================================================================
+% ==============================================================================
function tag = getTag(handle)
-
% if a tag is given, use it as comment
tag = get(handle, 'tag');
if ~isempty(tag)
@@ -1137,1694 +1198,2343 @@ function tag = getTag(handle)
else
tag = sprintf('Axis at [%.2g %.2f %.2g %.2g]', get(handle, 'position'));
end
-
end
-% =========================================================================
-function options = getAxisOptions(m2t, handle, axis)
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if ~strcmpi(axis,'x') && ~strcmpi(axis,'y') && ~strcmpi(axis,'z')
- error('matlab2tikz:illegalAxisSpecifier',...
- 'Illegal axis specifier ''%s''.', axis);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- options = {};
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % axis colors
- color = get(handle, [upper(axis),'Color']);
- if (any(color)) % color not black [0,0,0]
- [m2t, col] = getColor(m2t, handle, color, 'patch');
- if strcmp(get(handle, 'box'), 'on')
- % 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 = addToOptions(options, 'separate axis lines', []);
- end
- options = ...
- addToOptions(options, ...
- ['every outer ', axis, ' axis line/.append style'], ...
- ['{', col, '}']);
- options = ...
- addToOptions(options, ...
- ['every ',axis,' tick label/.append style'], ...
- ['{font=\color{',col,'}}']);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % handle the orientation
- isAxisReversed = strcmp(get(handle,[upper(axis),'Dir']), 'reverse');
- m2t.([axis 'AxisReversed']) = isAxisReversed;
- if isAxisReversed
- options = addToOptions(options, ...
- [axis, ' dir'], 'reverse');
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- isAxisLog = strcmp(get(handle,[upper(axis),'Scale']), 'log');
- if isAxisLog
- options = addToOptions(options, ...
- [axis,'mode'], 'log');
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % get axis limits
- limits = get(handle, [upper(axis),'Lim']);
- options = addToOptions(options, ...
- [axis,'min'], sprintf(m2t.ff, limits(1)));
- options = addToOptions(options, ...
- [axis,'max'], sprintf(m2t.ff, limits(2)));
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % get ticks along with the labels
- [ticks, tickLabels, hasMinorTicks] = getAxisTicks(m2t, handle, axis);
-
- % 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 = addToOptions(options, ...
- [axis,'tick'], sprintf('{%s}', ticks));
- end
- if ~isempty(tickLabels)
- options = addToOptions(options, ...
- [axis,'ticklabels'], sprintf('{%s}', tickLabels));
- end
- if hasMinorTicks
- options = addToOptions(options, ...
- [axis,'minorticks'], 'true');
- if m2t.cmdOpts.Results.strict
- options = ...
- addToOptions(options, ...
- sprintf('minor %s tick num', axis), ...
- sprintf('{%d}', matlabDefaultNumMinorTicks));
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % get axis label
- axisLabel = get(get(handle, [upper(axis),'Label']), 'String');
- if ~isempty(axisLabel)
- axisLabelInterpreter = ...
- get(get(handle, [upper(axis),'Label']), 'Interpreter');
- label = prettyPrint(m2t, axisLabel, axisLabelInterpreter);
- if length(label) > 1
- % If there's more than one cell item, the list
- % is displayed multi-row in MATLAB(R).
- % To replicate the same in Pgfplots, one can
- % use xlabel={first\\second\\third} only if the
- % alignment or the width of the "label box"
- % is defined. This is a restriction that comes with
- % TikZ nodes.
- options = addToOptions(options, ...
- [axis, 'label style'], '{align=center}');
- end
- label = join(m2t, label,'\\[1ex]');
- %if isVisible(handle)
- options = addToOptions(options, ...
- [axis, 'label'], sprintf('{%s}', label));
- %else
- % m2t.axesContainers{end}.options{end+1} = ...
- % sprintf(['extra description/.code={\n', ...
- % '\\node[/pgfplots/every axis label,/pgfplots/every axis %s label]{%s};\n', ...
- % '}'], axis, label);
- %end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % get grids
- hasGrid = false;
- if strcmp(get(handle, [upper(axis),'Grid']), 'on');
- options = addToOptions(options, ...
- [axis, 'majorgrids'], []);
- hasGrid = true;
- end
- if strcmp(get(handle, [upper(axis),'MinorGrid']), 'on');
- options = addToOptions(options, ...
- [axis, 'minorgrids'], []);
- hasGrid = true;
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-end
-% =========================================================================
-function bool = axisIsVisible(axisHandle)
-
- if ~isVisible(axisHandle)
- % An invisible axes container *can* have visible children, so don't
- % immediately bail out here.
- children = get(axisHandle, 'Children');
- bool = false;
- for child = children(:)'
- if isVisible(child)
- bool = true;
- return;
- end
- end
- else
- bool = true;
- 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.cmdOpts.Results.strict
+ [m2t, col] = getColor(m2t, handle, color, 'patch');
+ if strcmpi(get(handle, 'box'), 'on')
+ % 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
+ options = ...
+ opts_add(options, ...
+ ['every outer ', axis, ' axis line/.append style'], ...
+ ['{', col, '}']);
+ options = ...
+ opts_add(options, ...
+ ['every ',axis,' tick label/.append style'], ...
+ ['{font=\color{',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);
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % get grids
+ if strcmpi(getOrDefault(handle, [upper(axis),'Grid'], 'off'), 'on');
+ options = opts_add(options, [axis, 'majorgrids'], []);
+ end
+ if strcmpi(getOrDefault(handle, [upper(axis),'MinorGrid'], 'off'), 'on');
+ options = opts_add(options, [axis, 'minorgrids'], []);
+ end
end
-% =========================================================================
-function [m2t, str] = drawLine(m2t, handle, yDeviation)
- % Returns the code for drawing a regular line.
- % This is an extremely common operation and takes place in most of the
- % not too fancy plots.
- %
- % This function handles error bars, too.
-
- str = [];
+% ==============================================================================
+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);
+ if isempty(ticks)
+ % If no ticks are present, we need to enforce this in any case.
+ pgfTicks = '\empty';
+ else
+ if strcmpi(tickMode, 'auto') && ~m2t.cmdOpts.Results.strict
+ % If the ticks are set automatically, and strict conversion is
+ % not required, then let Pgfplots take care of the ticks.
+ % In most cases, this looks a lot better anyway.
+ pgfTicks = [];
+ else % strcmpi(tickMode,'manual') || m2t.cmdOpts.Results.strict
+ pgfTicks = join(m2t, cellstr(num2str(ticks(:))), ', ');
+ end
+ end
- if ~isVisible(handle)
- return
- end
+ keywordTickLabelMode = [upper(axis), 'TickLabelMode'];
+ tickLabelMode = get(handle, keywordTickLabelMode);
+ keywordTickLabel = [upper(axis), 'TickLabel'];
+ tickLabels = cellstr(get(handle, keywordTickLabel));
+ if strcmpi(tickLabelMode, 'auto') && ~m2t.cmdOpts.Results.strict
+ pgfTickLabels = [];
+ else % strcmpi(tickLabelMode,'manual') || m2t.cmdOpts.Results.strict
+ keywordScale = [upper(axis), 'Scale'];
+ isAxisLog = strcmpi(getOrDefault(handle,keywordScale, 'lin'), 'log');
+ [pgfTicks, pgfTickLabels] = ...
+ matlabTicks2pgfplotsTicks(m2t, ticks, tickLabels, isAxisLog, tickLabelMode);
+ end
- % Don't allow lines is there's already stacked bars.
- % This could be somewhat controversial: Why not remove the stacked bars then?
- % Anyways, with stacked bar plots, lines are typically base lines and such,
- % so nothing of great interest. Throw those out for now.
- if m2t.axesContainers{end}.stackedBarsPresent
- return
- end
+ keywordMinorTick = [upper(axis), 'MinorTick'];
+ hasMinorTicks = strcmpi(getOrDefault(handle, keywordMinorTick, 'off'), 'on');
+ tickDirection = getOrDefault(handle, 'TickDir', 'in');
- % This is for a quirky workaround for stacked bar plots.
- m2t.axesContainers{end}.nonbarPlotsPresent = true;
-
- lineStyle = get(handle, 'LineStyle');
- lineWidth = get(handle, 'LineWidth');
- marker = get(handle, 'Marker');
-
- % Get draw options.
- color = get(handle, 'Color');
- [m2t, xcolor] = getColor(m2t, handle, color, 'patch');
- lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
- [m2t, markerOptions] = getMarkerOptions(m2t, handle);
- drawOptions = [{sprintf('color=%s', xcolor)}, ... % color
- lineOptions, ...
- markerOptions];
-
- % Check for "special" lines, e.g.:
- if strcmp(get(handle, 'Tag'), 'zplane_unitcircle')
- % Draw unit circle and axes.
- % TODO Don't hardcode "10".
- opts = join(m2t, 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)];
- return
- end
-
- hasLines = ~strcmp(lineStyle,'none') && lineWidth>0.0;
- hasMarkers = ~strcmp(marker,'none');
- if ~hasLines && ~hasMarkers
- return
- end
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Plot the actual line data.
- % First put them all together in one multiarray.
- % This also implicitly makes sure that the lengths match.
- xData = get(handle, 'XData');
- yData = get(handle, 'YData');
- zData = get(handle, 'ZData');
- % We would like to do
- % data = [xData(:), yData(:), zData(:)],
- % but Octave fails. Hence this isempty() construction.
- if isempty(zData)
- data = [xData(:), yData(:)];
- else
- data = applyHgTransform(m2t, [xData(:), yData(:), zData(:)]);
- end
-
- % check if the *optional* argument 'yDeviation' was given
- hasDeviations = false;
- if nargin > 2
- data = [data, yDeviation(:,1:2)];
- hasDeviations = true;
- end
+ options = setAxisTicks(m2t, options, axis, pgfTicks, pgfTickLabels, ...
+ hasMinorTicks, tickDirection);
+end
+% ==============================================================================
+function options = setAxisTicks(m2t, options, axis, ticks, tickLabels,hasMinorTicks, tickDir)
+% 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.cmdOpts.Results.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
+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.
+ children = get(axisHandle, 'Children');
+ bool = false;
+ for child = children(:)'
+ if isVisible(child)
+ bool = true;
+ return;
+ end
+ end
+ else
+ bool = true;
+ end
+end
+% ==============================================================================
+function [m2t, str] = drawLine(m2t, h, yDeviation)
+% 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 ~isVisible(h)
+ return
+ end
- % Check if any value is infinite/NaN. In that case, add appropriate option.
- if any(~isfinite(data(:)))
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'unbounded coords', 'jump');
- end
+ % Check if there is anything to plot (line annotation has no marker)
+ lineStyle = get(h, 'LineStyle');
+ lineWidth = get(h, 'LineWidth');
+ marker = getOrDefault(h, 'Marker','none');
+ hasLines = ~isNone(lineStyle) && lineWidth > 0;
+ hasMarkers = ~isNone(marker);
+ if ~hasLines && ~hasMarkers
+ return
+ end
- if ~isempty(zData)
- % Don't try to be smart in parametric 3d plots: Just plot all the data.
- [m2t, table] = makeTable(m2t, {'','',''}, data);
- str = sprintf('%s\\addplot3 [%s]\n table[row sep=crcr] {%s};\n ', ...
- str, join(m2t, drawOptions, ','), table);
+ % Color
+ color = get(h, 'Color');
+ [m2t, xcolor] = getColor(m2t, h, color, 'patch');
+ % Line and marker options
+ lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
+ [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')
+ % Draw unit circle and axes.
+ % TODO Don't hardcode "10".
+ opts = opts_print(m2t, 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)];
+ return
+ end
- m2t.currentAxesContain3dData = true;
- else
- xLim = get(m2t.currentHandles.gca, 'XLim');
- yLim = get(m2t.currentHandles.gca, 'YLim');
- % split the data into logical chunks
- dataCell = splitLine(m2t, hasLines, hasMarkers, hasDeviations, data, xLim, yLim);
-
- % plot them
- for k = 1:length(dataCell)
- % If the line has a legend string, make sure to only include a legend
- % entry for the *last* occurence of the plot series.
- % Hence the condition k<length(xDataCell).
- %if ~isempty(m2t.legendHandles) && (~m2t.currentHandleHasLegend || k < length(dataCell))
- if ~m2t.currentHandleHasLegend || k < length(dataCell)
- % No legend entry found. Don't include plot in legend.
- opts = join(m2t, [drawOptions, {'forget plot'}], ',');
- else
- opts = join(m2t, drawOptions, ',');
- end
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [data] = getXYZDataFromLine(m2t, h);
- [m2t, Part] = plotLine2d(m2t, opts, dataCell{k});
- str = [str, Part];
- end
- end
+ % check if the *optional* argument 'yDeviation' was given
+ hasDeviations = false;
+ if nargin > 2
+ data = [data, yDeviation(:,1:2)];
+ hasDeviations = true;
+ end
- if m2t.cmdOpts.Results.automaticLabels
- [m2t, label] = addLabel(m2t);
- str = [str, label];
- end
+ % Check if any value is infinite/NaN. In that case, add appropriate option.
+ m2t = jumpAtUnboundCoords(m2t, data);
+ [m2t, str] = writePlotData(m2t, str, data, drawOptions);
+ [m2t, str] = addLabel(m2t, str);
end
-% =========================================================================
-function [m2t, str] = addLabel(m2t)
-
- [pathstr, name] = fileparts(m2t.cmdOpts.Results.filename); %#ok
- label = sprintf('addplot:%s%d', name, m2t.automaticLabelIndex);
- str = sprintf('\\label{%s}\n', label);
- m2t.automaticLabelIndex = m2t.automaticLabelIndex + 1;
+% ==============================================================================
+function [m2t, str] = writePlotData(m2t, str, data, drawOptions)
+% actually writes the plot data to file
+ is3D = m2t.axesContainers{end}.is3D;
+ if is3D
+ % Don't try to be smart in parametric 3d plots: Just plot all the data.
+ [m2t, table, tabOpts] = makeTable(m2t, {'','',''}, data);
+ str = sprintf('%s\\addplot3 [%s]\n table[%s] {%s};\n ', ...
+ str, opts_print(m2t, drawOptions, ','), ...
+ opts_print(m2t, tabOpts,','), table);
+ else
+ % split the data into logical chunks
+ dataCell = splitLine(m2t, data);
+
+ % plot them
+ 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<length(xDataCell).
+ %if ~isempty(m2t.legendHandles) && (~m2t.currentHandleHasLegend || k < length(dataCell))
+ if ~m2t.currentHandleHasLegend || k < length(dataCell)
+ % No legend entry found. Don't include plot in legend.
+ hiddenDrawOptions = maybeShowInLegend(false, drawOptions);
+ opts = opts_print(m2t, hiddenDrawOptions, ',');
+ else
+ opts = opts_print(m2t, drawOptions, ',');
+ end
- userWarning(m2t, 'Automatically added label ''%s'' for line plot.', label);
+ [m2t, Part] = plotLine2d(m2t, opts, dataCell{k});
+ str = [str, Part];
+ end
+ end
+end
+% ==============================================================================
+function [data] = getXYZDataFromLine(m2t, h)
+% Retrieves the X, Y and Z (if appropriate) data from a Line object
+%
+% First put them all together in one multiarray.
+% This also implicitly makes sure that the lengths match.
+ try
+ xData = get(h, 'XData');
+ yData = get(h, 'YData');
+ catch
+ % Line annotation
+ xData = get(h, 'X');
+ yData = get(h, 'Y');
+ end
+ is3D = m2t.axesContainers{end}.is3D;
+ if ~is3D
+ data = [xData(:), yData(:)];
+ else
+ zData = get(h, 'ZData');
+ data = applyHgTransform(m2t, [xData(:), yData(:), zData(:)]);
+ end
+end
+% ==============================================================================
+function [m2t, generatedCodeSoFar, labelCode] = addLabel(m2t, generatedCodeSoFar)
+% conditionally add a LaTeX label after the current plot
+ if ~exist('generatedCodeSoFar','var') || isempty(generatedCodeSoFar)
+ generatedCodeSoFar = '';
+ end
+ if m2t.cmdOpts.Results.automaticLabels
+ [pathstr, name] = fileparts(m2t.cmdOpts.Results.filename); %#ok
+ labelName = sprintf('addplot:%s%d', name, m2t.automaticLabelIndex);
+ labelCode = sprintf('\\label{%s}\n', labelName);
+ m2t.automaticLabelIndex = m2t.automaticLabelIndex + 1;
+ userWarning(m2t, 'Automatically added label ''%s'' for line plot.', labelName);
+ generatedCodeSoFar = [generatedCodeSoFar, labelCode];
+ end
end
-% =========================================================================
+% ==============================================================================
function [m2t,str] = plotLine2d(m2t, opts, data)
+ errorbarMode = (size(data,2) == 4); % is (optional) yDeviation given?
- % check if the *optional* argument 'yDeviation' was given
- errorbarMode = (size(data,2) == 4);
-
- str = '';
- if errorbarMode
- m2t = needsPgfplotsVersion(m2t, [1,9]);
- str = sprintf('plot [error bars/.cd, y dir = both, y explicit]\n');
- end
-
- % Convert to string array then cell to call sprintf once (and no loops).
- if errorbarMode
- tabOpts = 'row sep=crcr, y error plus index=2, y error minus index=3';
- else
- tabOpts = 'row sep=crcr';
- end
- [m2t, table] = makeTable(m2t, repmat({''}, size(data,2)), data);
- str = sprintf('\\addplot [%s]\n %s table[%s]{%s};\n',...
- opts, str, tabOpts, table);
-end
-% =========================================================================
-function dataCell = splitLine(m2t, hasLines, hasMarkers, hasDeviations, data, xLim, yLim)
- % Split the xData, yData into several chunks of data for each of which
- % an \addplot will be generated.
- % Splitting criteria are:
- % * Visibility.
- % * Data set too large.
-
- dataCell{1} = data;
-
- % Split each of the current chunks further with respect to outliers.
- dataCell = splitByArraySize(m2t, hasLines, dataCell);
-end
-% =========================================================================
-function dataCellNew = splitByArraySize(m2t, hasLines, dataCell)
- % 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 plots, and this
- % function does the job.
-
- dataCellNew = cell(0);
-
- for data = dataCell
- chunkStart = 1;
- len = size(data{1}, 1);
- while chunkStart <= len
- chunkEnd = min(chunkStart + m2t.cmdOpts.Results.maxChunkLength - 1, len);
-
- % Copy over the data to the new containers.
- dataCellNew{end+1} = data{1}(chunkStart:chunkEnd,:);
-
- % If the plot has lines, add an extra (overlap) point to the data
- % stream; otherwise the line between two data chunks would be broken.
- if hasLines && chunkEnd~=len
- dataCellNew{end} = [dataCellNew{end};...
- data{1}(chunkEnd+1,:)];
- end
+ str = '';
+ if errorbarMode
+ m2t = needsPgfplotsVersion(m2t, [1,9]);
+ str = sprintf('plot [error bars/.cd, y dir = both, y explicit]\n');
+ end
- chunkStart = chunkEnd + 1;
- end
- end
+ % Convert to string array then cell to call sprintf once (and no loops).
+ [m2t, table, tabOpts] = makeTable(m2t, repmat({''}, size(data,2)), data);
+ if errorbarMode
+ tabOpts = opts_add(tabOpts, 'y error plus index', '2');
+ tabOpts = opts_add(tabOpts, 'y error minus index', '3');
+ end
+ str = sprintf('\\addplot [%s]\n %s table[%s]{%s};\n',...
+ opts, str, opts_print(m2t, tabOpts, ', '), table);
+end
+% ==============================================================================
+function dataCell = splitLine(m2t, data)
+% Split the xData, yData into several chunks of data for each of which
+% an \addplot will be generated.
+ dataCell{1} = data;
+
+ % Split each of the current chunks further with respect to outliers.
+ dataCell = splitByArraySize(m2t, dataCell);
+end
+% ==============================================================================
+function dataCellNew = splitByArraySize(m2t, dataCell)
+% 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 plots, and this
+% function does the job.
+ dataCellNew = cell(0);
+
+ %TODO: pre-allocate the cell array such that it doesn't grow during the loop
+ %TODO: scale `maxChunkLength` with the number of columns in the data array
+
+ for data = dataCell
+ chunkStart = 1;
+ len = size(data{1}, 1);
+ while chunkStart <= len
+ chunkEnd = min(chunkStart + m2t.cmdOpts.Results.maxChunkLength - 1, len);
+
+ % Copy over the data to the new containers.
+ dataCellNew{end+1} = data{1}(chunkStart:chunkEnd,:);
+
+ % Add an extra (overlap) point to the data stream;
+ % otherwise the line between two data chunks would be broken.
+ % Technically, this is only needed when the plot has a line
+ % connecting the points, but the additional cost when there is no
+ % line doesn't justify the added complexity.
+ if chunkEnd~=len
+ dataCellNew{end} = [dataCellNew{end};...
+ data{1}(chunkEnd+1,:)];
+ end
+ chunkStart = chunkEnd + 1;
+ end
+ end
end
-% =========================================================================
+% ==============================================================================
function lineOpts = getLineOptions(m2t, lineStyle, lineWidth)
- % Gathers the line options.
+% Gathers the line options.
+ lineOpts = opts_new();
- lineOpts = cell(0);
-
- if ~strcmp(lineStyle, 'none') && (lineWidth > m2t.tol)
- lineOpts{end+1} = sprintf('%s', translateLineStyle(lineStyle));
- end
+ if ~isNone(lineStyle) && (lineWidth > 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 m2t.cmdOpts.Results.strict ...
- || ~abs(lineWidth-matlabDefaultLineWidth) <= m2t.tol
- lineOpts{end+1} = sprintf('line width=%.1fpt', lineWidth);
- 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 m2t.cmdOpts.Results.strict ...
+ || ~abs(lineWidth-matlabDefaultLineWidth) <= m2t.tol
+ lineOpts = opts_add(lineOpts, 'line width', sprintf('%.1fpt', lineWidth));
+ end
end
-% =========================================================================
+% ==============================================================================
function [m2t, drawOptions] = getMarkerOptions(m2t, h)
- % Handles the marker properties of a line (or any other) plot.
+% Handles the marker properties of a line (or any other) plot.
+ drawOptions = opts_new();
- drawOptions = cell(0);
+ marker = getOrDefault(h, 'Marker', 'none');
- marker = get(h, 'Marker');
+ if ~isNone(marker)
+ markerSize = get(h, 'MarkerSize');
+ lineStyle = get(h, 'LineStyle');
+ lineWidth = get(h, 'LineWidth');
- if ~strcmp(marker, 'none')
- markerSize = get(h, 'MarkerSize');
- lineStyle = get(h, 'LineStyle');
- lineWidth = get(h, 'LineWidth');
+ [tikzMarkerSize, isDefault] = ...
+ translateMarkerSize(m2t, marker, markerSize);
- [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.cmdOpts.Results.strict || ~isDefault
+ drawOptions = opts_add(drawOptions, 'mark size', ...
+ sprintf('%.1fpt', tikzMarkerSize));
+ end
- % 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.cmdOpts.Results.strict || ~isDefault
- drawOptions{end+1} = sprintf('mark size=%.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
- markOptions = cell(0);
- % make sure that the markers get painted in solid (and not dashed)
- % if the 'lineStyle' is not solid (otherwise there is no problem)
- if ~strcmp(lineStyle, 'solid')
- markOptions{end+1} = 'solid';
- end
+ % print no lines
+ if isNone(lineStyle) || lineWidth==0
+ drawOptions = opts_add(drawOptions, 'only marks');
+ end
- % print no lines
- if strcmp(lineStyle,'none') || lineWidth==0
- drawOptions{end+1} = 'only marks';
- end
+ % get the marker color right
+ markerFaceColor = get(h, 'markerfaceColor');
+ markerEdgeColor = get(h, 'markeredgeColor');
- % get the marker color right
- markerFaceColor = get(h, 'markerfaceColor');
- markerEdgeColor = get(h, 'markeredgeColor');
- [tikzMarker, markOptions] = translateMarker(m2t, marker, ...
- markOptions, ~strcmp(markerFaceColor,'none'));
- if ~strcmp(markerFaceColor,'none')
- [m2t, xcolor] = getColor(m2t, h, markerFaceColor, 'patch');
- if ~isempty(xcolor)
- markOptions{end+1} = sprintf('fill=%s', xcolor);
- end
- end
- if ~strcmp(markerEdgeColor,'none') && ~strcmp(markerEdgeColor,'auto')
- [m2t, xcolor] = getColor(m2t, h, markerEdgeColor, 'patch');
- if ~isempty(xcolor)
- markOptions{end+1} = sprintf('draw=%s', xcolor);
- end
- end
+ [tikzMarker, markOptions] = translateMarker(m2t, marker, ...
+ markOptions, ~isNone(markerFaceColor));
- % add it all to drawOptions
- drawOptions{end+1} = sprintf('mark=%s', tikzMarker);
+ [m2t, markOptions] = setColor(m2t, h, markOptions, 'fill', markerFaceColor);
- if ~isempty(markOptions)
- mo = join(m2t, markOptions, ',');
- drawOptions{end+1} = ['mark options={', mo, '}'];
- end
- end
+ if ~strcmpi(markerEdgeColor,'auto')
+ [m2t, markOptions] = setColor(m2t, h, markOptions, 'draw', markerEdgeColor);
+ end
+ % add it all to drawOptions
+ drawOptions = opts_add(drawOptions, 'mark', tikzMarker);
+
+ if ~isempty(markOptions)
+ mo = opts_print(m2t, markOptions, ',');
+ drawOptions = opts_add(drawOptions, 'mark options', ['{' mo '}']);
+ 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
- % edgelength of a square vs. the diagonal length of it).
-
- 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
+ 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
- % 6pt is the default MATLAB marker size for all markers
- defaultMatlabMarkerSize = 6;
- isDefault = abs(matlabMarkerSize(1)-defaultMatlabMarkerSize)<m2t.tol;
- % matlabMarkerSize can be vector data, use first index to check the default
- % marker size. When the script also handles different markers together with
- % changing size and colour, the test should be extended to a vector norm, e.g.
- % sqrt(e^T*e) < tol, where e=matlabMarkerSize-defaultMatlabMarkerSize
-
- switch (matlabMarker)
- case 'none'
- tikzMarkerSize = [];
- case {'+','o','x','*','p','pentagram','h','hexagram'}
- % In MATLAB, the marker size refers to the edge length of a
- % square (for example) (~diameter), whereas in TikZ the
- % distance of an edge to the center is the measure (~radius).
- % Hence divide by 2.
- tikzMarkerSize = matlabMarkerSize(:) / 2;
- case '.'
- % as documented on the Matlab help pages:
- %
- % Note that MATLAB draws the point marker (specified by the '.'
- % symbol) at one-third the specified size.
- % The point (.) marker type does not change size when the
- % specified value is less than 5.
- %
- tikzMarkerSize = matlabMarkerSize(:) / 2 / 3;
- case {'s','square'}
- % Matlab measures the diameter, TikZ half the edge length
- tikzMarkerSize = matlabMarkerSize(:) / 2 / sqrt(2);
- case {'d','diamond'}
- % MATLAB measures the width, TikZ the height of the diamond;
- % the acute angle (at the top and the bottom of the diamond)
- % is a manually measured 75 degrees (in TikZ, and MATLAB
- % probably very similar); use this as a base for calculations
- tikzMarkerSize = matlabMarkerSize(:) / 2 / atan(75/2 *pi/180);
- case {'^','v','<','>'}
- % 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
+ 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)<m2t.tol;
+ % matlabMarkerSize can be vector data, use first index to check the default
+ % marker size. When the script also handles different markers together with
+ % changing size and color, the test should be extended to a vector norm, e.g.
+ % sqrt(e^T*e) < tol, where e=matlabMarkerSize-defaultMatlabMarkerSize
+
+ switch (matlabMarker)
+ case 'none'
+ tikzMarkerSize = [];
+ case {'+','o','x','*','p','pentagram','h','hexagram'}
+ % In MATLAB, the marker size refers to the edge length of a
+ % square (for example) (~diameter), whereas in TikZ the
+ % distance of an edge to the center is the measure (~radius).
+ % Hence divide by 2.
+ tikzMarkerSize = matlabMarkerSize(:) / 2;
+ case '.'
+ % as documented on the Matlab help pages:
+ %
+ % Note that MATLAB draws the point marker (specified by the '.'
+ % symbol) at one-third the specified size.
+ % The point (.) marker type does not change size when the
+ % specified value is less than 5.
+ %
+ tikzMarkerSize = matlabMarkerSize(:) / 2 / 3;
+ case {'s','square'}
+ % Matlab measures the diameter, TikZ half the edge length
+ tikzMarkerSize = matlabMarkerSize(:) / 2 / sqrt(2);
+ case {'d','diamond'}
+ % MATLAB measures the width, TikZ the height of the diamond;
+ % the acute angle (at the top and the bottom of the diamond)
+ % is a manually measured 75 degrees (in TikZ, and MATLAB
+ % probably very similar); use this as a base for calculations
+ tikzMarkerSize = matlabMarkerSize(:) / 2 / atan(75/2 *pi/180);
+ case {'^','v','<','>'}
+ % 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)
- % This function is used for getMarkerOptions() as well as drawScatterPlot().
-
- if(~ischar(matlabMarker))
- error('matlab2tikz:translateMarker:MarkerNotAString',...
- 'Variable matlabMarker is not a string.');
- end
+ 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
- userInfo(m2t, '\nMake sure to load \\usetikzlibrary{plotmarks} in the preamble.\n');
- 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{end+1} = 'rotate=180';
-
- case '<'
- tikzMarker = 'triangle';
- markOptions{end+1} = 'rotate=90';
-
- case '>'
- tikzMarker = 'triangle';
- markOptions{end+1} = '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',...
+ 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
+ userInfo(m2t, '\nMake sure to load \\usetikzlibrary{plotmarks} in the preamble.\n');
+ 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
+ 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
+% Draws a 'patch' graphics object (as found in contourf plots, for example).
+%
+ str = '';
- % This is for a quirky workaround for stacked bar plots.
- m2t.axesContainers{end}.nonbarPlotsPresent = true;
-
- % MATLAB's patch elements are matrices in which each column represents a a
- % distinct graphical object. Usually there is only one column, but there may
- % be more (-->hist plots, although they are now handled within the barplot
- % framework).
- XData = get(handle, 'XData');
- YData = get(handle, 'YData');
- ZData = get(handle, 'ZData');
-
- % see if individual color values are present
- CData = get(handle, 'CData');
-
- % If the data points are given in three vectors, we are dealing with one
- % single patch. If they are all matrices, then the columns of matrix
- % represent one patch each.
- if min(size(XData)) == 1
- % Make sure vectors are column vectors.
- XData = XData(:);
- YData = YData(:);
- ZData = ZData(:);
- CData = CData(:);
- end
+ if ~isVisible(handle)
+ return
+ end
- numPatches = size(XData, 2);
-
- for k = 1:numPatches
- xData = XData(:, k);
- yData = YData(:, k);
-
- if isempty(ZData)
- columnNames = {'x', 'y'};
- data = [xData(:), yData(:)];
- plotType = 'addplot';
- else
- zData = ZData(:, k);
- columnNames = {'x', 'y', 'z'};
- data = applyHgTransform(m2t, [xData(:), yData(:), zData(:)]);
- plotType = 'addplot3';
- m2t.currentAxesContain3dData = true;
- end
+ % This is for a quirky workaround for stacked bar plots.
+ m2t.axesContainers{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.axesContainers{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
- cData = CData(:, k);
- % -----------------------------------------------------------------------
- % gather the draw options
- % Make sure that legends are shown in area mode.
- drawOptions = {'area legend'};
-
- % Use the '\\' as a row separator to make sure that the generated figures
- % work in subplot environments.
- tableOptions = {'row sep=crcr'};
-
- [m2t, markerOptions] = getMarkerOptions(m2t, handle);
- drawOptions = [drawOptions, markerOptions];
-
- % Add the proper color map even if the map data isn't directly used in
- % the plot to make sure that we get correct color bars.
- if length(cData) == length(xData)
- % Add the color map.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
- end
- % If full color data is provided, we can use point meta color data.
- % For some reason, this only works for filled contours in Pgfplots, so
- % fall back to explicit color specifications for line plots.
- if length(cData) == length(xData) ...
- && ~strcmp(get(handle, 'FaceColor'), 'none')
- data = [data, cData(:)];
- drawOptions{end+1} = 'patch';
- columnNames{end+1} = 'c';
- tableOptions{end+1} = 'point meta=\thisrow{c}';
- else
- % Probably one color only, so things we're probably only dealing with
- % one patch here.
- % line width
- lineStyle = get(handle, 'LineStyle');
- lineWidth = get(handle, 'LineWidth');
- lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
- drawOptions = [drawOptions, lineOptions];
-
- % Find out color values.
- % fill color
- faceColor = get(handle, 'FaceColor');
- if ~strcmp(faceColor, 'none')
- [m2t, xFaceColor] = getColor(m2t, handle, faceColor, 'patch');
- drawOptions{end+1} = sprintf('fill=%s', xFaceColor);
- xFaceAlpha = get(handle, 'FaceAlpha');
- if abs(xFaceAlpha - 1.0) > m2t.tol
- drawOptions{end+1} = sprintf('opacity=%s', xFaceAlpha);
- end
- end
+ % Process fill, edge colors and shader
+ [m2t,patchOptions, s] = shaderOpts(m2t,handle,'patch');
- % draw color
- edgeColor = get(handle, 'EdgeColor');
- lineStyle = get(handle, 'LineStyle');
- if strcmp(lineStyle, 'none') || strcmp(edgeColor, 'none')
- drawOptions{end+1} = 'draw=none';
- else
- [m2t, xEdgeColor] = getColor(m2t, handle, edgeColor, 'patch');
- if isempty(xEdgeColor)
- % getColor() wasn't able to return a color. This is because
- % cdata was an actual vector with different values in it,
- % meaning that the color changes along the edge. This is the
- % case, for example, with waterfall() plots.
- % An actual color maps is needed here.
- %
- drawOptions{end+1} = 'mesh'; % or surf
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
- % Append upper and lower limit of the color mapping.
- clim = caxis;
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'point meta min', sprintf(m2t.ff, clim(1)));
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'point meta max', sprintf(m2t.ff, clim(2)));
- % Note:
- % Pgfplots can't currently use FaceColor and colormapped edge
- % color in one go. The option 'surf' makes sure that
- % colormapped edge colors are used. Face colors are not
- % displayed.
- else
- % getColor() returned a reasonable color value.
- drawOptions{end+1} = sprintf('draw=%s', xEdgeColor);
- end
- end
- end
+ % Return empty axes if no face or edge colors
+ if isNone(s.plotType)
+ return
+ end
- if ~m2t.currentHandleHasLegend
- % No legend entry found. Don't include plot in legend.
- drawOptions{end+1} = 'forget plot';
- 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
+ lineStyle = get(handle, 'LineStyle');
+ lineWidth = get(handle, 'LineWidth');
+ lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
+ drawOptions = opts_merge(drawOptions, lineOptions);
+
+ % 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);
+ [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!
- drawOpts = join(m2t, drawOptions, ',');
- % -----------------------------------------------------------------------
- if any(~isfinite(data(:)))
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'unbounded coords', 'jump');
- end
- % Plot the actual data.
- [m2t, table] = makeTable(m2t, columnNames, data);
-
- % Some patches need to be closed by adding a "--cycle"; some don't.
- % TODO find out when to insert --cycle
- str = sprintf('%s\n\\%s[%s]\ntable[%s] {%%\n%s};\n\n',...
- str, plotType, drawOpts, join(m2t, tableOptions, ', '), table);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- end
-end
-% =========================================================================
-function [m2t, str] = drawImage(m2t, handle)
+ drawOptions = getPatchShape(m2t, handle, drawOptions, patchOptions);
- str = [];
+ [m2t, drawOptions, Vertices, Faces, verticesTableOptions, ptType, ...
+ columnNames] = setColorsOfPatches(m2t, handle, drawOptions, ...
+ Vertices, Faces, verticesTableOptions, ptType, columnNames, ...
+ isFaceColorFlat, s);
+ end
- if ~isVisible(handle)
- return
- end
+ drawOptions = maybeShowInLegend(m2t.currentHandleHasLegend, drawOptions);
+ m2t = jumpAtUnboundCoords(m2t, Faces(:));
- % read x-, y-, and color-data
- xData = get(handle, 'XData');
- yData = get(handle, 'YData');
- cdata = get(handle, 'CData');
+ % 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
+ drawOpts = opts_print(m2t, drawOptions,',');
- m = size(cdata, 1);
- n = size(cdata, 2);
+ % Plot the actual data.
+ [m2t, verticesTable, tabOpts] = makeTable(m2t, columnNames, Vertices);
+ tabOpts = opts_merge(tabOpts, verticesTableOptions);
- if ~strcmp(get(m2t.currentHandles.gca,'Visible'), 'off')
- % Flip the image over as the PNG gets written starting at (0,0) that is,
- % the top left corner.
- % MATLAB quirk: In case the axes are invisible, don't do this.
- cdata = cdata(m:-1:1,:,:);
- end
+ str = sprintf('%s\n\\%s[%s]\ntable[%s] {%s}%s;\n',...
+ str, plotCmd, drawOpts, opts_print(m2t, 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
- if (m2t.cmdOpts.Results.imagesAsPng)
- m2t.imageAsPngNo = m2t.imageAsPngNo + 1;
- % ------------------------------------------------------------------------
- % draw a png image
- % Take the TikZ file base name and change the extension .png.
- [pathstr, name] = fileparts(m2t.tikzFileName);
- pngFileName = fullfile(pathstr, ...
- [name '-' num2str(m2t.imageAsPngNo) '.png']);
- pngReferencePath = fullfile(m2t.relativeDataPath, ...
- [name '-' num2str(m2t.imageAsPngNo) '.png']);
- pngReferencePath = TeXpath(pngReferencePath);
-
- % Get color indices for indexed color images and truecolor values
- % otherwise. Don't use ismatrix(), c.f.
- % <https://github.com/nschloe/matlab2tikz/issues/143>.
- if ndims(cdata) == 2
- [m2t, colorData] = cdata2colorindex(m2t, cdata, handle);
- else
- colorData = cdata;
- end
+ % TODO: this function can probably be split further, just look at all those
+ % parameters being passed.
- % flip the image if reverse
- if m2t.xAxisReversed
- colorData = colorData(:, n:-1:1, :);
- end
- if m2t.yAxisReversed
- colorData = colorData(m:-1:1, :, :);
- end
+ fvCData = get(handle,'FaceVertexCData');
+ rowsCData = size(fvCData,1);
- % Write an indexed or a truecolor image
- % Don't use ismatrix(), cf.
- % <https://github.com/nschloe/matlab2tikz/issues/143>.
- if (ndims(colorData) == 2)
- % According to imwrite's documentation there is support for 1-bit,
- % 2-bit, 4-bit and 8-bit (i.e., 256 colors) indexed images only.
- % When having more colors, a truecolor image must be generated and
- % used instead.
- if size(m2t.currentHandles.colormap, 1) <= 256
- imwrite(colorData, m2t.currentHandles.colormap, ...
- pngFileName, 'png');
- else
- imwrite(ind2rgb(colorData, m2t.currentHandles.colormap), ...
- pngFileName, 'png');
- end
- else
- imwrite(colorData, ...
- pngFileName, 'png', ...
- 'Alpha', get(handle, 'AlphaData'));
- end
- % -----------------------------------------------------------------------
- % dimensions of a pixel in axes units
- if n==1
- xLim = get(m2t.currentHandles.gca, 'XLim');
- xw = xLim(2) - xLim(1);
- else
- xw = (xData(end)-xData(1)) / (n-1);
- end
- if m==1
- yLim = get(m2t.currentHandles.gca, 'YLim');
- yw = yLim(2) - yLim(1);
- else
- yw = (yData(end)-yData(1)) / (m-1);
- end
+ % We have CData for either all faces or vertices
+ if rowsCData > 1
- opts = cell(0,2);
- opts = addToOptions(opts, 'xmin', sprintf(m2t.ff, xData(1 ) - xw/2));
- opts = addToOptions(opts, 'xmax', sprintf(m2t.ff, xData(end) + xw/2));
- opts = addToOptions(opts, 'ymin', sprintf(m2t.ff, yData(1 ) - yw/2));
- opts = addToOptions(opts, 'ymax', sprintf(m2t.ff, yData(end) + yw/2));
-
- str = sprintf('\\addplot [forget plot] graphics [%s] {%s};\n', ...
- prettyprintOpts(m2t, opts, ','), 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);
- else
- % -----------------------------------------------------------------------
- % draw the thing
- userWarning(m2t, ['It is highly recommended to use PNG data to store images.\n', ...
- 'Make sure to set ''imagesAsPng'' to true.']);
-
- % Generate uniformly distributed X, Y, although xData and yData may be
- % non-uniform.
- % This is MATLAB(R) behaviour.
- switch length(xData)
- case 2 % only the limits given; common for generic image plots
- hX = 1;
- case m % specific x-data is given
- hX = (xData(end)-xData(1)) / (length(xData)-1);
- otherwise
- error('drawImage:arrayLengthMismatch', ...
- 'Array lengths not matching (%d = size(cdata,1) ~= length(xData) = %d).', m, length(xData));
- end
- X = xData(1):hX:xData(end);
-
- switch length(yData)
- case 2 % only the limits given; common for generic image plots
- hY = 1;
- case n % specific y-data is given
- hY = (yData(end)-yData(1)) / (length(yData)-1);
- otherwise
- error('drawImage:arrayLengthMismatch', ...
- 'Array lengths not matching (%d = size(cdata,2) ~= length(yData) = %d).', n, length(yData));
- end
- Y = yData(1):hY:yData(end);
-
- m = length(X);
- n = length(Y);
- [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);
- % <http://www.mathworks.de/support/service_requests/Service_Request_Detail.do?ID=183481&filter=&sort=&statusorder=0&dateorder=0>.
- for i = 1:m
- for j = 1:n
- str = strcat(str, ...
- sprintf(['\\fill [%s] (axis cs:', m2t.ff,',', m2t.ff,') rectangle (axis cs:',m2t.ff,',',m2t.ff,');\n'], ...
- xcolor{m-i+1,j}, Y(j)-hY/2, X(i)-hX/2, Y(j)+hY/2, X(i)+hX/2 ));
- end
- end
- % ------------------------------------------------------------------------
- end
+ % Add the color map
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
- % Make sure that the axes are still visible above the image.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'axis on top', []);
-end
-% =========================================================================
-function [m2t, str] = drawHggroup(m2t, h)
+ % Determine if mapping is direct or scaled
+ CDataMapping = get(handle,'CDataMapping');
+ if strcmpi(CDataMapping, 'direct')
+ drawOptions = opts_add(drawOptions, 'colormap access','direct');
+ end
- % Octave doesn't have the handle() function, so there's no way to determine
- % the nature of the plot anymore at this point. Set to 'unknown' to force
- % fallback handling. This produces something for bar plots, for example.
- try
- cl = class(handle(h));
- catch %#ok
- cl = 'unknown';
- 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
- switch(cl)
- case 'specgraph.barseries'
- % hist plots and friends
- [m2t, str] = drawBarseries(m2t, h);
+ % Point meta as true color CData, i.e. RGB in [0,1]
+ if size(fvCData,2) == 3
+ % Create additional custom colormap
+ m2t.axesContainers{end}.options(end+1,:) = ...
+ {matlab2pgfplotsColormap(m2t, fvCData, 'patchmap'), []};
+ drawOptions = opts_append(drawOptions, 'colormap name','patchmap');
- case 'specgraph.stemseries'
- % stem plots
- [m2t, str] = drawStemSeries(m2t, h);
+ % Index into custom colormap
+ fvCData = (0:rowsCData-1)';
+ end
- case 'specgraph.stairseries'
- % stair plots
- [m2t, str] = drawStairSeries(m2t, h);
+ % 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
- case {'specgraph.areaseries'}
- % scatter plots
- [m2t,str] = drawAreaSeries(m2t, h);
+ else
+ % Scalar FaceVertexCData, i.e. one color mapping for all patches,
+ % used e.g. by Octave in drawing barseries
- case {'specgraph.quivergroup'}
- % quiver arrows
- [m2t, str] = drawQuiverGroup(m2t, h);
+ [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 ~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
+ options = opts_add(options, property, xcolor);
+ 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
+ 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
- case {'specgraph.errorbarseries'}
- % error bars
- [m2t,str] = drawErrorBars(m2t, h);
+ % Add draw options
+ drawOptions = opts_add(drawOptions, 'patch type', 'polygon');
+ drawOptions = opts_add(drawOptions, 'vertex count', ...
+ sprintf('%d', vertexCount));
+ end
- case {'specgraph.scattergroup'}
- % scatter plots
- [m2t,str] = drawScatterPlot(m2t, h);
+ 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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'unbounded coords', 'jump');
+ end
+end
+% ==============================================================================
+function [m2t, str] = drawImage(m2t, handle)
+ str = '';
- case {'specgraph.contourgroup', 'hggroup'}
- % handle all those the usual way
- [m2t, str] = handleAllChildren(m2t, h);
+ if ~isVisible(handle)
+ return
+ end
- case 'unknown'
- % Weird spurious class from Octave.
- [m2t, str] = handleAllChildren(m2t, h);
+ % read x-, y-, and color-data
+ xData = get(handle, 'XData');
+ yData = get(handle, 'YData');
+ cData = get(handle, 'CData');
- 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
+ if (m2t.cmdOpts.Results.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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'axis on top', []);
end
-% =========================================================================
-function [m2t,env] = drawSurface(m2t, handle)
+% ==============================================================================
+function [m2t, str] = imageAsPNG(m2t, handle, xData, yData, cData)
+ m2t.imageAsPngNo = m2t.imageAsPngNo + 1;
+ % ------------------------------------------------------------------------
+ % draw a png image
+ [pngFileName, pngReferencePath] = externalFilename(m2t, m2t.imageAsPngNo, '.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
- str = [];
- [m2t, opts, plotType] = surfaceOpts(m2t, handle);
+ m = size(cData, 1);
+ n = size(cData, 2);
- dx = get(handle, 'XData');
- dy = get(handle, 'YData');
- dz = get(handle, 'ZData');
- if any(~isfinite(dx(:))) || any(~isfinite(dy(:))) || any(~isfinite(dz(:)))
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'unbounded coords', 'jump');
+ alphaData = normalizedAlphaValues(m2t, get(handle,'AlphaData'), handle);
+ if numel(alphaData) == 1
+ alphaData = alphaData(ones(size(colorData(:,:,1))));
end
+ [colorData, alphaData] = flipImageIfAxesReversed(m2t, colorData, alphaData);
- [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(:)';
+ % Write an indexed or a truecolor image
+ hasAlpha = true;
+ if isfloat(alphaData) && all(alphaData(:) == 1)
+ alphaOpts = {};
+ hasAlpha = false;
+ else
+ alphaOpts = {'Alpha', alphaData};
end
- if isvector(dy)
- dy = dy(:) * ones(1,numrows);
+ if (ndims(colorData) == 2) %#ok don't use ismatrix (cfr. #143)
+ if size(m2t.currentHandles.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.currentHandles.colormap, ...
+ pngFileName, 'png');
+ else % use true-color instead
+ imwrite(ind2rgb(colorData, m2t.currentHandles.colormap), ...
+ pngFileName, 'png', alphaOpts{:});
+ end
+ else
+ imwrite(colorData, pngFileName, 'png', alphaOpts{:});
end
-
- % Add 'z buffer=sort' to the options to make sphere plot and the like not
- % overlap. There are different options here some of which may be more
- % advantagous in other situations; check out Pgfplots' manual here.
- % Since 'z buffer=sort' is computationally more expensive for LaTeX, try
- % to avoid it for the most default situations, e.g., when dx and dy are
- % rank-1-matrices.
- if any(~isnan(dx(1,:)) & dx(1,:) ~= dx(2,:)) ...
- || any(~isnan(dy(:,1)) & dy(:,1) ~= dy(:,2))
- opts{end+1} = 'z buffer=sort';
+ % -----------------------------------------------------------------------
+ % dimensions of a pixel in axes units
+ if n == 1
+ xLim = get(m2t.currentHandles.gca, 'XLim');
+ xw = xLim(2) - xLim(1);
+ else
+ xw = (xData(end)-xData(1)) / (n-1);
+ end
+ if m == 1
+ yLim = get(m2t.currentHandles.gca, 'YLim');
+ yw = yLim(2) - yLim(1);
+ else
+ yw = (yData(end)-yData(1)) / (m-1);
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).
- %
+ 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));
+
+ str = sprintf('\\addplot [forget plot] graphics [%s] {%s};\n', ...
+ opts_print(m2t, opts, ','), 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)
- % Check if we need extra CData.
- colors = get(handle, 'CData');
- if length(size(colors)) == 3 && size(colors, 3) == 3
- % Explicit RGB-coded colors.
- opts{end+1} = 'mesh/color input=explicit';
-
- formatType = 'table[row sep=crcr,header=false,meta index=3]';
- r = colors(:, :, 1);
- g = colors(:, :, 2);
- b = colors(:, :, 3);
- colorFormat = join(m2t, repmat({m2t.ff},[3 1]),',');
- color = arrayfun(@(r,g,b)(sprintf(colorFormat,r,g,b)), ...
- r(:),g(:),b(:),'UniformOutput',false);
-
- %formatType = 'table[row sep=crcr,header=false]';
- %formatString = [m2t.ff, ' ', m2t.ff, ' ', m2t.ff, '\\\\\n'];
- %data = applyHgTransform(m2t, [dx(:), dy(:), dz(:)]);
-
- %elseif length(size(colors)) > 2 || any(isnan(colors(:)))
- % needsPointmeta = false;
+ % set up cData
+ if ndims(cData) == 3
+ cData = cData(end:-1:1,:,:);
else
- opts{end+1} = matlab2pgfplotsColormap(m2t, ...
- m2t.currentHandles.colormap);
- % If NaNs are present in the color specifications, don't use them for
- % Pgfplots; they may be interpreted as strings there. The option
- % 'header=false' will be explicitly added.
- % 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(colors)) ...
- | (abs(colors - dz) > 1.0e-10));
- if needsPointmeta
- % Get color map.
- formatType = 'table[row sep=crcr,header=false,meta index=3]';
- opts{end+1} = 'point meta=explicit';
- color = colors(:);
- else
- formatType = 'table[row sep=crcr,header=false]';
- color = '';
- end
+ cData = cData(end:-1:1,:);
end
- data = applyHgTransform(m2t, [dx(:), dy(:), dz(:)]);
- % Add mesh/rows=<num 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{end+1} = sprintf('mesh/rows=%d', numrows);
- opts = join(m2t, opts, ',\n');
- str = [str, sprintf(['\n\\addplot3[%%\n%s,\n', opts ,']'], plotType)];
+ % Generate uniformly distributed X, Y, although xData and yData may be
+ % non-uniform.
+ % This is MATLAB(R) behavior.
+ switch length(xData)
+ case 2 % only the limits given; common for generic image plots
+ hX = 1;
+ case size(cData,1) % specific x-data is given
+ hX = (xData(end)-xData(1)) / (length(xData)-1);
+ otherwise
+ error('drawImage:arrayLengthMismatch', ...
+ 'Array lengths not matching (%d = size(cdata,1) ~= length(xData) = %d).', size(cData,1), length(xData));
+ end
+ X = xData(1):hX:xData(end);
- % TODO Check if surf plot is 'spectrogram' or 'surf' and run corresponding
- % algorithm.
- % Spectrograms need to have the grid removed,
- % m2t.axesContainers{end}.options{end+1} = 'grid=none';
- % Here is where everything is put together.
- tabArgs = {'',data(:,1),'',data(:,2),'',data(:,3)};
- if ~isempty(color)
- tabArgs(end+1:end+2) = {'',color};
+ switch length(yData)
+ case 2 % only the limits given; common for generic image plots
+ hY = 1;
+ case size(cData,2) % specific y-data is given
+ hY = (yData(end)-yData(1)) / (length(yData)-1);
+ otherwise
+ error('drawImage:arrayLengthMismatch', ...
+ 'Array lengths not matching (%d = size(cData,2) ~= length(yData) = %d).', size(cData,2), length(yData));
+ end
+ Y = yData(1):hY:yData(end);
+ [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);
+ % <http://www.mathworks.de/support/service_requests/Service_Request_Detail.do?ID=183481&filter=&sort=&statusorder=0&dateorder=0>.
+ % An alternative approach could be to use 'surf' or 'patch' of pgfplots
+ % with inline tables.
+ str = '';
+ m = length(X);
+ n = length(Y);
+ for i = 1:m
+ for j = 1:n
+ str = [str, ...
+ 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
+ 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 revesed 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.currentHandles.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.currentHandles.gcf, 'Alphamap'));
+ otherwise
+ error('matlab2tikz:UnknownAlphaMapping', ...
+ 'Unknown alpha mapping "%s"', alphaMapping);
end
- [m2t, table] = makeTable(m2t, tabArgs{:});
- str = sprintf('%s\n%s {%s};\n', str, formatType, table);
- env = str;
+ 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 = get(h,'children');
+ 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
- % 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.
+ % Only first child can be in the legend
+ m2t.currentHandleHasLegend = false;
+ end
+ str = strcat(str,sprintf('\n'));
+ str = [str{:}];
- if m2t.cmdOpts.Results.automaticLabels
- [m2t, label] = addLabel(m2t);
- str = [str, label]; %#ok
+ % Restore group's legend state
+ m2t.currentHandleHasLegend = hasLegend;
end
-
- m2t.currentAxesContain3dData = true;
end
-% =========================================================================
-function [m2t, str] = drawText(m2t, handle)
- % Adding text node anywhere in the axex 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.
-
- 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.
- % 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'))) ...
- || strcmp(get(handle, 'Visible'), 'off') ...
- || (strcmp(get(handle, 'HandleVisibility'), 'off') && ~m2t.cmdOpts.Results.showHiddenStrings)
- return;
- end
+% ==============================================================================
+function [m2t, str] = drawContourHG2(m2t, h)
+ str = '';
+
+ % 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 = strcmpi(get(h,'Fill'),'on');
+ if isFilled
+ [m2t, str] = drawFilledContours(m2t, str, h, contours, istart, nrows);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % get required properties
- color = get(handle, 'Color');
- [m2t, tcolor] = getColor(m2t, handle, color, 'patch');
- bgColor = get(handle,'BackgroundColor');
- EdgeColor = get(handle, 'EdgeColor');
- HorizontalAlignment = get(handle, 'HorizontalAlignment');
- String = get(handle, 'String');
- Interpreter = get(handle, 'Interpreter');
- String = prettyPrint(m2t, String, Interpreter);
- % For now, don't handle multiline strings.
- % Sometimes, the cells are nested; take care of this, too.
- while iscell(String)
- String = String{1};
- end
- VerticalAlignment = get(handle, 'VerticalAlignment');
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % translate them to pgf style
- style = cell(0);
- if ~strcmpi(bgColor,'none')
- [m2t, bcolor] = getColor(m2t, handle, bgColor, 'patch');
- style{end+1} = ['fill=' bcolor];
- end
- switch VerticalAlignment
- case {'top', 'cap'}
- style{end+1} = 'below';
- case {'baseline', 'bottom'}
- style{end+1} = 'above';
- end
- switch HorizontalAlignment
- case 'left'
- style{end+1} = 'right';
- case 'right'
- style{end+1} = 'left';
- end
- % remove invisible border around \node to make the text align precisely
- style{end+1} = 'inner sep=0mm';
-
- % Add rotation.
- rot = get(handle, 'Rotation');
- if rot ~= 0.0
- style{end+1} = sprintf(['rotate=', m2t.ff], rot);
- end
+ else
+ % Add colormap
+ cmap = m2t.currentHandles.colormap;
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ matlab2pgfplotsColormap(m2t, cmap));
- fontStyle = getFontStyle(m2t, handle);
- if ~isempty(fontStyle)
- style{end+1} = prettyprintOpts(m2t, fontStyle, ', ');
- end
+ % Contour table in Matlab format
+ plotoptions = opts_new();
+ plotoptions = opts_add(plotoptions,'contour prepared');
+ plotoptions = opts_add(plotoptions,'contour prepared format','matlab');
- style{end+1} = ['text=' tcolor];
- if ~strcmp(EdgeColor, 'none')
- [m2t, ecolor] = getColor(m2t, handle, EdgeColor, 'patch');
- style{end+1} = ['draw=', ecolor];
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % plot the thing
- pos = get(handle, 'Position');
- units = get(handle, 'Units');
- if length(pos) == 2
- if strcmp(units, 'normalized')
- posString = sprintf(['(rel axis cs:', m2t.ff, ',', m2t.ff, ')'], pos);
- else
- posString = sprintf(['(axis cs:', m2t.ff, ',', m2t.ff, ')'], pos);
+ % Labels
+ if strcmpi(get(h,'ShowText'),'off')
+ plotoptions = opts_add(plotoptions,'contour/labels','false');
end
- xlim = get(m2t.currentHandles.gca,'XLim');
- ylim = get(m2t.currentHandles.gca,'YLim');
- if pos(1) < xlim(1) || pos(1) > xlim(2) ...
- || pos(2) < ylim(1) || pos(2) > ylim(2)
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'clip', 'false');
- end
- elseif length(pos) == 3
- pos = applyHgTransform(m2t, pos);
- if strcmp(units, 'normalized')
- posString = sprintf(['(rel axis cs:',m2t.ff,',',m2t.ff,',',m2t.ff,')'], pos);
- else
- posString = sprintf(['(axis cs:',m2t.ff,',',m2t.ff,',',m2t.ff,')'], pos);
- end
+ % Make contour table
+ [m2t, table, tabOpts] = makeTable(m2t, {'',''}, contours);
+
+ str = sprintf('\\addplot[%s] table[%s] {%%\n%s};\n', ...
+ opts_print(m2t, plotoptions, ', '),...
+ opts_print(m2t, tabOpts, ','), table);
- xlim = get(m2t.currentHandles.gca, 'XLim');
- ylim = get(m2t.currentHandles.gca, 'YLim');
- zlim = get(m2t.currentHandles.gca, 'ZLim');
- if pos(1) < xlim(1) || pos(1) > xlim(2) ...
- || pos(2) < ylim(1) || pos(2) > ylim(2) ...
- || pos(3) < zlim(1) || pos(3) > zlim(2)
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'clip', 'false');
- end
- else
- error('matlab2tikz:drawText', ...
- 'Illegal text position specification.');
- end
- str = sprintf('\\node[%s]\nat %s {%s};\n', ...
- join(m2t, style,', '), posString, String);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-end
-% =========================================================================
-function [m2t, str] = drawRectangle(m2t, handle)
- 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 strcmp(get(handle, 'Visible'), 'off') ...
- || strcmp(get(handle, 'HandleVisibility'), 'off')
- return;
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, str, 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.
+
+ % 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).
+ 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
- % TODO handle Curvature = [0.8 0.4]
+ % Continue numbering
+ from = from + nmembers;
+ ifree = ifree & ~igroup;
+ end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- lineStyle = get(handle, 'LineStyle');
- lineWidth = get(handle, 'LineWidth');
- if (strcmp(lineStyle,'none') || lineWidth==0)
- return
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Get draw options.
- lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
-
- colorOptions = cell(0);
- % fill color
- faceColor = get(handle, 'FaceColor');
- if ~strcmp(faceColor, 'none')
- [m2t, xFaceColor] = getColor(m2t, handle, faceColor, 'patch');
- colorOptions{end+1} = sprintf('fill=%s', xFaceColor);
- end
- % draw color
- edgeColor = get(handle, 'EdgeColor');
- lineStyle = get(handle, 'LineStyle');
- if strcmp(lineStyle, 'none') || strcmp(edgeColor, 'none')
- colorOptions{end+1} = 'draw=none';
- else
- [m2t, xEdgeColor] = getColor(m2t, handle, edgeColor, 'patch');
- colorOptions{end+1} = sprintf('draw=%s', xEdgeColor);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- pos = pos2dims(get(handle, 'Position'));
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- drawOptions = [lineOptions, colorOptions];
- % plot the thing
- str = sprintf(['\\draw[%s] (axis cs:',m2t.ff,',',m2t.ff,') rectangle (axis cs:',m2t.ff,',',m2t.ff,');\n'], ...
- join(m2t, drawOptions,', '), pos.left, pos.bottom, pos.right, pos.top ...
- );
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-end
-% =========================================================================
-function [m2t,surfOptions,plotType] = surfaceOpts(m2t, handle)
-
- faceColor = get(handle, 'FaceColor');
- edgeColor = get(handle, 'EdgeColor');
-
- % Check for surf or mesh plot. Second argument in if-check corresponds to
- % default values for mesh plot in MATLAB.
- if strcmpi(faceColor, 'none') || ...
- (strcmpi(edgeColor, 'flat') && isequal(faceColor, [1 1 1]))
- plotType = 'mesh';
- else
- plotType = 'surf';
- end
+ % Reorder the contours
+ cellcont(order,1) = cellcont;
+
+ % Add zero level fill
+ xdata = get(h,'XData');
+ ydata = get(h,'YData');
+ 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
+ drawOpts = opts_new();
+
+ % Get color
+ zval = cellcont{ii}(1,1);
+ [m2t, xcolor] = getColor(m2t,h,zval,'image');
+ drawOpts = opts_add(drawOpts,'fill',xcolor);
+
+ % Toggle legend entry
+ hasLegend = ii == 1 && m2t.currentHandleHasLegend;
+ drawOpts = maybeShowInLegend(hasLegend, drawOpts);
+
+ % Print table
+ [m2t, table, tabOpts] = makeTable(m2t, columnNames, cellcont{ii}(2:end,:));
+
+ % Fillplot
+ str = sprintf('%s\\addplot[%s] table[%s] {%%\n%s};\n', ...
+ str, opts_print(m2t,drawOpts,','), opts_print(m2t,tabOpts,','), table);
+ end
+end
+% ==============================================================================
+function [m2t, str] = drawHggroup(m2t, h)
+% Octave doesn't have the handle() function, so there's no way to determine
+% the nature of the plot anymore at this point. Set to 'unknown' to force
+% fallback handling. This produces something for bar plots, for example.
+% #COMPLEX: big switch-case
+ try
+ cl = class(handle(h));
+ catch %#ok
+ cl = 'unknown';
+ end
- surfOptions = cell(0);
+ 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);
- % Set opacity if FaceAlpha < 1 in MATLAB
- faceAlpha = get(handle, 'FaceAlpha');
- if isnumeric(faceAlpha) && faceAlpha ~= 1.0
- surfOptions{end+1} = sprintf(['opacity=', m2t.ff], faceAlpha);
- end
+ case 'unknown'
+ % Weird spurious class from Octave.
+ [m2t, str] = handleAllChildren(m2t, h);
- % TODO Revisit this selection and create a bunch of test plots.
- if strcmpi(plotType, 'surf')
- % Set shader for surface plot.
- if strcmpi(edgeColor, 'none') && strcmpi(faceColor, 'flat')
- surfOptions{end+1} = 'shader=flat';
- elseif isnumeric(edgeColor) && strcmpi(faceColor, 'flat')
- [m2t, xEdgeColor] = getColor(m2t, handle, edgeColor, 'patch');
- % same as shader=flat,draw=\pgfkeysvalueof{/pgfplots/faceted color}
- if all(get(handle,'ZData')==0) %pcolor plot
- surfOptions{end+1} = 'shader=flat corner';
- else % regular surface plot
- surfOptions{end+1} = 'shader=faceted';
- end
- surfOptions{end+1} = sprintf('draw=%s', xEdgeColor);
- elseif strcmpi(edgeColor, 'none') && strcmpi(faceColor, 'interp')
- surfOptions{end+1} = 'shader=interp';
- else
- surfOptions{end+1} = 'shader=faceted interp';
- end
- elseif strcmpi(plotType, 'mesh')
- surfOptions{end+1} = 'shader=flat';
- else
- error('matlab2tikz:surfaceOpts', ...
- 'Illegal plot type ''%s''.', plotType);
- end
+ 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 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 = findobj(m2t.currentHandles.gcf,'Tag','scribeOverlay');
+ annotHandles = findobj(get(annotPanes,'Children'),'Visible','on');
+ else
+ annotHandles = findobj(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 %#ok
+ 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.axesContainers{end} = addChildren(m2t.axesContainers{end}, str);
+end
+% ==============================================================================
+function [m2t,str] = drawSurface(m2t, h)
+
+ [m2t, opts, s] = shaderOpts(m2t, h,'surf');
+ tabOpts = 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.axesContainers{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.axesContainers{end}.options(end+1,:) = ...
+ {matlab2pgfplotsColormap(m2t, CData, 'patchmap'), []};
+
+ % Index into custom colormap
+ color = (0:nrows-1)';
+
+ tabOpts = opts_add(tabOpts, 'colormap name','surfmap');
+ else
+ opts = opts_add(opts,matlab2pgfplotsColormap(m2t, m2t.currentHandles.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
+ tabOpts = opts_add(tabOpts, 'point meta','\thisrow{c}');
+
+ data = [data, color];
+
+ % Add mesh/rows=<num 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(m2t, opts, ','));
+
+ % Print the data
+ [m2t, table, tabOptsExtra] = makeTable(m2t, columnNames, data);
+ tabOpts = opts_merge(tabOptsExtra, tabOpts);
+
+ % Here is where everything is put together
+ str = sprintf('%s\ntable[%s] {%%\n%s};\n', ...
+ str, opts_print(m2t, 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, str] = addLabel(m2t, str);
+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.axesContainers{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'))) ...
+ || strcmpi(get(handle, 'Visible'), 'off') ...
+ || (strcmpi(get(handle, 'HandleVisibility'), 'off') && ...
+ ~m2t.cmdOpts.Results.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.
+
+ str = '';
+
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % get required properties
+ String = get(handle, 'String');
+ Interpreter = get(handle, 'Interpreter');
+ String = prettyPrint(m2t, String, Interpreter);
+ % Concatenate multiple lines
+ String = join(m2t, String, '\\');
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % 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);
+
+ style = opts_merge(style, getFontStyle(m2t, handle));
+
+ color = get(handle, 'Color');
+ [m2t, tcolor] = getColor(m2t, handle, color, 'patch');
+ style = opts_add(style, 'text', tcolor);
+
+ EdgeColor = get(handle, 'EdgeColor');
+ [m2t, style] = setColor(m2t, handle, style, 'draw', EdgeColor);
+
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % plot the thing
+ [m2t, posString] = getPositionOfText(m2t, handle);
+
+ str = sprintf('\\node[%s]\nat %s {%s};\n', ...
+ opts_print(m2t, style, ', '), posString, String);
+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.axesContainers{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.currentHandles.gca, 'XLim',[-Inf +Inf]);
+ ylim = getOrDefault(m2t.currentHandles.gca, 'YLim',[-Inf +Inf]);
+ zlim = getOrDefault(m2t.currentHandles.gca, 'ZLim',[-Inf +Inf]);
+ is3D = m2t.axesContainers{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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ '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) || strcmpi(get(h, 'HandleVisibility'), 'off')
+ return;
+ end
+
+ % TODO handle Curvature = [0.8 0.4]
+
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % Get draw options.
+ lineStyle = get(h, 'LineStyle');
+ lineWidth = get(h, 'LineWidth');
+
+ drawOptions = getLineOptions(m2t, lineStyle, lineWidth);
+
+ [m2t, drawOptions] = getRectangleFaceOptions(m2t, h, drawOptions);
+ [m2t, drawOptions] = getRectangleEdgeOptions(m2t, h, drawOptions);
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ pos = pos2dims(get(h, 'Position'));
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % plot the thing
+ str = sprintf(['\\draw[%s] (axis cs:',m2t.ff,',',m2t.ff, ')', ...
+ ' rectangle (axis cs:',m2t.ff,',',m2t.ff,');\n'], ...
+ opts_print(m2t, drawOptions, ', '), ...
+ 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')
+ 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')
+ 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 [m2t, str] = drawScatterPlot(m2t, h)
+ str = '';
+
+ xData = get(h, 'XData');
+ yData = get(h, 'YData');
+ zData = get(h, 'ZData');
+ cData = get(h, 'CData');
+ sData = get(h, 'SizeData');
+
+ matlabMarker = get(h, 'Marker');
+ markerFaceColor = get(h, 'MarkerFaceColor');
+ markerEdgeColor = get(h, 'MarkerEdgeColor');
+ hasFaceColor = ~isNone(markerFaceColor);
+ hasEdgeColor = ~isNone(markerEdgeColor);
+ markOptions = opts_new();
+ [tikzMarker, markOptions] = translateMarker(m2t, matlabMarker, ...
+ markOptions, hasFaceColor);
+
+ constMarkerkSize = length(sData) == 1; % constant marker size
+
+ % Rescale marker size (not definitive, follow discussion in #316)
+ sData = translateMarkerSize(m2t, matlabMarker, sqrt(sData)/2);
+
+ drawOptions = opts_new();
+ if length(cData) == 3
+ [m2t, drawOptions] = getScatterOptsOneColor(m2t, h, drawOptions, ...
+ markOptions, tikzMarker, ...
+ cData, sData, constMarkerkSize);
+ elseif size(cData,2) == 3
+ drawOptions = getScatterOptsRGB(m2t, drawOptions);
+ else
+ [m2t, drawOptions] = getScatterOptsColormap(m2t, h, drawOptions, ...
+ markOptions, tikzMarker, hasEdgeColor, hasFaceColor);
+ end
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % Plot the thing.
+ [env, data, sColumn] = organizeScatterData(m2t, xData, yData, zData, sData);
+
+ if ~constMarkerkSize %
+ drawOptions = opts_add(drawOptions, 'visualization depends on', ...
+ ['{\thisrowno{', num2str(sColumn), '} \as \perpointmarksize}']);
+ drawOptions = opts_add(drawOptions, ...
+ 'scatter/@pre marker code/.append style', ...
+ '{/tikz/mark size=\perpointmarksize}');
+ end
+ drawOpts = opts_print(m2t, drawOptions, ',');
- str = [];
-
- xData = get(h, 'XData');
- yData = get(h, 'YData');
- zData = get(h, 'ZData');
- cData = get(h, 'CData');
- sData = get(h, 'SizeData');
-
- matlabMarker = get(h, 'Marker');
- markerFaceColor = get(h, 'MarkerFaceColor');
- markerEdgeColor = get(h, 'MarkerEdgeColor');
- hasFaceColor = ~strcmp(markerFaceColor,'none');
- hasEdgeColor = ~strcmp(markerEdgeColor,'none');
- markOptions = cell(0);
- [tikzMarker, markOptions] = translateMarker(m2t, matlabMarker, ...
- markOptions, hasFaceColor);
-
- if length(sData) == 1
- constMarkerkSize = true; % constant marker size
- else % changing marker size; rescale the size data according to the marker
- constMarkerkSize = false;
- % MathWorks says
- % <http://www.mathworks.se/help/matlab/ref/scattergroupproperties.html>:
- % SizeData
- % Size of markers in square points. Area of the marker in the scatter
- % graph in units of points. Since there are 72 points to one inch, to
- % specify a marker that has an area of one square inch you would use a
- % value of 72^2.
- %
- % Pgfplots on the other hand uses the radius.
- sData = sqrt(sData / pi);
- end
+ [data, metaPart] = addCDataToScatterData(data, cData);
- if length(cData) == 3
- % No special treatment for the colors or markers are needed.
- % All markers have the same color.
- if hasFaceColor && ~strcmp(markerFaceColor,'flat');
- [m2t, xcolor] = getColor(m2t, h, markerFaceColor,'patch');
- else
- [m2t, xcolor] = getColor(m2t, h, cData, 'patch');
- end
- if hasEdgeColor && ~strcmp(markerEdgeColor,'flat');
+ % The actual printing.
+ nColumns = size(data, 2);
+ [m2t, table, tabOpts] = makeTable(m2t, repmat({''},1,nColumns), data);
+ tabOpts = opts_merge(tabOpts, metaPart);
+
+ str = sprintf('%s\\%s[%s] plot table[%s]{%s};\n', str, env, ...
+ drawOpts, opts_print(m2t, tabOpts, ','), table);
+end
+% ==============================================================================
+function [m2t, drawOptions] = getScatterOptsOneColor(m2t, h, drawOptions, ...
+ markOptions, tikzMarker, cData, sData, constMarkerkSize)
+% 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', cData);
+ [m2t, ecolor, hasEdgeColor] = getColorOfMarkers(m2t, h, 'MarkerEdgeColor', cData);
+
+ if constMarkerkSize
+ drawOptions = opts_add(drawOptions, 'only marks');
+ drawOptions = opts_add(drawOptions, 'mark', tikzMarker);
+ drawOptions = opts_add(drawOptions, 'mark options', ...
+ ['{' opts_print(m2t, markOptions, ',') '}']);
+ drawOptions = opts_add(drawOptions, 'mark size', ...
+ sprintf('%.4fpt', sData)); % FIXME: investigate whether to use `m2t.ff`
+ if hasFaceColor && hasEdgeColor
+ drawOptions = opts_add(drawOptions, 'draw', ecolor);
+ drawOptions = opts_add(drawOptions, 'fill', xcolor);
+ else
+ drawOptions = opts_add(drawOptions, 'color', xcolor);
+ end
+ else % if changing marker size but same color on all marks
+ markerOptions = opts_new();
+ markerOptions = opts_add(markerOptions, 'mark', tikzMarker);
+ markerOptions = opts_add(markerOptions, 'mark options', ...
+ ['{' opts_print(m2t, markOptions, ',') '}']);
+ 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, 'scatter');
+ drawOptions = opts_add(drawOptions, 'only marks');
+ drawOptions = opts_add(drawOptions, 'color', xcolor);
+ drawOptions = opts_add(drawOptions, 'mark', tikzMarker);
+ drawOptions = opts_add(drawOptions, 'mark options', ...
+ ['{' opts_print(m2t, markOptions, ',') '}']);
+
+ if ~hasFaceColor
+ drawOptions = opts_add(drawOptions, ...
+ 'scatter/use mapped color', xcolor);
+ else
+ drawOptions = opts_add(drawOptions, ...
+ 'scatter/use mapped color', ...
+ ['{' opts_print(m2t, markerOptions,',') '}']);
+ end
+ end
+end
+function drawOptions = getScatterOptsRGB(m2t, drawOptions)
+% scatter plots with each marker a different RGB color (not yet supported!)
+ drawOptions = opts_add(drawOptions, 'only marks');
+ 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
+end
+function [m2t, drawOptions] = getScatterOptsColormap(m2t, h, drawOptions, ...
+ markOptions, tikzMarker, hasEdgeColor, hasFaceColor)
+% scatter plot where the colors are set using a color map
+ markerOptions = opts_new();
+ markerOptions = opts_add(markerOptions, 'mark', tikzMarker);
+ markerOptions = opts_add(markerOptions, 'mark options', ...
+ ['{' opts_print(m2t, markOptions, ',') '}']);
+
+ if hasEdgeColor && hasFaceColor
[m2t, ecolor] = getColor(m2t, h, markerEdgeColor,'patch');
- else
- [m2t, ecolor] = getColor(m2t, h, cData, 'patch');
- end
- if constMarkerkSize % if constant marker size, do nothing special
- drawOptions = { 'only marks', ...
- ['mark=' tikzMarker], ...
- ['mark options={', join(m2t, markOptions, ','), '}'] };
- if hasFaceColor && hasEdgeColor
- drawOptions{end+1} = { ['draw=' ecolor], ...
- ['fill=' xcolor] };
- else
- drawOptions{end+1} = ['color=' xcolor];
- end
- else % if changing marker size but same color on all marks
- markerOptions = { ['mark=', tikzMarker], ...
- ['mark options={', join(m2t, markOptions, ','), '}'] };
- if hasEdgeColor
- markerOptions{end+1} = ['draw=' ecolor];
- else
- markerOptions{end+1} = ['draw=' xcolor];
- end
- if hasFaceColor
- markerOptions{end+1} = ['fill=' xcolor];
- end
- % for changing marker size, the 'scatter' option has to be added
- drawOptions = { 'scatter', ...
- 'only marks', ...
- ['color=' xcolor], ...
- ['mark=' tikzMarker], ...
- ['mark options={', join(m2t, markOptions, ','), '}'] };
- if ~hasFaceColor
- drawOptions{end+1} = { ['scatter/use mapped color=' xcolor] };
- else
- drawOptions{end+1} = { ['scatter/use mapped color={', join(m2t, markerOptions,','), '}'] };
- end
- end
- elseif size(cData,2) == 3
- drawOptions = { 'only marks' ...
- % TODO Get this in order as soon as Pgfplots can do "scatter rgb".
-% 'scatter rgb' ...
- };
- else
- markerOptions = { ['mark=', tikzMarker], ...
- ['mark options={', join(m2t, markOptions, ','), '}'] };
- if hasEdgeColor && hasFaceColor
- [m2t, ecolor] = getColor(m2t, h, markerEdgeColor,'patch');
- markerOptions{end+1} = ['draw=' ecolor];
- else
- markerOptions{end+1} = 'draw=mapped color';
- end
- if hasFaceColor
- markerOptions{end+1} = 'fill=mapped color';
- end
- drawOptions = { 'scatter', ...
- 'only marks', ...
- 'scatter src=explicit', ...
- ['scatter/use mapped color={', join(m2t, markerOptions,','), '}'] };
- % Add color map.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Plot the thing.
- if isempty(zData)
- env = 'addplot';
- if length(sData) == 1
- nColumns = 2;
- data = [xData(:), yData(:)];
- else
- nColumns = 3;
- sColumn = 2;
- data = [xData(:), yData(:), sData(:)];
- end
- else
- env = 'addplot3';
- m2t.currentAxesContain3dData = true;
- if length(sData) == 1
- nColumns = 3;
- data = applyHgTransform(m2t, [xData(:),yData(:),zData(:)]);
- else
- nColumns = 4;
- sColumn = 3;
- data = applyHgTransform(m2t, [xData(:),yData(:),zData(:),sData(:)]);
- end
- end
- if ~constMarkerkSize %
- drawOptions{end+1} = { ['visualization depends on={\thisrowno{', num2str(sColumn), '} \as \perpointmarksize}'], ...
- 'scatter/@pre marker code/.append style={/tikz/mark size=\perpointmarksize}' };
- end
- drawOpts = join(m2t, drawOptions, ',');
+ markerOptions = opts_add(markerOptions, 'draw', ecolor);
+ else
+ markerOptions = opts_add(markerOptions, 'draw', 'mapped color');
+ end
+ if hasFaceColor
+ markerOptions = opts_add(markerOptions, 'fill', 'mapped color');
+ end
+ drawOptions = opts_add(drawOptions, 'scatter');
+ drawOptions = opts_add(drawOptions, 'only marks');
+ drawOptions = opts_add(drawOptions, 'scatter src', 'explicit');
+ drawOptions = opts_add(drawOptions, 'scatter/use mapped color', ...
+ ['{' opts_print(m2t, markerOptions, ',') '}']);
+ % Add color map.
+ m2t.axesContainers{end}.options = opts_append(...
+ m2t.axesContainers{end}.options, ...
+ matlab2pgfplotsColormap(m2t, m2t.currentHandles.colormap), []);
+end
+% ==============================================================================
+function [env, data, sColumn] = organizeScatterData(m2t, xData, yData, zData, sData)
+% reorganizes the {X,Y,Z,S} data into a single matrix
+ sColumn = [];
+ if ~m2t.axesContainers{end}.is3D
+ env = 'addplot';
+ if length(sData) == 1
+ data = [xData(:), yData(:)];
+ else
+ sColumn = 2;
+ data = [xData(:), yData(:), sData(:)];
+ end
+ else
+ env = 'addplot3';
+ if length(sData) == 1
+ data = applyHgTransform(m2t, [xData(:),yData(:),zData(:)]);
+ else
+ sColumn = 3;
+ data = applyHgTransform(m2t, [xData(:),yData(:),zData(:),sData(:)]);
+ end
+ end
+end
+% ==============================================================================
+function [data, metaOptions] = addCDataToScatterData(data, cData)
+% adds the cData vector to the data table of a scatter plot
+ metaOptions = opts_new();
+ 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));
+ else
+ metaOptions = opts_add(metaOptions, ...
+ 'meta index', sprintf('%d', size(data,2)));
+ 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)
- metaPart = '';
- if length(cData) == 3
- % If size(cData,1)==1, then all the colors are the same and have
- % already been accounted for above.
+ if ~isVisible(h)
+ str = '';
+ return;
+ end
- 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));
- else
- metaPart = sprintf('meta index=%d',size(data,2));
- data = [data, cData(:)];
- nColumns = nColumns + 1;
- end
+ % Init options
+ opts = opts_new();
- % The actual printing.
- [m2t, table] = makeTable(m2t, repmat({''},1,nColumns), data);
- str = sprintf('%s\\%s[%s] plot table[row sep=crcr,%s]{%s};\n', str, env, ...
- drawOpts, metaPart, table);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % Face
+ faceColor = get(h,'FaceColor');
+ [m2t, opts] = setColor(m2t, h, opts, 'fill', faceColor, 'none');
+
+ % FaceAlpha
+ faceAlpha = get(h, 'FaceAlpha');
+ if ~isNone(faceColor) && isnumeric(faceAlpha) && faceAlpha ~= 1.0
+ opts = opts_add(opts,'fill opacity', sprintf(m2t.ff,faceAlpha));
+ end
+
+ % Edge
+ edgeColor = get(h, 'EdgeColor');
+ [m2t, opts] = setColor(m2t, h, opts, 'draw', edgeColor, 'none');
+
+ % Data
+ binEdges = get(h, 'BinEdges');
+ binValue = get(h, 'Values');
+ data = [binEdges(:), [binValue(:); binValue(end)]];
+
+ % Bar type (depends on orientation)
+ isVertical = strcmpi(get(h,'Orientation'),'vertical');
+ if isVertical
+ opts = opts_add(opts, 'ybar interval');
+ else
+ opts = opts_add(opts, 'xbar interval');
+ data = fliplr(data);
+ end
+
+ % Make table
+ [m2t, table, tableOptions] = makeTable(m2t, {'x','y'},data);
+
+ % Add 'area legend' (x/ybar interval legend do not seem to work)
+ opts = opts_add(opts, 'area legend');
+
+ % Print out
+ drawOpts = opts_print(m2t, opts, ',');
+ tabOpts = opts_print(m2t, tableOptions, ',');
+ str = sprintf('\\addplot[%s] plot 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 hist.
- % The main pillar is Pgfplots's '{x,y}bar' plot.
- %
- % TODO Get rid of code duplication with 'drawAxes'.
-
- % m2t.barplotId is set to [] in drawAxes(), so all of the values are computed
- % anew for subplots.
- if ~isfield(m2t, 'barplotId') || isempty(m2t.barplotId)
- % 'barplotId' provides a consecutively numbered ID for each
- % barseries plot. This allows for a proper handling of multiple bars.
- m2t.barplotId = [];
- m2t.barplotTotalNumber = [];
- m2t.barShifts = [];
- m2t.addedAxisOption = false;
- end
+% Takes care of plots like the ones produced by MATLAB's hist.
+% The main pillar is Pgfplots's '{x,y}bar' plot.
+%
+% TODO Get rid of code duplication with 'drawAxes'.
- % 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.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'log origin', 'infty');
- end
+ str = '';
- str = [];
-
- % -----------------------------------------------------------------------
- % The bar plot implementation in Pgfplots lacks certain functionalities;
- % for example, it can't plot bar plots and non-bar plots in the same
- % axis (while MATLAB can).
- % The following checks if this is the case and cowardly bails out if so.
- % On top of that, the number of bar plots is counted.
- if isempty(m2t.barplotTotalNumber)
- m2t.barplotTotalNumber = 0;
- siblings = get(get(h, 'Parent'), 'Children');
- for s = siblings(:)'
-
- % skip invisible objects
- if ~isVisible(s)
- continue;
- end
+ if ~isVisible(h)
+ return; % don't bother drawing invisible things
+ end
- if strcmpi(get(s, 'Type'), 'hggroup')
- cl = class(handle(s));
- switch cl
- case 'specgraph.barseries'
- m2t.barplotTotalNumber = m2t.barplotTotalNumber + 1;
- case 'specgraph.errorbarseries'
- % TODO
- % Unfortunately, MATLAB(R) treats error bars and
- % corresponding bar plots as siblings of a common axes
- % object. For error bars to work with bar plots -- which
- % is trivially possible in Pgfplots -- one has to match
- % errorbar and bar objects (probably by their values).
- userWarning(m2t, 'Error bars discarded (to be implemented).');
- otherwise
- error('matlab2tikz:drawBarseries', ...
- 'Unknown class''%s''.', cl);
- end
- end
- end
- end
- % -----------------------------------------------------------------------
+ % 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.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'log origin', 'infty');
+ %TODO: wait for pgfplots to implement other base values (see #438)
+ end
- xData = get(h, 'XData');
- yData = get(h, 'YData');
+ xData = get(h, 'XData');
+ yData = get(h, 'YData');
- % init drawOptions
- drawOptions = cell(0);
+ % init drawOptions
+ drawOptions = opts_new();
- barlayout = get(h, 'BarLayout');
- isHoriz = strcmp(get(h, 'Horizontal'), 'on');
- if (isHoriz)
- barType = 'xbar';
- else
- barType = 'ybar';
- end
- numBars = m2t.barplotTotalNumber;
- switch barlayout
- case 'grouped'
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % grouped plots
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % set ID
- if isempty(m2t.barplotId)
- m2t.barplotId = 1;
- else
- m2t.barplotId = m2t.barplotId + 1;
- end
+ [barType, isHoriz] = getOrientationOfBarSeries(h);
+ [m2t, drawOptions] = setBarLayoutOfBarSeries(m2t, h, barType, drawOptions);
-% % From <MATLAB>/toolbox/matlab/specgraph/makebars.m
-% % plottype==0 means 'grouped'
-% if m==1 || plottype~=0,
-% groupWidth = 1.0;
-% else
-% groupWidth = min(groupWidth, m/(m+1.5));
-% end
-% xx(:,3:4) = 1;
-% if plottype==0,
-% xx = (xx-0.5)*barwidth*groupwidth/m;
-% else
-% xx = (xx-0.5)*barwidth*groupwidth;
-% end
-
- % Maximum group with relative to the minumum distance between to
- % x-values.
- maxGroupWidth = 0.8;
- if numBars == 1
- groupWidth = 1.0;
- else
- groupWidth = min(maxGroupWidth, numBars/(numBars+1.5));
- end
+ % define edge color
+ edgeColor = get(h, 'EdgeColor');
+ lineStyle = get(h, 'LineStyle');
+ if isNone(lineStyle)
+ drawOptions = opts_add(drawOptions, 'draw', 'none');
+ else
+ [m2t, drawOptions] = setColor(m2t, h, drawOptions, 'draw', ...
+ edgeColor, 'none');
+ end
- % ---------------------------------------------------------------
- % Calculate the width of each bar and the center point shift.
- % The following is taken from MATLAB (see makebars.m) without
- % the special handling for hist plots or other fancy options.
- % ---------------------------------------------------------------
- if isempty(m2t.barShifts)
- % 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.
- % The bar width is assumed to be groupWidth/numBars.
- m2t.barShifts = ((1:numBars) - 0.5) * groupWidth / numBars ...
- - 0.5* groupWidth;
- end
- % ---------------------------------------------------------------
-
- % 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.
- barWidth = get(h, 'BarWidth') * groupWidth / numBars;
-
- % The minimum distance between two x-values. This is the scaling
- % factor for all other lengths about the bars.
- dx = min(diff(xData));
-
- % MATLAB treats shift and width in normalized coordinate units,
- % whereas Pgfplots requires physical units (pt,cm,...); hence
- % have the units converted.
- if (isHoriz)
- physicalBarWidth = dx * barWidth * m2t.unitlength.y.value;
- physicalBarShift = dx * m2t.barShifts(m2t.barplotId) * m2t.unitlength.y.value;
- phyicalBarUnit = m2t.unitlength.y.unit;
- else
- physicalBarWidth = dx * barWidth * m2t.unitlength.x.value;
- physicalBarShift = dx * m2t.barShifts(m2t.barplotId) * m2t.unitlength.x.value;
- phyicalBarUnit = m2t.unitlength.x.unit;
- end
- drawOptions = {drawOptions{:}, ...
- barType, ...
- sprintf(['bar width=',m2t.ff,'%s'], physicalBarWidth, phyicalBarUnit)};
- if physicalBarShift ~= 0.0
- drawOptions{end+1} = ...
- sprintf(['bar shift=',m2t.ff,'%s'], physicalBarShift, phyicalBarUnit);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % end grouped plots
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- case 'stacked'
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Stacked plots --
- % Add option 'ybar stacked' to the options of the surrounding
- % axis environment (and disallow anything else but stacked
- % plots).
- % Make sure this happens exactly *once*.
- if isempty(m2t.addedAxisOption) || ~m2t.addedAxisOption
- m2t.axesContainers{end}.stackedBarsPresent = true;
- bWFactor = get(h, 'BarWidth');
- % Add 'ybar stacked' to the containing axes environment.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- [barType,' stacked'], []);
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'bar width', ...
- sprintf([m2t.ff,'%s'], m2t.unitlength.x.value*bWFactor, m2t.unitlength.x.unit));
- m2t.addedAxisOption = true;
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [m2t, drawOptions] = getFaceColorOfBar(m2t, h, drawOptions);
+
+ % Add 'area legend' to the options as otherwise the legend indicators
+ % will just highlight the edges.
+ drawOptions = opts_add(drawOptions, 'area legend');
+
+ % plot the thing
+ if isHoriz
+ [yDataPlot, xDataPlot] = deal(xData, yData); % swap values
+ else
+ [xDataPlot, yDataPlot] = deal(xData, yData);
+ end
- otherwise
- error('matlab2tikz:drawBarseries', ...
+ [m2t, table, tabOpts] = makeTable(m2t, '', xDataPlot, '', yDataPlot);
+ str = sprintf('\\addplot[%s] plot table[%s] {%s};\n', ...
+ opts_print(m2t, drawOptions, ','), ...
+ opts_print(m2t, tabOpts, ','), table);
+end
+% ==============================================================================
+function [barType, isHorizontal] = getOrientationOfBarSeries(h)
+% determines the orientation (horizontal/vertical) of a BarSeries object
+ isHorizontal = strcmpi(get(h, 'Horizontal'), 'on');
+ if isHorizontal
+ barType = 'xbar';
+ else
+ barType = 'ybar';
+ 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 <MATLAB>/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;
+
+ % 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.
+ barWidth = get(h, 'BarWidth') * assumedBarWidth;
+
+ % Bar type
+ drawOptions = opts_add(drawOptions, barType);
+
+ % Bar width
+ drawOptions = opts_add(drawOptions, 'bar width', ...
+ formatDim(barWidth, ''));
+ % Bar shift
+ if barShift ~= 0
+ drawOptions = opts_add(drawOptions, 'bar shift', ...
+ formatDim(barShift, ''));
+ end
+
+ case 'stacked' % stacked plots
+ % Pass option to parent axis & disallow anything but stacked plots
+ % Make sure this happens exactly *once*.
+
+ if ~m2t.axesContainers{end}.barAddedAxisOption;
+ barWidth = get(h, 'BarWidth');
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, ...
+ 'bar width', formatDim(barWidth,''));
+ m2t.axesContainers{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
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % define edge color
- edgeColor = get(h, 'EdgeColor');
- lineStyle = get(h, 'LineStyle');
- if strcmp(lineStyle, 'none') || strcmp(edgeColor, 'none')
- drawOptions{end+1} = 'draw=none';
- else
- [m2t, xEdgeColor] = getColor(m2t, h, edgeColor, 'patch');
- drawOptions{end+1} = sprintf('draw=%s', xEdgeColor);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % define face color;
- % quite oddly, this value is not coded in the handle itself, but in its
- % child patch.
- child = get(h, 'Children');
- faceColor = get(child, 'FaceColor');
- if ~strcmp(faceColor, 'none')
- [m2t, xFaceColor] = getColor(m2t, h, faceColor, 'patch');
- drawOptions{end+1} = sprintf('fill=%s', xFaceColor);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Add 'area legend' to the options as otherwise the legend indicators
- % will just highlight the edges.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'area legend', []);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % plot the thing
- if isHoriz
- [yDataPlot, xDataPlot] = deal(xData, yData); % swap values
- else
- [xDataPlot, yDataPlot] = deal(xData, yData);
- end
+ 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);
- drawOpts = join(m2t, drawOptions, ',');
- [m2t, table ] = makeTable(m2t, '', xDataPlot, '', yDataPlot);
- str = sprintf('%s\\addplot[%s] plot table[row sep=crcr] {%%\n%s};\n', ...
- str, drawOpts, table);
+ else
+ % In Octave, the bargroup is a replicated cell array. Pick first
+ bargroup = bargroup{1};
+ end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % Get bar series Id
+ [dummy, barSeriesId] = ismember(h, bargroup);
+end
+% ==============================================================================
+function [m2t, drawOptions] = getFaceColorOfBar(m2t, h, drawOptions)
+% retrieve the FaceColor of a barseries object
+ if ~isempty(get(h,'Children'))
+ % quite oddly, before MATLAB R2014b this value is stored in a child
+ % patch and not in the object itself
+ obj = get(h, 'Children');
+ else % R2014b and newer
+ obj = h;
+ end
+ faceColor = get(obj, 'FaceColor');
+ [m2t, drawOptions] = setColor(m2t, h, drawOptions, 'fill', faceColor);
end
-% =========================================================================
+% ==============================================================================
function [m2t, str] = drawStemSeries(m2t, h)
[m2t, str] = drawStemOrStairSeries_(m2t, h, 'ycomb');
end
@@ -2832,280 +3542,370 @@ function [m2t, str] = drawStairSeries(m2t, h)
[m2t, str] = drawStemOrStairSeries_(m2t, h, 'const plot');
end
function [m2t, str] = drawStemOrStairSeries_(m2t, h, plotType)
- str = [];
+ str = '';
- lineStyle = get(h, 'LineStyle');
- lineWidth = get(h, 'LineWidth');
- marker = get(h, 'Marker');
+ lineStyle = get(h, 'LineStyle');
+ lineWidth = get(h, 'LineWidth');
+ marker = get(h, 'Marker');
- if ((strcmp(lineStyle,'none') || lineWidth==0) && strcmp(marker,'none'))
- return % nothing to plot!
- end
+ if (isNone(lineStyle) || lineWidth==0) && isNone(marker)
+ return % nothing to plot!
+ end
+
+ % deal with draw options
+ color = get(h, 'Color');
+ [m2t, plotColor] = getColor(m2t, h, color, 'patch');
+
+ lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
+ [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);
+
+ % insert draw options
+ drawOpts = opts_print(m2t, drawOptions, ',');
+
+ % plot the thing
+ xData = get(h, 'XData');
+ yData = get(h, 'YData');
+ [m2t, table, tabOpts] = makeTable(m2t, '', xData, '', yData);
+
+ str = sprintf('%s\\addplot[%s] plot table[%s] {%s};\n', ...
+ str, drawOpts, opts_print(m2t, tabOpts, ','), table);
+end
+% ==============================================================================
+function [m2t, str] = drawAreaSeries(m2t, h)
+% Takes care of MATLAB's stem plots.
+%
+% TODO Get rid of code duplication with 'drawAxes'.
+
+ str = '';
+
+ if ~isfield(m2t, 'addedAreaOption') || isempty(m2t.addedAreaOption) || ~m2t.addedAreaOption
+ % Add 'area style' to axes options.
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'area style');
+ m2t.axesContainers{end}.options = ...
+ opts_add(m2t.axesContainers{end}.options, 'stack plots', 'y');
+ m2t.addedAreaOption = true;
+ end
- %% deal with draw options
- color = get(h, 'Color');
- [m2t, plotColor] = getColor(m2t, h, color, 'patch');
+ % Handle draw options.
+ % define edge color
+ drawOptions = opts_new();
+ edgeColor = get(h, 'EdgeColor');
+ [m2t, xEdgeColor] = getColor(m2t, h, edgeColor, 'patch');
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % define face color;
+ if ~isempty(get(h,'Children'))
+ % quite oddly, before MATLAB R2014b this value is stored in a child
+ % patch and not in the object itself
+ obj = get(h, 'Children');
+ else % R2014b and newer
+ obj = h;
+ end
+ faceColor = get(obj, 'FaceColor');
+ [m2t, xFaceColor] = getColor(m2t, h, faceColor, 'patch');
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % gather the draw options
+ lineStyle = get(h, 'LineStyle');
+ drawOptions = opts_add(drawOptions, 'fill', xFaceColor);
+ if isNone(lineStyle)
+ drawOptions = opts_add(drawOptions, 'draw', 'none');
+ else
+ drawOptions = opts_add(drawOptions, 'draw', xEdgeColor);
+ end
- lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
- [m2t, markerOptions] = getMarkerOptions(m2t, h);
+ % Toggle legend entry
+ drawOptions = maybeShowInLegend(m2t.currentHandleHasLegend, drawOptions);
- drawOptions = [plotType, ...
- sprintf('color=%s', plotColor),...
- lineOptions, ...
- markerOptions];
+ drawOpts = opts_print(m2t, drawOptions, ',');
+
+ % plot the thing
+ xData = get(h, 'XData');
+ yData = get(h, 'YData');
+ [m2t, table, tabOpts] = makeTable(m2t, '', xData, '', yData);
+ str = sprintf('%s\\addplot[%s] plot table[%s]{%s}\n\\closedcycle;\n',...
+ str, drawOpts, opts_print(m2t, tabOpts, ','), table);
+end
+% ==============================================================================
+function [m2t, str] = drawQuiverGroup(m2t, h)
+% Takes care of MATLAB's quiver plots.
- %% insert draw options
- drawOpts = join(m2t, drawOptions, ',');
+ % used for arrow styles, in case there are more than one quiver fields
+ m2t.quiverId = m2t.quiverId + 1;
+
+ str = '';
+
+ [x,y,z,u,v,w] = getAndRescaleQuivers(m2t,h);
+ is3D = m2t.axesContainers{end}.is3D;
+
+ % prepare output
+ if is3D
+ name = 'addplot3';
+ format = [m2t.ff,',',m2t.ff,',',m2t.ff];
+ else % 2D plotting
+ name = 'addplot';
+ format = [m2t.ff,',',m2t.ff];
+ end
+
+ data = NaN(6,numel(x));
+ data(1,:) = x;
+ data(2,:) = y;
+ data(3,:) = z;
+ data(4,:) = x + u;
+ data(5,:) = y + v;
+ data(6,:) = z + w;
+
+ if ~is3D
+ data([3 6],:) = []; % remove Z-direction
+ end
+
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % gather the arrow options
+ showArrowHead = get(h, 'ShowArrowHead');
+ lineStyle = get(h, 'LineStyle');
+ lineWidth = get(h, 'LineWidth');
+
+ if (isNone(lineStyle) || lineWidth==0) && ~showArrowHead
+ return
+ end
+
+ arrowOpts = opts_new();
+ if showArrowHead
+ arrowOpts = opts_add(arrowOpts, '->');
+ else
+ arrowOpts = opts_add(arrowOpts, '-');
+ end
+
+ color = get(h, 'Color');
+ lineOpts = getLineOptions(m2t, lineStyle, lineWidth);
+ [m2t, arrowcolor] = getColor(m2t, h, color, 'patch');
+ arrowOpts = opts_add(arrowOpts, 'color', arrowcolor);
+ arrowOpts = opts_merge(arrowOpts, lineOpts);
+
+ % define arrow style
+ arrowOptions = opts_print(m2t, arrowOpts, ',');
+
+ % Append the arrow style to the TikZ options themselves.
+ % TODO: Look into replacing this by something more 'local',
+ % (see \pgfplotset{}).
+ m2t.content.options = opts_add(m2t.content.options,...
+ sprintf('arrow%d/.style', m2t.quiverId), ...
+ ['{', arrowOptions, '}']);
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ % return the vector field code
+ str = [str, ...
+ sprintf(['\\',name,' [arrow',num2str(m2t.quiverId), '] ', ...
+ 'coordinates{(',format,') (',format,')};\n'],...
+ data)];
+ %FIXME: external
+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.axesContainers{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 getOrDefault(h, 'AutoScale', true)
+ 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.
+ if isa(h,'matlab.graphics.chart.primitive.ErrorBar') % MATLAB R2014b+
+ hData = h;
+ upDev = get(h, 'UData');
+ loDev = get(h, 'LData');
+
+ yDeviations = [upDev(:), loDev(:)];
+
+ else % Legacy Handling (Octave and MATLAB R2014a and older):
+ % 'errorseries' plots have two line-plot children, one of which contains
+ % the information about the center points; 'XData' and 'YData' components
+ % are both of length n.
+ % The other contains the information about the deviations (errors), more
+ % more precisely: the lines to be drawn. Those are
+ % ___
+ % |
+ % |
+ % X <-- (x0,y0)
+ % |
+ % _|_
+ %
+ % X: x0, x0, x0-eps, x0+eps, x0-eps, x0+eps;
+ % Y: y0-dev, y0+dev, y0-dev, y0-dev, y0+dev, y0+dev.
+ %
+ % Hence, 'XData' and 'YData' are of length 6*n and contain redundant info.
+ % Some versions of MATLAB(R) insert more columns with NaNs (to be able to
+ % pass the entire X, Y arrays into plot()) such that the data is laid out as
+ %
+ % X: x0, x0, NaN, x0-eps, x0+eps, NaN, x0-eps, x0+eps;
+ % Y: y0-dev, y0+dev, NaN, y0-dev, y0-dev, NaN, y0+dev, y0+dev,
+ %
+ % or with another columns of NaNs added at the end.
+ c = get(h, 'Children');
+
+ % Find out which contains the data and which the deviations.
+ %TODO: this can be simplified using sort
+ n1 = length(get(c(1),'XData'));
+ n2 = length(get(c(2),'XData'));
+ if n2 == 6*n1
+ % 1 contains centerpoint info
+ dataIdx = 1;
+ errorIdx = 2;
+ numDevData = 6;
+ elseif n1 == 6*n2
+ % 2 contains centerpoint info
+ dataIdx = 2;
+ errorIdx = 1;
+ numDevData = 6;
+ elseif n2 == 9*n1-1 || n2 == 9*n1
+ % 1 contains centerpoint info
+ dataIdx = 1;
+ errorIdx = 2;
+ numDevData = 9;
+ elseif n1 == 9*n2-1 || n1 == 9*n2
+ % 2 contains centerpoint info
+ dataIdx = 2;
+ errorIdx = 1;
+ numDevData = 9;
+ else
+ error('drawErrorBars:errorMatch', ...
+ 'Sizes of and error data not matching (6*%d ~= %d and 6*%d ~= %d, 9*%d-1 ~= %d, 9*%d-1 ~= %d).', ...
+ n1, n2, n2, n1, n1, n2, n2, n1);
+ end
+ hData = c(dataIdx);
+ hError = c(errorIdx);
- %% plot the thing
- xData = get(h, 'XData');
- yData = get(h, 'YData');
- [m2t, table] = makeTable(m2t, '', xData, '', yData);
+ % prepare error array (that is, gather the y-deviations)
+ yValues = get(hData , 'YData');
+ yErrors = get(hError, 'YData');
- str = sprintf('%s\\addplot[%s] plot table[row sep=crcr] {%s};\n', ...
- str, drawOpts, table);
-end
-% =========================================================================
-function [m2t, str] = drawAreaSeries(m2t, h)
- % Takes care of MATLAB's stem plots.
- %
- % TODO Get rid of code duplication with 'drawAxes'.
+ n = length(yValues);
- str = [];
+ yDeviations = zeros(n, 2);
- if ~isfield(m2t, 'addedAreaOption') || isempty(m2t.addedAreaOption) || ~m2t.addedAreaOption
- % Add 'area style' to axes options.
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'area style', []);
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, 'stack plots', 'y');
- m2t.addedAreaOption = true;
- end
+ %TODO: this can be vectorized
+ for k = 1:n
+ % upper deviation
+ kk = numDevData*(k-1) + 1;
+ upDev = abs(yValues(k) - yErrors(kk));
- % Handle draw options.
- % define edge color
- drawOptions = {};
- edgeColor = get(h, 'EdgeColor');
- [m2t, xEdgeColor] = getColor(m2t, h, edgeColor, 'patch');
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % define face color;
- % quite oddly, this value is not coded in the handle itself, but in its
- % child patch.
- child = get(h, 'Children');
- faceColor = get(child, 'FaceColor');
- [m2t, xFaceColor] = getColor(m2t, h, faceColor, 'patch');
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % gather the draw options
- lineStyle = get(h, 'LineStyle');
- drawOptions{end+1} = sprintf('fill=%s', xFaceColor);
- if strcmp(lineStyle, 'none')
- drawOptions{end+1} = 'draw=none';
- else
- drawOptions{end+1} = sprintf('draw=%s', xEdgeColor);
- end
- drawOpts = join(m2t, drawOptions, ',');
+ % lower deviation
+ kk = numDevData*(k-1) + 2;
+ loDev = abs(yValues(k) - yErrors(kk));
- % plot the thing
- xData = get(h, 'XData');
- yData = get(h, 'YData');
- [m2t, table] = makeTable(m2t, '', xData, '', yData);
- str = sprintf('%s\\addplot[%s] plot table[row sep=crcr]{%s}\n\\closedcycle;\n',...
- str, drawOpts, table);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ yDeviations(k,:) = [upDev loDev];
+ end
+ end
+ % Now run drawLine() with deviation information.
+ [m2t, str] = drawLine(m2t, hData, yDeviations);
end
-% =========================================================================
-function [m2t, str] = drawQuiverGroup(m2t, h)
- % Takes care of MATLAB's quiver plots.
+% ==============================================================================
+function [m2t, str] = drawEllipse(m2t, handle)
+% Takes care of MATLAB's ellipse annotations.
+%
- if ~isfield(m2t, 'quiverId')
- % used for arrow styles, in case there are more than one quiver fields
- m2t.quiverId = 0;
- else
- m2t.quiverId = m2t.quiverId + 1;
- end
+% c = get(h, 'Children');
- str = [];
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % One could get(h,'{X,Y,U,V}Data') in which the intended arrow lengths
- % are stored. MATLAB(R) however applies some quite sophisticated scaling
- % here to avoid overlap of arrows.
- % The actual length of the arrows is stored in c(1) of
- %
- % c = get(h, 'Children');
- %
- % 'XData' and 'YData' of c(1) will be of the form
- %
- % [arrow1point1, arrow1point2, NaN, arrow2point1, arrow2point2, NaN].
- %
- c = get(h, 'Children');
-
- xData = get(c(1), 'XData');
- yData = get(c(1), 'YData');
- zData = get(c(1), 'ZData');
-
- step = 3;
- m = length(xData(1:step:end)); % number of arrows
-
- if(isempty(zData))
- name = 'addplot';
- format = [m2t.ff,',',m2t.ff];
- data = zeros(4,m);
- data(1,:) = xData(1:step:end);
- data(2,:) = yData(1:step:end);
- data(3,:) = xData(2:step:end);
- data(4,:) = yData(2:step:end);
- else
- m2t.currentAxesContain3dData = true;
- name = 'addplot3';
- format = [m2t.ff,',',m2t.ff,',',m2t.ff];
- data = zeros(6,m);
- data(1,:) = xData(1:step:end);
- data(2,:) = yData(1:step:end);
- data(3,:) = zData(1:step:end);
- data(4,:) = xData(2:step:end);
- data(5,:) = yData(2:step:end);
- data(6,:) = zData(2:step:end);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % gather the arrow options
- showArrowHead = get(h, 'ShowArrowHead');
- lineStyle = get(h, 'LineStyle');
- lineWidth = get(h, 'LineWidth');
-
- if (strcmp(lineStyle,'none') || lineWidth==0) && ~showArrowHead
- return
- end
+ drawOptions = opts_new();
- arrowOpts = cell(0);
- if showArrowHead
- arrowOpts = [arrowOpts, '->'];
- else
- arrowOpts = [arrowOpts, '-'];
- end
+ p = get(handle,'position');
+ radius = p([3 4]) / 2;
+ center = p([1 2]) + radius;
- color = get(h, 'Color');
- [m2t, arrowcolor] = getColor(m2t, h, color, 'patch');
- arrowOpts = [arrowOpts, ...
- sprintf('color=%s', arrowcolor), ... % color
- getLineOptions(m2t, lineStyle, lineWidth), ... % line options
- ];
-
- % define arrow style
- arrowOptions = join(m2t, arrowOpts, ',');
-
- % Append the arrow style to the TikZ options themselves.
- % TODO: Look into replacing this by something more 'local',
- % (see \pgfplotset{}).
- arrowStyle = ['arrow',num2str(m2t.quiverId), '/.style={',arrowOptions,'}'];
- m2t.content.options = addToOptions(m2t.content.options,...
- sprintf('arrow%d/.style', m2t.quiverId), ...
- ['{', arrowOptions, '}']);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % return the vector field code
- str = [str, ...
- sprintf(['\\',name,' [arrow',num2str(m2t.quiverId), '] ', ...
- 'coordinates{(',format,') (',format,')};\n'],...
- data)];
- %FIXME: external
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-end
-% =========================================================================
-function [m2t, str] = drawErrorBars(m2t, h)
- % Takes care of MATLAB's error bar plots.
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % 'errorseries' plots have two line-plot children, one of which contains
- % the information about the center points; 'XData' and 'YData' components
- % are both of length n.
- % The other contains the information about the deviations (errors), more
- % more precisely: the lines to be drawn. Those are
- % ___
- % |
- % |
- % X <-- (x0,y0)
- % |
- % |
- % _|_
- %
- % X: x0, x0, x0-eps, x0+eps, x0-eps, x0+eps;
- % Y: y0-dev, y0+dev, y0-dev, y0-dev, y0+dev, y0+dev.
- %
- % Hence, 'XData' and 'YData' are of length 6*n and contain redundant info.
- % Some versions of MATLAB(R) insert more columns with NaNs (to be able to
- % pass the entire X, Y arrays into plot()) such that the data is laid out as
- %
- % X: x0, x0, NaN, x0-eps, x0+eps, NaN, x0-eps, x0+eps;
- % Y: y0-dev, y0+dev, NaN, y0-dev, y0-dev, NaN, y0+dev, y0+dev,
- %
- % or with another columns of NaNs added at the end.
- %
- % This function accounts for all variants.
- %
- c = get(h, 'Children');
-
- % Find out which contains the data and which the deviations.
- n1 = length(get(c(1),'XData'));
- n2 = length(get(c(2),'XData'));
- if n2 == 6*n1
- % 1 contains centerpoint info
- dataIdx = 1;
- errorIdx = 2;
- numDevData = 6;
- elseif n1 == 6*n2
- % 2 contains centerpoint info
- dataIdx = 2;
- errorIdx = 1;
- numDevData = 6;
- elseif n2 == 9*n1-1 || n2 == 9*n1
- % 1 contains centerpoint info
- dataIdx = 1;
- errorIdx = 2;
- numDevData = 9;
- elseif n1 == 9*n2-1 || n1 == 9*n2
- % 2 contains centerpoint info
- dataIdx = 2;
- errorIdx = 1;
- numDevData = 9;
- else
- error('drawErrorBars:errorMatch', ...
- 'Sizes of and error data not matching (6*%d ~= %d and 6*%d ~= %d, 9*%d-1 ~= %d, 9*%d-1 ~= %d).', ...
- n1, n2, n2, n1, n1, n2, n2, n1);
- end
+ lineStyle = get(handle, 'LineStyle');
+ lineWidth = get(handle, 'LineWidth');
- % prepare error array (that is, gather the y-deviations)
- yValues = get(c(dataIdx) , 'YData');
- yErrors = get(c(errorIdx), 'YData');
+ color = get(handle, 'Color');
+ [m2t, xcolor] = getColor(m2t, handle, color, 'patch');
+ lineOptions = getLineOptions(m2t, lineStyle, lineWidth);
- n = length(yValues);
+ filling = get(handle, 'FaceColor');
- yDeviations = zeros(n, 2);
+ % 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);
- for k = 1:n
- % upper deviation
- kk = numDevData*(k-1) + 1;
- upDev = abs(yValues(k) - yErrors(kk));
+ drawCommand = '\filldraw';
+ end
+ drawOptions = opts_merge(drawOptions, lineOptions);
- % lower deviation
- kk = numDevData*(k-1) + 2;
- loDev = abs(yValues(k) - yErrors(kk));
+ opt = opts_print(m2t, drawOptions, ',');
- yDeviations(k,:) = [upDev loDev];
- end
+ 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.
- % Now, pull drawLine() with deviation information.
- [m2t, str] = drawLine(m2t, c(dataIdx), yDeviations);
+ % handleAllChildren to draw the arrow
+ [m2t, str] = handleAllChildren(m2t, handle);
+ % handleAllChildren ignores the text, unless hidden strings are shown
+ if ~m2t.cmdOpts.Results.showHiddenStrings
+ child = findobj(handle, 'type', 'text');
+ [m2t, str{end+1}] = drawText(m2t, child);
+ end
end
-% =============================================================================
+% ==============================================================================
function out = linearFunction(X, Y)
- % Return the linear function that goes through (X[1], Y[1]), (X[2], Y[2]).
+% 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.
-
+% Translates a Pgfplots colormap to a MATLAB color map.
matlabColormap = zeros(numColors, 3);
% Point indices between which to interpolate.
I = [1, 2];
@@ -3119,77 +3919,79 @@ function matlabColormap = pgfplots2matlabColormap(points, rgb, numColors)
matlabColormap(k,:) = f(x);
end
end
-% =============================================================================
-function pgfplotsColormap = matlab2pgfplotsColormap(m2t, matlabColormap)
- % Translates a MATLAB color map into a Pgfplots colormap.
-
- % 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.
+% ==============================================================================
+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) ...
- };
+ % '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:
@@ -3204,7 +4006,7 @@ function pgfplotsColormap = matlab2pgfplotsColormap(m2t, matlabColormap)
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);
+ map{1}.name, alpha);
pgfplotsColormap = map{1}.name;
return
end
@@ -3213,735 +4015,732 @@ function pgfplotsColormap = matlab2pgfplotsColormap(m2t, matlabColormap)
% 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.
- steps = [1, 2];
- colors = [matlabColormap(1,:); matlabColormap(2,:)];
- f = linearFunction(steps, colors);
- k = 3;
m = size(matlabColormap, 1);
- 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,:));
+ 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
- k = k+1;
+ steps(end) = m;
+ colors(end,:) = matlabColormap(m,:);
end
- steps(end) = m;
- colors(end,:) = matlabColormap(m,:);
% Get it in Pgfplots-readable form.
unit = 'pt';
colSpecs = {};
for k = 1:length(steps)
x = steps(k)-1;
- sprintf('rgb(%d%s)=(%g, %g, %g)', x, unit, colors(k));
colSpecs{k} = sprintf('rgb(%d%s)=(%g,%g,%g)', x, unit, colors(k,:));
end
- pgfplotsColormap = sprintf('colormap={mymap}{[1%s] %s}', unit, join(m2t, colSpecs, '; '));
+ pgfplotsColormap = sprintf('colormap={%s}{[1%s] %s}',name, unit, join(m2t, colSpecs, '; '));
end
-% =========================================================================
+% ==============================================================================
function 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 m2t.cmdOpts.Results.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
+ 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 m2t.cmdOpts.Results.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 = {'font', fontStyle};
- else
- fontStyle = cell(0,2);
- 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 = cell(0, 2);
- cbarOptions = {};
- cbarStyleOptions = {};
-
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % set position, ticks etc. of the colorbar
- loc = get(handle, 'Location');
-
- % MATLAB(R)'s keywords are camel cased (e.g., 'NorthOutside'), in Octave
- % small cased ('northoutside'). Hence, use lower() for uniformity.
- switch lower(loc)
- case 'north'
- cbarOptions{end+1} = 'horizontal';
- cbarStyleOptions = {cbarStyleOptions{:},...
- 'at={(0.5,0.97)}',...
- 'anchor=north', ...
- 'xticklabel pos=lower', ...
- 'width=0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'};
- case 'south'
- cbarOptions{end+1} = 'horizontal';
- cbarStyleOptions = {cbarStyleOptions{:},...
- 'at={(0.5,0.03)}',...
- 'anchor=south', ...
- 'xticklabel pos=upper', ...
- 'width=0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'};
- case 'east'
- cbarOptions{end+1} = 'right';
- cbarStyleOptions = {cbarStyleOptions{:},...
- 'at={(0.97,0.5)}',...
- 'anchor=east', ...
- 'xticklabel pos=left', ...
- 'width=0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'};
- case 'west'
- cbarOptions{end+1} = 'left';
- cbarStyleOptions = {cbarStyleOptions{:},...
- 'at={(0.03,0.5)}',...
- 'anchor=west', ...
- 'xticklabel pos=right', ...
- 'width=0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'};
- case 'eastoutside'
- %cbarOptions{end+1} = 'right';
- case 'westoutside'
- cbarOptions{end+1} = 'left';
- case 'northoutside'
- % TODO move to top
- cbarOptions{end+1} = 'horizontal';
- cbarStyleOptions = {cbarStyleOptions{:},...
- 'at={(0.5,1.03)}',...
- 'anchor=south', ...
- 'xticklabel pos=upper'};
- case 'southoutside'
- cbarOptions{end+1} = 'horizontal';
- otherwise
- error('matlab2tikz:getColorOptions:unknownLocation',...
- 'getColorbarOptions: Unknown ''Location'' %s.', loc)
- end
+ % begin collecting axes options
+ axisOptions = opts_new();
+ cbarStyleOptions = opts_new();
- % TODO proper use of addToOptions()
- xo = getAxisOptions(m2t, handle, 'x');
- for k = 1:size(xo, 1)
- if strcmp(xo{k,1}, 'xmin') ...
- || strcmp(xo{k,1}, 'xmax') ...
- || strcmp(xo{k,1}, 'xtick')
- % filter a bunch of keywords
- continue;
- end
- cbarStyleOptions{end+1} = [xo{k,1}, '=', xo{k,2}];
- end
- yo = getAxisOptions(m2t, handle, 'y');
- for k = 1:size(yo, 1)
- if strcmp(yo{k,1}, 'ymin') ...
- || strcmp(yo{k,1}, 'ymax')
- % filter a bunch of keywords
- continue;
- end
- cbarStyleOptions{end+1} = [yo{k,1}, '=', yo{k,2}];
- end
+ [cbarTemplate, cbarStyleOptions] = getColorbarPosOptions(handle, ...
+ cbarStyleOptions);
- %if strcmp(get(handle, 'YScale'), 'log')
- % cbarStyleOptions{end+1} = 'ymode=log';
- %end
- %
- %% Get axis labels.
- %for axis = 'xy'
- % axisLabel = get(get(handle, [upper(axis),'Label']), 'String');
- % if ~isempty(axisLabel)
- % axisLabelInterpreter = ...
- % get(get(handle, [upper(axis),'Label']), 'Interpreter');
- % label = prettyPrint(m2t, axisLabel, axisLabelInterpreter);
- % if length(label) > 1
- % % If there's more than one cell item, the list
- % % is displayed multi-row in MATLAB(R).
- % % To replicate the same in Pgfplots, one can
- % % use xlabel={first\\second\\third} only if the
- % % alignment or the width of the "label box"
- % % is defined. This is a restriction that comes with
- % % TikZ nodes.
- % m2t.axesContainers{end}.options = ...
- % addToOptions(m2t.axesContainers{end}.options, ...
- % [axis, 'label style'], '{align=center}');
- % end
- % label = join(m2t, label,'\\[1ex]');
- % cbarStyleOptions{end+1} = sprintf([axis,'label={%s}'], label);
- % end
- %end
-
- % title
- title = get(get(handle, 'Title'), 'String');
- if ~isempty(title)
- titleInterpreter = get(get(handle, 'Title'), 'Interpreter');
- title = prettyPrint(m2t, title, titleInterpreter);
- if length(title) > 1
- m2t.axesContainers{end}.options = ...
- addToOptions(m2t.axesContainers{end}.options, ...
- 'title style', '{align=center}');
- end
- title = join(m2t, title, '\\[1ex]');
- cbarStyleOptions{end+1} = sprintf('title={%s}', title);
- end
+ % 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 m2t.cmdOpts.Results.strict
- % Sampled colors.
- numColors = size(m2t.currentHandles.colormap, 1);
- cbarOptions{end+1} = 'sampled';
- cbarStyleOptions{end+1} = sprintf('samples=%d', numColors+1);
- end
+ 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
- % Merge them together in axisOptions.
- if isempty(cbarOptions)
- axisOptions = addToOptions(axisOptions, 'colorbar', []);
- else
- if length(cbarOptions) > 1
- userWarning(m2t, ...
- 'Pgfplots cannot deal with more than one colorbar option yet.');
- end
- axisOptions = addToOptions(axisOptions, ...
- ['colorbar ', cbarOptions{1}], []);
- end
+ % TODO HG2: colorbar ticks and colorbar tick labels
- if ~isempty(cbarStyleOptions)
- axisOptions = addToOptions(axisOptions, ...
- 'colorbar style', ...
- ['{', join(m2t, cbarStyleOptions, ','), '}']);
- end
+ 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.cmdOpts.Results.strict
+ % Sampled colors.
+ numColors = size(m2t.currentHandles.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
- % Append upper and lower limit of the colorbar.
- % TODO Use caxis not only for color bars.
- clim = caxis(get(handle, 'axes'));
- axisOptions = addToOptions(axisOptions, 'point meta min', sprintf(m2t.ff, clim(1)));
- axisOptions = addToOptions(axisOptions, 'point meta max', sprintf(m2t.ff, clim(2)));
+ % Merge them together in axisOptions.
+ axisOptions = opts_add(axisOptions, strtrim(['colorbar ', cbarTemplate]));
+
+ if ~isempty(cbarStyleOptions)
+ axisOptions = opts_add(axisOptions, ...
+ 'colorbar style', ...
+ ['{' opts_print(m2t, cbarStyleOptions, ',') '}']);
+ end
- % do _not_ handle colorbar's children
+ % 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';
+ 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 isreal(color) && length(color)==3 && ~any(isnan(color))
- % everything alright: rgb color here
- [m2t, xcolor] = rgb2colorliteral(m2t, color);
- else
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- switch mode
- case 'patch'
- [m2t, xcolor] = patchcolor2xcolor(m2t, color, handle);
- case 'image'
- [m2t, colorindex] = cdata2colorindex(m2t, color, handle);
- m = size(colorindex, 1);
- n = size(colorindex, 2);
- xcolor = cell(m, n);
- for i = 1:m
- for j = 1:n
- [m2t, xc] = rgb2colorliteral(m2t, m2t.currentHandles.colormap(colorindex(i,j), :));
- xcolor{i, j} = xc;
- end
- end
- otherwise
- error(['matlab2tikz:getColor', ...
- 'Argument ''mode'' has illegal value ''%s''.'], ...
- mode);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- end
+% 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.currentHandles.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 ~ischar(color)
- error('patchcolor2xcolor:illegalInput', ...
- 'Input argument ''color'' not a string.');
- end
-
- switch color
- case 'flat'
- % look for CData at different places
- cdata = get(patchhandle, 'CData');
- if isempty(cdata) || ~isnumeric(cdata)
- c = get(patchhandle, 'Children');
- cdata = get(c, 'CData');
- end
-
- col1 = cdata(1,1);
- if all(isnan(cdata) | abs(cdata-col1)<1.0e-10)
- [m2t, colorindex] = cdata2colorindex(m2t, col1, patchhandle);
- [m2t, xcolor] = rgb2colorliteral(m2t, m2t.currentHandles.colormap(colorindex, :));
- else
- % Don't return anything meaningful and count on the caller to
- % make soemthing of it.
- xcolor = [];
- end
-
- case 'auto'
- color = get(patchhandle, 'Color');
- [m2t, xcolor] = rgb2colorliteral(m2t, color);
-
- case 'none'
- error('matlab2tikz:anycolor2rgb:ColorModelNoneNotAllowed',...
- ['Color model ''none'' not allowed here. ',...
- 'Make sure this case gets intercepted before.']);
-
- otherwise
- error('matlab2tikz:anycolor2rgb:UnknownColorModel',...
- 'Don''t know how to handle the color model ''%s''.',color);
- end
+% 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.currentHandles.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'
+ error('matlab2tikz:anycolor2rgb:ColorModelNoneNotAllowed',...
+ ['Color model ''none'' not allowed here. ',...
+ 'Make sure this case gets intercepted before.']);
+
+ 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 = get(patchhandle, 'Children');
+ cdata = get(child, 'CData');
+ end
+ if isempty(cdata) || ~isnumeric(cdata)
+ % R2014b+: CData is implicit by the ordering of the siblings
+ siblings = get(get(patchhandle, 'Parent'), 'Children');
+ 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.
+% 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',...
+ if ~isnumeric(cdata) && ~islogical(cdata)
+ error('matlab2tikz:cdata2colorindex:unknownCDataType',...
'Don''t know how to handle CData ''%s''.',cdata);
- end
+ end
- axeshandle = m2t.currentHandles.gca;
-
- % -----------------------------------------------------------------------
- % For the following, see, for example, the MATLAB help page for 'image',
- % section 'Image CDataMapping'.
- switch get(imagehandle, 'CDataMapping')
- case 'scaled'
- % need to scale within clim
- % see MATLAB's manual page for caxis for details
- clim = get(axeshandle, 'clim');
- m = size(m2t.currentHandles.colormap, 1);
- colorindex = zeros(size(cdata));
- idx1 = cdata <= clim(1);
- idx2 = cdata >= clim(2);
- idx3 = ~idx1 & ~idx2;
- colorindex(idx1) = 1;
- colorindex(idx2) = m;
- colorindex(idx3) = fix((cdata(idx3)-clim(1)) / (clim(2)-clim(1)) *m) ...
- + 1;
- case 'direct'
- % direct index
- colorindex = cdata;
-
- otherwise
+ axeshandle = m2t.currentHandles.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.currentHandles.colormap, 1);
+ colorindex = zeros(size(cdata));
+ idx1 = cdata <= clim(1);
+ idx2 = cdata >= clim(2);
+ idx3 = ~idx1 & ~idx2;
+ colorindex(idx1) = 1;
+ colorindex(idx2) = m;
+ colorindex(idx3) = fix((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
- % -----------------------------------------------------------------------
+ 'Unknown CDataMapping ''%s''.',cdatamapping);
+ end
end
-% =========================================================================
+% ==============================================================================
function [m2t, key, lOpts] = getLegendOpts(m2t, handle)
+% Need to check that there's nothing inside visible before we
+% abandon this legend -- an invisible property of the parent just
+% means the legend has no box.
+ children = get(handle, 'Children');
+ if ~isVisible(handle) && ~any(isVisible(children))
+ return
+ end
- % Need to check that there's nothing inside visible before we
- % abandon this legend -- an invisible property of the parent just
- % means the legend has no box.
- children = get(handle, 'Children');
- if ~isVisible(handle) && ~any(isVisible(children))
- return
- end
+ lStyle = opts_new();
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % handle legend location
- loc = get(handle, 'Location');
- dist = 0.03; % distance to to axes in normalized coordinated
- anchor = [];
- % 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'
- % don't 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'
- % TODO handle position with pixels
- legendPos = get(handle, 'Position');
- unit = get(handle, 'Units');
- switch unit
- case 'normalized'
- position = legendPos(1:2);
- case 'pixels'
- % Calculate where the legend is located w.r.t. the axes.
- axesPos = get(m2t.currentHandles.gca, 'Position');
- % 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);
- otherwise
- position = pos(1:2);
- userWarning(m2t, [' Unknown legend length unit ''', unit, '''' ...
- '. Handling like ''normalized''.']);
- 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.']);
- otherwise
- userWarning(m2t, [' Unknown legend location ''',loc,'''' ...
- '. Choosing default.']);
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % handle alignment of legend text and pictograms
- textalign = [];
- pictalign = [];
- switch m2t.env
- 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 = 'left';
- pictalign = 'right';
- case 'right'
- % pictogram left of flush left text (default)
- textalign = 'right';
- 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
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- lStyle = cell(0);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % append to legend options
- if ~isempty(anchor)
- lStyle = {lStyle{:}, ...
- sprintf(['at={(',m2t.ff,',',m2t.ff,')}'], position), ...
- sprintf('anchor=%s', anchor)};
- end
- % handle orientation
- ori = get(handle, 'Orientation');
- switch lower(ori)
- case 'horizontal'
- numLegendEntries = length(get(handle, 'String'));
- lStyle{end+1} = sprintf('legend columns=%d', numLegendEntries);
- case 'vertical'
- % Use default.
- otherwise
- userWarning(m2t, [' Unknown legend orientation ''',ori,'''' ...
- '. Choosing default (vertical).']);
- end
+ lStyle = legendPosition(m2t, handle, lStyle);
+ lStyle = legendOrientation(m2t, handle, lStyle);
+ lStyle = legendEntryAlignment(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))
- lStyle = {lStyle{:}, 'fill=none', 'draw=none'};
- else
- % handle colors
- edgeColor = get(handle, 'EdgeColor');
- if all(edgeColor == [1,1,1])
- lStyle = {lStyle{:}, 'draw=none'};
- else
- [m2t, col] = getColor(m2t, handle, edgeColor, 'patch');
- lStyle = {lStyle{:}, sprintf('draw=%s', col)};
- end
+ % If the plot has 'legend boxoff', we have the 'not visible'
+ % property, so turn off line and background fill.
+ if ~isVisible(handle) || strcmpi(get(handle,'box'),'off')
+ 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 = get(handle, 'Color');
- if ~all(edgeColor == [1,1,1])
- [m2t, col] = getColor(m2t, handle, fillColor, 'patch');
- lStyle = {lStyle{:}, sprintf('fill=%s', col)};
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ [fillColor, isDfltFill] = getAndCheckDefault('Legend', handle, ...
+ 'Color', [1 1 1]);
+ if isNone(fillColor)
+ lStyle = opts_add(lStyle, 'fill', 'none');
- % alignment of legend text and pictograms, if available
- if ~isempty(textalign) && ~isempty(pictalign)
- lStyle = {lStyle{:}, ...
- sprintf('nodes=%s', textalign), ...
- sprintf('legend plot pos=%s', 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.
- %lStyle{end+1} = 'nodes=right';
- lStyle{end+1} = 'legend cell align=left';
- end
+ elseif ~isDfltFill
+ [m2t, col] = getColor(m2t, handle, fillColor, 'patch');
+ lStyle = opts_add(lStyle, 'fill', col);
+ end
+ end
+ % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- if ~isempty(lStyle)
- key = 'legend style';
- lOpts = join(m2t, lStyle,',');
- end
+ key = 'legend style';
+ lOpts = opts_print(m2t, lStyle, ',');
+end
+% ==============================================================================
+function [lStyle] = legendOrientation(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 [pgfTicks, pgfTickLabels, hasMinorTicks] = getAxisTicks(m2t, handle, axis)
- % Return axis tick marks Pgfplots style. Nice: Tick lengths and such
- % details are taken care of by Pgfplots.
+% ==============================================================================
+function [lStyle] = legendPosition(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.currentHandles.gca, 'Position');
+ axesUnit = get(m2t.currentHandles.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
- if ~strcmpi(axis,'x') && ~strcmpi(axis,'y') && ~strcmpi(axis,'z')
- error('matlab2tikz:illegalAxisSpecifier',...
- 'Illegal axis specifier ''%s''.', axis);
- end
+ otherwise
+ userWarning(m2t, [' Unknown legend location ''',loc,'''' ...
+ '. Choosing default.']);
+ return % use defaults
+ end
- keywordTickMode = [upper(axis), 'TickMode'];
- tickMode = get(handle, keywordTickMode);
- keywordTick = [upper(axis), 'Tick'];
- ticks = get(handle, keywordTick);
- if isempty(ticks)
- % If no ticks are present, we need to enforce this in any case.
- pgfTicks = '\empty';
- else
- if strcmp(tickMode, 'auto') && ~m2t.cmdOpts.Results.strict
- % If the ticks are set automatically, and strict conversion is
- % not required, then let Pgfplots take care of the ticks.
- % In most cases, this looks a lot better anyway.
- pgfTicks = [];
- else % strcmp(tickMode,'manual') || m2t.cmdOpts.Results.strict
- pgfTicks = join(m2t, cellstr(num2str(ticks(:))), ', ');
- end
- 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);
- keywordTickLabelMode = [upper(axis), 'TickLabelMode'];
- tickLabelMode = get(handle, keywordTickLabelMode);
- keywordTickLabel = [upper(axis), 'TickLabel'];
- tickLabels = cellstr(get(handle, keywordTickLabel));
- if strcmp(tickLabelMode, 'auto') && ~m2t.cmdOpts.Results.strict
- pgfTickLabels = [];
- else % strcmp(tickLabelMode,'manual') || m2t.cmdOpts.Results.strict
- keywordScale = [upper(axis), 'Scale'];
- isAxisLog = strcmp(get(handle,keywordScale), 'log');
- [pgfTicks, pgfTickLabels] = ...
- matlabTicks2pgfplotsTicks(m2t, ticks, tickLabels, isAxisLog, tickLabelMode);
- end
+end
+% ==============================================================================
+function [lStyle] = legendEntryAlignment(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
- keywordMinorTick = [upper(axis), 'MinorTick'];
- hasMinorTicks = strcmp(get(handle, keywordMinorTick), 'on');
+ % 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
- % ticks and tick labels (if at all necessary).
-
- if isempty(ticks)
- pTicks = '\empty';
- pTickLabels = [];
- return
- end
+% 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), ',');
+ % 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
+ % 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
+ % 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
- % What MATLAB does when there the number of ticks and tick labels do not
- % coincide is somewhat unclear. To fix bug
- % https://github.com/nschloe/matlab2tikz/issues/161,
- % cut off the first entries in `ticks`.
- m = length(ticks);
- n = length(tickLabels);
- if n < m
- ticks = ticks(m-n+1:end);
- end
+ ticks = removeSuperfluousTicks(ticks, tickLabels);
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- % Check if tickLabels are really necessary (and not already covered by
- % the tick values themselves).
- plotLabelsNecessary = false;
+ isNeeded = isTickLabelsNecessary(m2t, ticks, tickLabels, isLogAxis);
- 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
+ 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
- plotLabelsNecessary = true;
- break;
- end
- end
- % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- if plotLabelsNecessary
- % if the axis is logscaled, MATLAB does not store the labels,
- % but the exponents to 10
- if isLogAxis
- for k = 1:length(tickLabels)
- if isnumeric(tickLabels{k})
- str = num2str(tickLabels{k});
- else
- str = tickLabels{k};
- end
- if strcmpi(tickLabelMode,'auto')
- tickLabels{k} = sprintf('$10^{%s}$', str);
- end
- end
- end
- tickLabels = cellfun(@(l)(sprintf('{%s}',l)), tickLabels, ...
- 'UniformOutput', false);
- pTickLabels = join(m2t, tickLabels, ',');
- else
- pTickLabels = [];
- 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
+ % if the axis is logscaled, MATLAB does not store the labels,
+ % but the exponents to 10
+ if isLogAxis
+ for k = 1:length(tickLabels)
+ if isnumeric(tickLabels{k})
+ str = num2str(tickLabels{k});
+ else
+ str = tickLabels{k};
+ end
+ if strcmpi(tickLabelMode,'auto')
+ tickLabels{k} = sprintf('$10^{%s}$', str);
+ end
+ 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',...
+ if(~ischar(matlabLineStyle))
+ error('matlab2tikz:translateLineStyle:NotAString',...
'Variable matlabLineStyle is not a string.');
- end
+ end
- switch (matlabLineStyle)
- case 'none'
- tikzLineStyle = '';
- case '-'
- tikzLineStyle = 'solid';
- case '--'
- tikzLineStyle = 'dashed';
- case ':'
- tikzLineStyle = 'dotted';
- case '-.'
- tikzLineStyle = 'dash pattern=on 1pt off 3pt on 3pt off 3pt';
- otherwise
- error('matlab2tikz:translateLineStyle:UnknownLineStyle',...
+ 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
end
-% =========================================================================
-function [m2t, table] = makeTable(m2t, varargin)
-% [m2t,table] = makeTable(m2t, 'name1', data1, 'name2', data2, ...)
-% [m2t,table] = makeTable(m2t, {'name1','name2',...}, {data1, data2, ...})
-% [m2t,table] = makeTable(m2t, {'name1','name2',...}, [data1(:), data2(:), ...])
+% ==============================================================================
+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.cmdOpts.Results.externalData
ROWSEP = sprintf('\n');
else
ROWSEP = sprintf('\\\\\n');
- end
- 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);
+ opts = opts_add(opts, 'row sep','crcr');
end
- nColumns = numel(data);
- nRows = cellfun(@numel, data);
+ nColumns = numel(data);
+ nRows = cellfun(@numel, data);
if ~all(nRows==nRows(1))
- warning('matlab2tikz:makeTableDifferentNumberOfRows',...
- 'Different data lengths [%s]. Only including the first %d ones.',...
- num2str(nRows), min(nRows));
+ error('matlab2tikz:makeTableDifferentNumberOfRows',...
+ 'Different data lengths [%s].', num2str(nRows));
end
- nRows = min(nRows);
+ nRows = nRows(1);
FORMAT = repmat({m2t.ff}, 1, nColumns);
FORMAT(cellfun(@isCellOrChar, data)) = {'%s'};
@@ -3954,11 +4753,9 @@ function [m2t, table] = makeTable(m2t, varargin)
table = cell(nRows,1);
for iRow = 1:nRows
- thisData = cellfun(@(x)(x(iRow)), data, 'UniformOutput', false);
+ thisData = cell(1,nColumns);
for jCol = 1:nColumns
- if iscell(thisData{jCol}) %TODO: probably this can be done more clearly
- thisData{jCol} = thisData{jCol}{1};
- end
+ thisData{1,jCol} = data{jCol}(iRow);
end
table{iRow} = sprintf(FORMAT, thisData{:});
end
@@ -3968,280 +4765,427 @@ function [m2t, table] = makeTable(m2t, varargin)
if m2t.cmdOpts.Results.externalData
% output data to external file
m2t.dataFileNo = m2t.dataFileNo + 1;
-
- %TODO: extract method: absolute/relative filename for PNG/DAT files
- [pathstr, name] = fileparts(m2t.tikzFileName);
- baseFilename = [name '-' num2str(m2t.dataFileNo) '.tsv'];
- filename = fullfile(pathstr, baseFilename);
- relativeFilename = fullfile(m2t.relativeDataPath, baseFilename);
+ [filename, latexFilename] = externalFilename(m2t, m2t.dataFileNo, '.tsv');
% write the data table to an external file
fid = fileOpenForWrite(m2t, filename);
+ finally_fclose_fid = onCleanup(@() fclose(fid));
+
fprintf(fid, '%s', table);
- fclose(fid);
% put the filename in the TikZ output
- table = TeXpath(relativeFilename);
+ table = latexFilename;
else
- % output data literally
- table = sprintf('%s', table);
+ % 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);
+ 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.extraRgbColorNames];
- colorSpecs = [xcolColorSpecs, m2t.extraRgbColorSpecs];
-
- %% check if rgb is a predefined color
- for kColor = 1:length(colorSpecs)
- Ck = colorSpecs{kColor}(:);
- if max(abs(Ck - rgb(:))) < m2t.colorPrecision
- colorLiteral = colorNames{kColor};
- return % exact color was predefined
- end
- end
+% 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.extraRgbColorNames];
+ colorSpecs = [xcolColorSpecs, m2t.extraRgbColorSpecs];
+
+ %% check if rgb is a predefined color
+ for kColor = 1:length(colorSpecs)
+ Ck = colorSpecs{kColor}(:);
+ if max(abs(Ck - rgb(:))) < m2t.colorPrecision
+ 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.colorPrecision
- colorLiteral = sprintf('%s!%d!%s', colorNames{iColor}, p*100, ...
- colorNames{jColor});
- return % linear combination found
- end
- 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.colorPrecision
+ 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.extraRgbColorNames)+1);
+ m2t.extraRgbColorNames{end+1} = colorLiteral;
+ m2t.extraRgbColorSpecs{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, ',')
+ 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(m2t.ff, cellstr{k});
+ elseif iscell(cellstr{k})
+ cellstr{k} = join(m2t, cellstr{k}, delimiter);
+ % 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 = [newstr{:}];
+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.currentHandles.gcf, 'Position');
+ figuresize = figuresize([3 4]);
+ figureunit = get(m2t.currentHandles.gcf, 'Units');
+
+ % Convert Figure Size
+ unit = 'in';
+ figuresize = convertUnits(figuresize, figureunit, unit);
+
+ % Split size into width and height
+ width = figuresize(1);
+ height = figuresize(2);
- %% Define colors that are not a linear combination of two known colors
- colorLiteral = sprintf('mycolor%d', length(m2t.extraRgbColorNames)+1);
- m2t.extraRgbColorNames{end+1} = colorLiteral;
- m2t.extraRgbColorSpecs{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 inbetween two strings, if desired).
- %
- % Example of usage:
- % join(m2t, cellstr, ',')
-
- if isempty(cellstr)
- newstr = [];
- return
- end
+% ==============================================================================
+function dimension = getFigureDimensions(m2t, widthString, heightString)
+% Returns the physical dimension of the figure.
- % convert all values to strings first
- nElem = numel(cellstr);
- for k = 1:nElem
- if isnumeric(cellstr{k})
- cellstr{k} = sprintf(m2t.ff, cellstr{k});
- elseif iscell(cellstr{k})
- cellstr{k} = join(m2t, cellstr{k}, delimiter);
- % this will fail for heavily nested cells
- elseif ~ischar(cellstr{k})
- error('matlab2tikz:join:NotCellstrOrNumeric',...
- 'Expected cellstr or numeric.');
- end
- end
+ [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
- % 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 = [newstr{:}];
+ % 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 dimension = getAxesDimensions(handle, ...
- widthString, heightString) % optional
- % Returns the physical dimension of the axes.
+% ==============================================================================
+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
- [width, height, unit] = getNaturalAxesDimensions(handle);
+ % First get the whole figures size
+ figDim = getFigureDimensions(m2t, widthString, heightString);
- % 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
+ % Get the relative position of the axis
+ relPos = getRelativeAxesPosition(m2t, handle, axesBoundingBox);
- % 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 texUnits = matlab2texUnits(matlabUnits, fallbackValue)
- switch matlabUnits
- case 'pixels'
- texUnits = 'px';
- case 'centimeters'
- texUnits = 'cm';
- case 'characters'
- texUnits = 'em';
- case 'points'
- texUnits = 'pt';
- case 'inches'
- texUnits = 'in';
- otherwise
- texUnits = fallbackValue;
- end
+ 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 [width, height, unit] = getNaturalAxesDimensions(handle)
-
- daspectmode = get(handle, 'DataAspectRatioMode');
- position = get(handle, 'Position');
- units = get(handle, 'Units');
-
- % Convert the MATLAB unit strings into TeX unit strings.
- units = matlab2texUnits(units, units);
-
- switch daspectmode
- case 'auto'
- % ---------------------------------------------------------------------
- % The plot will use the full size of the current figure.,
- if strcmp(units, 'normalized')
- % The dpi is needed to associate the size on the screen (in pixels)
- % to the physical size of the plot.
- % Unfortunately, MATLAB doesn't seem to be able to always make a
- % good guess about the current DPI (a bug is filed for this on
- % mathworks.com).
- dpi = get(0, 'ScreenPixelsPerInch');
-
- unit = 'in';
- figuresize = get(gcf, 'Position');
-
- % This assumes that figuresize is always in units of dots (pixels).
- % Apparently, this isn't always the case, so we're going to need to
- % fix things here.
- width = position(3) * figuresize(3) / dpi;
- height = position(4) * figuresize(4) / dpi;
-
- else % assume that TikZ knows the unit (in, cm,...)
- unit = units;
- width = position(3);
- height = position(4);
+% ==============================================================================
+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
- % ---------------------------------------------------------------------
-
- case 'manual'
- % ---------------------------------------------------------------------
- % When daspect was manually set, stick to it.
- % This is achieved here by explicitly determining the x-axis size
- % and adjusting the y-axis size based on this length.
-
- if strcmp(units, 'normalized')
- % The dpi is needed to associate the size on the screen (in pixels)
- % to the physical size of the plot (on a pdf, for example).
- % Unfortunately, MATLAB doesn't seem to be able to always make a
- % good guess about the current DPI (a bug is filed for this on
- % mathworks.com).
- dpi = get(0, 'ScreenPixelsPerInch');
-
- unit = 'in';
- figuresize = get(gcf, 'Position');
-
- width = position(3) * figuresize(3) / dpi;
-
- else % assume that TikZ knows the unit
- unit = units;
- width = position(3);
+
+ 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
- % set y-axis length
- xLim = get (handle, 'XLim');
- yLim = get (handle, 'YLim');
- aspectRatio = get (handle, 'DataAspectRatio'); % = daspect
+ % Use tex units, if possible (to make things simple)
+ srcUnit = matlab2texUnits(lower(srcUnit),lower(srcUnit));
+ dstUnit = matlab2texUnits(lower(dstUnit),lower(dstUnit));
- % Actually, we'd have
- %
- % xlength = (xLim(2)-xLim(1)) / aspectRatio(1);
- % ylength = (yLim(2)-yLim(1)) / aspectRatio(2);
- %
- % but as xlength is scaled to a fixed 'dimension.x', 'dimension.y'
- % needs to be rescaled accordingly.
- height = width ...
- * aspectRatio(1) / aspectRatio(2) ...
- * (yLim(2)-yLim(1)) / (xLim(2)-xLim(1));
- % ---------------------------------------------------------------------
- otherwise
- error('getAxesDimensions:illDaspectMode', ...
- 'Illegal DataAspectRatioMode ''%s''.', daspectmode);
- end
+ 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.cmdOpts.Results.width into value and unit.
+% Decompose m2t.cmdOpts.Results.width into value and unit.
% Regular expression to match '4.12cm', '\figurewidth', ...
fp_regex = '[-+]?\d*\.?\d*(?:e[-+]?\d+)?';
- pattern = strcat('(', fp_regex, ')?', '(\\?[a-z]+)');
+ 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);
+ 'The width string ''%s'' could not be decomposed into value-unit pair.', str);
end
if length(t{1}) == 1
@@ -4257,488 +5201,135 @@ function out = extractValueUnit(str)
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 newstr = escapeCharacters(str)
- % Replaces the single characters % and \ by their escaped versions
- % %% and \\, respectively.
-
- newstr = str;
- newstr = strrep(newstr, '%' , '%%');
- newstr = strrep(newstr, '\' , '\\');
-
-end
-% =========================================================================
-function out = isVisible(handles)
- % Determines whether an object is actually visible or not.
- %
- out = strcmp(get(handles,'Visible'), 'on');
- % There's another handle property, 'HandleVisibility', which may or may not
- % determine the visibility of the object. Empirically, it seems to be 'off'
- % whenever we're dealing with an object that's not user-created, such as
- % automatic axis ticks, baselines in bar plots, axis lines for polar plots
- % and so forth.
- % For now, don't check 'HandleVisibility'.
-
- %% Don't use short-circuit logical operators here to keep the function working
- %% for multiple handle inputs.
- %out = ~(strcmp(get(handles,'Visible'), 'off') ...
- % |strcmp(get(handles, 'HandleVisibility'), 'off'));
-end
-% =========================================================================
-function [relevantAxesHandles, alignmentOptions, plotOrder] =...
- alignSubPlots(m2t, axesHandles)
- % Returns the alignment options for all the axes enviroments.
- % The question whether two plots are aligned on the left, right, top, or
- % bottom is answered by looking at the 'Position' property of the
- % axes object.
- %
- % The second output argument 'ix' is the order in which the axes
- % environments need to be created. This is to make sure that plots
- % which act as a reference are processed first.
- %
- % The output vector 'alignmentOptions' contains:
- % - whether or not it is a reference (.isRef)
- % - axes name (.name), only set if .isRef is true
- % - the actual Pgfplots options (.opts)
- %
- % The routine tries to be smart in the sense that it will detect that in
- % a setup such as
- %
- % [AXES1 AXES2]
- % [AXES3 ]
- %
- % 'AXES1' will serve as a reference for AXES2 and AXES3.
- % It does so by first computing a 'dependency' graph, then traversing
- % the graph starting from a node (AXES) with maximal connections.
- %
- % TODO:
- % - diagonal connections 'a la
- % [AXES1 ]
- % [ AXES2]
- %
- % TODO: fix this function
- % TODO: look for unique IDs of the axes enviroments
- % which could be returned along with its properties
-
- relevantAxesHandles = [];
- for axesHandle = axesHandles(:)'
- % Only handle visible non-colorbar handles.
- if axisIsVisible(axesHandle) && ~strcmp(get(axesHandle,'Tag'), 'Colorbar')
- relevantAxesHandles(end+1) = axesHandle;
- end
- end
- numRelevantHandles = length(relevantAxesHandles);
-
- % initialize alignmentOptions
- alignmentOptions = struct();
- for k = 1:numRelevantHandles
- alignmentOptions(k).opts = cell(0);
- end
-
- % return immediately if nothing is to be aligned
- if numRelevantHandles <= 1
- plotOrder = 1;
- return
- end
-
- % Connectivity matrix of the graph.
- % Contains 0's where the axes environments are not aligned, and
- % positive integers where they are. The integer encodes how the axes
- % are aligned (top right:bottom left, and so on).
- C = zeros(numRelevantHandles, numRelevantHandles);
-
- % 'isRef' tells whether the respective plot acts as a position reference
- % for another plot.
- % TODO: preallocate this
- % Also, gather all the positions.
- axesPos = zeros(numRelevantHandles, 4);
- for k = 1:numRelevantHandles
- % `axesPos(i,:)` contains
- % (indices 1,3): the x-value of the left and the right axis, and
- % (indices 2,4): the y-value of the bottom and top axis,
- % of plot `i`.
- axesPos(k,:) = get(relevantAxesHandles(k), 'Position');
- axesPos(k,3) = axesPos(k,1) + axesPos(k,3);
- axesPos(k,4) = axesPos(k,2) + axesPos(k,4);
- end
-
-
- % Loop over all figures to see if axes are aligned.
- % Look for exactly *one* alignment, even if there might be more.
- %
- % Among all the {x,y}-alignments choose the one with the closest
- % {y,x}-distance. This is important, for example, in situations where
- % there are 3 plots on top of each other:
- % We want no. 2 to align below no. 1, and no. 3 below no. 2
- % (and not no. 1 again).
- %
- % There are nine alignments this algorithm can deal with:
- %
- % 3| |4
- % __ _____________ __
- % -2 | | 2
- % | |
- % | 5 |
- % | |
- % | |
- % -1_ |_____________| 1_
- %
- % -3| |-4
- %
- % They are coded in numbers -4 through 5. The matrix C will contain the
- % corresponding code at position (i,j), if plot number i and j are
- % aligned in such a way.
- % If two plots happen to coincide at both left and right axes, for
- % example, only one relation is stored.
- %
- for i = 1:numRelevantHandles
- for j = i+1:numRelevantHandles
- if max(abs(axesPos(i,:)-axesPos(j,:))) < m2t.tol
- % twins
- C(i,j) = 5;
- C(j,i) = -5;
-
- elseif abs(axesPos(i,1)-axesPos(j,1)) < m2t.tol;
- % Left axes align.
- % Check if the axes are on top of each other.
- if axesPos(i,2) > axesPos(j,4)
- C(i,j) = -3;
- C(j,i) = 3;
- elseif axesPos(j,2) > axesPos(i,4)
- C(i,j) = 3;
- C(j,i) = -3;
- end
-
- elseif abs(axesPos(i,1)-axesPos(j,3)) < m2t.tol
- % left axis of 'i' aligns with right axis of 'j'
- if axesPos(i,2) > axesPos(j,4)
- C(i,j) = -3;
- C(j,i) = 4;
- elseif axesPos(j,2) > axesPos(i,4)
- C(i,j) = 3;
- C(j,i) = -4;
- end
-
- elseif abs(axesPos(i,3)-axesPos(j,1)) < m2t.tol
- % right axis of 'i' aligns with left axis of 'j'
- if axesPos(i,2) > axesPos(j,4)
- C(i,j) = -4;
- C(j,i) = 3;
- elseif axesPos(j,2) > axesPos(i,4)
- C(i,j) = 4;
- C(j,i) = -3;
- end
-
- elseif abs(axesPos(i,3)-axesPos(j,1)) < m2t.tol
- % right axes of 'i' and 'j' align
- if axesPos(i,2) > axesPos(j,4)
- C(i,j) = -4;
- C(j,i) = 4;
- elseif axesPos(j,2) > axesPos(i,4)
- C(i,j) = 4;
- C(j,i) = -4;
- end
-
- elseif abs(axesPos(i,2)-axesPos(j,2)) < m2t.tol
- % lower axes of 'i' and 'j' align
- if axesPos(i,1) > axesPos(j,3)
- C(i,j) = -1;
- C(j,i) = 1;
- elseif axesPos(j,1) > axesPos(i,3)
- C(i,j) = 1;
- C(j,i) = -1;
- end
-
- elseif abs(axesPos(i,2)-axesPos(j,4)) < m2t.tol
- % lower axis of 'i' aligns with upper axis of 'j'
- if axesPos(i,1) > axesPos(j,3)
- C(i,j) = -1;
- C(j,i) = 2;
- elseif axesPos(j,1) > axesPos(i,3)
- C(i,j) = 1;
- C(j,i) = -2;
- end
-
- elseif abs(axesPos(i,4)-axesPos(j,2)) < m2t.tol
- % upper axis of 'i' aligns with lower axis of 'j'
- if axesPos(i,1) > axesPos(j,3)
- C(i,j) = -2;
- C(j,i) = 1;
- elseif axesPos(j,1) > axesPos(i,3)
- C(i,j) = 2;
- C(j,i) = -1;
- end
-
- elseif abs(axesPos(i,4)-axesPos(j,4)) < m2t.tol
- % upper axes of 'i' and 'j' align
- if axesPos(i,1) > axesPos(j,3)
- C(i,j) = -2;
- C(j,i) = 2;
- elseif axesPos(j,1) > axesPos(i,3)
- C(i,j) = 2;
- C(j,i) = -2;
- end
- end
- end
- end
-
- % Now, the matrix C contains all alignment information.
- % If, for any node, there is more than one plot that aligns with it in the
- % same way (e.g., upper left), then pick exactly *one* of them.
- % Take the one that is closest to the correspondig plot.
- for i = 1:numRelevantHandles
- for j = 1:numRelevantHandles
-
- if C(i,j)==0 || abs(C(i,j))==5 % don't check for double zeros (aka "no relation"'s) or triplets, quadruplets,...
- continue
- end
-
- % find doubles, and count C(i,j) in
- doub = find(C(i,j:numRelevantHandles)==C(i,j)) ...
- + j-1; % to get the actual index
-
- if length(doub)>1
- % Uh-oh, found doubles:
- % Pick the one with the minimal distance, delete the other
- % relations.
- switch C(i,j)
- case {1,2} % all plots sit right of 'i'
- dist = axesPos(doub,1) - axesPos(i,3);
- case {-1,-2} % all plots sit left of 'i'
- dist = axesPos(i,1) - axesPos(doub,3);
- case {3,4} % all plots sit above 'i'
- dist = axesPos(doub,2) - axesPos(i,4);
- case {-3,-4} % all plots sit below 'i'
- dist = axesPos(i,2) - axesPos(doub,4);
- otherwise
- error('alignSubPlots:illCode', ...
- 'Illegal alignment code %d.', C(i,j));
- end
-
- [dummy,idx] = min(dist); %#ok
- % 'idx' holds the index of the minimum.
- % If there is more than one, then
- % 'idx' has twins. min returns the one
- % with the lowest index.
-
- % delete the index from the 'remove list'
- doub(idx) = [];
- C(i,doub) = 0;
- C(doub,i) = 0;
- end
- end
- end
-
- % Alright. The matrix 'C' now contains exactly the alignment info that
- % we are looking for.
-
- %% Is each axes environment connected to at least one other?
- %noConn = find(~any(C,2));
- %for nc = noConn(:)'
- % userWarning(m2t, ['The axes environment no. %d is not aligned with',...
- % ' any other axes environment and will be plotted',...
- % ' right in the middle.'], nc);
- %end
-
- % Now, actually go ahead and process the info to return Pgfplots alignment
- % options.
-
- % Sort the axes environments by the number of connections they have.
- % That means: start with the plot which has the most connections.
- [dummy, IX] = sort(sum(C~=0, 2), 'descend'); %#ok
-
- plotOrder = zeros(1,numRelevantHandles);
- plotNumber = 0;
- for ix = IX(:)'
- [plotOrder,plotNumber,alignmentOptions] = ...
- setOptionsRecursion(plotOrder, plotNumber, C, alignmentOptions, ix, []);
- end
-
- % Burkart, July 23, 2011:
- % Now let's rearrange the plot order.
- % Theoretically this should be harmful in that it would result in
- % subplots that refer to another named subplot to be drawn before the
- % named subplot itself is drawn. However, this reordering actually fixes
- % one such problem that only occurred in Octave with test case
- % subplot2x2b. Oddly enough the error only showed when certain other test
- % cases (including subplot2x2b itself) had been run beforehand and not if
- % subplot2x2b was the first / only test case to be run or if a harmless
- % test case like one_point preceded subplot2x2b.
- % The exact mechanism that led to this bug was not uncovered but a
- % differently ordered axesPos near the top of this function eventually
- % led to the wrong plotOrder and thus to a subplot referring to one that
- % came later in the TikZ output.
- % The reordering was tested against the test suite and didn't break any
- % of the test cases, neither on Octave nor on MATLAB.
- newPlotOrder = zeros(1,numRelevantHandles);
- for k = 1:numRelevantHandles
- newPlotOrder(plotOrder(k)) = k;
- end
- plotOrder = newPlotOrder;
+ 'The width string ''%s'' could not be decomposed into value-unit pair.', str);
+ end
end
-% -----------------------------------------------------------------------
-% sets the alignment options for a specific node
-% and passes on the its children
-% -----------------------------------------------------------------------
-function [plotOrder, plotNumber, alignmentOptions] = setOptionsRecursion(plotOrder, plotNumber, C, alignmentOptions, k, parent)
-
- % return immediately if is has been processed before
- if plotOrder(k)
- return
+% ==============================================================================
+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 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
-
- plotNumber = plotNumber + 1;
-
- % TODO not looking at twins is probably not the right thing to do
- % find the non-zero elements in the k-th row
- unprocessedFriends = find(C(k,:)~=0 & ~plotOrder);
-
- unprocessedChildren = unprocessedFriends(abs(C(k,unprocessedFriends))~=5);
- unprocessedTwins = unprocessedFriends(abs(C(k,unprocessedFriends))==5);
-
- if ~isempty(unprocessedChildren) % Are there unprocessed children?
- % Give these axes a name.
- alignmentOptions(k).opts = ...
- addToOptions(alignmentOptions(k).opts, 'name', sprintf('plot%d', k));
+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
-
- if ~isempty(parent) % if a parent is given
- if (abs(C(parent,k))==5)
- % don't apply "at=" for younger twins
- else
- % See were this node sits with respect to its parent,
- % and adapt the option accordingly.
- anchor = cornerCode2pgfplotOption(C(k,parent));
- refPos = cornerCode2pgfplotOption(C(parent,k));
-
- % add the option
- alignmentOptions(k).opts = ...
- addToOptions(alignmentOptions(k).opts, 'at', sprintf('(plot%d.%s)', parent, refPos));
- alignmentOptions(k).opts = ...
- addToOptions(alignmentOptions(k).opts, 'anchor', anchor);
- 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 opts = addIfNotDefault(m2t, type, handle, key, default, pgfKey, opts)
+% sets an option in the options array named `pgfKey` if the MATLAB option is
+% not a default value
+% FIXME: this function is currently unused -- remove it in the future?
+ [value, isDefault] = getAndCheckDefault(type, handle, key, default);
+ if ~isDefault || m2t.cmdOpts.Results.strict
+ opts = opts_add(opts, pgfKey, value);
end
-
- plotOrder(k) = plotNumber;
-
- % Recursively loop over all dependent 'child' axes;
- % first the twins, though, to make sure they appear consecutively
- % in the TikZ file.
- for ii = unprocessedTwins
- [plotOrder,plotNumber,alignmentOptions] = setOptionsRecursion(plotOrder, plotNumber, C, alignmentOptions, ii, k);
- end
- for ii = unprocessedChildren
- [plotOrder,plotNumber,alignmentOptions] = setOptionsRecursion(plotOrder, plotNumber, C, alignmentOptions, ii, k);
- end
-
-end
-% =========================================================================
-% translates the corner code in a real option to Pgfplots
-function pgfOpt = cornerCode2pgfplotOption(code)
-
- switch code
- case 1
- pgfOpt = 'right of south east';
- case 2
- pgfOpt = 'right of north east';
- case 3
- pgfOpt = 'above north west';
- case 4
- pgfOpt = 'above north east';
- case -1
- pgfOpt = 'left of south west';
- case -2
- pgfOpt = 'left of north west';
- case -3
- pgfOpt = 'below south west';
- case -4
- pgfOpt = 'below south east';
- otherwise
- error('cornerCode2pgfplotOption:unknRelCode',...
- 'Illegal alignment code %d.', code);
- end
-
end
-% =========================================================================
-function refAxesId = getReferenceAxes(loc, colBarPos, axesHandlesPos)
-
- % if there is only one axes reference handle, it must be the parent
- if size(axesHandlesPos,1) == 1
- refAxesId = 1;
- return;
- end
-
- % MATLAB(R)'s keywords are camel cased (e.g., 'NorthOutside'), in Octave
- % small cased ('northoutside'). Hence, use lower() for uniformity.
- switch lower(loc)
- case { 'north', 'south', 'east', 'west' }
- userWarning(m2t, 'Don''t know how to deal with inner colorbars yet.');
- return;
-
- case {'northoutside'}
- % scan in 'axesHandlesPos' for the handle number that lies
- % directly below colBarHandle
- [m,refAxesId] = min(colBarPos(2) ...
- - axesHandlesPos(axesHandlesPos(:,4)<colBarPos(2),4)); %#ok
-
- case {'southoutside'}
- % scan in 'axesHandlesPos' for the handle number that lies
- % directly above colBarHandle
- [m,refAxesId] = min(axesHandlesPos(axesHandlesPos(:,2)>colBarPos(4),2)...
- - colBarPos(4)); %#ok
-
- case {'eastoutside'}
- % scan in 'axesHandlesPos' for the handle number that lies
- % directly left of colBarHandle
- [m,refAxesId] = min(colBarPos(1) ...
- - axesHandlesPos(axesHandlesPos(:,3)<colBarPos(1),3)); %#ok
-
- case {'westoutside'}
- % scan in 'axesHandlesPos' for the handle number that lies
- % directly right of colBarHandle
- [m,refAxesId] = min(axesHandlesPos(axesHandlesPos(:,1)>colBarPos(3),1) ...
- - colBarPos(3) ); %#ok
-
- otherwise
- error('getReferenceAxes:illLocation', ...
- 'Illegal ''Location'' ''%s''.', loc );
- end
+% ==============================================================================
+function bool = isVisible(handles)
+% Determines whether an object is actually visible or not.
+ bool = strcmpi(get(handles,'Visible'), 'on');
+ % There's another handle property, 'HandleVisibility', which may or may not
+ % determine the visibility of the object. Empirically, it seems to be 'off'
+ % whenever we're dealing with an object that's not user-created, such as
+ % automatic axis ticks, baselines in bar plots, axis lines for polar plots
+ % and so forth. For now, don't check 'HandleVisibility'.
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 = double(axesHandles(idx));
+
+ % Compute the bounding box if width or height of the figure are set by
+ % parameter
+ if ~isempty(m2t.cmdOpts.Results.width) || ~isempty(m2t.cmdOpts.Results.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.
+% Display usage information.
+ if m2t.cmdOpts.Results.showInfo
+ mess = sprintf(message, varargin{:});
- if ~m2t.cmdOpts.Results.showInfo
- return
- end
-
- mess = sprintf(message, varargin{:});
-
- % Replace '\n' by '\n *** ' and print.
- mess = strrep(mess, sprintf('\n'), sprintf('\n *** '));
- fprintf(' *** %s\n', mess);
+ 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.cmdOpts.Results.showWarnings
- return
- end
-
- warning('matlab2tikz:userWarning', message, varargin{:});
+% Drop-in replacement for warning().
+ if m2t.cmdOpts.Results.showWarnings
+ warning('matlab2tikz:userWarning', message, varargin{:});
+ end
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.cmdOpts.Results.(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;
- end
-
- if iscell(children)
+ elseif iscell(children)
for k = 1:length(children)
parent = addChildren(parent, children{k});
end
@@ -4750,9 +5341,8 @@ function parent = addChildren(parent, children)
end
end
end
-% =========================================================================
+% ==============================================================================
function printAll(m2t, env, fid)
-
if isfield(env, 'colors') && ~isempty(env.colors)
fprintf(fid, '%s', env.colors);
end
@@ -4760,7 +5350,8 @@ function printAll(m2t, env, fid)
if isempty(env.options)
fprintf(fid, '\\begin{%s}\n', env.name);
else
- fprintf(fid, '\\begin{%s}[%%\n%s\n]\n', env.name, prettyprintOpts(m2t, env.options, sprintf(',\n')));
+ fprintf(fid, '\\begin{%s}[%%\n%s\n]\n', env.name, ...
+ opts_print(m2t, env.options, sprintf(',\n')));
end
for item = env.content
@@ -4777,616 +5368,808 @@ function printAll(m2t, env, fid)
end
% End the tikzpicture environment with an empty comment and no newline
- % so no additional space is generated by the tikzpicture in TeX.
- % This is useful if something should immediately follow the picture,
- % e.g. another picture, with a separately defined spacing or without
- % any spacing at all between both pictures.
- if strcmp(env.name, 'tikzpicture')
+ % 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
-
- % Multiline handling.
- % Make sure that the input string is really a cellstr that contains only
- % one-line strings.
- if ischar(strings)
- strings = cellstr(strings);
- elseif iscellstr(strings)
- cs = {};
- for s = strings
- tmp = cellstr(s);
- cs = {cs{:}, tmp{:}};
- end
- strings = cs;
- else
- error('matlab2tikz:prettyPrint', 'Data type not understood.');
- end
-
- % Now loop over the strings and return them pretty-printed in c.
- c = {};
- for s = strings
-
- % If the user set the matlab2tikz parameter 'parseStrings' to false, no
- % parsing of strings takes place, thus making the user 100% responsible.
- if ~m2t.cmdOpts.Results.parseStrings
- c = strings;
- return
- end
+% 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
+
+ strings = cellstrOneLinePerCell(strings);
+
+ % Now loop over the strings and return them pretty-printed in c.
+ c = {};
+ for k = 1:length(strings)
+ % linear indexing for independence of cell array dimensions
+ s = strings{k};
+
+ % If the user set the matlab2tikz parameter 'parseStrings' to false, no
+ % parsing of strings takes place, thus making the user 100% responsible.
+ if ~m2t.cmdOpts.Results.parseStrings
+ c = 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
+ % 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
- % 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 $...$ but otherwise leave untouched
- string = regexprep(s, '^\$\$(.*)\$\$$', '$$1$');
-
- case 'tex' % Subset of plain TeX markup language
-
- % Deal with UTF8 characters.
- string = s;
- 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(m2t, '\\{', '\{');
- string = regexprep(string, '(?<!\\textbackslash){', repl);
- repl = switchMatOct(m2t, '\\}', '\}');
- string = regexprep(string, '(?<!\\textbackslash{)}', repl);
- string = strrep(string, '$', '\$');
- string = strrep(string, '%', '\%');
- string = strrep(string, '_', '\_');
- string = strrep(string, '^', '\textasciicircum{}');
- string = strrep(string, '#', '\#');
- string = strrep(string, '&', '\&');
- string = strrep(string, '~', '\textasciitilde{}'); % or '\~{}'
- % Clean up: remove superfluous '{}' if it's followed by a backslash
- string = strrep(string, '{}\', '\');
- % Clean up: remove superfluous '{}' at the end of 'string'
- string = regexprep(string, '\{\}$', '');
-
- % Make sure to return a string and not a cellstr.
- if iscellstr(string)
- string = string{1};
- end
- otherwise
- error('matlab2tikz:prettyPrint', 'Unknown interpreter');
- end
- c{end+1} = string;
- end
+ % 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, '(?<!\\textbackslash){', repl);
+ repl = switchMatOct('\\}', '\}');
+ string = regexprep(string, '(?<!\\textbackslash{)}', repl);
+ string = strrep(string, '$', '\$');
+ string = strrep(string, '%', '\%');
+ string = strrep(string, '_', '\_');
+ string = strrep(string, '^', '\textasciicircum{}');
+ string = strrep(string, '#', '\#');
+ string = strrep(string, '&', '\&');
+ string = strrep(string, '~', '\textasciitilde{}'); % or '\~{}'
+ % Clean up: remove superfluous '{}' if it's followed by a backslash
+ string = strrep(string, '{}\', '\');
+ % Clean up: remove superfluous '{}' at the end of 'string'
+ string = regexprep(string, '\{\}$', '');
+
+ % Make sure to return a string and not a cellstr.
+ if iscellstr(string)
+ string = string{1};
+ end
+ otherwise
+ error('matlab2tikz:prettyPrint', 'Unknown interpreter');
+ end
+ c{end+1} = string;
+ end
end
-% =========================================================================
+% ==============================================================================
+function strings = cellstrOneLinePerCell(strings)
+% convert to cellstr that contains only one-line strings
+ if ischar(strings)
+ strings = cellstr(strings);
+ elseif iscellstr(strings)
+ cs = {};
+ for s = strings
+ tmp = cellstr(s);
+ cs = {cs{:}, tmp{:}};
+ end
+ strings = cs;
+ else
+ error('matlab2tikz:cellstrOneLinePerCell', ...
+ 'Data type not understood.');
+ end
+end
+% ==============================================================================
function parsed = parseTexString(m2t, string)
+ if iscellstr(string)
+ % Convert cell string to regular string, otherwise MATLAB complains
+ string = string{:};
+ end
- % Convert cell string to regular string, otherwise MATLAB complains
- if iscellstr(string)
- string = string{:};
- end
-
- % Get the position of all braces
- bracesPos = regexp(string, '\{|\}');
+ % Get the position of all braces
+ bracesPos = regexp(string, '\{|\}');
- % Exclude braces that are part of any of these MATLAB-supported TeX commands:
- % \color{...} \color[...]{...} \fontname{...} \fontsize{...}
- [sCmd, eCmd] = regexp(string, '\\(color(\[[^\]]*\])?|fontname|fontsize)\{[^}]*\}');
- for i = 1:length(sCmd)
- bracesPos(bracesPos >= sCmd(i) & bracesPos <= eCmd(i)) = [];
- end
+ % Exclude braces that are part of any of these MATLAB-supported TeX commands:
+ % \color{...} \color[...]{...} \fontname{...} \fontsize{...}
+ [sCmd, eCmd] = regexp(string, '\\(color(\[[^\]]*\])?|fontname|fontsize)\{[^}]*\}');
+ for i = 1:length(sCmd)
+ bracesPos(bracesPos >= 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 = '(?<!\\)(\\\\)*\\(\{|\})';
- escaped = regexp(string, expr, 'end');
- % It's necessary to go over 'string' with the same RegEx again to catch
- % overlapping matches, e.g. string == '\{\}'. In such a case the simple
- % regexp(...) above only finds the first brace. What we have to do is look
- % only at the part of 'string' that starts with the first brace but doesn't
- % encompass its escaping backslash. Iterating over all previously found
- % matches makes sure all overlapping matches are found, too. That way even
- % cases like string == '\{\} \{\}' are handled correctly.
- % The call to unique(...) is not necessary to get the behavior described, but
- % by removing duplicates in 'escaped' it's cleaner than without.
- for i = escaped
- escaped = unique([escaped, regexp(string(i:end), expr, 'end') + i-1]);
- end
- % Now do the actual removal of escaped braces
- for i = 1:length(escaped)
- bracesPos(bracesPos == escaped(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 = '(?<!\\)(\\\\)*\\(\{|\})';
+ escaped = regexp(string, expr, 'end');
+ % It's necessary to go over 'string' with the same RegEx again to catch
+ % overlapping matches, e.g. string == '\{\}'. In such a case the simple
+ % regexp(...) above only finds the first brace. What we have to do is look
+ % only at the part of 'string' that starts with the first brace but doesn't
+ % encompass its escaping backslash. Iterating over all previously found
+ % matches makes sure all overlapping matches are found, too. That way even
+ % cases like string == '\{\} \{\}' are handled correctly.
+ % The call to unique(...) is not necessary to get the behavior described, but
+ % by removing duplicates in 'escaped' it's cleaner than without.
+ for i = escaped
+ escaped = unique([escaped, regexp(string(i:end), expr, 'end') + i-1]);
+ end
+ % Now do the actual removal of escaped braces
+ for i = 1:length(escaped)
+ bracesPos(bracesPos == escaped(i)) = [];
+ end
- parsed = '';
- % Have a virtual brace one character left of where the actual string
- % begins (remember, MATLAB strings start counting at 1, not 0). This is
- % to make sure substrings left of the first brace get parsed, too.
- prevBracePos = 0;
- % Iterate over all the brace positions in order to split up 'string'
- % at those positions and then parse the substrings. A virtual brace is
- % added right of where the actual string ends to make sure substrings
- % right of the right-most brace get parsed as well.
- for currBracePos = [bracesPos, length(string)+1]
- if (prevBracePos + 1) < currBracePos
- % Parse the substring between (but not including) prevBracePos
- % and currBracePos, i.e. between the previous brace and the
- % current one (but only if there actually is a non-empty
- % substring). Then append it to the output string.
- substring = string(prevBracePos+1 : currBracePos-1);
- parsed = [parsed, parseTexSubstring(m2t, substring)];
- end
- if currBracePos <= length(string)
- % Append the brace itself to the output string, but only if the
- % current brace position is within the limits of the string, i.e.
- % don't append anything for the last, virtual brace that is only
- % there to enable parsing of substrings beyond the right-most
- % actual brace.
- brace = string(currBracePos);
- parsed = [parsed, brace];
- end
- % The current brace position will be next iteration's previous one
- prevBracePos = currBracePos;
- end
+ parsed = '';
+ % Have a virtual brace one character left of where the actual string
+ % begins (remember, MATLAB strings start counting at 1, not 0). This is
+ % to make sure substrings left of the first brace get parsed, too.
+ prevBracePos = 0;
+ % Iterate over all the brace positions in order to split up 'string'
+ % at those positions and then parse the substrings. A virtual brace is
+ % added right of where the actual string ends to make sure substrings
+ % right of the right-most brace get parsed as well.
+ for currBracePos = [bracesPos, length(string)+1]
+ if (prevBracePos + 1) < currBracePos
+ % Parse the substring between (but not including) prevBracePos
+ % and currBracePos, i.e. between the previous brace and the
+ % current one (but only if there actually is a non-empty
+ % substring). Then append it to the output string.
+ substring = string(prevBracePos+1 : currBracePos-1);
+ parsed = [parsed, parseTexSubstring(m2t, substring)];
+ end
+ if currBracePos <= length(string)
+ % Append the brace itself to the output string, but only if the
+ % current brace position is within the limits of the string, i.e.
+ % don't append anything for the last, virtual brace that is only
+ % there to enable parsing of substrings beyond the right-most
+ % actual brace.
+ brace = string(currBracePos);
+ parsed = [parsed, brace];
+ end
+ % The current brace position will be next iteration's previous one
+ prevBracePos = currBracePos;
+ end
- % Enclose everything in $...$ to use math mode
- parsed = ['$' parsed '$'];
- % ...except when everything is text
- parsed = regexprep(parsed, '^\$\\text\{([^}]*)\}\$$', '$1');
- % start-> $ \text {(non-}) } $<-end
- % ...or when the parsed string is empty
- parsed = regexprep(parsed, '^\$\$$', '');
+ % Enclose everything in $...$ to use math mode
+ parsed = ['$' parsed '$'];
+ % ...except when everything is text
+ parsed = regexprep(parsed, '^\$\\text\{([^}]*)\}\$$', '$1');
+ % start-> $ \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
- % Keep a copy of the original input string for potential warning messages
- % referring to the string as it was originally used in MATLAB/Octave and
- % not the current value of the variable 'string' halfway into the m2t
- % conversion.
- origstr = string;
-
- % Font families (italic, bold, etc.) get a trailing '{}' because in
- % MATLAB 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 = switchMatOct(m2t, '}$1\\text{', '}$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 = switchMatOct(m2t, '$1}_\\text{', '$1}_\text{');
- string = regexprep(string, '(?<!\\)((\\\\)*)_', repl);
-
- % '^' has to be in math mode so long as it's not escaped as '\^' in which
- % case it is expressed as '\textasciicircum{}' for compatibility with
- % regular TeX. Same thing here regarding even/odd number of backslashes
- % as in the case of underscores above.
- repl = switchMatOct(m2t, '$1\\textasciicircum{}', '$1\textasciicircum{}');
- string = regexprep(string, '(?<!\\)((\\\\)*)\\\^', repl);
- repl = switchMatOct(m2t, '$1}^\\text{', '$1}^\text{');
- string = regexprep(string, '(?<!\\)((\\\\)*)\^', repl);
-
- % '\\' 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{}');
-
- % Escape plain "&" in MATLAB and replace it and the following character with
- % a space in Octave unless the "&" is already escaped
- switch m2t.env
- case 'MATLAB'
- string = strrep(string, '&', '\&');
- case 'Octave'
- % Ampersands should already be escaped in Octave.
- % Octave (tested with 3.6.0) handles un-escaped ampersands a little
- % funny in that it removes the following character, if there is one:
- % 'abc&def' -> '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, '(?<!\\)&(?!})', ' ');
- string = regexprep(string, '(?<!\\)&}', '}');
- if regexp(string, '(?<!\\)&\\')
- % If there's a backslash after the ampersand, that means not only
- % the backslash should be removed but the whole escape sequence,
- % e.g. '\delta' or '\$'. Actually the '\delta' case is the
- % trickier one since by now 'string' would have been turned from
- % 'abc&\deltaef' into '\text{abc&}\delta{}\text{ef}', i.e. after
- % the ampersand first comes a closing brace and then '\delta';
- % the latter as well as the ampersand itself should be removed
- % while the brace must remain in place to avoid unbalanced braces.
- userWarning(m2t, ...
- ['TeX string ''%s'' contains a special character ' ...
- 'after an un-escaped ''&''. The output generated ' ...
- 'by matlab2tikz will not precisely match that ' ...
- 'which you see in Octave itself in that the ' ...
- 'special character and the preceding ''&'' is ' ...
- 'not replaced with a space.'], origstr)
- end
- otherwise
- errorUnknownEnvironment();
- end
- % Escape plain "~" in MATLAB and replace escaped "\~" in Octave with a proper
- % escape sequence. An un-escaped "~" produces weird output in Octave, thus
- % give a warning in that case
- switch m2t.env
- case 'MATLAB'
- string = strrep(string, '~', '\textasciitilde{}'); % or '\~{}'
- case 'Octave'
- string = strrep(string, '\~', '\textasciitilde{}'); % ditto
- if regexp(string, '(?<!\\)~')
- userWarning(m2t, ...
- ['TeX string ''%s'' contains un-escaped ''~''. ' ...
- 'For proper display in Octave you probably ' ...
- 'want to escape it even though that''s ' ...
- 'incompatible with MATLAB. ' ...
- 'In the matlab2tikz output it will have its ' ...
- 'usual TeX function as a non-breaking space.'], ...
- origstr)
- end
- otherwise
- errorUnknownEnvironment();
- end
-
- % 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{');
-
- % Some further processing makes the output behave more like TeX math mode,
- % but only if the matlab2tikz parameter parseStringsAsMath=true.
- if m2t.cmdOpts.Results.parseStringsAsMath
-
- % Some characters should be in math mode: =-+/,.()<>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
+ % 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
- % \text{ } should be a math-mode space
- string = regexprep(string, '\\text\{(\s+)}', '$1');
+ % 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, '(?<!\\)((\\\\)*)_', repl);
+
+ % '^' has to be in math mode so long as it's not escaped as '\^' in which
+ % case it is expressed as '\textasciicircum{}' for compatibility with
+ % regular TeX. Same thing here regarding even/odd number of backslashes
+ % as in the case of underscores above.
+ repl = '$1\\textasciicircum{}';
+ string = regexprep(string, '(?<!\\)((\\\\)*)\\\^', repl);
+ repl = '$1}^\\text{';
+ string = regexprep(string, '(?<!\\)((\\\\)*)\^', repl);
+
+ % '<' and '>' 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.cmdOpts.Results.parseStringsAsMath == 0
+ string = regexprep(string, '<', '\\textless{}');
+ string = regexprep(string, '>', '\\textgreater{}');
+ end
- % '<<' probably means 'much smaller than', i.e. '\ll'
- repl = switchMatOct(m2t, '$1\\ll{}$2', '$1\ll{}$2');
- string = regexprep(string, '([^<])<<([^<])', repl);
+ % 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
- % Single letters are most likely variables and thus should be in math mode
- string = regexprep(string, '\\text\{([a-zA-Z])\}', '$1');
+ % 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, '(?<!\\)\{\}(\\)', '$1');
+ % \alpha{}\text{...} -> \alpha\text{...}
+
+ % Clean up: convert '{}}' to '}' unless it's prefixed by a backslash
+ string = regexprep(string, '(?<!\\)\{\}\}', '}');
+
+ % Clean up: remove '{}' at the end of 'string' unless it's prefixed by a
+ % backslash
+ string = regexprep(string, '(?<!\\)\{\}$', '');
+end
+% ==============================================================================
+function string = escapeTildes(m2t, string, origstr)
+% Escape plain "~" in MATLAB and replace escaped "\~" in Octave with a proper
+% escape sequence. An un-escaped "~" produces weird output in Octave, thus
+% give a warning in that case
+ switch getEnvironment
+ case 'MATLAB'
+ string = strrep(string, '~', '\textasciitilde{}'); % or '\~{}'
+ case 'Octave'
+ string = strrep(string, '\~', '\textasciitilde{}'); % ditto
+ if regexp(string, '(?<!\\)~')
+ userWarning(m2t, ...
+ ['TeX string ''%s'' contains un-escaped ''~''. ' ...
+ 'For proper display in Octave you probably ' ...
+ 'want to escape it even though that''s ' ...
+ 'incompatible with MATLAB. ' ...
+ 'In the matlab2tikz output it will have its ' ...
+ 'usual TeX function as a non-breaking space.'], ...
+ origstr)
+ end
+ otherwise
+ errorUnknownEnvironment();
+ end
+end
+% ==============================================================================
+function string = escapeAmpersands(m2t, string, origstr)
+% Escape plain "&" in MATLAB and replace it and the following character with
+% a space in Octave unless the "&" is already escaped
+ switch getEnvironment
+ case 'MATLAB'
+ string = strrep(string, '&', '\&');
+ case 'Octave'
+ % Ampersands should already be escaped in Octave.
+ % Octave (tested with 3.6.0) handles un-escaped ampersands a little
+ % funny in that it removes the following character, if there is one:
+ % 'abc&def' -> '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, '(?<!\\)&(?!})', ' ');
+ string = regexprep(string, '(?<!\\)&}', '}');
+ if regexp(string, '(?<!\\)&\\')
+ % If there's a backslash after the ampersand, that means not only
+ % the backslash should be removed but the whole escape sequence,
+ % e.g. '\delta' or '\$'. Actually the '\delta' case is the
+ % trickier one since by now 'string' would have been turned from
+ % 'abc&\deltaef' into '\text{abc&}\delta{}\text{ef}', i.e. after
+ % the ampersand first comes a closing brace and then '\delta';
+ % the latter as well as the ampersand itself should be removed
+ % while the brace must remain in place to avoid unbalanced braces.
+ userWarning(m2t, ...
+ ['TeX string ''%s'' contains a special character ' ...
+ 'after an un-escaped ''&''. The output generated ' ...
+ 'by matlab2tikz will not precisely match that ' ...
+ 'which you see in Octave itself in that the ' ...
+ 'special character and the preceding ''&'' is ' ...
+ 'not replaced with a space.'], origstr)
+ end
+ otherwise
+ errorUnknownEnvironment();
+ end
+end
+% ==============================================================================
+function [string] = parseStringsAsMath(m2t, string)
+% Some further processing makes the output behave more like TeX math mode,
+% but only if the matlab2tikz parameter parseStringsAsMath=true.
+ if m2t.cmdOpts.Results.parseStringsAsMath
+
+ % Some characters should be in math mode: =-+/,.()<>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
- end % parseStringsAsMath
+ % \text{ } should be a math-mode space
+ string = regexprep(string, '\\text\{(\s+)}', '$1');
- % Clean up: remove empty \text{}
- string = strrep(string, '\text{}', '');
- % \text{}\alpha{}\text{...} -> \alpha{}\text{...}
+ % '<<' probably means 'much smaller than', i.e. '\ll'
+ repl = switchMatOct('$1\\ll{}$2', '$1\ll{}$2');
+ string = regexprep(string, '([^<])<<([^<])', repl);
- % 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, '(?<!\\)\{\}(\\)', '$1');
- % \alpha{}\text{...} -> \alpha\text{...}
+ % '>>' probably means 'much greater than', i.e. '\gg'
+ repl = switchMatOct('$1\\gg{}$2', '$1\gg{}$2');
+ string = regexprep(string, '([^>])>>([^>])', repl);
- % Clean up: convert '{}}' to '}' unless it's prefixed by a backslash
- string = regexprep(string, '(?<!\\)\{\}\}', '}');
+ % Single letters are most likely variables and thus should be in math mode
+ string = regexprep(string, '\\text\{([a-zA-Z])\}', '$1');
- % Clean up: remove '{}' at the end of 'string' unless it's prefixed by a
- % backslash
- string = regexprep(string, '(?<!\\)\{\}$', '');
+ 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
+% 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
-% =========================================================================
-function opts = addToOptions(opts, key, value)
- % Adds a key-value pair to a struct and does some sanity-checking before.
-
- % Make sure to convert the value to a char first.
- value = char(value);
-
- % Check if the key already exists.
- for k = 1:size(opts,1)
- if strcmp(opts{k,1}, key)
- % The key already exists in struct.
- if strcmp(opts{k,2}, value)
- % The suggested value is the same as the one that's already
- % there. Do nothing.
- return;
- else
- error('matlab2tikz:addToOptions', ...
- ['Trying to add (%s, %s) to struct, but it already ' ...
- 'contains the conflicting key-value pair (%s, %s).'], ...
- key, value, key, opts{k,2});
- end
- 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);
- % The key doesn't exist. Just add it.
- opts = cat(1, opts, {key, 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 = merge(opts1, opts2)
- % Merges two option lists.
- opts = opts1;
- for k = 1:size(opts2, 1)
- opts = addToOptions(opts, opts2{k,1}, opts2{k,2});
- 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 str = prettyprintOpts(m2t, opts, sep)
- nOpts = size(opts,1);
- c = cell(nOpts,1);
- 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 = join(m2t, c, sep);
-end
-% =========================================================================
-function [env,versionString] = getEnvironment()
- % Check if we are in MATLAB or Octave.
- % Calling ver with an argument: iterating over all entries is very slow
- alternatives = {'MATLAB','Octave'};
- for iCase = 1:numel(alternatives)
- env = alternatives{iCase};
- vData = ver(env);
- if ~isempty(vData)
- versionString = vData.Version;
- return; % found the right environment
- end
- end
- % otherwise:
- env = [];
- versionString = [];
-end
-% =========================================================================
-function isBelow = isVersionBelow(env, versionA, versionB)
- % Checks if version string or vector versionA is smaller than
- % version string or vector versionB.
-
- vA = versionArray(env, versionA);
- vB = versionArray(env, versionB);
-
- isBelow = false;
- for i = 1:min(length(vA), length(vB))
- if vA(i) > vB(i)
- isBelow = false;
- break;
- elseif vA(i) < vB(i)
- isBelow = true;
- break
+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
-
end
-% =========================================================================
-function arr = versionArray(env, 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 env
- case 'MATLAB'
- split = regexp(str, '\.', 'split');
- case 'Octave'
- split = strsplit(str, '.');
- otherwise
- errorUnknownEnvironment();
+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
- arr = str2num(char(split)); %#ok
- else
- arr = str;
- end
end
-% =========================================================================
-function [retval] = switchMatOct(m2t, matlabValue, octaveValue)
- % Returns one of two provided values depending on whether matlab2tikz is
- % run on MATLAB or on Octave.
-
- switch m2t.env
- case 'MATLAB'
- retval = matlabValue;
- case 'Octave'
- retval = octaveValue;
- otherwise
- errorUnknownEnvironment();
- 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 checkDeprecatedEnvironment(m2t, minimalVersions)
- if isempty(m2t.envVersion)
- warning('matlab2tikz:cannotDetermineEnvironment',...
- 'Could not determine environment version. Continuing and hoping for the best.');
- else
- if isfield(minimalVersions, m2t.env)
- minVersion = minimalVersions.(m2t.env);
- envWithVersion = sprintf('%s %s',m2t.env, minVersion.name);
- if isVersionBelow(m2t.env, m2t.envVersion, minVersion.num)
- warningMessage = ['\n',...
- '================================================================================\n\n', ...
- ' matlab2tikz is tested and developed on %s and\n', ...
- ' later versions of %s.\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', ...
- '\n', ...
- '================================================================================'];
- warning('matlab2tikz:deprecatedEnvironment',warningMessage, ...
- envWithVersion, m2t.env);
+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(m2t, opts, sep)
+% pretty print an options array
+ 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 = join(m2t, c, sep);
+end
+% ==============================================================================
+function [env, versionString] = getEnvironment()
+% Checks if we are in MATLAB or Octave.
+ persistent cache
+
+ alternatives = {'MATLAB', 'Octave'};
+ if isempty(cache)
+ for iCase = 1:numel(alternatives)
+ env = alternatives{iCase};
+ vData = ver(env);
+ if ~isempty(vData) % found the right environment
+ versionString = vData.Version;
+ % store in cache
+ cache.env = env;
+ cache.versionString = versionString;
+ return;
+ end
+ end
+ % fall-back values
+ env = '';
+ versionString = '';
+ else
+ env = cache.env;
+ versionString = cache.versionString;
+ end
+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 bool = 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');
+ % Empty difference then same version
+ bool = ~isempty(difference) && deltaAB(difference) < 0;
+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 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 [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
+ else
errorUnknownEnvironment();
- end
- end
+ end
end
-% =========================================================================
+% ==============================================================================
function errorUnknownEnvironment()
- error('matlab2tikz:unknownEnvironment',...
- 'Unknown environment. Need MATLAB(R) or Octave.')
+ error('matlab2tikz:unknownEnvironment',...
+ 'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment);
end
-% =========================================================================
+% ==============================================================================
function m2t = needsPgfplotsVersion(m2t, minVersion)
- if isVersionBelow(m2t, m2t.pgfplotsVersion, minVersion)
+ if isVersionBelow(m2t.pgfplotsVersion, minVersion)
m2t.pgfplotsVersion = minVersion;
end
end
-% =========================================================================
-function str = formatPgfplotsVersion(m2t, version)
- version = versionArray(m2t, version);
+% ==============================================================================
+function str = formatPgfplotsVersion(version)
+ version = versionArray(version);
if all(isfinite(version))
str = sprintf('%d.',version);
str = str(1:end-1); % remove the last period
@@ -5394,54 +6177,51 @@ function str = formatPgfplotsVersion(m2t, version)
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.
+% SOMEPATH/src/matlab2tikz.m with a .git directory in SOMEPATH.
%
-% The HEAD of that repository is determined from file information
-% only (i.e. git doesn't have to be installed at the user side): in git, a
-% branch file just contains a tree-ish (commit hash OR dynamic reference)
-%
-% When the tree-ish is a dynamic reference (ref:refs/heads/master),
-% this reference is followed as to obtain an absolute tree-ish (hash).
- maxIter = 10; % stop following dynamic references after a while
- refPrefix = 'ref:';
- try
- % get the matlab2tikz directory
- m2tDir = fileparts(mfilename('fullpath'));
- gitDir = fullfile(m2tDir,'..','.git');
-
- treeish = [refPrefix,'HEAD'];
- formatted = '';
-
- nIter = 1;
- while any(strfind(treeish, refPrefix)) && nIter < maxIter
- refName = treeish(numel(refPrefix)+1:end);
- branchFile = fullfile(gitDir, refName);
-
- if exist(branchFile, 'file')
- fid = fopen(branchFile,'r');
- treeish = fscanf(fid,'%s');
- fclose(fid);
- else
- treeish = '';
- return;
+% 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
-
- nIter = nIter + 1;
- end
- if nIter >= maxIter
+ catch %#ok
treeish = '';
- return;
end
- catch %#ok
- treeish = '';
- end
- if ~isempty(treeish)
- formatted = sprintf('(commit %s)',treeish);
- end
+ if ~isempty(treeish)
+ formatted = sprintf('(commit %s)',treeish);
+ end
end
-% =========================================================================
+% ==============================================================================
diff --git a/src/private/isAxis3D.m b/src/private/isAxis3D.m
new file mode 100644
index 0000000..084d7a8
--- /dev/null
+++ b/src/private/isAxis3D.m
@@ -0,0 +1,5 @@
+function bool = isAxis3D(axisHandle)
+% Check if elevation is not orthogonal to xy plane
+ axisView = get(axisHandle,'view');
+ bool = ~ismember(axisView(2),[90,-90]);
+end
diff --git a/src/private/m2tUpdater.m b/src/private/m2tUpdater.m
new file mode 100644
index 0000000..b93c2d7
--- /dev/null
+++ b/src/private/m2tUpdater.m
@@ -0,0 +1,242 @@
+function upgradeSuccess = m2tUpdater(name, fileExchangeUrl, version, verbose, env)
+%UPDATER Auto-update matlab2tikz.
+% Only for internal usage.
+
+% Copyright (c) 2012--2014, Nico Schlömer <nico.schloemer at gmail.com>
+% 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.
+% =========================================================================
+
+ % 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 = '';
+ 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(env, 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(env, tags{ii});
+ end
+ [~, imax] = max(tagnum);
+ mostRecentVersion = tags{imax};
+ % No new
+ else
+ mostRecentVersion = '';
+ end
+
+ upgradeSuccess = false;
+ if ~isempty(mostRecentVersion)
+ userInfo(verbose, '**********************************************\n');
+ userInfo(verbose, 'New version available! (%s)\n', mostRecentVersion);
+ userInfo(verbose, '**********************************************\n');
+
+ userInfo(verbose, 'By upgrading you may lose any custom changes.\n');
+ reply = input([' *** Would you like ', name, ' to self-upgrade? y/n [n]:'],'s');
+ if strcmpi(reply, 'y')
+ % 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
+ if ispc
+ printPath = strrep(targetPath,'\','\\');
+ else
+ printPath = targetPath;
+ end
+ userInfo(verbose, ['Downloading and unzipping to ''', printPath, ''' ...']);
+
+ % 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 = '(?<=\<a href=")[\w\-\/:\.]+(?=">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
+
+ % Cleanup
+ newFolderStructure = [getFolders(unzippedFilesTarget); unzippedFilesTarget];
+ deleteFolderFiles = setdiff(currentFolderFiles, newFolderStructure);
+ for ii = 1:numel(deleteFolderFiles)
+ x = deleteFolderFiles{ii};
+ if exist(x, 'file')
+ delete(x);
+ elseif exist(x, 'dir')
+ rmdir(x,'s');
+ end
+ end
+
+ upgradeSuccess = true; %~isempty(unzippedFiles);
+ userInfo(verbose, 'UPDATED: the current conversion will be terminated. Please, re-run it.');
+ catch
+ userInfo(verbose, ['FAILED: continuing with the' name ' conversion.']);
+ end
+ end
+ userInfo(verbose, '');
+ end
+end
+% =========================================================================
+function isBelow = isVersionBelow(env, versionA, versionB)
+ % Checks if version string or vector versionA is smaller than
+ % version string or vector versionB.
+
+ vA = versionArray(env, versionA);
+ vB = versionArray(env, versionB);
+
+ isBelow = false;
+ for i = 1:min(length(vA), length(vB))
+ if vA(i) > vB(i)
+ isBelow = false;
+ break;
+ elseif vA(i) < vB(i)
+ isBelow = true;
+ break
+ end
+ end
+
+end
+% =========================================================================
+function arr = versionArray(env, str)
+ % Converts a version string to an array, e.g.,
+ % '2.62.8.1' to [2, 62, 8, 1].
+
+ if ischar(str)
+ if strcmpi(env, 'MATLAB')
+ split = regexp(str, '\.', 'split');
+ elseif strcmpi(env, 'Octave')
+ split = strsplit(str, '.');
+ end
+ arr = str2num(char(split)); %#ok
+ else
+ arr = str;
+ end
+
+end
+% =========================================================================
+function userInfo(verbose, message, varargin)
+ % Display usage information.
+
+ if ~verbose
+ return
+ end
+
+ mess = sprintf(message, varargin{:});
+
+ % Replace '\n' by '\n *** ' and print.
+ mess = strrep( mess, sprintf('\n'), sprintf('\n *** ') );
+ fprintf( ' *** %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<AGROW>
+ end
+
+ % Drop directories
+ list(pdir) = [];
+end
+% =========================================================================
+function list = getFolders(list)
+ % Extract the folder structure from a list of files and folders
+
+ for ii = 1:numel(list)
+ if exist(list{ii},'file') == 2
+ list{ii} = fileparts(list{ii});
+ end
+ end
+ list = unique(list);
+end
+% =========================================================================
diff --git a/src/updater.m b/src/updater.m
deleted file mode 100644
index ea7adcd..0000000
--- a/src/updater.m
+++ /dev/null
@@ -1,142 +0,0 @@
-function updater(name, fileExchangeUrl, version, verbose, env)
-%UPDATER Auto-update matlab2tikz.
-% Only for internal usage.
-
-% Copyright (c) 2012--2014, Nico Schlömer <nico.schloemer at gmail.com>
-% 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.
-% =========================================================================
- try
- html = urlread([fileExchangeUrl, '/all_files']);
- catch %#ok
- % Couldn't load the URL -- never mind.
- html = '';
- end
- % Search for a string "/version-1.6.3" in the HTML. This assumes
- % that the package author has added a file by that name to
- % to package. This is a rather dirty hack around FileExchange's
- % lack of native versioning information.
- mostRecentVersion = regexp(html, '/version-(\d+\.\d+\.\d+)', 'tokens');
- if ~isempty(mostRecentVersion)
- if isVersionBelow(env, version, mostRecentVersion{1}{1})
- userInfo(verbose, '**********************************************\n');
- userInfo(verbose, 'New version available! (%s)\n', mostRecentVersion{1}{1});
- userInfo(verbose, '**********************************************\n');
-
- reply = input([' *** Would you like ', name, ' to self-upgrade? y/n [n]:'],'s');
- if strcmp(reply, 'y')
- % Download the files and unzip its contents into the folder
- % above the folder that contains the current script.
- % This assumes that the file structure is something like
- %
- % src/matlab2tikz.m
- % src/[...]
- % 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 = [pathstr, filesep, '..', filesep];
- userInfo(verbose, ['Downloading and unzipping to ', targetPath, '...']);
- upgradeSuccess = false;
- try
- unzippedFiles = unzip([fileExchangeUrl, '?download=true'], targetPath);
- upgradeSuccess = true; %~isempty(unzippedFiles);
- userInfo(verbose, 'done.');
- catch
- userInfo(verbose, 'FAILED.');
- end
- if upgradeSuccess
- % TODO explicitly delete all of the old content
- % Delete old version number file.
- versionFile = [pathstr, filesep, 'version-', version];
- if exist(versionFile, 'file') == 2
- delete(versionFile);
- end
- % TODO anything better than error()?
- error('Upgrade successful. Please re-execute.');
- else
- error('Upgrade failed.');
- end
- end
- userInfo(verbose, '');
- end
- end
-end
-% =========================================================================
-function isBelow = isVersionBelow(env, versionA, versionB)
- % Checks if version string or vector versionA is smaller than
- % version string or vector versionB.
-
- vA = versionArray(env, versionA);
- vB = versionArray(env, versionB);
-
- isBelow = false;
- for i = 1:min(length(vA), length(vB))
- if vA(i) > vB(i)
- isBelow = false;
- break;
- elseif vA(i) < vB(i)
- isBelow = true;
- break
- end
- end
-
-end
-% =========================================================================
-function arr = versionArray(env, str)
- % Converts a version string to an array, e.g.,
- % '2.62.8.1' to [2, 62, 8, 1].
-
- if ischar(str)
- if strcmpi(env, 'MATLAB')
- split = regexp(str, '\.', 'split');
- elseif strcmpi(env, 'Octave')
- split = strsplit(str, '.');
- end
- arr = str2num(char(split)); %#ok
- else
- arr = str;
- end
-
-end
-% =========================================================================
-function userInfo(verbose, message, varargin)
- % Display usage information.
-
- if ~verbose
- return
- end
-
- mess = sprintf(message, varargin{:});
-
- % Replace '\n' by '\n *** ' and print.
- mess = strrep( mess, sprintf('\n'), sprintf('\n *** ') );
- fprintf( ' *** %s\n', mess );
-
-end
-% =========================================================================
diff --git a/test/README b/test/README
deleted file mode 100644
index 3719269..0000000
--- a/test/README
+++ /dev/null
@@ -1,19 +0,0 @@
-This test module is part of matlab2tikz.
-
-It provides the means for easily comparing the results of a direct PDF print of a figure in MATLAB as opposed to having it exported by matlab2tikz. As a matter of fact, this test suite can take any matlab2xxx-converter as argument (see step 2 below) with possibly minor modifications to the LaTeX file output.
-
-
-=======
- USAGE:
-=======
- 1.) Open MATLAB, make matlab2tikz and matlab2tikz_acidtest available for calling (by putting in the MATLAB Path,for example).
-
- 2.) Call the script by
-
- >> matlab2tikz_acidtest( @matlab2tikz );
-
- What happens it that MATLAB generates a number of figures as defined in testfunctions.m, and exports them as PDF (using the print command) and by matlab2tikz. At the same time, a LaTeX file is created in the tex-subfolder.
-
- 3.) You can compile 'tex/acid.tex' with 'make' (making use of 'tex/Makefile').
-
-If all goes well, the result will be the file 'tex/acid.pdf' which contains a list of the test figures, exported as PDF and right next to it the matlab2tikz generated plot.
diff --git a/test/README.md b/test/README.md
new file mode 100644
index 0000000..129388b
--- /dev/null
+++ b/test/README.md
@@ -0,0 +1,91 @@
+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 `testHeadless;`, or, equivalently,
+`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 `testGraphical;` or, equivalently,
+`makeLatexReport(testGraphical)`.
+This generates a LaTeX report in `test/tex/acid.tex` which can then be compiled.
+Compilation of this file can be done using the Makefile `test/tex/Makefile`
+if you are on a Unix-like system (or have cygwin installed on Windows).
+
+If all goes well, the result will be the file `test/tex/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 you
+`help testMatlab2tikz` will not work!
+
+Also, both can be called with a single output argument, for programmatical
+access to the test results.
+
+Automated Tests
+===============
+
+The automated tests run using [Travis-CI](https://travis-ci.org).
+These are effectively the "headless" tests.
+The script used is `runMatlab2TikzTests` that is interpreted using Octave.
+You are of course free to run this script on a development machine.
diff --git a/test/codeReport.m b/test/codeReport.m
new file mode 100644
index 0000000..a97c09a
--- /dev/null
+++ b/test/codeReport.m
@@ -0,0 +1,268 @@
+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)
+%
+% See also: checkcode, mlint,
+
+
+ %% input options
+ ipp = m2tInputParser();
+ ipp = ipp.addParamValue(ipp, 'function', 'matlab2tikz', @ischar);
+ ipp = ipp.addParamValue(ipp, 'complexityThreshold', 10, @isnumeric);
+ ipp = ipp.parse(ipp, varargin{:});
+
+ %% 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
+ disp(codelinks(report, ipp.Results.function));
+
+ 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 ''(?<function>[A-Za-z0-9_]+)'' is (?<complexity>[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
+
+ % 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_]+)`', ...
+ ['`<a href="matlab:edit ' functionName '>$1">$1</a>`']);
+%NOTE: editing function>subfunction will focus on that particular subfunction
+% in the editor (this also works for the main function)
+end
+function str = bold(str)
+str = ['**' str '**'];
+end
+
+%% PLOTTING ====================================================================
+function h = statisticsPlot(stat, xLabel, yLabel)
+% plot a histogram and box plot
+ nCategories = numel(stat.categories);
+ colors = colorscheme;
+
+ h(1) = subplot(5,1,1:4);
+ hold all;
+ hb = bar(stat.binCenter, stat.histogram, 'stacked');
+
+ for iCat = 1:nCategories
+ category = stat.categories{iCat};
+
+ set(hb(iCat), 'DisplayName', category, 'FaceColor', colors.(category), ...
+ 'LineStyle','none');
+ end
+
+ %xlabel(xLabel);
+ ylabel(yLabel);
+
+ h(2) = subplot(5,1,5);
+ hold all;
+
+ boxplot(stat.values,'orientation','horizontal',...
+ 'boxstyle', 'outline', ...
+ 'symbol', 'o', ...
+ 'colors', colors.All);
+ xlabel(xLabel);
+
+ xlims = [min(stat.binCenter)-1 max(stat.binCenter)+1];
+ c = 1;
+ ylims = (ylim(h(2)) - c)/3 + c;
+
+ set(h,'XTickMode','manual','XTick',stat.binCenter,'XLim',xlims);
+ set(h(1),'XTickLabel','');
+ set(h(2),'YTickLabel','','YLim',ylims);
+ linkaxes(h, 'x');
+end
+function colors = colorscheme()
+% recognizable color scheme for the categories
+ colors.All = [ 0 113 188]/255;
+ colors.Good = [118 171 47]/255;
+ colors.Bad = [161 19 46]/255;
+ colors.Accepted = [236 176 31]/255;
+end
diff --git a/test/data/converted/Makefile b/test/data/converted/Makefile
new file mode 100644
index 0000000..2bab3eb
--- /dev/null
+++ b/test/data/converted/Makefile
@@ -0,0 +1,23 @@
+# ./Makefile
+
+ECHOCMD:=/bin/echo -e
+LATEX:=lualatex --shell-escape -interaction=batchmode
+
+TEST_SRCS:=$(wildcard test*-converted.tex)
+TEST_PDFS:=$(TEST_SRCS:.tex=.pdf)
+
+default: $(TEST_PDFS)
+
+%.pdf: %.tex
+ @$(LATEX) $<
+
+.PHONY: clean
+
+clean:
+ rm -f test*-converted.aux \
+ test*-converted.log \
+ test*-converted.pdf
+
+distclean: clean
+ rm -f test*-converted.tex \
+ test*-converted*.png
diff --git a/test/data/reference/Makefile b/test/data/reference/Makefile
new file mode 100644
index 0000000..1c39fac
--- /dev/null
+++ b/test/data/reference/Makefile
@@ -0,0 +1,19 @@
+# ./Makefile
+
+EPSTOPDF:=epstopdf
+
+REFERENCE_EPSS:=$(wildcard test*-reference.eps)
+REFERENCE_PDFS:=$(REFERENCE_EPSS:.eps=.pdf)
+
+default: $(REFERENCE_PDFS)
+
+%.pdf: %.eps
+ $(EPSTOPDF) $<
+
+.PHONY: clean
+
+clean:
+ rm -f test*-reference.pdf
+
+distclean: clean
+ rm -f test*-reference.eps
diff --git a/test/makeLatexReport.m b/test/makeLatexReport.m
new file mode 100644
index 0000000..ccaa5e8
--- /dev/null
+++ b/test/makeLatexReport.m
@@ -0,0 +1,211 @@
+function makeLatexReport(status)
+% generate a LaTeX report
+
+ % first, initialize the tex output
+ texfile = 'tex/acid.tex';
+ fh = fopen(texfile, 'w');
+ finally_fclose_fh = onCleanup(@() fclose(fh));
+
+ assert(fh ~= -1, 'Could not open TeX file ''%s'' for writing.', texfile);
+ texfile_init(fh);
+
+ for k = 1:length(status)
+ % ...and finally write the bits to the LaTeX file
+ texfile_addtest(fh, status{k});
+ end
+
+ % Write the summary table to the LaTeX file
+ texfile_tab_completion_init(fh)
+ for k = 1:length(status)
+ stat = status{k};
+ testNumber = stat.index;
+ % Break table up into pieces if it gets too long for one page
+ if ~mod(k,35)
+ texfile_tab_completion_finish(fh);
+ texfile_tab_completion_init(fh);
+ end
+
+ fprintf(fh, '%d & \\texttt{%s}', testNumber, name2tex(stat.function));
+ if stat.skip
+ fprintf(fh, ' & --- & skipped & ---');
+ else
+ for err = [stat.plotStage.error, ...
+ stat.saveStage.error, ...
+ stat.tikzStage.error]
+ if err
+ fprintf(fh, ' & \\textcolor{red}{failed}');
+ else
+ fprintf(fh, ' & \\textcolor{green!50!black}{passed}');
+ end
+ end
+ end
+ fprintf(fh, ' \\\\\n');
+ end
+ texfile_tab_completion_finish(fh);
+
+ % Write the error messages to the LaTeX file if there are any
+ if errorHasOccurred(status)
+ fprintf(fh, '\\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
+
+ fprintf(fh, '\n\\subsection*{Test case %d: \\texttt{%s}}\n', testNumber, name2tex(stat.function));
+ print_verbatim_information(fh, 'Plot generation', stat.plotStage.message);
+ print_verbatim_information(fh, 'PDF generation' , stat.saveStage.message);
+ print_verbatim_information(fh, 'matlab2tikz' , stat.tikzStage.message);
+ end
+ fprintf(fh, '\n\\normalsize\n\n');
+ end
+
+ texfile_finish(fh, status);
+end
+% =========================================================================
+function texfile_init(texfile_handle)
+
+ fprintf(texfile_handle, ...
+ ['\\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(texfile_handle, status)
+
+ [env,versionString] = getEnvironment();
+
+ testsuites = unique(cellfun(@(s) func2str(s.testsuite) , status, ...
+ 'UniformOutput', false));
+ testsuites = name2tex(m2tstrjoin(testsuites, ', '));
+
+ fprintf(texfile_handle, ...
+ [
+ '\\newpage\n',...
+ '\\begin{tabular}{ll}\n',...
+ ' Suite & ' testsuites ' \\\\ \n', ...
+ ' Created & ' datestr(now) ' \\\\ \n', ...
+ ' OS & ' OSVersion ' \\\\ \n',...
+ ' ' env ' & ' versionString ' \\\\ \n', ...
+ VersionControlIdentifier, ...
+ ' TikZ & \\expandafter\\csname ver at tikz.sty\\endcsname \\\\ \n',...
+ ' Pgfplots & \\expandafter\\csname ver at pgfplots.sty\\endcsname \\\\ \n',...
+ '\\end{tabular}\n',...
+ '\\end{document}']);
+
+end
+% =========================================================================
+function print_verbatim_information(texfile_handle, title, contents)
+ if ~isempty(contents)
+ fprintf(texfile_handle, ...
+ ['\\subsubsection*{%s}\n', ...
+ '\\begin{verbatim}\n%s\\end{verbatim}\n'], ...
+ title, contents);
+ end
+end
+% =========================================================================
+function texfile_addtest(texfile_handle, 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;
+
+ fprintf(texfile_handle, ...
+ ['\\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(texfile_handle)
+
+ fprintf(texfile_handle, ['\\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(texfile_handle)
+
+ fprintf(texfile_handle, ['\\end{tabular}\n' , ...
+ '\\end{table}\n\n' ]);
+
+end
+% =========================================================================
+function texName = name2tex(matlabIdentifier)
+% convert a MATLAB identifier/function handle to a TeX string
+ if isa(matlabIdentifier, 'function_handle')
+ matlabIdentifier = func2str(matlabIdentifier);
+ end
+ texName = strrep(matlabIdentifier, '_', '\_');
+end
+% =========================================================================
+function str = formatIssuesForTeX(issues)
+% make links to GitHub issues for the LaTeX output
+ issues = issues(:)';
+ if isempty(issues)
+ str = '';
+ return
+ end
+ BASEURL = 'https://github.com/matlab2tikz/matlab2tikz/issues/';
+ SEPARATOR = sprintf(' \n');
+ strs = arrayfun(@(n) sprintf(['\\href{' BASEURL '%d}{\\#%d}'], n,n), issues, ...
+ 'UniformOutput', false);
+ strs = [strs; repmat({SEPARATOR}, 1, numel(strs))];
+ str = sprintf('{\\color{blue} \\texttt{%s}}', [strs{:}]);
+end
+% ==============================================================================
diff --git a/test/makeTravisReport.m b/test/makeTravisReport.m
new file mode 100644
index 0000000..c976da9
--- /dev/null
+++ b/test/makeTravisReport.m
@@ -0,0 +1,73 @@
+function nErrors = makeTravisReport(status)
+% make a readable Travis report
+ stdout = 1;
+
+ [reliableTests, unreliableTests] = splitUnreliableTests(status);
+
+ if ~isempty(unreliableTests)
+ fprintf(stdout, ...
+ ['\nThe following tests are known to be unreliable. ' ...
+ 'They, however, do not cause the build to fail.\n\n']);
+ displaySummaryTable(stdout, unreliableTests);
+ fprintf(stdout, ...
+ '\n\nOnly the following tests determine the build outcome:\n');
+ end
+ displaySummaryTable(stdout, reliableTests);
+
+ if nargout >= 1
+ nErrors = countNumberOfErrors(reliableTests);
+ end
+end
+% ==============================================================================
+function displaySummaryTable(stream, status)
+ % display a summary table of all tests
+ for iTest = 1:numel(status)
+ fprintf(stream, '%s\n', formatSummaryRow(status{iTest}));
+ end
+
+ nErrors = countNumberOfErrors(status);
+ if nErrors > 0
+ fprintf(stream,'\n%3d of %3d tests failed. :-( \n', nErrors, numel(status));
+ else
+ fprintf(stream,'\nAll tests were successful. :-) \n');
+ end
+end
+% ==============================================================================
+function str = formatSummaryRow(oneStatus)
+ % format the status of a single test for the summary table
+ testNumber = oneStatus.index;
+ testSuite = func2str(oneStatus.testsuite);
+ summary = '';
+ if oneStatus.skip
+ summary = 'SKIPPED';
+ 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('hash %32s != (%32s) %s', ...
+ thisStage.found, thisStage.expected, summary);
+ otherwise
+ summary = sprintf('%s %s FAILED', summary, thisStage);
+ end
+ end
+ if isempty(summary)
+ summary = 'OK';
+ end
+ summary = strtrim(summary);
+ end
+ functionName = strjust(sprintf('%25s', oneStatus.function), 'left');
+
+ str = sprintf('%15s(%3d) %s: %s', ...
+ testSuite, testNumber, functionName, summary);
+end
+% ==============================================================================
diff --git a/test/matlab2tikz_acidtest.m b/test/matlab2tikz_acidtest.m
deleted file mode 100644
index 6d88849..0000000
--- a/test/matlab2tikz_acidtest.m
+++ /dev/null
@@ -1,376 +0,0 @@
-function matlab2tikz_acidtest(varargin)
-%MATLAB2TIKZ_ACIDTEST unit test driver for matlab2tikz
-%
-% MATLAB2TIKZ_ACIDTEST('testFunctionIndices', INDICES, ...) or
-% MATLAB2TIKZ_ACIDTEST(INDICES, ...) runs the test only for the specified
-% indices. When empty, all tests are run. (Default: []).
-%
-% MATLAB2TIKZ_ACIDTEST('extraOptions', {'name',value, ...}, ...)
-% passes the cell array of options to MATLAB2TIKZ. Default: {}
-%
-% See also matlab2tikz, testfunctions
-
-% Copyright (c) 2008--2014, Nico Schlömer <nico.schloemer at gmail.com>
-% 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.
-%
-% =========================================================================
-
- % In which environment are we?
- env = getEnvironment();
- if ~strcmp(env, 'MATLAB') && ~strcmp(env, 'Octave')
- error('Unknown environment. Need MATLAB(R) or GNU Octave.')
- end
-
- % -----------------------------------------------------------------------
- matlab2tikzOpts = matlab2tikzInputParser;
-
- matlab2tikzOpts = matlab2tikzOpts.addOptional(matlab2tikzOpts, ...
- 'testFunctionIndices', ...
- [], @isfloat);
- matlab2tikzOpts = matlab2tikzOpts.addParamValue(matlab2tikzOpts, ...
- 'extraOptions', {}, @iscell);
-
- matlab2tikzOpts = matlab2tikzOpts.parse(matlab2tikzOpts, varargin{:});
- % -----------------------------------------------------------------------
-
- % first, initialize the tex output
- texfile = 'tex/acid.tex';
- fh = fopen(texfile, 'w');
- assert(fh ~= -1, 'Could not open TeX file ''%s'' for writing.', texfile);
- texfile_init(fh);
-
- % output streams
- stdout = 1;
-
- % query the number of test functions
- [dummya, dummyb, dummyc, dummy, n] = testfunctions(0);
-
- if ~isempty(matlab2tikzOpts.Results.testFunctionIndices)
- indices = matlab2tikzOpts.Results.testFunctionIndices;
- % kick out the illegal stuff
- I = find(indices>=1) & find(indices<=n);
- indices = indices(I);
- else
- indices = 1:n;
- end
-
- ploterrmsg = cell(length(indices), 1);
- tikzerrmsg = cell(length(indices), 1);
- pdferrmsg = cell(length(indices), 1);
- ploterror = false(length(indices), 1);
- tikzerror = false(length(indices), 1);
- pdferror = false(length(indices), 1);
- desc = cell(length(indices), 1);
- funcName = cell(length(indices), 1);
- for k = 1:length(indices)
- fprintf(stdout, 'Executing test case no. %d...\n', indices(k));
-
- % open a window
- fig_handle = figure;
-
- % plot the figure
- try
- [desc{k}, extraOpts, extraCFOpts, funcName{k}] = testfunctions(indices(k));
- catch %#ok
- e = lasterror('reset'); %#ok
- ploterrmsg{k} = format_error_message(e);
-
- for ee = e.stack
- if isempty(funcName{k}) && ~isempty(regexp(ee.name, '^testfunctions>','once'))
- % extract function name
- funcName{k} = regexprep(ee.name, '^testfunctions>(.*)', '$1');
- end
- end
- desc{k} = '\textcolor{red}{Error during plot generation.}';
- disp_error_message(env, ploterrmsg{k});
- ploterror(k) = true;
- end
-
- % plot not sucessful
- if isempty(desc{k})
- close(fig_handle);
- continue
- end
-
- pdf_file = sprintf('data/test%d-reference.pdf' , indices(k));
- eps_file = sprintf('data/test%d-reference.eps' , indices(k));
- fig_file = sprintf('data/test%d-reference' , indices(k));
- gen_file = sprintf('data/test%d-converted.tex' , indices(k));
-
- tic;
- % Save reference output as PDF
- try
- switch env
- case 'MATLAB'
- % MATLAB does not generate properly cropped PDF files.
- % So, we generate EPS files that are converted later on.
- print(gcf, '-depsc2', eps_file);
-
- case 'Octave'
- % In Octave, figures are properly cropped when using print().
- print(pdf_file, '-dpdf', '-S415,311', '-r150');
- pause(1.0)
- otherwise
- error('Unknown environment. Need MATLAB(R) or GNU Octave.')
- end
- catch %#ok
- e = lasterror('reset'); %#ok
- pdferrmsg{k} = format_error_message(e);
- disp_error_message(env, pdferrmsg{k});
- pdferror(k) = true;
- end
- % now, test matlab2tikz
- try
- cleanfigure(extraCFOpts{:});
- matlab2tikz('filename', gen_file, ...
- 'showInfo', false, ...
- 'checkForUpdates', false, ...
- 'relativeDataPath', '../data/', ...
- 'width', '\figurewidth', ...
- matlab2tikzOpts.Results.extraOptions{:}, ...
- extraOpts{:} ...
- );
- catch %#ok
- e = lasterror('reset'); %#ok
- tikzerrmsg{k} = format_error_message(e);
- disp_error_message(env, tikzerrmsg{k});
- tikzerror(k) = true;
- end
-
- % Add new entries as they should be discovered
- manualCloseFuncs = {'freqResponsePlot', ...
- 'zplanePlot2'};
-
- switch funcName{k}
- case manualCloseFuncs
- closeAll = true;
- otherwise
- closeAll = false;
- end
-
- % Make underscores in function names TeX compatible
- funcName{k} = strrep(funcName{k}, '_', '\_');
-
- % ...and finally write the bits to the LaTeX file
- texfile_addtest(fh, fig_file, gen_file, desc{k}, funcName{k}, ...
- indices(k), pdferror(k), tikzerror(k));
-
- if ~closeAll
- close(fig_handle);
- else
- close all;
- end
-
- elapsedTime = toc;
- fprintf(stdout, '%s ', strrep(funcName{k}, '\_', '_'));
- fprintf(stdout, 'done (%4.2fs).\n\n', elapsedTime);
- end
-
- % Write the summary table to the LaTeX file
- texfile_tab_completion_init(fh)
- for k = 1:length(indices)
- % Break table up into pieces if it gets too long for one page
- if ~mod(k,35)
- texfile_tab_completion_finish(fh);
- texfile_tab_completion_init(fh);
- end
-
- fprintf(fh, '%d & \\texttt{%s}', indices(k), funcName{k});
- if isempty(desc{k})
- fprintf(fh, ' & --- & skipped & ---');
- else
- for err = [ploterror(k), pdferror(k), tikzerror(k)]
- if err
- fprintf(fh, ' & \\textcolor{red}{failed}');
- else
- fprintf(fh, ' & \\textcolor{green!50!black}{passed}');
- end
- end
- end
- fprintf(fh, ' \\\\\n');
- end
- texfile_tab_completion_finish(fh);
-
- % Write the error messages to the LaTeX file if there are any
- if any([ploterror ; tikzerror ; pdferror])
- fprintf(fh, '\\section*{Error messages}\n\\scriptsize\n');
- for k = 1:length(indices)
- if isempty(ploterrmsg{k}) && isempty(tikzerrmsg{k}) && isempty(pdferrmsg{k})
- continue % No error messages for this test case
- end
-
- fprintf(fh, '\n\\subsection*{Test case %d: \\texttt{%s}}\n', indices(k), funcName{k});
- print_verbatim_information(fh, 'Plot generation', ploterrmsg{k});
- print_verbatim_information(fh, 'PDF generation' , pdferrmsg{k} );
- print_verbatim_information(fh, 'matlab2tikz' , tikzerrmsg{k});
- end
- fprintf(fh, '\n\\normalsize\n\n');
- end
-
- % now, finish off the file and close file and window
- texfile_finish(fh);
- fclose(fh);
-
-end
-% =========================================================================
-function texfile_init(texfile_handle)
-
- fprintf(texfile_handle, ...
- ['\\documentclass[landscape]{scrartcl}\n' , ...
- '\\pdfminorversion=6\n\n' , ...
- '\\usepackage{amsmath} %% required for $\text{xyz}$\n\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(texfile_handle)
-
- fprintf(texfile_handle, '\\end{document}');
-
-end
-% =========================================================================
-function print_verbatim_information(texfile_handle, title, contents)
- if ~isempty(contents)
- fprintf(texfile_handle, ...
- ['\\subsubsection*{%s}\n', ...
- '\\begin{verbatim}\n%s\\end{verbatim}\n'], ...
- title, contents);
- end
-end
-% =========================================================================
-function texfile_addtest(texfile_handle, ref_file, gen_file, desc, ...
- funcName, funcId, ref_error, gen_error)
- % Actually add the piece of LaTeX code that'll later be used to display
- % the given test.
-
- fprintf(texfile_handle, ...
- ['\\begin{figure}\n' , ...
- ' \\centering\n' , ...
- ' \\begin{tabular}{cc}\n' , ...
- ' %s & %s \\\\\n' , ...
- ' reference rendering & generated\n' , ...
- ' \\end{tabular}\n' , ...
- ' \\caption{%s \\texttt{%s}, \\texttt{testFunctions(%d)}}\n', ...
- '\\end{figure}\n' , ...
- '\\clearpage\n\n'],...
- include_figure(ref_error, 'includegraphics', ref_file), ...
- include_figure(gen_error, 'input', gen_file), ...
- desc, funcName, funcId);
-
-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(texfile_handle)
-
- fprintf(texfile_handle, ['\\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(texfile_handle)
-
- fprintf(texfile_handle, ['\\end{tabular}\n' , ...
- '\\end{table}\n\n' ]);
-
-end
-% =========================================================================
-function [env,versionString] = getEnvironment()
- % Check if we are in MATLAB or Octave.
- % Calling ver with an argument: iterating over all entries is very slow
- alternatives = {'MATLAB','Octave'};
- for iCase = 1:numel(alternatives)
- env = alternatives{iCase};
- vData = ver(env);
- if ~isempty(vData)
- versionString = vData.Version;
- return; % found the right environment
- end
- end
- % otherwise:
- env = [];
- versionString = [];
-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)
- 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(env, msg)
- stderr = 2;
- % When displaying the error message in MATLAB, all backslashes
- % have to be replaced by two backslashes. This must not, however,
- % be applied constantly as the string that's saved to the LaTeX
- % output must have only one backslash.
- if strcmp(env, 'MATLAB')
- fprintf(stderr, strrep(msg, '\', '\\'));
- else
- fprintf(stderr, msg);
- end
-end
-% =========================================================================
diff --git a/test/myCount.dat b/test/myCount.dat
deleted file mode 100644
index 69e029e..0000000
--- a/test/myCount.dat
+++ /dev/null
@@ -1,24 +0,0 @@
- 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
diff --git a/test/pointReductionTest.m b/test/pointReductionTest.m
deleted file mode 100644
index 6b6718f..0000000
--- a/test/pointReductionTest.m
+++ /dev/null
@@ -1,34 +0,0 @@
-% ==============================================================================
-function pointReductionTest()
-
- breakTime = 5.0;
-
- testPlots = {@testPlot1, ...
- };
- %@testPlot2};
-
- for testPlot = testPlots
- testPlot();
- 'a'
- %pause(breakTime);
- %pointReduction2d(0.1);
- pause(breakTime);
- 'b'
- end
-
- close all;
-
-end
-% ==============================================================================
-function testPlot1()
- x = -pi:pi/1000:pi;
- y = tan(sin(x)) - sin(tan(x));
- plot(x,y,'--rs');
-end
-% ==============================================================================
-function testPlot2()
- x = -pi:pi/1000:pi;
- y = exp(tan(sin(x)) - sin(tan(x)));
- semilogy(x,y,'--rs');
-end
-% ==============================================================================
diff --git a/test/private/OSVersion.m b/test/private/OSVersion.m
new file mode 100644
index 0000000..bb6ef53
--- /dev/null
+++ b/test/private/OSVersion.m
@@ -0,0 +1,16 @@
+function [formatted, OSType, OSVersion] = OSVersion()
+ if ismac
+ OSType = 'Mac OS';
+ [dummy, OSVersion] = system('sw_vers -productVersion'); %#ok
+ elseif ispc
+ OSType = '';% will already contain Windows in the output of `ver`
+ [dummy, OSVersion] = system('ver'); %#ok
+ elseif isunix
+ OSType = 'Unix';
+ [dummy, OSVersion] = system('uname -r'); %#ok
+ else
+ OSType = '';
+ OSVersion = '';
+ end
+ formatted = strtrim([OSType ' ' OSVersion]);
+end
diff --git a/test/private/VersionControlIdentifier.m b/test/private/VersionControlIdentifier.m
new file mode 100644
index 0000000..a5b5c54
--- /dev/null
+++ b/test/private/VersionControlIdentifier.m
@@ -0,0 +1,46 @@
+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
+end
diff --git a/test/private/calculateMD5Hash.m b/test/private/calculateMD5Hash.m
new file mode 100644
index 0000000..b64f0a0
--- /dev/null
+++ b/test/private/calculateMD5Hash.m
@@ -0,0 +1,34 @@
+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.
+
+ % Java only has reliable support for absolute paths.
+ absoluteFilename = fullfile(pwd, filename);
+
+ % Based on code by St�phane Pinchaux and Bastian Ebeling that can be
+ % found at <http://stackoverflow.com/questions/12140458/>.
+
+ fis = java.io.FileInputStream(java.io.File(absoluteFilename));
+ MD5 = java.security.MessageDigest.getInstance('MD5');
+ dis = java.security.DigestInputStream(fis, MD5);
+
+ while(dis.read() ~= -1), end; % read the whole file
+
+ hash = reshape(dec2hex(typecast(MD5.digest(),'uint8')).', 1, 32);
+ end
+
+ hash = lower(hash);
+end
diff --git a/test/private/cleanFiles.m b/test/private/cleanFiles.m
new file mode 100644
index 0000000..7aef15b
--- /dev/null
+++ b/test/private/cleanFiles.m
@@ -0,0 +1,17 @@
+function cleanFiles(cleanBefore)
+% clean output files in ./tex using make
+ if cleanBefore && exist(fullfile('tex','Makefile'),'file')
+ fprintf(1, 'Cleaning output files...\n');
+ cwd = pwd;
+ try
+ cd('tex');
+ [exitCode, output] = system('make distclean');
+ fprintf(1,'%s\n', output);
+ assert(exitCode==0, 'Exit code 0 means correct execution');
+ catch
+ % This might happen when make is not present
+ fprintf(2, '\tNot completed succesfully\n\n');
+ end
+ cd(cwd);
+ end
+end
diff --git a/test/private/countNumberOfErrors.m b/test/private/countNumberOfErrors.m
new file mode 100644
index 0000000..6d15ceb
--- /dev/null
+++ b/test/private/countNumberOfErrors.m
@@ -0,0 +1,16 @@
+function nErrors = countNumberOfErrors(status)
+% counts the number of errors in a status cell array
+ nErrors = 0;
+ % probably this can be done more compactly using cellfun, etc.
+ for iTest = 1:numel(status)
+ S = status{iTest};
+ stages = getStagesFromStatus(S);
+ errorInThisTest = false;
+ for jStage = 1:numel(stages)
+ errorInThisTest = errorInThisTest || S.(stages{jStage}).error;
+ end
+ if errorInThisTest
+ nErrors = nErrors + 1;
+ end
+ end
+end
diff --git a/test/private/emptyStage.m b/test/private/emptyStage.m
new file mode 100644
index 0000000..34d892a
--- /dev/null
+++ b/test/private/emptyStage.m
@@ -0,0 +1,4 @@
+function stage = emptyStage()
+% constructs an empty (workflow) stage struct
+ stage = struct('message', '', 'error' , false);
+end
diff --git a/test/private/emptyStatus.m b/test/private/emptyStatus.m
new file mode 100644
index 0000000..774a6d7
--- /dev/null
+++ b/test/private/emptyStatus.m
@@ -0,0 +1,17 @@
+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());
+end
diff --git a/test/private/errorHandler.m b/test/private/errorHandler.m
new file mode 100644
index 0000000..ab7a32b
--- /dev/null
+++ b/test/private/errorHandler.m
@@ -0,0 +1,41 @@
+function [stage, errorHasOccurred] = errorHandler(e)
+% common error handler code: save and print to console
+ errorHasOccurred = true;
+ stage = emptyStage();
+ stage.message = format_error_message(e);
+ stage.error = errorHasOccurred;
+
+ disp_error_message(stage.message);
+end
+% ==============================================================================
+function msg = format_error_message(e)
+ msg = '';
+ if ~isempty(e.message)
+ msg = sprintf('%serror: %s\n', msg, e.message);
+ end
+ if ~isempty(e.identifier)
+ if strfind(lower(e.identifier),'testmatlab2tikz:')
+ % When "errors" occur in the test framework, i.e. a hash mismatch
+ % or no hash provided, there is no need to be very verbose.
+ % So we don't return the msgid and the stack trace in those cases!
+ return % only return the message
+ end
+ msg = sprintf('%serror: %s\n', msg, e.identifier);
+ end
+ if ~isempty(e.stack)
+ msg = sprintf('%serror: called from:\n', msg);
+ for ee = e.stack(:)'
+ msg = sprintf('%serror: %s at line %d, in function %s\n', ...
+ msg, ee.file, ee.line, ee.name);
+ end
+ end
+end
+% ==============================================================================
+function disp_error_message(msg)
+ stderr = 2;
+ % The error message should not contain any more escape sequences and
+ % hence can be output literally to stderr.
+
+ fprintf(stderr, '%s', msg);
+end
+% ==============================================================================
diff --git a/test/private/errorHasOccurred.m b/test/private/errorHasOccurred.m
new file mode 100644
index 0000000..e73c0f1
--- /dev/null
+++ b/test/private/errorHasOccurred.m
@@ -0,0 +1,16 @@
+function errorOccurred = errorHasOccurred(status)
+% determines whether an error has occurred from a status struct OR cell array
+% of status structs
+ errorOccurred = false;
+ if iscell(status)
+ for iStatus = 1:numel(status)
+ errorOccurred = errorOccurred || errorHasOccurred(status{iStatus});
+ end
+ else
+ stages = getStagesFromStatus(status);
+ for iStage = 1:numel(stages)
+ thisStage = status.(stages{iStage});
+ errorOccurred = errorOccurred || thisStage.error;
+ end
+ end
+end
diff --git a/test/private/execute_hash_stage.m b/test/private/execute_hash_stage.m
new file mode 100644
index 0000000..1e268f5
--- /dev/null
+++ b/test/private/execute_hash_stage.m
@@ -0,0 +1,34 @@
+function [status] = execute_hash_stage(status, ipp)
+ % test stage: check recorded hash checksum
+ calculated = '';
+ expected = '';
+ try
+ expected = getReferenceHash(status, ipp);
+ calculated = calculateMD5Hash(status.tikzStage.texFile);
+
+ % do the actual check
+ if ~strcmpi(expected, calculated)
+ % throw an error to signal the testing framework
+ error('testMatlab2tikz:HashMismatch', ...
+ 'The hash "%s" does not match the reference hash "%s"', ...
+ calculated, expected);
+ end
+ catch %#ok
+ e = lasterror('reset'); %#ok
+ [status.hashStage] = errorHandler(e);
+ end
+ status.hashStage.expected = expected;
+ status.hashStage.found = calculated;
+end
+% ==============================================================================
+function hash = getReferenceHash(status, ipp)
+ % retrieves a reference hash from a hash table
+ % WARNING: do not make `hashTable` persistent, since this is slower
+
+ hashTable = loadHashTable(ipp.Results.testsuite);
+ if isfield(hashTable.contents, status.function)
+ hash = hashTable.contents.(status.function);
+ else
+ hash = '';
+ end
+end
diff --git a/test/private/execute_plot_stage.m b/test/private/execute_plot_stage.m
new file mode 100644
index 0000000..ff1519e
--- /dev/null
+++ b/test/private/execute_plot_stage.m
@@ -0,0 +1,45 @@
+function [status] = execute_plot_stage(defaultStatus, ipp)
+% plot a test figure
+ testsuite = ipp.Results.testsuite;
+ testNumber = defaultStatus.index;
+
+ % open a window
+ fig_handle = figure('visible',ipp.Results.figureVisible);
+ errorHasOccurred = false;
+
+ % plot the figure
+ try
+ status = testsuite(testNumber);
+
+ catch %#ok
+ e = lasterror('reset'); %#ok
+
+ status.description = '\textcolor{red}{Error during plot generation.}';
+ [status.plotStage, errorHasOccurred] = errorHandler(e);
+
+ % Automaticall mark the test as unreliable
+ %
+ % Since metadata is not set in this case, also stat.unreliable is
+ % not returned. So ideally, we should
+ % FIXME: implement #484 to get access to the meta data
+ % but we can work around this issue by forcefully setting that value.
+ % The rationale for setting this to true:
+ % - the plot part is not the main task of M2T
+ % (so breaking a single test is less severe in this case),
+ % - if the plotting fails, the test is not really reliable anyway,
+ % - this allows to get full green on Travis.
+ status.unreliable = true;
+
+ end
+
+ status = fillStruct(status, defaultStatus);
+ if isempty(status.function)
+ allFuncs = testsuite(0);
+ status.function = func2str(allFuncs{testNumber});
+ end
+ status.plotStage.fig_handle = fig_handle;
+
+ if status.skip || errorHasOccurred
+ close(fig_handle);
+ end
+end
diff --git a/test/private/execute_save_stage.m b/test/private/execute_save_stage.m
new file mode 100644
index 0000000..f92a236
--- /dev/null
+++ b/test/private/execute_save_stage.m
@@ -0,0 +1,61 @@
+function [status] = execute_save_stage(status, ipp)
+% save stage: saves the figure to EPS/PDF depending on env
+ testNumber = status.index;
+
+ reference_eps = sprintf('data/reference/test%d-reference.eps', testNumber);
+ reference_pdf = sprintf('data/reference/test%d-reference.pdf', testNumber);
+ reference_fig = sprintf('data/reference/test%d-reference', testNumber);
+
+ % Save reference output as PDF
+ try
+ switch getEnvironment
+ case 'MATLAB'
+ % MATLAB does not generate properly cropped PDF files.
+ % So, we generate EPS files that are converted later on.
+ print(gcf, '-depsc2', reference_eps);
+
+ fixLineEndingsInWindows(reference_eps);
+
+ case 'Octave'
+ % In Octave, figures are properly cropped when using print().
+ print(reference_pdf, '-dpdf', '-S415,311', '-r150');
+ pause(1.0)
+ otherwise
+ error('matlab2tikz:UnknownEnvironment', ...
+ 'Unknown environment. Need MATLAB(R) or GNU Octave.')
+ end
+ catch %#ok
+ e = lasterror('reset'); %#ok
+ [status.saveStage] = errorHandler(e);
+ end
+ status.saveStage.epsFile = reference_eps;
+ status.saveStage.pdfFile = reference_pdf;
+ status.saveStage.texReference = reference_fig;
+end
+% ==============================================================================
+function fixLineEndingsInWindows(filename)
+% On R2014b Win, line endings in .eps are Unix style (LF) instead of Windows
+% style (CR+LF). This causes problems in the MikTeX `epstopdf` for some files
+% as dicussed in:
+% * https://github.com/matlab2tikz/matlab2tikz/issues/370
+% * http://tex.stackexchange.com/questions/208179
+ if ispc
+ fid = fopen(filename,'r+');
+ finally_fclose_fid = onCleanup(@() fclose(fid));
+ testline = fgets(fid);
+ CRLF = sprintf('\r\n');
+ endOfLine = testline(end-1:end);
+ if ~strcmpi(endOfLine, CRLF)
+ endOfLine = testline(end); % probably an LF
+
+ % Rewind, read the whole
+ fseek(fid,0,'bof');
+ str = fread(fid,'*char')';
+
+ % Replace, overwrite and close
+ str = strrep(str, endOfLine, CRLF);
+ fseek(fid,0,'bof');
+ fprintf(fid,'%s',str);
+ end
+ end
+end
diff --git a/test/private/execute_tikz_stage.m b/test/private/execute_tikz_stage.m
new file mode 100644
index 0000000..f779880
--- /dev/null
+++ b/test/private/execute_tikz_stage.m
@@ -0,0 +1,26 @@
+function [status] = execute_tikz_stage(status, ipp)
+% test stage: TikZ file generation
+ testNumber = status.index;
+ gen_tex = sprintf('data/converted/test%d-converted.tex', testNumber);
+ gen_pdf = sprintf('data/converted/test%d-converted.pdf', testNumber);
+ % now, test matlab2tikz
+ try
+ cleanfigure(status.extraCleanfigureOptions{:});
+ matlab2tikz('filename', gen_tex, ...
+ 'showInfo', false, ...
+ 'checkForUpdates', false, ...
+ 'dataPath', 'data/converted/', ...
+ 'standalone', true, ...
+ ipp.Results.extraOptions{:}, ...
+ status.extraOptions{:} ...
+ );
+ 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;
+end
diff --git a/test/private/execute_type_stage.m b/test/private/execute_type_stage.m
new file mode 100644
index 0000000..2f7a04f
--- /dev/null
+++ b/test/private/execute_type_stage.m
@@ -0,0 +1,14 @@
+function [status] = execute_type_stage(status, ipp)
+ try
+ filename = status.tikzStage.texFile;
+ stream = 1; % stdout
+ if errorHasOccurred(status) && exist(filename, 'file')
+ fprintf(stream, '\n%%%%%%%% BEGIN FILE "%s" %%%%%%%%\n', filename);
+ type(filename);
+ fprintf(stream, '\n%%%%%%%% END FILE "%s" %%%%%%%%\n', filename);
+ end
+ catch
+ e = lasterror('reset');
+ [status.typeStage] = errorHandler(e);
+ end
+end
diff --git a/test/private/fillStruct.m b/test/private/fillStruct.m
new file mode 100644
index 0000000..1ddc6d8
--- /dev/null
+++ b/test/private/fillStruct.m
@@ -0,0 +1,10 @@
+function [status] = fillStruct(status, defaultStatus)
+% fills non-existant fields of |data| with those of |defaultData|
+ fields = fieldnames(defaultStatus);
+ for iField = 1:numel(fields)
+ field = fields{iField};
+ if ~isfield(status,field)
+ status.(field) = defaultStatus.(field);
+ end
+ end
+end
diff --git a/test/private/getEnvironment.m b/test/private/getEnvironment.m
new file mode 100644
index 0000000..86f8351
--- /dev/null
+++ b/test/private/getEnvironment.m
@@ -0,0 +1,25 @@
+function [env, versionString] = getEnvironment()
+% Checks if we are in MATLAB or Octave.
+ persistent cache
+
+ alternatives = {'MATLAB', 'Octave'};
+ if isempty(cache)
+ for iCase = 1:numel(alternatives)
+ env = alternatives{iCase};
+ vData = ver(env);
+ if ~isempty(vData) % found the right environment
+ versionString = vData.Version;
+ % store in cache
+ cache.env = env;
+ cache.versionString = versionString;
+ return;
+ end
+ end
+ % fall-back values
+ env = '';
+ versionString = '';
+ else
+ env = cache.env;
+ versionString = cache.versionString;
+ end
+end
\ No newline at end of file
diff --git a/test/private/getStagesFromStatus.m b/test/private/getStagesFromStatus.m
new file mode 100644
index 0000000..7ca6669
--- /dev/null
+++ b/test/private/getStagesFromStatus.m
@@ -0,0 +1,5 @@
+function stages = getStagesFromStatus(status)
+% retrieves the different (names of) stages of a status struct
+ fields = fieldnames(status);
+ stages = fields(cellfun(@(f) ~isempty(strfind(f,'Stage')), fields));
+end
diff --git a/test/private/hashTableName.m b/test/private/hashTableName.m
new file mode 100644
index 0000000..faa1c6d
--- /dev/null
+++ b/test/private/hashTableName.m
@@ -0,0 +1,55 @@
+function filename = hashTableName(suite)
+ % determines the file name of a hash table
+ %
+ % The MD5 file is assumed to be in the same directory as the test suite.
+ % It has a file name "$SUITE.$ENV.$VER.md5"
+ % where the following fields are filled:
+ % $ENV: the environment (either "MATLAB" or "Octave")
+ % $VER: the version (e.g. "3.8.0" for Octave, "8.3" for MATLAB 2014a)
+ % $SUITE: the name (and path) of the test suite
+ %
+ % For the $VER-part, a fall-back mechanism is present that prefers the exact
+ % version but will use the closest available file if such file does not
+ % exist.
+ [pathstr,name, ext] = fileparts(which(func2str(suite)));
+ [env, version] = getEnvironment();
+ ext = sprintf('.%s.%s.md5', env, version);
+ relFilename = [name ext];
+ filename = fullfile(pathstr, relFilename);
+
+ if ~exist(filename,'file')
+ % To avoid having to create a file for each release of the environment,
+ % also other versions are tried. The file for different releases are checked
+ % in the following order:
+ % 1. the currently running version (handled above!)
+ % 2. the newest older version (e.g. use R2014b's file in R2015a)
+ % 3. the oldest newer version (e.g. use R2014a's file in R2013a)
+ pattern = sprintf('%s.%s.*.md5', name, env);
+ candidates = dir(fullfile(pathstr, pattern));
+
+ % We just need the file names.
+ filenames = arrayfun(@(c)c.name, candidates, 'UniformOutput', false);
+
+ % Add the expected version to the results, and sort the names by
+ % version (this is the same as alphabetically).
+ filenames = sort([filenames; {relFilename}]);
+ nFiles = numel(filenames);
+ iCurrent = find(ismember(filenames, relFilename));
+ % determine the fall-back candidates:
+ iNewestOlder = iCurrent - 1;
+ iOldestNewer = iCurrent + 1;
+
+ inRange = @(idx)(idx <= nFiles && idx >= 1);
+ if inRange(iNewestOlder)
+ % use the newest older version
+ relFilename = filenames{iNewestOlder};
+ elseif inRange(iOldestNewer)
+ % use the oldest newer version
+ relFilename = filenames{iOldestNewer};
+ else
+ % use the exact version anyhow
+ end
+
+ filename = fullfile(pathstr, relFilename);
+ end
+end
diff --git a/test/private/initializeWorkingDirectory.m b/test/private/initializeWorkingDirectory.m
new file mode 100644
index 0000000..350be85
--- /dev/null
+++ b/test/private/initializeWorkingDirectory.m
@@ -0,0 +1,23 @@
+function cwd = initializeWorkingDirectory()
+% Initialize working directory. Change into 'test' folder of matlab2tikz.
+% Return current working directory so that after tests it can be restored.
+ fprintf('Initialize working directory...\n');
+ cwd = pwd;
+ m2t_path = which('matlab2tikz.m');
+ bSuccess = 0;
+
+ if ~isempty(m2t_path)
+ test_path = fullfile(fileparts(m2t_path), '..', 'test');
+
+ if isdir(test_path)
+ cd(test_path);
+ fprintf('Successfully changed into test folder...\n');
+ bSuccess = 1;
+ end
+ end
+
+ if ~bSuccess
+ error('matlab2tikz:initializeWorkingDirectory', ...
+ 'Could not change into test folder.');
+ end
+end
diff --git a/test/private/loadHashTable.m b/test/private/loadHashTable.m
new file mode 100644
index 0000000..3d88017
--- /dev/null
+++ b/test/private/loadHashTable.m
@@ -0,0 +1,19 @@
+function hashTable = loadHashTable(suite)
+ % loads a reference hash table from disk
+ hashTable.suite = suite;
+ hashTable.contents = struct();
+ filename = hashTableName(suite);
+ if exist(filename, 'file')
+ fid = fopen(filename, 'r');
+ finally_fclose_fid = onCleanup(@() fclose(fid));
+
+ data = textscan(fid, '%s : %s');
+ if ~isempty(data) && ~all(cellfun(@isempty, data))
+ functions = cellfun(@strtrim, data{1},'UniformOutput', false);
+ hashes = cellfun(@strtrim, data{2},'UniformOutput', false);
+ for iFunc = 1:numel(functions)
+ hashTable.contents.(functions{iFunc}) = hashes{iFunc};
+ end
+ end
+ end
+end
diff --git a/test/private/m2tstrjoin.m b/test/private/m2tstrjoin.m
new file mode 100644
index 0000000..81f44fe
--- /dev/null
+++ b/test/private/m2tstrjoin.m
@@ -0,0 +1,16 @@
+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
+
+ nElem = numel(cellstr);
+
+ newstr = cell(2,nElem);
+ newstr(1,:) = reshape(cellstr, 1, nElem);
+ newstr(2,1:nElem-1) = {delimiter}; % put delimiters in-between the elements
+ newstr = [newstr{:}];
+
+end
diff --git a/test/private/splitUnreliableTests.m b/test/private/splitUnreliableTests.m
new file mode 100644
index 0000000..950b553
--- /dev/null
+++ b/test/private/splitUnreliableTests.m
@@ -0,0 +1,7 @@
+function [reliableTests, unreliableTests] = splitUnreliableTests(status)
+ % splits tests between reliable and unreliable tests
+ knownToFail = cellfun(@(s)s.unreliable, status);
+
+ unreliableTests = status( knownToFail);
+ reliableTests = status(~knownToFail);
+end
diff --git a/test/private/testMatlab2tikz.m b/test/private/testMatlab2tikz.m
new file mode 100644
index 0000000..dbfa217
--- /dev/null
+++ b/test/private/testMatlab2tikz.m
@@ -0,0 +1,159 @@
+function status = 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
+%
+% See also matlab2tikz, ACID
+
+% Copyright (c) 2008--2014, Nico Schlömer <nico.schloemer at gmail.com>
+% 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.
+%
+% =========================================================================
+
+ % 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.parse(ipp, varargin{:});
+
+ ipp = sanitizeInputs(ipp);
+
+ % -----------------------------------------------------------------------
+ if strcmpi(env, 'Octave')
+ if ~ipp.Results.figureVisible
+ % Use the gnuplot backend to work around an fltk bug, see
+ % <http://savannah.gnu.org/bugs/?43429>.
+ 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
+
+ % start overall timing
+ elapsedTimeOverall = tic;
+ status = runIndicatedTests(ipp);
+
+ % print out overall timing
+ elapsedTimeOverall = toc(elapsedTimeOverall);
+ stdout = 1;
+ fprintf(stdout, 'overall time: %4.2fs\n\n', elapsedTimeOverall);
+end
+% INPUT VALIDATION =============================================================
+function bool = isFunction(f)
+ bool = isa(f,'function_handle');
+end
+function ipp = sanitizeInputs(ipp)
+ % sanitize all input arguments
+ ipp = sanitizeFunctionIndices(ipp);
+ ipp = sanitizeFigureVisible(ipp);
+end
+function ipp = sanitizeFunctionIndices(ipp)
+% sanitize the passed function indices to the range of the test suite
+ % query the number of test functions
+ testsuite = ipp.Results.testsuite;
+ n = length(testsuite(0));
+
+ if ~isempty(ipp.Results.testFunctionIndices)
+ indices = ipp.Results.testFunctionIndices;
+ % kick out the illegal stuff
+ I = find(indices>=1 & indices<=n);
+ indices = indices(I); %#ok
+ else
+ indices = 1:n;
+ end
+ ipp.Results.testFunctionIndices = indices;
+end
+function ipp = sanitizeFigureVisible(ipp)
+ % sanitizes the figure visible option from boolean to ON/OFF
+ if ipp.Results.figureVisible
+ ipp.Results.figureVisible = 'on';
+ else
+ ipp.Results.figureVisible = 'off';
+ end
+end
+% TEST RUNNER ==================================================================
+function status = runIndicatedTests(ipp)
+% run all indicated tests in the test suite
+ % cell array to accomodate different structure
+ indices = ipp.Results.testFunctionIndices;
+ testsuite = ipp.Results.testsuite;
+ testsuiteName = func2str(testsuite);
+ stdout = 1;
+ status = cell(length(indices), 1);
+
+ for k = 1:length(indices)
+ testNumber = indices(k);
+
+ fprintf(stdout, 'Executing %s test no. %d...\n', testsuiteName, indices(k));
+
+ status{k} = emptyStatus(testsuite, testNumber);
+
+ elapsedTime = tic;
+
+ status{k} = feval(ipp.Results.actionsToExecute, status{k}, ipp);
+
+ elapsedTime = toc(elapsedTime);
+ status{k}.elapsedTime = elapsedTime;
+ fprintf(stdout, '%s ', status{k}.function);
+ if status{k}.skip
+ fprintf(stdout, 'skipped (%4.2fs).\n\n', elapsedTime);
+ else
+ fprintf(stdout, 'done (%4.2fs).\n\n', elapsedTime);
+ end
+ end
+end
diff --git a/test/runMatlab2TikzTests.m b/test/runMatlab2TikzTests.m
new file mode 100755
index 0000000..c08aa30
--- /dev/null
+++ b/test/runMatlab2TikzTests.m
@@ -0,0 +1,32 @@
+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');
+
+%% 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{:});
+
+nErrors = makeTravisReport(status);
+
+%% Calculate exit code
+if CI_MODE
+ exit(nErrors);
+end
diff --git a/test/saveHashTable.m b/test/saveHashTable.m
new file mode 100644
index 0000000..4107dda
--- /dev/null
+++ b/test/saveHashTable.m
@@ -0,0 +1,37 @@
+function saveHashTable(status)
+% SAVEHASHTABLE saves the references hashes for the Matlab2Tikz tests
+%
+% Usage:
+% SAVEHASHTABLE(status)
+%
+% Inputs:
+% - status: output cell array of the testing functions
+%
+% See also: runMatlab2TikzTests, testMatlab2tikz
+ suite = status{1}.testsuite; %TODO: handle multiple test suites in a single array
+ filename = hashTableName(suite);
+
+ % sort by file names to allow humans better traversal of such files
+ funcNames = cellfun(@(s) s.function, status, 'UniformOutput', false);
+ [dummy, iSorted] = sort(funcNames); %#ok
+ status = status(iSorted);
+
+ % write to file
+ fid = fopen(filename,'w+');
+ finally_fclose_fid = onCleanup(@() fclose(fid));
+
+ for iFunc = 1:numel(status)
+ S = status{iFunc};
+ thisFunc = S.function;
+ if isfield(S.hashStage,'found')
+ thisHash = S.hashStage.found;
+ else
+ warning('SaveHashTable:NoHashFound',...
+ 'No hash found for "%s". Assuming empty.', S.function);
+ thisHash = ''; % FIXME: when does this happen??
+ end
+ if ~isempty(thisHash)
+ fprintf(fid, '%s : %s\n', thisFunc, thisHash);
+ end
+ end
+end
diff --git a/test/suites/ACID.MATLAB.8.3.md5 b/test/suites/ACID.MATLAB.8.3.md5
new file mode 100644
index 0000000..0847d82
--- /dev/null
+++ b/test/suites/ACID.MATLAB.8.3.md5
@@ -0,0 +1,98 @@
+alphaImage : cddba9cf77f448c298ab82a614bac188
+alphaTest : 3188c4306f1059094eb3b88f7c32dc17
+annotationAll : df4230616857219a6b087e2c6d3742b2
+annotationSubplots : c6503415f55382be61b1d16fce6a5176
+annotationText : c8898f7f8f7f07f113570c9637225438
+annotationTextUnits : ecd459e742d4996818cc57b9c576a52b
+areaPlot : 988877a36cdb4a55afecf8b816dd634a
+axesColors : 62b8d24ae88ad1df29ed94ff50818f36
+axesLocation : e665762e22cc0cb64d3d23b5f1fcbd66
+bars : 548d03a2eb2214eb6d61af4a5d4533db
+besselImage : 524f559e7344525d628b4caca6a08ce3
+bodeplots : ecdb9cfda5f04a8ab0f6b4aeb5ea5443
+clownImage : 1b5d18fa51afaafe1912b41bc3883d42
+colorbarLabelTitle : 7df4a3ca4a410b13791daf8985870d2f
+colorbarLogplot : 49a3a756b9983cef4bb45edaec154c8e
+compassplot : 47f9da0691be90876346741a491f1a59
+contourPenny : 359cb1d39f446ece912dd2b3a9ddbc4e
+croppedImage : 02fe44965e2adc9e2ad0031fa0fc1716
+customLegend : f88e20eeb1e66503d54acddb4c754c7f
+decayingharmonic : a934c2b070af0185d7da073bffc635fa
+double_axes : 350d0e6e87bfeb7c779115045770ff5b
+double_axes2 : d35253ca08f9d7675a6fc0a4828e55d3
+double_colorbar : 3ac60136b1bdf95e66e3ddfa2302983c
+errorBars : c426c8a73a5680311d9661bd3336e98f
+errorBars2 : df9d1c66fa9a72efc0785a07b292ce6e
+fill3plot : 57aa8c4eba5070a5b3a960ebaadace93
+freqResponsePlot : e77100f363f6502a1e0604f9edde0d27
+herrorbarPlot : c3e1e86cf3abd40b6f3e61fa4787c804
+hgTransformPlot : f57d93fab8b9d3534965a277d144ac07
+hist3d : 030888bc27c04c0cca04b591576b78c3
+imageOrientation_PNG : 22063338c9eca0429f5beb56ff1af712
+imageOrientation_inline : 95b55b00b4a78ce2048a14d0d5c62a4c
+imagescplot : a594a023a15fb1c138c31674e18b3683
+imagescplot2 : 4e0233c5501d7ceb40d3292fd70bbc66
+latexInterpreter : ec686ebe3ad0c580069cbcf074d8416c
+latexmath2 : 3afa21f3dd2a287a0b4a95d28f84248d
+legendplot : 16c13cf0a2fd68274a4c735f0f48afc8
+legendplotBoxoff : 02ad1b473edc824087aa1248607931fb
+legendsubplots : aa821b26c5907ffa9a4f3d652a288157
+linesWithOutliers : fbf2681ac8a5e3906f9101e5ec698cd5
+logbaseline : c23bc69240b0aa59f2513648c0176951
+logicalImage : 9df254152bc618c6b4345af07827aca2
+logplot : a67560843fc2da1d8e8ed3b8e1053474
+mandrillImage : 3fa2496006868b94b65243553c66c10e
+manualAlignment : 9c4902fd722fcab9eaecc7060f044e88
+many_random_points : dca8a1876ddb6d9137842ae4fdb2bd5b
+markerSizes : 107f288ba56f3b8b0ffb4451e71e2019
+markerSizes2 : 96e29fde18b4b5d72f1182bda1cc626f
+meshPlot : 8add69082c7a518e3a679ffa914d5887
+mixedBarLine : 7fd3492ff6942a79977940e4ca463778
+moreLegends : 6d33153548c28341e8cb8ddc73c64f7c
+multiline_labels : 8259a62fe803c105fcef80ba8611c6be
+multipleAxes : 62ea3df8f3595e1ae3b900745f898fc1
+multiplePatches : 3fdceeb9e1aed6c4130988558ef6c863
+myBoxplot : a2b5f11f657e1fc92785355d678460db
+overlappingPlots : 18cf2acfd5edfd9466576a3a660bc3b6
+pColorPlot : cf970804d3e47193c92fb251ccf2355d
+parameterCurve3d : 6021198f4a70f990720e302739b2df80
+parameterSurf : a9b32b0319c0ec1fabdbeb0ff1be165b
+peaks_contour : bc176f0f75404e8cd45fb90e50550bae
+peaks_contourf : c3da2e11ea8dab2a1090d308aeecc512
+pixelLegend : 8375db270b039bcea73f79ebae930ee2
+plain_cos : 654eaff6ca2fd5d5b77dca1cb440149d
+polarplot : 8499d786860e8ee2dac6502e8b6c635f
+quiver3plot : 019f1aa9d8245a046fb4f1c2c35946cf
+quiveroverlap : 1bbbae36858575a3d37b830ea3fcf78c
+quiverplot : eed3b7d5dc17458d0b4d2bbdb2647fa9
+randomWithLines : 0a6d635c0d124f241039bc7b450b2953
+rectanglePlot : c48a3d22dac900605e1700f15a353b8f
+rlocusPlot : 970105e228cc0b89906788ddc7ff7844
+roseplot : 59cd0d60b108e52dbe79503c4aa6650f
+scatter3Plot : 75935adb9bc4d37b59f335ac1748f2f5
+scatterPlot : 4c2b1cd7cd9640e128d9455cc8153bc2
+scatterPlotMarkers : 7a4451ce95e6d0738170c8c3d20c1ae2
+scatterPlotRandom : b9dc9458c51e6d7094a4c4ab3c0af483
+sine_with_annotation : 4de8d486e140bc67cb806d4cc8c40a5f
+sine_with_markers : e896ebe97102325c66e6d6382d096f0e
+spectro : af1820b55e9da0dbbfae66e2dc356f0a
+spherePlot : 45023e71fe4e74560fd2ba2bdaddb97a
+stackedBarsWithOther : 7adf14a89282e6c2a4a55f824875512f
+stairsplot : e7ac11b99558c8c49ceaeb339fddc011
+stemplot : e40d5dc2b86b171be92ea77d897d5b7b
+stemplot2 : 2474469021fcdb235f22623818c1599a
+subplot2x2b : 6881a3e0c3286a751a187b5abab6290d
+subplotCustom : 3c6405ffd12385ebeb14b736463f775f
+superkohle : a70211961087dc165f884f27da9d4036
+surfPlot : 31405ae9c165db43bbc3c3060e02f491
+surfPlot2 : a2f97d5c1856a881d33025e55be65cb5
+texInterpreter : cebfffa9581f101899bb40808fadd265
+texcolor : 14d8e8dc34142facd0f51e594c01584a
+texrandom : 603ed498ac3665b819a5c1cc3f0bf99b
+textAlignment : 23292f1cfd8d00f6579fc51eb5fcea66
+textext : 722452f2ae6c6f56ec9434a244d036e4
+xAxisReversed : a4c3e353e4d6b0c7fda50ce5d960a651
+ylabels : f6233236a7ffb8fc81378444fb7ff4b3
+zoom : f2c21f8fdc9e3a6db456a7419cc86c9b
+zplanePlot1 : 4307d1607424b4d7ac83f47dbeeb20b2
+zplanePlot2 : 2eaab167e650fef118b46d1f548a6461
diff --git a/test/suites/ACID.MATLAB.8.4.md5 b/test/suites/ACID.MATLAB.8.4.md5
new file mode 100644
index 0000000..71748ee
--- /dev/null
+++ b/test/suites/ACID.MATLAB.8.4.md5
@@ -0,0 +1,98 @@
+alphaImage : c298acf90ebbed60a6d6cfebe0d9aa55
+alphaTest : d5fdf3131e98b6948639a3a28d1e5d65
+annotationAll : 226844085fa4baa0589e7eb4adffc69c
+annotationSubplots : a6361658274847e0c6d6386759d0f7a6
+annotationText : e81e891540ead4a05870771c092a9f75
+annotationTextUnits : ceb8f3654da0eef87e15494591024950
+areaPlot : 3ef21a6c3aea1401b6cc4afcdf427119
+axesColors : b30a83133a3d4785ac79e85457306601
+axesLocation : 3ae9cd9edf8b3ff2d46f594cbe1e9a86
+bars : d85ca6286f7643b97fcfe57f87d4021b
+besselImage : dc98fd21efedd5794d48024d334c19b8
+bodeplots : 00f713aeefde59ade94c74b869a32e95
+clownImage : c27cd454ae291b401c7d9fc7fd1efb84
+colorbarLabelTitle : 6684cd23c11336f00c76c2a1147a4e05
+compassplot : 832026d56bd49c899f01267deb999606
+contourPenny : 33c206795e296e58802dcd582c623d05
+croppedImage : 246bac4197e33b66b368e3726150fb91
+customLegend : e616a6987c50730e086d9903894fd039
+decayingharmonic : 5a707ac29c4a48e57dddb9bbf1948438
+double_axes : eef91ca5e5e9a14b376bf272a9edf444
+double_axes2 : 5f695c2398efc10db86d2e659d3b6c14
+double_colorbar : effce959246ead301919d3cfdc917cc6
+errorBars : d947dab08d432c8ed7cf1ea85484ccd3
+errorBars2 : 036c42ee22df7a959c93fcb655b2f126
+fill3plot : 63098c48728675b8f8d7c18e9236cdf4
+freqResponsePlot : 5ba971d451a7506f50a87cc03e81be85
+herrorbarPlot : 5d7caa3e83db9e504aa6d89d4cff00c2
+hgTransformPlot : 33043aceda8a80c914cafdb60d7e871e
+hist3d : 491ff99c47d24368a39084fd0645860c
+histogramPlot : 04c8b0bc8254effd2bbc6c7b380249ed
+imageOrientation_PNG : ce4f2b0d352e88274a26277c61360199
+imageOrientation_inline : 4add859a7ee89dc2e939e3535ba120ab
+imagescplot : c95430098d3f1e88cf1fdf4a12622441
+imagescplot2 : fdf39848b8e8e855c33507a33b234fab
+latexInterpreter : 1773751ee12ea224762a8d32935b00d7
+latexmath2 : b86c52efe6f4d5f3cb59cac9b67c81af
+legendplot : 7054ed0ea5ccca16b74e32b01787a93a
+legendplotBoxoff : 37f2b2cdda77ef5a55d770a653e6998a
+legendsubplots : 2554ef8c5c180faccb9457c8159686e6
+linesWithOutliers : 6add41e81f7a3036042792c3ff993db1
+logbaseline : 6faf2c5fdcd9b6578c6812be3394bec7
+logicalImage : d4fe7a0305e4b36c292f24c65f3560ab
+logplot : ca3f85050beb641236ba94a1d9bc4b5f
+mandrillImage : df2e2423bf2ed78eff79c8ab76729c9b
+manualAlignment : 3e840b0e26811095e21ca7c3a5a8db93
+many_random_points : 23a82cbc539bb830392a75f02c6bd80c
+markerSizes : 3edf22bf0eb1437e6ba5f41a1cc76dda
+markerSizes2 : 58fab2f0b8b04e9f6d91949aa64b0aa0
+meshPlot : 322946810f74c3b029f80df4188c7366
+mixedBarLine : 03ddee4b3b7592d6eac61a97912f6640
+moreLegends : 6e7542e1e7691bfeb65ec16c901d005e
+multiline_labels : f469978c28dab64e4ab9e0435a92b3d6
+multipleAxes : 62ea3df8f3595e1ae3b900745f898fc1
+multiplePatches : c30cfb0be1f8231e9cb88258e3cdc540
+myBoxplot : 54568259920da17d058ff0580831eb57
+overlappingPlots : 11e38420732e5b031e01bf1e874fb0ee
+pColorPlot : 3c92ed387513b6492999c08b983c52a3
+parameterCurve3d : 2df6f224f80d5880511bbba7a4467e4a
+parameterSurf : 8ddaacbb7b6b87beda5db9f7874d254d
+peaks_contour : e0174fb6308d54f584cbb661ce7d8e7a
+peaks_contourf : ddb1ec21fe11c28effa5fdf41c394e04
+pixelLegend : 46b18e59eac87834a6316c78239078ac
+plain_cos : 800518479e8ce94b5d65255bfd5ffeec
+polarplot : 0a41e46a6aa5ff807595abb09b0f17ca
+quiver3plot : f8dbf572e4cfb51aa660cee0ca2ae485
+quiveroverlap : a32b7bde9de0ccd62d9f48bb3b3b6fa2
+quiverplot : d7e5ca878c297485d79c7fdd6a92ec4e
+randomWithLines : dbe9b29796129c55850d8048fc361ec7
+rectanglePlot : 2069bc16ff640c4476afb8b305212088
+rlocusPlot : 1abd12c0602542fd5f3b5c477783e10d
+roseplot : 7d3511f4c5828889fc07fab8ae47c709
+scatter3Plot : 46439d021fa7940a4f1744ed9a094fa4
+scatterPlot : 620705bf2393466eac9ebf2cda29ba6e
+scatterPlotMarkers : b973b38b54e409c0df6f20c4d71879c8
+scatterPlotRandom : 06bfaf212c4244defc00906daae15136
+sine_with_annotation : 9da03e407cbb6e787e2e83e1ddd9753a
+sine_with_markers : f1a01159807bc315f26a6529136f9aa7
+spectro : 3873841545890b3527c6e59f9e274198
+spherePlot : ffd1fc633f879e9135ecb3888340baf1
+stackedBarsWithOther : 391673d9eb634f34d32ae3f25c163cb9
+stairsplot : 3603e55388786f35b564ea5422b43d66
+stemplot : b41cfec41aaa0f4f5fc37f2775187386
+stemplot2 : baa69434364669c14c592cffaa9c469c
+subplot2x2b : c6e82de40e07dfc7d9b429e6bdcbf7f6
+subplotCustom : 9e60fdf0f636770e831a2875cafb7345
+superkohle : 71b508784b86e0bbbd841fbb767e26e2
+surfPlot : 11806d3066010b5d673edaec852fc733
+surfPlot2 : 7f18c911921f713ab30b29817f09326b
+texInterpreter : c7df54b34b356911475efc3e31d92062
+texcolor : f2458aab1d0b475f0def459bc9c82fbc
+texrandom : 6a2bf21fb18c93512f564f158ade6773
+textAlignment : b131477df618b41e8a4514308248cf13
+textext : c72dc12d1a4ea8874eb13b1f2284b57e
+xAxisReversed : a6fb90307acfb9efa552fff7246970da
+ylabels : bd3466819526022cc716612fa68406ef
+zoom : 42d2f19c771f22d251052cc449426e4d
+zplanePlot1 : 835fb07e22adc498c1c63999cefd3fd7
+zplanePlot2 : 9e9a840ab9ae45c6344d79f15d379a1f
diff --git a/test/suites/ACID.Octave.3.8.0.md5 b/test/suites/ACID.Octave.3.8.0.md5
new file mode 100644
index 0000000..92bcb27
--- /dev/null
+++ b/test/suites/ACID.Octave.3.8.0.md5
@@ -0,0 +1,76 @@
+alphaImage : 1076913ce8efec21281049346d26be2e
+alphaTest : 1d9b2f330022aa59b34f041fd250c6b2
+areaPlot : ecb720e74be0a3ad04d107adb726d2b1
+axesColors : 407b8163dca72a7c1f63a1a8d482381e
+axesLocation : 8e7390412c7f6f0cd3bafa7e8395a75e
+bars : 6935b0d152d286e31116671823061376
+besselImage : 60dbd0e716e44bab34559269ef15c154
+colorbarLabelTitle : 4c4aa51de646d9c084dad8b04440c08f
+colorbarLogplot : 3c15d1c0d2a97173475357419e711e04
+compassplot : 3251d29bb3650f52612463eb1129ed45
+customLegend : d970fb66ae84c0dae1e8d3cbd1e16680
+decayingharmonic : 698f03be4ae992c189b9939943582796
+double_axes : 9cbfe4265d7a4ed12ca17580c11ac171
+double_axes2 : 3a81de3827ae481ca051f0a1508b8b1c
+errorBars : 51fdde5ec181938a9de6262560491a43
+errorBars2 : c781f9350cada6b1698829321694c84b
+herrorbarPlot : ca9b319142233b2bf14fd6c4f131e573
+imageOrientation_PNG : 8182b4b0a2fa56c9c5b6b42ad326ab06
+imageOrientation_inline : 8c396f08d2c610d93c519389ea589025
+imagescplot : c95430098d3f1e88cf1fdf4a12622441
+imagescplot2 : fdf39848b8e8e855c33507a33b234fab
+latexInterpreter : edc6283343164bc2f7d2227c50ca43d5
+latexmath2 : 5bdc4616c58c67ad328cbaf8713b5725
+legendplot : 23cfbe175fb05b42c9e20b6ebc2416fa
+legendplotBoxoff : c91de6fe6d42673453eda6843066e0e5
+legendsubplots : 440e8e28ef4a64a5ebd03c95e630320e
+linesWithOutliers : 8da18a00a4bfa8f112826f96487fded1
+logbaseline : d8508bd5aaa0c6357686ae326df4bcec
+logicalImage : d4fe7a0305e4b36c292f24c65f3560ab
+logplot : f54c7491ec3c26dcbf88cc572436dc97
+manualAlignment : 2d54a4bf7d018b4c143f9cee886ab5de
+many_random_points : 23a82cbc539bb830392a75f02c6bd80c
+markerSizes : f9656e9dff3b55a8c7bc4a2fefde206a
+markerSizes2 : 634e8eb72369f623b393012a7ace7959
+meshPlot : 322946810f74c3b029f80df4188c7366
+mixedBarLine : 6264bc48c4e16474ccc3dea893db68e3
+moreLegends : e6245e2b73dfd60c0e107738f6a941f6
+multiline_labels : 71e49d8f0d60613fa2c36f06eb152f44
+multipleAxes : a8c3f25ef10aa52214db072173d46b4e
+multiplePatches : 4a0e6a29dcc0e57066dba16ae3d12fd3
+overlappingPlots : a94c4dcdc8caa545a14fa5815d72f643
+pColorPlot : 46d44f808adb8f0ea884392da92c1ac8
+parameterCurve3d : 5e2e2f78d50874964e8b7af774d5968c
+peaks_contour : d9a075d6de94ca915404f783f87c4c8e
+peaks_contourf : 5833084f6c625ffdb3ec2916a68dbdb1
+pixelLegend : e9b3dfd6c5d861f5ab6fe79fc3c0bbf8
+plain_cos : dd5137db9daaa34b73e86e9d7c789b9c
+polarplot : 78e491b278403b8871ef9467ab102adf
+quiver3plot : faeef672605720b23fe82161e42a9f7d
+quiveroverlap : 913205a0496287c9e3750b2195d093a8
+quiverplot : 3f607c696a85871ed338802168b89108
+randomWithLines : 24f71c02ff9cd50de07b42543729eb52
+rectanglePlot : 34660c54438edc971f710f0555f6d7bc
+roseplot : 5fd9e855a27096be3dc208638bc8f265
+scatter3Plot : 709df0d59d931e9e8e5a454dd763a883
+scatterPlotMarkers : 0901e645e5b9df5ca2b944a246016300
+scatterPlotRandom : 6fadda0940c7c468a1aee0aef8203c4e
+sine_with_annotation : 003ad7b3f61ec1e55c923d86e84b8a1e
+sine_with_markers : 33e7e752de0d54a80fd95a70fa17a1ef
+spherePlot : 4f8881f1d81962a213aa0526566f976b
+stackedBarsWithOther : f52a0c1cfdb43af5b6ab059bacf9e3b1
+stairsplot : 0751a29f386b8d1308a65930b545828d
+stemplot : 5f99f06c9de266fbd209d6b573225100
+stemplot2 : 59d7df36b28584cee65fb3b1fd3d69ee
+subplot2x2b : 825b9f80ed6daed72534dfac46bbeaf7
+subplotCustom : 2b0d4515df00a18414c5d47d9e079bea
+surfPlot : d49ce46819b288a1a95343ce7d5c44bf
+surfPlot2 : 431d13922aa7917cf6fc49983cbd7442
+texInterpreter : 0dadacc761202fb046cd43aec6725358
+texcolor : c64bd79b9f491230d1bbb9bb21e6b919
+texrandom : d377f4757d072c8b0c823906a7feb193
+textAlignment : fc333da307a6498618bff9681b74eac0
+textext : a7d42c336b9453d01920743af865c04f
+xAxisReversed : 4fb5232751d05148c769be866e5349b1
+ylabels : c3b3370e58651301e942c768fa8f7dd3
+zoom : 87f4ac21b4dba35ecfc3166c7602c31e
diff --git a/test/testfunctions.m b/test/suites/ACID.m
similarity index 56%
rename from test/testfunctions.m
rename to test/suites/ACID.m
index 6ffee8c..05fb47b 100644
--- a/test/testfunctions.m
+++ b/test/suites/ACID.m
@@ -1,7 +1,7 @@
% =========================================================================
-% *** FUNCTION testfunctions
+% *** FUNCTION ACID
% ***
-% *** Standard example plot from MATLAB's help pages.
+% *** MATLAB2TikZ ACID test functions
% ***
% =========================================================================
% ***
@@ -31,11 +31,11 @@
% *** POSSIBILITY OF SUCH DAMAGE.
% ***
% =========================================================================
-function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctions(k)
+function [status] = ACID(k)
% assign the functions to test
testfunction_handles = { ...
- @one_point , ...
+ @multiline_labels , ...
@plain_cos , ...
@sine_with_markers , ...
@markerSizes , ...
@@ -47,7 +47,6 @@ function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctio
@peaks_contourf , ...
@many_random_points , ...
@double_colorbar , ...
- @subplot_colorbar , ...
@randomWithLines , ...
@double_axes , ...
@double_axes2 , ...
@@ -60,7 +59,6 @@ function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctio
@quiveroverlap , ...
@quiverplot , ...
@quiver3plot , ...
- @imageplot , ...
@logicalImage , ...
@imagescplot , ...
@imagescplot2 , ...
@@ -70,21 +68,14 @@ function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctio
@compassplot , ...
@stemplot , ...
@stemplot2 , ...
- @groupbars , ...
@bars , ...
- @subplotBars , ...
- @hbars , ...
- @stackbars , ...
@xAxisReversed , ...
@errorBars , ...
@errorBars2 , ...
- @subplot2x2 , ...
@subplot2x2b , ...
@manualAlignment , ...
- @subplot3x1 , ...
@subplotCustom , ...
@legendsubplots , ...
- @legendsubplots2 , ...
@bodeplots , ...
@rlocusPlot , ...
@mandrillImage , ...
@@ -99,7 +90,6 @@ function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctio
@scatterPlotRandom , ...
@scatterPlot , ...
@scatter3Plot , ...
- @scatter3Plot2 , ...
@spherePlot , ...
@surfPlot , ...
@surfPlot2 , ...
@@ -112,7 +102,7 @@ function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctio
@texcolor , ...
@textext , ...
@texrandom , ...
- @latexmath1 , ...
+ @latexInterpreter , ...
@latexmath2 , ...
@parameterCurve3d , ...
@parameterSurf , ...
@@ -125,87 +115,96 @@ function [desc, extraOpts, extraCFOptions, funcName, numFunctions] = testfunctio
@customLegend , ...
@pixelLegend , ...
@croppedImage , ...
- @doubleAxes , ...
@pColorPlot , ...
@hgTransformPlot , ...
- @scatter3Plot3 , ...
@scatterPlotMarkers , ...
@multiplePatches , ...
@logbaseline , ...
- @alphaImage
+ @alphaImage , ...
+ @annotationAll , ...
+ @annotationSubplots , ...
+ @annotationText , ...
+ @annotationTextUnits , ...
+ @imageOrientation_PNG, ...
+ @imageOrientation_inline, ...
+ @texInterpreter , ...
+ @stackedBarsWithOther, ...
+ @colorbarLabelTitle , ...
+ @textAlignment , ...
+ @overlappingPlots , ...
+ @histogramPlot , ...
+ @alphaTest
};
- numFunctions = length( testfunction_handles );
-
- desc = '';
- funcName = '';
- extraOpts = {};
- extraCFOptions = {};
+ numFunctions = length( testfunction_handles );
if (k<=0)
+ status = testfunction_handles;
return; % This is used for querying numFunctions.
elseif (k<=numFunctions)
- funcName = func2str(testfunction_handles{k});
-
- try
- nargs = nargout(funcName);
- catch %#ok
- % In Octave 3.6.4, `nargout` seems not to be able to handle
- % everything as MATLAB does:
- % * it does not support function handles (so using strings)
- % * it cannot handle subfunctions apparently, so always use the
- % default syntax there
- %TODO: handle diferent number of arguments in Octave
- warning('testfunctions:nargoutOctave',...
- 'Cannot determine number of output of "%s". Assuming 2.',funcName)
- nargs = 2;
- end
-
- switch nargs
- case 3
- [desc, extraOpts, extraCFOptions] = testfunction_handles{k}();
-
- case 1
- desc = testfunction_handles{k}();
-
- otherwise
- [desc, extraOpts] = testfunction_handles{k}();
-
- end
+ status = testfunction_handles{k}();
+ status.function = func2str(testfunction_handles{k});
else
error('testfunctions:outOfBounds', ...
'Out of bounds (number of testfunctions=%d)', numFunctions);
end
- return;
end
% =========================================================================
-function [description, extraOpts] = one_point()
+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];
- k = 1:1:length(m);
- plot(k,m,'*-');
+ plot(m,'*-'); hold on;
+ plot(m(end:-1:1)-0.5,'x--');
- title({'title', 'multline'})
- %legend(char('Multi-Line Legend Entry','Wont Work 2^2=4'))
- legend('Multi-Line Legend Entry Wont Work 2^2=4')
- xlabel({'one','two','three'});
+ 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,'XTick',0:1:length(m)-1);
-
+ set(gca,'YTick', []);
set(gca,'XTickLabel',{});
-
- description = 'Plot only one single point.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts, extraOptsCleanFigure] = plain_cos()
+function [stat] = plain_cos()
+ stat.description = 'Plain cosine function with minimumPointsDistance of $0.5$.';
+ stat.extraCleanfigureOptions = {'minimumPointsDistance', 0.5};
fplot( @cos, [0,2*pi] );
@@ -214,31 +213,20 @@ function [description, extraOpts, extraOptsCleanFigure] = plain_cos()
set(gca, 'YTick', []);
% Adjust the aspect ratio when in MATLAB(R) or Octave >= 3.4.
- env = getEnvironment();
- switch env
- case 'MATLAB'
- daspect([ 1 2 1 ])
- case 'Octave'
- if isVersionBelow( env, 3, 4 )
- % Octave < 3.4 doesn't have daspect unfortunately.
- else
- daspect([ 1 2 1 ])
- end
- otherwise
- error( 'Unknown environment. Need MATLAB(R) or GNU Octave.' )
+ if isOctave('<=', [3,4])
+ % Octave < 3.4 doesn't have daspect unfortunately.
+ else
+ daspect([ 1 2 1 ])
end
-
- description = 'Plain cosine function with minimumPointsDistance of $0.5$.' ;
- extraOpts = {};
- extraOptsCleanFigure = {'minimumPointsDistance', 0.5};
-
end
% =========================================================================
-function [description, extraOpts] = sine_with_markers ()
+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 = tan(sin(x)) - sin(tan(x));
+ y = sin(x);
y(3) = NaN;
y(7) = Inf;
y(11) = -Inf;
@@ -255,14 +243,11 @@ function [description, extraOpts] = sine_with_markers ()
set(gca,'XTick',[0]);
set(gca,'XTickLabel',{'null'});
-
- description = [ 'Twisted plot of the sine function. ' ,...
- 'Pay particular attention to how markers and Infs/NaNs are treated.' ];
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = markerSizes()
+function [stat] = markerSizes()
+ stat.description = 'Marker sizes.';
+
hold on;
h = fill([1 1 2 2],[1 2 2 1],'r');
@@ -270,16 +255,14 @@ function [description, extraOpts] = markerSizes()
plot([0],[0],'go','Markersize',14,'LineWidth',10)
plot([0],[0],'bo','Markersize',14,'LineWidth',1)
-
- description = 'Marker sizes.';
- extraOpts = {};
end
-
% =========================================================================
-function [description, extraOpts] = markerSizes2()
+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));
@@ -295,19 +278,19 @@ function [description, extraOpts] = markerSizes2()
xlim([min(n)-1 max(n)+1]);
ylim([0 d*(nStyles+1)]);
set(gca,'XTick',n,'XTickLabel',s,'XTickLabelMode','manual');
-
- description = 'Line plot with with different marker sizes.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = sine_with_annotation ()
+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
x = -pi:.1:pi;
y = sin(x);
h = plot(x,y);
set(gca,'XTick',-pi:pi/2:pi);
- set(gca,'XTickLabel',{'-pi','-pi/2','0','pi/2','pi'});
+ set(gca,'XTickLabel',{'-pi','-pi/2','0','pi/2','pi'});
xlabel('-\pi \leq \Theta \leq \pi');
ylabel('sin(\Theta)');
@@ -315,28 +298,28 @@ function [description, extraOpts] = sine_with_annotation ()
text(-pi/4,sin(-pi/4),'\leftarrow sin(-\pi\div4)',...
'HorizontalAlignment','left');
- set(findobj(gca,'Type','line','Color',[0 0 1]),...
- 'Color','red',...
- 'LineWidth',10);
-
- description = [ 'Plot of the sine function. ' ,...
- 'Pay particular attention to how titles and annotations are treated.' ];
- extraOpts = {};
+ % Doesn't work in Octave
+ %set(findobj(gca,'Type','line','Color',[0 0 1]),...
+ % 'Color','red',...
+ % 'LineWidth',10);
end
% =========================================================================
-function [description, extraOpts] = linesWithOutliers()
+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] );
-
- description = 'Lines with outliers.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = peaks_contour()
+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
[C, h] = contour(peaks(20),10);
clabel(C, h);
@@ -347,17 +330,17 @@ function [description, extraOpts] = peaks_contour()
colormap winter;
- description = 'Test contour plots.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = contourPenny()
+function [stat] = contourPenny()
+ stat.description = 'Contour plot of a US\$ Penny.';
+ stat.unreliable = isMATLAB();
+ % FIXME: see #604; contour() produces inconsistent output
+ stat.issues = [49 404];
if ~exist('penny.mat','file')
- fprintf( 'penny data set not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'penny data set not found. Skipping.\n\n' );
+ stat.skip = true;
return;
end
@@ -365,33 +348,34 @@ function [description, extraOpts] = contourPenny()
contour(flipud(P));
axis square;
- description = 'Contour plot of a US\$ Penny.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = peaks_contourf ()
+function [stat] = peaks_contourf ()
+ stat.description = 'Test the contourfill plots.';
+ stat.unreliable = isMATLAB; % FIXME: inspect this
+ stat.issues = 582;
- contourf(peaks(20), 10);
+ [trash, h] = contourf(peaks(20), 10);
+ hold on
+ plot(1:20)
colorbar();
- legend('my legend');
-% colorbar('NorthOutside');
-% colorbar('SouthOutside');
-% colorbar('WestOutside');
-
-% colormap([0:0.1:1; 1:-0.1:0; 0:0.1:1]')
+ legend(h, 'Contour');
colormap hsv;
-
- description = 'Test the contourfill plots.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = double_colorbar()
+function [stat] = double_colorbar()
+ stat.description = 'Double colorbar.';
+ stat.unreliable = isMATLAB(); % FIXME: see #590, #604
+
+ if isOctave()
+ fprintf( 'Octave can''t handle tight axes.\n\n' );
+ stat.skip = true;
+ return
+ end
vspace = linspace(-40,40,20);
- speed_map = rand(20);
- Q1_map = rand(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)
@@ -408,73 +392,59 @@ function [description, extraOpts] = double_colorbar()
axis square
xlabel('$v_{2d}$')
ylabel('$v_{2q}$')
-
- description = 'Double colorbar.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = subplot_colorbar()
-
- img = rand(100);
- vec = rand(100,1);
-
- subplot(2,1,1);
- imagesc(img,[0 1]);
- colorbar;
+function [stat] = randomWithLines()
+ stat.description = 'Lissajous points with lines.';
- subplot(2,1,2);
- plot(vec);
+ beta = 42.42;
+ t = 1:150;
+ X = [sin(t); cos(beta * t)].';
- description = 'Subplot colorbar.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = randomWithLines()
-
- X = randn(150,2);
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,[2*std(X(:,2)) 2*std(X(:,2))],'k--');
- plot(M,[-2*std(X(:,2)) -2*std(X(:,2))],'k--');
+ plot(M,mn + 1*[s s],'--');
+ plot(M,mn - 2*[s s],'--');
axis('tight');
-
- description = 'Random points with lines.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = many_random_points ()
+function [stat] = many_random_points ()
+ stat.description = 'Test the performance when drawing many points.';
n = 1e3;
+ alpha = 1024;
+ beta = 1;
+ gamma = 5.47;
- xy = rand(n,2);
- plot ( xy(:,1), xy(:,2), '.r' );
- axis([ 0, 1, 0, 1 ])
-
- description = 'Test the performance when drawing many points.';
- extraOpts = {};
+ x = cos( (1:n) * alpha );
+ y = sin( (1:n) * beta + gamma);
+ plot ( x, y, '.r' );
+ axis([ 0, 1, 0, 1 ])
end
% =========================================================================
-function [description, extraOpts] = double_axes()
+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];
- figure(1)
grid on
h1 = plot(x,y,'Color','k');
% following code is taken from `floatAxisX.m'
% get position of axes
- allAxes = get(gcf,'Children');
+ allAxes = findobj(gcf,'type','axes');
naxes = length(allAxes);
ax1Pos = get(allAxes(naxes),'position');
@@ -518,12 +488,10 @@ function [description, extraOpts] = double_axes()
set(ax3,'XMinorTick','on','color','none','xcolor',get(hl1,'color'))
xlabel('secondary axis')
-
- description = 'Double axes';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = double_axes2()
+function [stat] = double_axes2()
+ stat.description = 'Double overlayed axes with a flip.' ;
ah1=axes;
ph=plot([0 1],[0 1]);
@@ -538,45 +506,35 @@ function [description, extraOpts] = double_axes2()
% make the background transparent
set(ah1,'color','none')
% move these axes to the back
- set(gcf,'Child',flipud(get(gcf,'Children')))
-
- description = 'Double overlayed axes with a flip.' ;
- extraOpts = {};
-
+ set(gcf,'Children',flipud(get(gcf,'Children')))
end
% =========================================================================
-function [description, extraOpts] = logplot()
+function [stat] = logplot()
+ stat.description = 'Test logscaled axes.';
+ stat.unreliable = isMATLAB(); %FIXME: #590
x = logspace(-1,2);
loglog(x,exp(x),'-s')
grid on;
-
- description = 'Test logscaled axes.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = colorbarLogplot()
+function [stat] = colorbarLogplot()
+ stat.description = 'Logscaled colorbar.';
+ stat.unreliable = isOctave || isMATLAB; %FIXME: investigate
- switch getEnvironment()
- case 'MATLAB'
- imagesc([1 10 100]);
- case 'Octave'
- % TODO find out what to do for octave here
- description = 'Logscaled colorbar -- unavailable in Octave.';
- extraOpts = {};
- return;
- otherwise
- error( 'Unknown environment. Need MATLAB(R) or Octave.' )
+ 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
- set(colorbar(), 'YScale', 'log');
-
- description = 'Logscaled colorbar.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = legendplot()
+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');
@@ -594,132 +552,106 @@ function [description, extraOpts] = legendplot()
legend( 'sin(x)', 'cos(x)', 'Location','NorthOutside', ...
'Orientation', 'Horizontal' );
grid on;
-
- description = 'Test inserting of legends.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = legendplotBoxoff ()
+function [stat] = legendplotBoxoff ()
+ stat.description = 'Test inserting of legends.';
+ stat.issues = [607,609];
x = -pi:pi/20:pi;
- plot( x, cos(x),'-ro',...
- x, sin(x),'-.b' ...
- );
- h = legend( 'cos_x', 'one pretty long legend sin_x', 2 );
- set( h, 'Interpreter', 'none' );
- legend boxoff;
-
- description = 'Test inserting of legends.';
- extraOpts = {};
-
+ 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 [description, extraOpts] = moreLegends()
+function [stat] = moreLegends()
+ stat.description = 'More legends.';
+ stat.unreliable = isMATLAB('>=', [8,4]); % R2014b and newer
x = 0:.1:7;
y1 = sin(x);
y2 = cos(x);
[ax,h1,h2] = plotyy(x,y1,x,y2);
legend([h1;h2],'Sine','Cosine');
-
- description = 'More legends.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = zoom()
-
- fplot( @sin, [0,2*pi], '-*' );
- hold on;
- delta = pi/10;
-
- plot( [pi/2, pi/2], [1-2*delta, 1+2*delta], 'r' ); % vertical line
- plot( [pi/2-2*delta, pi/2+2*delta], [1, 1], 'g' ); % horizontal line
-
- % diamond
- plot( [ pi/2-delta, pi/2 , pi/2+delta, pi/2 , pi/2-delta ], ...
- [ 1 , 1-delta, 1, 1+delta, 1 ], 'y' );
-
- % boundary lines with markers
- plot([ pi/2-delta, pi/2 , pi/2+delta, pi/2+delta pi/2+delta, pi/2, pi/2-delta, pi/2-delta ], ...
- [ 1-delta, 1-delta, 1-delta, 1, 1+delta, 1+delta, 1+delta, 1 ], ...
- 'ok', ...
- 'MarkerSize', 20, ...
- 'MarkerFaceColor', 'g' ...
- );
-
- hold off;
-
- axis([pi/2-delta, pi/2+delta, 1-delta, 1+delta] );
-
- description = 'Plain cosine function, zoomed in.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = bars()
-
- bins = -0.5:0.1:0.5;
- bins = 10 * bins;
+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(); % FIXME: investigate
+
+ % dataset grouped
+ bins = 10 * (-0.5:0.1:0.5);
numEntries = length(bins);
- numBars = 3;
- data = round(100 * rand(numEntries, numBars));
- b = bar(bins,data, 1.5);
+ 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
- set(b(1),'FaceColor','m','EdgeColor','none')
+ % dataset stacked
+ data = ACID_data;
+ Y = round(abs(data(2:6,1:3))/10);
- description = 'Plot with bars.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = subplotBars()
- subplot(2,1,1);
- X = rand(1,10);
- bar(X);
+ 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,1,2);
- bins = -0.5:0.1:0.5;
- bins = 10 * bins;
- numEntries = length(bins);
- numBars = 3;
- data = round(100 * rand(numEntries, numBars));
+ subplot(2,2,2);
+ barh(bins, plotData, 'grouped', 'BarWidth', 1.3);
- bar(bins,data, 1.5);
+ subplot(2,2,3);
+ bar(Y, 'stacked');
- description = 'Bars in subplots.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = hbars()
- y = [75.995 91.972 105.711 123.203 131.669 ...
- 150.697 179.323 203.212 226.505 249.633 281.422];
- barh(y);
- description = 'Horizontal bars.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = groupbars()
- X = [1,2,3,4,5];
- Y = round(rand(5,2)*20);
-% bar(X,Y,'group','BarWidth',1.0);
- makebars(X,Y,1.0,'grouped');
-% set(gca,'XTick',[4,4.2,4.25,4.3,4.4,4.45,4.5]);
- title 'Group';
- description = 'Plot with bars in groups.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = stackbars()
- Y = round(rand(5,3)*10);
- bar(Y,'stack');
- title 'Stack';
+ 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')
- description = 'Plot of stacked bars.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = stemplot()
+function [stat] = stemplot()
+ stat.description = 'A simple stem plot.' ;
x = 0:25;
y = [exp(-.07*x).*cos(x);
@@ -728,36 +660,28 @@ function [description, extraOpts] = stemplot()
legend( 'exp(-.07x)*cos(x)', 'exp(.05*x)*cos(x)', 'Location', 'NorthWest');
set(h(1),'MarkerFaceColor','blue')
set(h(2),'MarkerFaceColor','red','Marker','square')
-
- description = 'A simple stem plot.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = stemplot2()
+function [stat] = stemplot2()
+ stat.description = 'Another simple stem plot.';
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');
-
- description = 'Another simple stem plot.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = stairsplot()
-
- x = linspace(-2*pi,2*pi,40);
- stairs(x,sin(x))
-
- description = 'A simple stairs plot.' ;
- extraOpts = {};
+function [stat] = stairsplot()
+ stat.description = 'A simple stairs plot.' ;
+ x = linspace(-2*pi,2*pi,40)';
+ h = stairs([sin(x), 0.2*cos(x)]);
+ legend(h(2),'second entry')
end
% =========================================================================
-function [description, extraOpts] = quiverplot()
+function [stat] = quiverplot()
+ stat.description = 'A combined quiver/contour plot of $x\exp(-x^2-y^2)$.' ;
[X,Y] = meshgrid(-2:.2:2);
Z = X.*exp(-X.^2 - Y.^2);
@@ -767,13 +691,11 @@ function [description, extraOpts] = quiverplot()
quiver(X,Y,DX,DY);
colormap hsv;
hold off
-
- description = 'A combined quiver/contour plot of $x\exp(-x^2-y^2)$.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = quiver3plot()
+function [stat] = quiver3plot()
+ stat.description = 'Three-dimensional quiver plot.' ;
+ stat.unreliable = isMATLAB(); %FIXME: #590
vz = 10; % Velocity
a = -32; % Acceleration
@@ -792,13 +714,10 @@ function [description, extraOpts] = quiver3plot()
scale = 0;
quiver3(x,y,z,u,v,w,scale)
view([70 18])
-
- description = 'Three-dimensional quiver plot.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = quiveroverlap ()
+function [stat] = quiveroverlap ()
+ stat.description = 'Quiver plot with avoided overlap.';
x = [0 1];
y = [0 0];
@@ -806,69 +725,48 @@ function [description, extraOpts] = quiveroverlap ()
v = [1 1];
quiver(x,y,u,v);
-
- description = 'Quiver plot with avoided overlap.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = polarplot ()
+function [stat] = polarplot ()
+ stat.description = 'A simple polar plot.' ;
+ stat.extraOptions = {'showHiddenStrings',true};
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
t = 0:.01:2*pi;
polar(t,sin(2*t).*cos(2*t),'--r')
-
- description = 'A simple polar plot.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = roseplot ()
+function [stat] = roseplot ()
+ stat.description = 'A simple rose plot.' ;
+ stat.extraOptions = {'showHiddenStrings',true};
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
- theta = 2*pi*rand(1,50);
+ theta = 2*pi*sin(linspace(0,8,100));
rose(theta);
-
- description = 'A simple rose plot.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = compassplot ()
+function [stat] = compassplot ()
+ stat.description = 'A simple compass plot.' ;
+ stat.extraOptions = {'showHiddenStrings',true};
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
- Z = eig(randn(20,20));
+ Z = (1:20).*exp(1i*2*pi*cos(1:20));
compass(Z);
-
- description = 'A simple compass plot.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = imageplot ()
-
- n = 10;
- density = 0.5;
+function [stat] = logicalImage()
+ stat.description = 'An image plot of logical matrix values.' ;
+ stat.unreliable = isOctave; %FIXME: investigate
+ % different `width`, see issue #552# (comment 76918634)
- subplot(1,2,1);
- A = sprand( n, n, density );
- imagesc( A );
-
- subplot(1,2,2);
- A = sprand( n, n, density );
- imagesc( A );
-
- description = 'An image plot of matrix values.' ;
- %extraOpts = {'imagesAsPng', false};
- extraOpts = {};
-
-end
-% =========================================================================
-function [description, extraOpts] = logicalImage()
- data = rand(10,10);
- imagesc(data > 0.5);
- description = 'An image plot of logical matrix values.' ;
- extraOpts = {};
+ plotData = magic(10);
+ imagesc(plotData > mean(plotData(:)));
+ colormap('hot');
end
% =========================================================================
-function [description, extraOpts] = imagescplot()
+function [stat] = imagescplot()
+ stat.description = 'An imagesc plot of $\sin(x)\cos(y)$.';
+ stat.unreliable = isOctave; %FIXME: investigate
pointsX = 10;
pointsY = 20;
@@ -876,13 +774,11 @@ function [description, extraOpts] = imagescplot()
y = 0:1/pointsY:1;
z = sin(x)'*cos(y);
imagesc(x,y,z);
-
- description = 'An imagesc plot of $\sin(x)\cos(y)$.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = imagescplot2()
+function [stat] = imagescplot2()
+ stat.description = 'A trimmed imagesc plot.';
+ stat.unreliable = isOctave; %FIXME: investigate
a=magic(10);
x=-5:1:4;
@@ -893,13 +789,10 @@ function [description, extraOpts] = imagescplot2()
ylim([12,15])
grid on;
-
- description = 'A trimmed imagesc plot.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = xAxisReversed ()
+function [stat] = xAxisReversed ()
+ stat.description = 'Reversed axes with legend.' ;
n = 100;
x = (0:1/n:1);
@@ -908,61 +801,30 @@ function [description, extraOpts] = xAxisReversed ()
set(gca,'XDir','reverse');
set(gca,'YDir','reverse');
legend( 'Location', 'SouthWest' );
-
- description = 'Reversed axes with legend.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = subplot2x2 ()
+function [stat] = subplot2x2b ()
+ stat.description = 'Three aligned subplots on a $2\times 2$ subplot grid.' ;
+ stat.unreliable = isOctave || isMATLAB('>=', [8,4]); % R2014b and newer
x = (1:5);
subplot(2,2,1);
- y = rand(1,5);
+ y = sin(x.^3);
plot(x,y);
subplot(2,2,2);
- y = rand(1,5);
- plot(x,y);
-
- subplot(2,2,3);
- y = rand(1,5);
- plot(x,y);
-
- subplot(2,2,4);
- y = rand(1,5);
- plot(x,y);
-
-
- description = 'Four aligned subplots on a $2\times 2$ subplot grid.' ;
- extraOpts = {};
-
-end
-% =========================================================================
-function [description, extraOpts] = subplot2x2b ()
-
- x = (1:5);
-
- subplot(2,2,1);
- y = rand(1,5);
- plot(x,y);
-
- subplot(2,2,2);
- y = rand(1,5);
+ y = cos(x.^3);
plot(x,y);
subplot(2,2,3:4);
- y = rand(1,5);
+ y = tan(x);
plot(x,y);
-
-
- description = 'Three aligned subplots on a $2\times 2$ subplot grid.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = manualAlignment()
+function [stat] = manualAlignment()
+ stat.description = 'Manually aligned figures.';
+ stat.unreliable = isOctave || isMATLAB; % FIXME: investigate
xrange = linspace(-3,4,2*1024);
@@ -974,83 +836,54 @@ function [description, extraOpts] = manualAlignment()
axes('Position', [0.1 0.25 0.85 0.6]);
plot(xrange);
set(gca,'XTick',[]);
-
- description = 'Manually aligned figures.';
- extraOpts = {};
-
-end
-% =========================================================================
-function [description, extraOpts] = subplot3x1 ()
-
- x = (1:5);
-
- subplot(3,1,1);
- y = rand(1,5);
- plot(x,y);
-
- subplot(3,1,2);
- y = rand(1,5);
- plot(x,y);
-
- subplot(3,1,3);
- y = rand(1,5);
- plot(x,y);
-
- description = 'Three aligned subplots on a $3\times 1$ subplot grid.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = subplotCustom ()
+function [stat] = subplotCustom ()
+ stat.description = 'Three customized aligned subplots.';
+ stat.unreliable = isMATLAB(); % FIXME: #590
x = (1:5);
- y = rand(1,5);
+ y = cos(sqrt(x));
subplot( 'Position', [0.05 0.1 0.3 0.3] )
plot(x,y);
-
- y = rand(1,5);
+ y = sin(sqrt(x));
subplot( 'Position', [0.35 0.5 0.3 0.3] )
plot(x,y);
- y = rand(1,5);
+ y = tan(sqrt(x));
subplot( 'Position', [0.65 0.1 0.3 0.3] )
plot(x,y);
-
- description = 'Three customized aligned subplots.' ;
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = errorBars ()
-
- data = 1:10;
- eH = rand(10,1);
- eL = rand(10,1);
- %hold on;
- %bar(1:10, data)
- errorbar(1:10, data, eL, eH, '.')
+function [stat] = errorBars()
+ stat.description = 'Generic error bar plot.';
+ stat.unreliable = isMATLAB('>=', [8,4]); % R2014b and newer, see #590, #604
+ data = ACID_data;
+ plotData = 1:10;
- description = 'Generic error bar plot.';
- extraOpts = {};
+ eH = abs(data(1:10,1))/10;
+ eL = abs(data(1:10,3))/50;
+ errorbar(1:10, plotData, eL, eH, '.')
end
% =========================================================================
-function [description, extraOpts] = errorBars2 ()
-
- data = load( 'myCount.dat' );
+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' );
-
- description = 'Another error bar example.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = legendsubplots()
+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
@@ -1062,8 +895,8 @@ function [description, extraOpts] = legendsubplots()
t = 0:(4*pi)/length:4*pi;
x = t;
a = t;
- y = sin(t) + 0.1*randn(1,length+1);
- b = sin(t) + 0.1*randn(1,length+1) + 0.05*cos(2*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);
@@ -1100,18 +933,14 @@ function [description, extraOpts] = legendsubplots()
xlabel('Time/s')
ylabel('\Delta V')
title('Differential time traces');
-
- description = [ 'Subplots with legends. ' , ...
- 'Increase value of "length" in the code to stress-test your TeX installation.' ];
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = legendsubplots2()
+function [stat] = bodeplots()
+ stat.description = 'Bode plots with legends.';
if isempty(which('tf'))
- fprintf( 'function "tf" not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'function "tf" not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
@@ -1136,7 +965,6 @@ function [description, extraOpts] = legendsubplots2()
LCL=(s^2*C*L2+1)/(s^2*C*L1+1);
t=logspace(3,5,1000);
- hold off
bode(LCL,t)
hold on
bode(LCLd,t)
@@ -1145,64 +973,48 @@ function [description, extraOpts] = legendsubplots2()
grid on
legend('Perfect LCL',' Real LCL','Location','SW')
-
- description = ['Another subplot with legends.'];
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = bodeplots()
-
- % check if the control toolbox is installed
- if length(ver('control')) ~= 1
- fprintf( 'Control toolbox not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
- return
- end
-
- g = tf([1 0.1 7.5],[1 0.12 9 0 0]);
- bode(g)
- description = 'Bode diagram of frequency response.';
- extraOpts = {};
+ % Work around a peculiarity in R2014a and older: 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 [description, extraOpts] = rlocusPlot()
+function [stat] = rlocusPlot()
+ stat.description = 'rlocus plot.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
if isempty(which('tf'))
- fprintf( 'function "tf" not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'function "tf" not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
s=tf('s');
rlocus(tf([1 1],[4 3 1]))
-
- description = 'rlocus plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = mandrillImage()
+function [stat] = mandrillImage()
+ stat.description = 'Picture of a mandrill.';
if ~exist('mandrill.mat','file')
- fprintf( 'mandrill data set not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'mandrill data set not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
data = load( 'mandrill' );
- set( gcf, 'color', 'k' )
- image( data.X )
- colormap( data.map )
- axis off
- axis image
-
- description = 'Picture of a mandrill.';
- extraOpts = {};
+ image( data.X ) % show image
+ colormap( data.map ) % adapt colormap
+ axis image % pixels should be square
+ axis off % disable axis
end
% =========================================================================
-function [description, extraOpts] = besselImage()
+function [stat] = besselImage()
+ stat.description = 'Bessel function.';
+ stat.unreliable = isOctave || isMATLAB; %FIXME: investigate
nu = -5:0.25:5;
beta = 0:0.05:2.5;
@@ -1223,103 +1035,95 @@ function [description, extraOpts] = besselImage()
xlabel('Order')
ylabel('\beta')
set(gca,'YDir','normal')
-
- description = 'Bessel function.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = clownImage()
+function [stat] = clownImage()
+ stat.description = 'Picture of a clown.';
if ~exist('clown.mat','file')
- fprintf( 'clown data set not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'clown data set not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
data = load( 'clown' );
imagesc( data.X )
colormap( gray )
-
- description = 'Picture of a clown.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = zplanePlot1()
+function [stat] = zplanePlot1()
+ stat.description = 'Representation of the complex plane with zplane.';
+ stat.unreliable = isMATLAB; % FIXME: investigate
% check of the signal processing toolbox is installed
if length(ver('signal')) ~= 1
fprintf( 'Signal toolbox not found. Skip.\n\n' );
- description = [];
- extraOpts = {};
+ stat.skip = true;
+
return
end
[z,p] = ellip(4,3,30,200/500);
zplane(z,p);
title('4th-Order Elliptic Lowpass Digital Filter');
-
- description = 'Representation of the complex plane with zplane.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = zplanePlot2()
+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
if length(ver('signal')) ~= 1
fprintf( 'Signal toolbox not found. Skip.\n\n' );
- description = [];
- extraOpts = {};
+ 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
-
- description = 'Representation of the complex plane with zplane.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = freqResponsePlot()
+function [stat] = freqResponsePlot()
+ stat.description = 'Frequency response plot.';
+ stat.closeall = true;
+ stat.issues = [409];
+ stat.unreliable = isMATLAB;
% check of the signal processing toolbox is installed
if length(ver('signal')) ~= 1
fprintf( 'Signal toolbox not found. Skip.\n\n' );
- description = [];
- extraOpts = {};
+ 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
-
- description = 'Frequency response plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = axesLocation()
- plot(rand(1,10));
+function [stat] = axesLocation()
+ stat.description = 'Swapped axis locations.';
+ stat.issues = 259;
+
+ plot(cos(1:10));
set(gca,'XAxisLocation','top');
set(gca,'YAxisLocation','right');
-
- description = 'Swapped axis locations.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = axesColors()
+function [stat] = axesColors()
+ stat.description = 'Custom axes colors.';
- plot(rand(1,10));
+ plot(sin(1:15));
set(gca,'XColor','g','YColor','b');
% set(gca,'XColor','b','YColor','k');
box off;
-
- description = 'Custom axes colors.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = multipleAxes()
+function [stat] = multipleAxes()
+ stat.description = 'Multiple axes.';
+ stat.unreliable = isMATLAB('>=', [8,4]); % R2014b and newer
x1 = 0:.1:40;
y1 = 4.*cos(x1)./(x1+2);
@@ -1346,40 +1150,31 @@ function [description, extraOpts] = multipleAxes()
% Now set the tick mark locations.
set(ax1,'XTick',xlimits(1):xinc:xlimits(2) ,...
'YTick',ylimits(1):yinc:ylimits(2) )
-
- description = 'Multiple axes.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = scatterPlotRandom()
+function [stat] = scatterPlotRandom()
+ stat.description = 'Generic scatter plot.';
n = 1:100;
- scatter(n, n, 1000*rand(length(n),1), n.^8);
+ scatter(n, n, 1000*(1+cos(n.^1.5)), n.^8);
colormap autumn;
- %x = randn( 10, 2 );
- %scatter( x(:,1), x(:,2) );
- description = 'Generic scatter plot.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = scatterPlot()
-
+function [stat] = scatterPlot()
+ stat.description = 'Scatter plot with MATLAB(R) stat.';
if ~exist('seamount.mat','file')
- fprintf( 'seamount data set not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ 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, '^' );
- description = 'Scatter plot with MATLAB(R) data.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = scatterPlotMarkers()
+function [stat] = scatterPlotMarkers()
+ stat.description = 'Scatter plot with with different marker sizes and legend.';
+ stat.unreliable = isOctave;
n = 1:10;
d = 10;
@@ -1400,14 +1195,10 @@ function [description, extraOpts] = scatterPlotMarkers()
ylim([0 d*(nStyles+1)]);
set(gca,'XTick',n,'XTickLabel',s,'XTickLabelMode','manual');
- legend(names{:});
-
- description = 'Scatter plot with with different marker sizes and legend.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = scatter3Plot()
+function [stat] = scatter3Plot()
+ stat.description = 'Scatter3 plot with MATLAB(R) stat.';
[x,y,z] = sphere(16);
X = [x(:)*.5 x(:)*.75 x(:)];
@@ -1417,64 +1208,25 @@ function [description, extraOpts] = scatter3Plot()
C = repmat([1 2 3],numel(x),1);
scatter3(X(:),Y(:),Z(:),S(:),C(:),'filled'), view(-60,60)
view(40,35)
-
- description = 'Scatter3 plot with MATLAB(R) data.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = scatter3Plot2()
-
- % Read image (Note: "peppers.png" is available with MATLAB)
- InpImg_RGB = imread('peppers.png');
-
- % Subsample image ("scatter3" can't cope with too many points)
- InpImg_RGB = InpImg_RGB(1:100:end, 1:100:end, 1:100:end );
-
- InpImg_RGB = reshape(InpImg_RGB, [], 1, 3);
-
- % Split up into single components
- r = InpImg_RGB(:,:,1);
- g = InpImg_RGB(:,:,2);
- b = InpImg_RGB(:,:,3);
-
- % Scatter-plot points
- scatter3(r,g,b,15,[r g b]);
- xlabel('R');
- ylabel('G');
- zlabel('B');
-
- description = 'Another Scatter3 plot.';
- extraOpts = {};
-
- return
-end
-% =========================================================================
-function [description, extraOpts] = scatter3Plot3()
- hold on;
- scatter3(rand(5,1),rand(5,1),rand(5,1),150,...
- 'MarkerEdgeColor','none','MarkerFaceColor','k');
- scatter3(rand(5,1),rand(5,1),rand(5,1),150,...
- 'MarkerEdgeColor','none','MarkerFaceColor','b');
-
- description = 'Scatter3 plot with 2 colors (Issue 292)';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = spherePlot()
+function [stat] = spherePlot()
+ stat.description = 'Stretched sphere with unequal axis limits.';
+ stat.unreliable = isOctave || isMATLAB('<', [8,4]); %FIXME: investigate
+ stat.issues = 560;
sphere(30);
title('a sphere: x^2+y^2+z^2');
xlabel('x');
ylabel('y');
zlabel('z');
- axis equal;
-
- description = 'Plot a sphere.';
- extraOpts = {};
+ set(gca,'DataAspectRatio',[1,1,.5],'xlim',[-1 2], 'zlim',[-1 0.8])
end
% =========================================================================
-function [description, extraOpts] = surfPlot()
+function [stat] = surfPlot()
+ stat.description = 'Surface plot.';
+ stat.unreliable = isMATLAB; % FIXME: investigate
+
[X,Y,Z] = peaks(30);
surf(X,Y,Z)
colormap hsv
@@ -1499,26 +1251,30 @@ function [description, extraOpts] = surfPlot()
xlabel( 'x' )
ylabel( 'y' )
zlabel( 'z' )
-
- description = 'Surface plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = surfPlot2()
- z = [ ones(15, 5) zeros(15,5); ...
- zeros(5,5) zeros(5,5)
- ];
+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' );
-
- description = 'Another surface plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = superkohle()
+function [stat] = superkohle()
+
+ if ~exist('initmesh')
+ fprintf( 'initmesh() not found. Skipping.\n\n' );
+ stat.skip = true;
+ return;
+ end
+
+ stat.description = 'Superkohle plot.';
x1=0;
x2=pi;
@@ -1547,12 +1303,12 @@ function [description, extraOpts] = superkohle()
ylabel('x2 axis');
axis([0 pi 0 pi -1 1]);
grid on;
-
- description = 'Superkohle plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = meshPlot()
+function [stat] = meshPlot()
+ stat.description = 'Mesh plot.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
+
[X,Y,Z] = peaks(30);
mesh(X,Y,Z)
colormap hsv
@@ -1561,12 +1317,11 @@ function [description, extraOpts] = meshPlot()
xlabel( 'x' )
ylabel( 'y' )
zlabel( 'z' )
-
- description = 'Mesh plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = ylabels()
+function [stat] = ylabels()
+ stat.description = 'Separate y-labels.';
+ stat.unreliable = isMATLAB('>=', [8,4]); % R2014b and newer
x = 0:.01:2*pi;
H = plotyy(x,sin(x),x,3*cos(x));
@@ -1575,19 +1330,18 @@ function [description, extraOpts] = ylabels()
ylabel(H(2),'3cos(x)');
xlabel(gca,'time')
-
- description = 'Separate y-labels.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = spectro()
+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. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'chirp() not found. Skipping.\n\n' );
+ stat.description = [];
+ stat.skip = true;
return
end
@@ -1595,25 +1349,26 @@ function [description, extraOpts] = spectro()
X = chirp(T,100,1,200,'q');
spectrogram(X,128,120,128,1E3);
title('Quadratic Chirp');
-
- description = 'Spectrogram plot';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = mixedBarLine()
+function [stat] = mixedBarLine()
+ stat.description = 'Mixed bar/line plot.';
+ stat.unreliable = isOctave || isMATLAB; %FIXME: investigate
+ % unreliable, see issue #614 (comment 92263263)
- x = rand(1000,1)*10;
+ data = ACID_data;
+ x = data(:);
hist(x,10)
y = ylim;
hold on;
- plot([3 3], y, '-r');
+ plot([mean(x) mean(x)], y, '-r');
hold off;
-
- description = 'Mixed bar/line plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = decayingharmonic()
+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;
@@ -1622,25 +1377,24 @@ function [description, extraOpts] = decayingharmonic()
t = 0:901;
y = A * exp(-alpha*t) .* sin(beta*t);
plot(t, y)
- title('{\itAe}^{-\alpha\itt}sin\beta{\itt}, \alpha<<\beta')
+ title('{\itAe}^{-\alpha\itt}sin\beta{\itt}, \alpha<<\beta, \beta>>\alpha, \alpha<\beta, \beta>\alpha, b>a')
xlabel('Time \musec.')
- ylabel('Amplitude')
-
- description = 'Decaying harmonic oscillation with \TeX{} title.';
- extraOpts = {};
+ ylabel('Amplitude |X|')
end
% =========================================================================
-function [description, extraOpts] = texcolor()
+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'])
-
- description = 'Multi-colored text using \TeX{} commands.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = textext()
+function [stat] = textext()
+ stat.description = 'Formatted text and special characters using \TeX{}.';
+ stat.unreliable = isMATLAB();
+
% 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' };
@@ -1648,14 +1402,21 @@ function [description, extraOpts] = textext()
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' )
-
- description = 'Formatted text and special characters using \TeX{}.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = texrandom()
+function [stat] = texrandom()
+ stat.description = 'Random TeX symbols';
+ stat.unreliable = true; % due to randomness
+
+ try
+ rng(42); %fix seed
+ %TODO: fully test tex conversion instead of a random subsample!
+ catch
+ warning('testfuncs:texrandom','Cannot fix seed for random generator!');
+ end
num = 20; % number of symbols per line
symbols = {'\it', '\bf', '\rm', '\sl', ...
@@ -1701,7 +1462,7 @@ function [description, extraOpts] = texrandom()
% \fontname{Times} etc. isn't included
% \fontsize{12} etc. isn't included
- switch getEnvironment()
+ switch getEnvironment
case 'MATLAB'
% MATLAB expects tilde and ampersand to be un-escaped and backslashes
% to be escaped
@@ -1911,25 +1672,31 @@ function [description, extraOpts] = texrandom()
end
title('Random TeX symbols \\\{\}\_\^$%#&')
-
- description = 'Random TeX symbols';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = latexmath1()
- % Adapted from an example at
- % http://www.mathworks.com/help/techdoc/ref/text_props.html#Interpreter
- axes
- title( '\omega\subseteq\Omega' );
- text( 0.5, 0.5, '$$\int_0^x\!\int_{\Omega} dF(u,v) d\omega$$', ...
- 'Interpreter', 'latex', ...
- 'FontSize', 16 )
+function [stat] = latexInterpreter()
+ stat.description = '\LaTeX{} interpreter test (display math not working)';
+ stat.issues = 448;
+ stat.unreliable = isMATLAB('>=',[8,4]); %FIXME: investigate
- description = 'A formula typeset using the \LaTeX{} interpreter.';
- extraOpts = {};
+ 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 [description, extraOpts] = latexmath2()
+function [stat] = latexmath2()
+ stat.description = 'Some nice-looking formulas typeset using the \LaTeX{} interpreter.';
+ stat.issues = 637;
+ stat.unreliable = isMATLAB('<',[8,4]); %FIXME: `at` is inconsistent, see #552
+
% Adapted from an example at
% http://www.mathworks.com/help/techdoc/creating_plots/f0-4741.html#bq558_t
set(gcf, 'color', 'white')
@@ -1937,11 +1704,6 @@ function [description, extraOpts] = latexmath2()
set(gcf, 'position', [2 2 4 6.5])
set(gca, 'visible', 'off')
- % Note: Most likely due to a bug in matlab2tikz the pgfplots output will
- % appear empty even though the LaTeX strings are contained in the
- % output file. This is because the following (or something like it)
- % is missing from the axis environment properties:
- % xmin=0, xmax=4, ymin=-1, ymax=6
% 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], ...
@@ -1953,53 +1715,54 @@ function [description, extraOpts] = latexmath2()
% [ '$$\left[ {\matrix{\cos(\phi) & -\sin(\phi) \cr' ...
% '\sin(\phi) & \cos(\phi) \cr}} \right]' ...
% '\left[ \matrix{x \cr y} \right]$$' ]);
- h(3) = text( 'units', 'inch', 'position', [.2 3], ...
+ 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', 'inch', 'position', [.2 2], ...
+ h(4) = text( 'units', 'inches', 'position', [.2 2], ...
'fontsize', 14, 'interpreter', 'latex', 'string', ...
- '$$e = \sum_{k=0}^\infty {1 \over {k!} } $$' );
- h(5) = text( 'units', 'inch', 'position', [.2 1], ...
+ '$$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 {1 \over 2}' ...
+ [ '$$m \ddot y = -m g + C_D \cdot {\frac{1}{2}}' ...
'\rho {\dot y}^2 \cdot A$$' ]);
- h(6) = text( 'units', 'inch', 'position', [.2 0], ...
+ 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}$$' );
-
- % TODO: On processing the matlab2tikz_acidtest output, LaTeX complains
- % about the use of \over:
- % Package amsmath Warning: Foreign command \over;
- % (amsmath) \frac or \genfrac should be used instead
-
- description = 'Some nice-looking formulas typeset using the \LaTeX{} interpreter.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = parameterCurve3d()
+function [stat] = parameterCurve3d()
+ stat.description = 'Parameter curve in 3D with text boxes in-/outise axis.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
+ stat.issues = 378;
+
ezplot3('sin(t)','cos(t)','t',[0,6*pi]);
- text(0.5, 0.5, 10, 'abs');
- description = 'Parameter curve in 3D.';
- extraOpts = {};
+ text(0.5, 0.5, 10, 'text inside axis limits');
+ text(0.0, 1.5, 10, 'text outside axis (will be removed by cleanfigure())');
end
% =========================================================================
-function [description, extraOpts] = parameterSurf()
+function [stat] = parameterSurf()
+ stat.description = 'Parameter and surface plot.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
if ~exist('TriScatteredInterp')
- fprintf( 'TriScatteredInterp() not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'TriScatteredInterp() not found. Skipping.\n\n' );
+ stat.skip = true;
return;
end
- x = rand(100,1)*4 - 2;
- y = rand(100,1)*4 - 2;
+ 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,'nearest');
- % F = TriScatteredInterp(x,y,z,'natural');
F = TriScatteredInterp(x,y,z,'linear');
% Evaluate the interpolant at the locations (qx, qy), qz
@@ -2013,17 +1776,14 @@ function [description, extraOpts] = parameterSurf()
plot3(x,y,z,'o')
view(gca,[-69 14]);
hold off
-
- description = 'Parameter and surface plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = fill3plot()
+function [stat] = fill3plot()
+ stat.description = 'fill3 plot.';
if ~exist('fill3','builtin')
- fprintf( 'fill3() not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'fill3() not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
@@ -2037,24 +1797,23 @@ function [description, extraOpts] = fill3plot()
fill3(x1,x2,h,'k');
view(45,22.5);
box on;
-
- description = 'fill3 plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = rectanglePlot()
+function [stat] = rectanglePlot()
+ stat.description = 'Rectangle handle.';
+ stat.unreliable = isMATLAB('>=',[8,4]); %FIXME: investigate
+
rectangle('Position', [0.59,0.35,3.75,1.37],...
'Curvature', [0.8,0.4],...
'LineWidth', 2, ...
'LineStyle', '--' ...
);
daspect([1,1,1]);
-
- description = 'Rectangle handle.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = herrorbarPlot()
+function [stat] = herrorbarPlot()
+ stat.description = 'herrorbar plot.';
+
hold on;
X = 1:10;
Y = 1:10;
@@ -2070,17 +1829,15 @@ function [description, extraOpts] = herrorbarPlot()
set(h, 'color', [0 1 0]);
end
legend([h1 h2], {'test1', 'test2'})
-
- description = 'herrorbar plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = hist3d()
+function [stat] = hist3d()
+ stat.description = '3D histogram plot.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
if ~exist('hist3','builtin') && isempty(which('hist3'))
- fprintf( 'Statistics toolbox not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'Statistics toolbox not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
@@ -2104,18 +1861,15 @@ function [description, extraOpts] = hist3d()
% n = hist3(dat); % Extract histogram data;
% % default to 10x10 bins
% view([-37.5, 30]);
-
- description = '3D histogram plot.';
- extraOpts = {};
-
end
% =========================================================================
-function [description, extraOpts] = myBoxplot()
+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. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'Statistics toolbox not found. Skipping.\n\n' );
+ stat.skip = true;
return
end
@@ -2132,21 +1886,20 @@ function [description, extraOpts] = myBoxplot()
];
boxplot(errors);
-
- description = 'Boxplot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = areaPlot()
-
- area(1:3, rand(3));
- legend('foo', 'bar', 'foobar');
+function [stat] = areaPlot()
+ stat.description = 'Area plot.';
- description = 'Area plot.';
- extraOpts = {};
+ M = magic(5);
+ M = M(1:3,2:4);
+ h = area(1:3, M);
+ legend(h([1,3]),'foo', 'foobar');
end
% =========================================================================
-function [description, extraOpts] = customLegend()
+function [stat] = customLegend()
+ stat.description = 'Custom legend.';
+ stat.unreliable = isMATLAB('<', [8,4]) || isOctave; %FIXME: investigate
x = -pi:pi/10:pi;
y = tan(sin(x)) - sin(tan(x));
@@ -2156,29 +1909,24 @@ function [description, extraOpts] = customLegend()
set(lh,'color','g')
set(lh,'edgecolor','r')
set(lh, 'position',[.5 .6 .1 .05])
-
- description = 'Custom legend.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = pixelLegend()
+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, 'position', [100 200 65 42])
-
- description = 'Legend with pixel position.';
- extraOpts = {};
+ set(lh, 'units','pixels','position', [100 200 65 42])
end
% =========================================================================
-function [description, extraOpts] = croppedImage()
+function [stat] = croppedImage()
+ stat.description = 'Custom legend.';
if ~exist('flujet.mat','file')
- fprintf( 'flujet data set not found. Abort.\n\n' );
- description = [];
- extraOpts = {};
+ fprintf( 'flujet data set not found. Skipping.\n\n' );
+ stat.skip = true;
return;
end
@@ -2192,28 +1940,13 @@ function [description, extraOpts] = croppedImage()
% colorbar at top
colorbar('north');
set(gca,'Units','normalized');
-
- description = 'Custom legend.';
- extraOpts = {};
-end
-% =========================================================================
-function [description, extraOpts] = doubleAxes()
-
- ah = axes;
- set(ah,'Units','pixels');
- set(ah,'Position',[18*4 18*3 114*4 114*3]);
- ah2 = axes;
- set(ah2,'units','pixels')
- set(ah2,'position',[18*4 18*3 114*4 114*3])
- grid(ah2,'on')
- set(ah2,'GridLineStyle','-')
-
- description = 'Double axes.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = pColorPlot()
+function [stat] = pColorPlot()
+ stat.description = 'pcolor() plot.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
+ ylim([-1 1]); xlim([-1 1]); hold on; % prevent error on octave
n = 6;
r = (0:n)'/n;
theta = pi*(-n:n)/n;
@@ -2223,11 +1956,10 @@ function [description, extraOpts] = pColorPlot()
pcolor(X,Y,C)
axis equal tight
- description = 'pcolor() plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = multiplePatches()
+function [stat] = multiplePatches()
+ stat.description = 'Multiple patches.';
xdata = [2 2 0 2 5;
2 8 2 4 5;
@@ -2241,12 +1973,17 @@ function [description, extraOpts] = multiplePatches()
p = patch(xdata,ydata,cdata,'Marker','o',...
'MarkerFaceColor','flat',...
'FaceColor','none');
-
- description = 'Multiple patches.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = hgTransformPlot()
+function [stat] = hgTransformPlot()
+ stat.description = 'hgtransform() plot.';
+ stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate
+
+ 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.
@@ -2255,7 +1992,7 @@ function [description, extraOpts] = hgTransformPlot()
grid on;
axis equal;
- [x y z] = cylinder([.2 0]);
+ [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');
@@ -2272,22 +2009,21 @@ function [description, extraOpts] = hgTransformPlot()
Txy = makehgtform('translate',[-1.5 -1.5 0]);
set(t2,'Matrix',Txy)
drawnow
-
- description = 'hgtransform() plot.';
- extraOpts = {};
end
% =========================================================================
-function [description, extraOpts] = logbaseline()
+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')
-
- description = 'Logplot with modified baseline.';
- extraOpts = {};
+ set(gca,'YScale','log');
end
% =========================================================================
-function [description, extraOpts] = alphaImage()
+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);
@@ -2295,50 +2031,552 @@ function [description, extraOpts] = alphaImage()
set(h_imsc, 'AlphaData', double(~mask));
set(h_imsc, 'AlphaDataMapping', 'scaled');
set(gca, 'ALim', [-1,1]);
-
- description = 'Image with alpha channel.';
- extraOpts = {};
+ 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 env = getEnvironment
- if ~isempty(ver('MATLAB'))
- env = 'MATLAB';
- elseif ~isempty(ver('Octave'))
- env = 'Octave';
- else
- env = [];
+function stat = annotationAll()
+ stat.description = 'All possible annotations with edited properties';
+ stat.unreliable = isMATLAB('<', [8,4]); % 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
+ annotation('arrow',[0.25 0.22], [0.96 0.05], 'LineStyle','-.',...
+ 'HeadStyle','cback2');
+
+ % 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
+ annotation('ellipse', [0.70 0.44 0.15 0.51], 'Color',[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 [below, noenv] = isVersionBelow ( env, threshMajor, threshMinor )
- % get version string for `env' by iterating over all toolboxes
- versionData = ver;
- versionString = '';
- for k = 1:max(size(versionData))
- if strcmp( versionData(k).Name, env )
- % found it: store and exit the loop
- versionString = versionData(k).Version;
- break
- end
+function [stat] = annotationSubplots()
+ stat.description = 'Annotated and unaligned subplots';
+ stat.unreliable = isMATLAB; % FIXME: investigate
+
+ if isempty(which('annotation'))
+ fprintf( 'annotation() not found. Skipping.\n\n' );
+ stat.skip = true;
+ return;
end
- if isempty( versionString )
- % couldn't find `env'
- below = true;
- noenv = true;
- return
+ 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
- majorVer = str2double(regexprep( versionString, '^(\d+)\..*', '$1' ));
- minorVer = str2double(regexprep( versionString, '^\d+\.(\d+\.?\d*)[^\d]*.*', '$1' ));
+ 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; % FIXME: investigate
+
+ if ~exist('annotation')
+ fprintf( 'annotation() not found. Skipping.\n\n' );
+ stat.skip = true;
+ return;
+ end
- if (majorVer < threshMajor) || (majorVer == threshMajor && minorVer < threshMinor)
- % version of `env' is below threshold
- below = true;
- else
- % version of `env' is same as or above threshold
- below = false;
+ 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
+ annotation(gcf,'textbox',...
+ [0.71643086172344 0.195876288659794 0.10020240480962 0.209240982129118],...
+ 'String',{'Multiple Lines due to fixed width'},...
+ 'FitBoxToText','off',...
+ 'Units','characters');
+
+ % 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 = isMATLAB('>=', [8,4]) || isOctave; % R2014b and newer
+end
+function [stat] = imageOrientation_PNG()
+% Run test and save pictures as external PNGs
+ [stat] = imageOrientation(true);
+ stat.unreliable = isMATLAB('>=', [8,4]) || isOctave; % R2014b and newer
+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;
+ 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 = isMATLAB || 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 || isMATLAB; %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('>=', [8,4]);
+ % FIXME this test is unreliable because the x/y lims of `ax2` are not set
+ % explicitly. We should not set them explicitly, rather implement #591
+ 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
- noenv = false;
+ 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
% =========================================================================
diff --git a/test/suites/issues.m b/test/suites/issues.m
new file mode 100644
index 0000000..79dc69f
--- /dev/null
+++ b/test/suites/issues.m
@@ -0,0 +1,43 @@
+function [ status ] = issues( k )
+%ISSUES M2T Test cases related to issues
+%
+% Issue-related test cases for matlab2tikz
+%
+% See also: ACID, matlab2tikz_acidtest
+ testfunction_handles = {
+ @scatter3Plot3
+ };
+
+ numFunctions = length( testfunction_handles );
+
+ if (k<=0)
+ status = testfunction_handles;
+ return; % This is used for querying numFunctions.
+
+ elseif (k<=numFunctions)
+ status = testfunction_handles{k}();
+ status.function = func2str(testfunction_handles{k});
+
+ else
+ error('issues:outOfBounds', ...
+ 'Out of bounds (number of testfunctions=%d)', numFunctions);
+ end
+
+end
+
+% =========================================================================
+function [stat] = scatter3Plot3()
+ stat.description = 'Scatter3 plot with 2 colors';
+ stat.issues = 292;
+
+ hold on;
+ x = sin(1:5);
+ y = cos(3.4 *(1:5));
+ z = x.*y;
+ scatter3(x,y,z,150,...
+ 'MarkerEdgeColor','none','MarkerFaceColor','k');
+ scatter3(-x,y,z,150,...
+ 'MarkerEdgeColor','none','MarkerFaceColor','b');
+end
+
+% =========================================================================
diff --git a/test/suites/private/getEnvironment.m b/test/suites/private/getEnvironment.m
new file mode 100644
index 0000000..86f8351
--- /dev/null
+++ b/test/suites/private/getEnvironment.m
@@ -0,0 +1,25 @@
+function [env, versionString] = getEnvironment()
+% Checks if we are in MATLAB or Octave.
+ persistent cache
+
+ alternatives = {'MATLAB', 'Octave'};
+ if isempty(cache)
+ for iCase = 1:numel(alternatives)
+ env = alternatives{iCase};
+ vData = ver(env);
+ if ~isempty(vData) % found the right environment
+ versionString = vData.Version;
+ % store in cache
+ cache.env = env;
+ cache.versionString = versionString;
+ return;
+ end
+ end
+ % fall-back values
+ env = '';
+ versionString = '';
+ else
+ env = cache.env;
+ versionString = cache.versionString;
+ end
+end
\ No newline at end of file
diff --git a/test/herrorbar.m b/test/suites/private/herrorbar.m
similarity index 83%
rename from test/herrorbar.m
rename to test/suites/private/herrorbar.m
index c0a6935..8e64067 100644
--- a/test/herrorbar.m
+++ b/test/suites/private/herrorbar.m
@@ -21,7 +21,7 @@ function hh = herrorbar(x, y, l, u, symbol)
% herrorbar(x,y,e)
% draws symmetric horizontal error bars of unit standard deviation.
%
-% This code is based on ERRORBAR provided in MATLAB.
+% This code is based on ERRORBAR provided in MATLAB.
%
% See also ERRORBAR
@@ -31,7 +31,7 @@ function hh = herrorbar(x, y, l, u, symbol)
% 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.
+% 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
@@ -44,11 +44,11 @@ if min(size(x))==1,
x = x(:);
y = y(:);
if nargin > 2,
- if ~isstr(l),
+ if ~ischar(l),
l = l(:);
end
if nargin > 3
- if ~isstr(u)
+ if ~ischar(u)
u = u(:);
end
end
@@ -58,7 +58,7 @@ else
end
if nargin == 3
- if ~isstr(l)
+ if ~ischar(l)
u = l;
symbol = '-';
else
@@ -72,7 +72,7 @@ if nargin == 3
end
if nargin == 4
- if isstr(u),
+ if ischar(u),
symbol = u;
u = l;
else
@@ -92,11 +92,11 @@ end
u = abs(u);
l = abs(l);
-if isstr(x) | isstr(y) | isstr(u) | isstr(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)),
+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
@@ -143,9 +143,10 @@ yb(9:9:end,:) = NaN;
[ls,col,mark,msg] = colstyle(symbol); if ~isempty(msg), error(msg); end
symbol = [ls mark col]; % Use marker only on data part
esymbol = ['-' col]; % Make sure bars are solid
-
-h = plot(xb,yb,esymbol); hold on
-h = [h;plot(x,y,symbol)];
+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
diff --git a/test/suites/private/isEnvironment.m b/test/suites/private/isEnvironment.m
new file mode 100644
index 0000000..b5f315a
--- /dev/null
+++ b/test/suites/private/isEnvironment.m
@@ -0,0 +1,46 @@
+function bool = isEnvironment(wantedEnvironment, varargin)
+% ISENVIRONMENT check for a particular environment (MATLAB/Octave)
+%
+% This function returns TRUE when it is run within the "wantedEnvironment"
+% (e.g. MATLAB or Octave). This environment can be tested to be a particular
+% version or be older/newer than a specified version.
+%
+% Usage:
+%
+% ISENVIRONMENT(ENV)
+% ISENVIRONMENT(ENV, VERSION)
+% ISENVIRONMENT(ENV, OP, VERSION)
+%
+% Parameters:
+% - `ENV`: the expected environment (e.g. 'MATLAB' or 'Octave')
+% - `VERSION`: a version number or string to compare against
+% e.g. "3.4" or equivalently [3,4]
+% - `OP`: comparison operator (e.g. '==', '<=', '<', ...) to define a range
+% of version numbers that return a TRUE value
+%
+% When `OP` is not specified, "==" is used.
+% When no `VERSION` is specified, all versions pass the check.
+%
+% See also: isMATLAB, isOctave, versionCompare
+ [env, thisVersion] = getEnvironment();
+ bool = strcmpi(env, wantedEnvironment);
+
+ switch numel(varargin)
+ case 0 % nothing to be done
+ return
+
+ case 1 % check equality
+ version = varargin{1};
+ operator = '==';
+ bool = bool && versionCompare(thisVersion, operator, version);
+
+ case 2
+ operator = varargin{1};
+ version = varargin{2};
+ bool = bool && versionCompare(thisVersion, operator, version);
+
+ otherwise
+ error('isEnvironment:BadNumberOfArguments', ...
+ '"isEnvironment" was called with an incorrect number of arguments.');
+ end
+end
diff --git a/test/suites/private/isMATLAB.m b/test/suites/private/isMATLAB.m
new file mode 100644
index 0000000..962ff77
--- /dev/null
+++ b/test/suites/private/isMATLAB.m
@@ -0,0 +1,4 @@
+function bool = isMATLAB(varargin)
+%ISMATLAB Determines whether (a certain) version of MATLAB is being used
+% See also: isEnvironment, isOctave
+bool = isEnvironment('MATLAB', varargin{:});
diff --git a/test/suites/private/isOctave.m b/test/suites/private/isOctave.m
new file mode 100644
index 0000000..28936e3
--- /dev/null
+++ b/test/suites/private/isOctave.m
@@ -0,0 +1,5 @@
+function bool = isOctave(varargin)
+%ISOCTAVE Determines whether (a certain) version of Octave is being used
+%
+% See also: isEnvironment, isMATLAB
+bool = isEnvironment('Octave', varargin{:});
diff --git a/test/suites/private/isVersionBelow.m b/test/suites/private/isVersionBelow.m
new file mode 100644
index 0000000..c078b26
--- /dev/null
+++ b/test/suites/private/isVersionBelow.m
@@ -0,0 +1,38 @@
+function isBelow = isVersionBelow(versionA, versionB)
+% Checks if versionA is smaller than versionB
+ vA = versionArray(versionA);
+ vB = versionArray(versionB);
+ n = min(length(vA), length(vB));
+ deltaAB = vA(1:n) - vB(1:n);
+ difference = find(deltaAB, 1, 'first');
+ if isempty(difference)
+ isBelow = false; % equal versions
+ else
+ isBelow = (deltaAB(difference) < 0);
+ end
+end
+% ==============================================================================
+function arr = versionArray(str)
+% Converts a version string to an array.
+ if ischar(str)
+ % Translate version string from '2.62.8.1' to [2; 62; 8; 1].
+ switch getEnvironment
+ case 'MATLAB'
+ split = regexp(str, '\.', 'split'); % compatibility MATLAB < R2013a
+ case 'Octave'
+ split = strsplit(str, '.');
+ otherwise
+ errorUnknownEnvironment();
+ end
+ arr = str2num(char(split)); %#ok
+ else
+ arr = str;
+ end
+ arr = arr(:)';
+end
+% ==============================================================================
+function errorUnknownEnvironment()
+error('matlab2tikz:unknownEnvironment',...
+ 'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment);
+end
+% ==============================================================================
diff --git a/test/suites/private/versionCompare.m b/test/suites/private/versionCompare.m
new file mode 100644
index 0000000..6ef6096
--- /dev/null
+++ b/test/suites/private/versionCompare.m
@@ -0,0 +1,20 @@
+function bool = versionCompare( vA, operator, vB )
+%VERSIONCOMPARE Performs a version comparison operation
+ switch operator
+ case '<'
+ bool = isVersionBelow(vA, vB);
+ case '>'
+ bool = isVersionBelow(vB, vA);
+ case {'<=', '=<'}
+ bool = ~isVersionBelow(vB, vA);
+ case {'>=', '=>'}
+ bool = ~isVersionBelow(vA, vB);
+ case {'=', '=='}
+ bool = ~isVersionBelow(vA, vB) && ~isVersionBelow(vB, vA);
+ case {'~=', '!='}
+ bool = isVersionBelow(vA, vB) || isVersionBelow(vB, vA);
+ otherwise
+ error('versionCompare:UnknownOperator',...
+ '"%s" is not a known comparison operator', operator);
+ end
+end
diff --git a/test/suites/testPatches.m b/test/suites/testPatches.m
new file mode 100644
index 0000000..6cd9454
--- /dev/null
+++ b/test/suites/testPatches.m
@@ -0,0 +1,121 @@
+function status = testPatches(k)
+% TESTPATCHES Test suite for patches
+%
+% See also: ACID, matlab2tikz_acidtest
+
+testfunction_handles = {
+ @patch01;
+ @patch02;
+ @patch03;
+ @patch04;
+ @patch05;
+ @patch06;
+ @patch07;
+ @patch08;
+ };
+
+numFunctions = length( testfunction_handles );
+
+if nargin < 1 || isempty(k) || k <= 0
+ status = testfunction_handles;
+ return; % This is used for querying numFunctions.
+
+elseif (k<=numFunctions)
+ status = testfunction_handles{k}();
+ status.function = func2str(testfunction_handles{k});
+
+else
+ error('patchTests:outOfBounds', ...
+ 'Out of bounds (number of testfunctions=%d)', numFunctions);
+end
+
+end
+
+% =========================================================================
+function p = patch00()
+% DO NOT INCLUDE IN ACID LIST
+% Base patch plot for following tests
+xdata = [2 2 0 2 5; 2 8 2 4 5; 8 8 2 4 8];
+ydata = [4 4 4 2 0; 8 4 6 2 2; 4 0 4 0 0];
+zdata = ones(3,5)*2;
+p = patch(xdata,ydata,zdata);
+end
+% =========================================================================
+function stat = patch01()
+stat.description = 'Set face color red';
+
+p = patch00();
+set(p,'FaceColor','r')
+end
+% =========================================================================
+function stat = patch02()
+stat.description = 'Flat face colors scaled in clim [0,40]';
+
+p = patch00();
+set(gca,'CLim',[0 40])
+cdata = [15 30 25 2 60];
+set(p,'FaceColor','flat','CData',cdata,'CDataMapping','scaled')
+end
+% =========================================================================
+function stat = patch03()
+stat.description = 'Flat face colors direct in clim [0,40]';
+
+p = patch00();
+set(gca,'CLim',[0 40])
+cdata = [15 30 25 2 60];
+set(p,'FaceColor','flat','CData',cdata,'CDataMapping','direct')
+end
+% =========================================================================
+function stat = patch04()
+stat.description = 'Flat face colors with 3D (truecolor) CData';
+
+p = patch00();
+cdata(:,:,1) = [0 0 1 0 0.8];
+cdata(:,:,2) = [0 0 0 0 0.8];
+cdata(:,:,3) = [1 1 1 0 0.8];
+set(p,'FaceColor','flat','CData',cdata)
+end
+% =========================================================================
+function stat = patch05()
+stat.description = 'Flat face color, scaled edge colors in clim [0,40]';
+
+p = patch00();
+set(gca,'CLim',[0 40])
+cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42];
+set(p,'FaceColor','flat','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','scaled')
+end
+% =========================================================================
+function stat = patch06()
+stat.description = 'Flat face color, direct edge colors in clim [0,40]';
+
+p = patch00();
+set(gca,'CLim',[0 40])
+cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42];
+set(p,'FaceColor','flat','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','direct')
+end
+% =========================================================================
+function stat = patch07()
+stat.description = 'Flat face color with 3D CData and interp edge colors';
+
+p = patch00();
+cdata(:,:,1) = [0 0 1 0 0.8;
+ 0 0 1 0.2 0.6;
+ 0 1 0 0.4 1];
+cdata(:,:,2) = [0 0 0 0 0.8;
+ 1 1 1 0.2 0.6;
+ 1 0 0 0.4 0];
+cdata(:,:,3) = [1 1 1 0 0.8;
+ 0 1 0 0.2 0.6;
+ 1 0 1 0.4 0];
+set(p,'FaceColor','flat','CData',cdata,'EdgeColor','interp','LineWidth',5)
+end
+% =========================================================================
+function stat = patch08()
+stat.description = 'Interp face colors, flat edges, scaled CData in clims [0,40]';
+
+p = patch00();
+set(gca,'CLim',[0 40])
+cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42];
+set(p,'FaceColor','interp','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','scaled')
+end
+% =========================================================================
diff --git a/test/suites/testSurfshader.m b/test/suites/testSurfshader.m
new file mode 100644
index 0000000..15ac25d
--- /dev/null
+++ b/test/suites/testSurfshader.m
@@ -0,0 +1,102 @@
+function status = testSurfshader(k)
+% TESTSURFSHADER Test suite for Surf/mesh shaders (coloring)
+%
+% See also: ACID, matlab2tikz_acidtest
+
+ testfunction_handles = {
+ @surfShader1;
+ @surfShader2;
+ @surfShader3;
+ @surfShader4;
+ @surfShader5;
+ @surfNoShader;
+ @surfNoPlot;
+ @surfMeshInterp;
+ @surfMeshRGB;
+ };
+
+ numFunctions = length( testfunction_handles );
+
+ if nargin < 1 || isempty(k) || k <= 0
+ status = testfunction_handles;
+ return; % This is used for querying numFunctions.
+
+ elseif (k<=numFunctions)
+ status = testfunction_handles{k}();
+ status.function = func2str(testfunction_handles{k});
+
+ else
+ error('patchTests:outOfBounds', ...
+ 'Out of bounds (number of testfunctions=%d)', numFunctions);
+ end
+
+end
+
+% =========================================================================
+function [stat] = surfShader1()
+ stat.description = 'shader=flat/(flat mean) | Fc: flat | Ec: none';
+
+ [X,Y,Z] = peaks(5);
+ surf(X,Y,Z,'FaceColor','flat','EdgeColor','none')
+end
+% =========================================================================
+function [stat] = surfShader2()
+ stat.description = 'shader=interp | Fc: interp | Ec: none';
+
+ [X,Y,Z] = peaks(5);
+ surf(X,Y,Z,'FaceColor','interp','EdgeColor','none')
+end
+% =========================================================================
+function [stat] = surfShader3()
+ stat.description = 'shader=faceted | Fc: flat | Ec: RGB';
+
+ [X,Y,Z] = peaks(5);
+ surf(X,Y,Z,'FaceColor','flat','EdgeColor','green')
+end
+% =========================================================================
+function [stat] = surfShader4()
+stat.description = 'shader=faceted | Fc: RGB | Ec: interp';
+if isMATLAB('<', [8,4]); %R2014a and older
+ warning('m2t:ACID:surfShader4',...
+ 'The MATLAB EPS export may behave strangely for this case');
+end
+
+[X,Y,Z] = peaks(5);
+surf(X,Y,Z,'FaceColor','blue','EdgeColor','interp')
+end
+% =========================================================================
+function [stat] = surfShader5()
+stat.description = 'shader=faceted interp | Fc: interp | Ec: flat';
+
+[X,Y,Z] = peaks(5);
+surf(X,Y,Z,'FaceColor','interp','EdgeColor','flat')
+end
+% =========================================================================
+function [stat] = surfNoShader()
+stat.description = 'no shader | Fc: RGB | Ec: RGB';
+
+[X,Y,Z] = peaks(5);
+surf(X,Y,Z,'FaceColor','blue','EdgeColor','yellow')
+end
+% =========================================================================
+function [stat] = surfNoPlot()
+stat.description = 'no plot | Fc: none | Ec: none';
+
+[X,Y,Z] = peaks(5);
+surf(X,Y,Z,'FaceColor','none','EdgeColor','none')
+end
+% =========================================================================
+function [stat] = surfMeshInterp()
+stat.description = 'mesh | Fc: none | Ec: interp';
+
+[X,Y,Z] = peaks(5);
+surf(X,Y,Z,'FaceColor','none','EdgeColor','interp')
+end
+% =========================================================================
+function [stat] = surfMeshRGB()
+stat.description = 'mesh | Fc: none | Ec: RGB';
+
+[X,Y,Z] = peaks(5);
+surf(X,Y,Z,'FaceColor','none','EdgeColor','green')
+end
+% =========================================================================
diff --git a/test/testGraphical.m b/test/testGraphical.m
new file mode 100644
index 0000000..d0a0eb6
--- /dev/null
+++ b/test/testGraphical.m
@@ -0,0 +1,47 @@
+function [ status ] = testGraphical( varargin )
+%TESTGRAPHICAL Runs the M2T test suite to produce graphical output
+%
+% This is quite a thin wrapper around testMatlab2tikz to run the test suite to
+% produce a PDF side-by-side report.
+%
+% Its allowed arguments are the same as those of testMatlab2tikz.
+%
+% Usage:
+%
+% status = testGraphical(...) % gives programmatical access to the data
+%
+% testGraphical(...); % automatically invokes makeLatexReport afterwards
+%
+% See also: testMatlab2tikz, testHeadless, makeLatexReport
+
+ cwd = initializeWorkingDirectory();
+ status = testMatlab2tikz('actionsToExecute', @actionsToExecute, ...
+ varargin{:});
+
+ if nargout == 0
+ makeLatexReport(status);
+ end
+
+ cd(cwd); % return to previous working directory
+end
+% ==============================================================================
+function status = actionsToExecute(status, ipp)
+ status = execute_plot_stage(status, ipp);
+
+ if status.skip
+ return
+ end
+
+ status = execute_save_stage(status, ipp);
+ status = execute_tikz_stage(status, ipp);
+ %status = execute_hash_stage(status, ipp); %cannot work with files in
+ %standalone mode!
+ status = execute_type_stage(status, ipp);
+
+ if ~status.closeall && ~isempty(status.plotStage.fig_handle)
+ close(status.plotStage.fig_handle);
+ else
+ close all;
+ end
+end
+% ==============================================================================
diff --git a/test/testHeadless.m b/test/testHeadless.m
new file mode 100644
index 0000000..f85f5ab
--- /dev/null
+++ b/test/testHeadless.m
@@ -0,0 +1,62 @@
+function [ status ] = testHeadless( varargin )
+%TESTGRAPHICAL Runs the M2T test suite without graphical output
+%
+% This is quite a thin wrapper around testMatlab2tikz to run the test suite to
+% produce a textual report and checks for regressions by checking the MD5 hash
+% of the output
+%
+% Its allowed arguments are the same as those of testMatlab2tikz.
+%
+% Usage:
+%
+% status = TESTHEADLESS(...) % gives programmatical access to the data
+%
+% TESTHEADLESS(...); % automatically invokes makeTravisReport afterwards
+%
+% See also: testMatlab2tikz, testGraphical, makeTravisReport
+
+% The width and height are specified to circumvent different DPIs in developer
+% machines. The float format reduces the probability that numerical differences
+% in the order of numerical precision disrupt the output.
+ extraOptions = {'width' ,'\figureWidth', ...
+ 'height','\figureHeight',...
+ 'floatFormat', '%4g', ... % see #604
+ 'extraCode',{ ...
+ '\newlength\figureHeight \setlength{\figureHeight}{6cm}', ...
+ '\newlength\figureWidth \setlength{\figureWidth}{10cm}'}
+ };
+
+ cwd = initializeWorkingDirectory();
+ status = testMatlab2tikz('extraOptions', extraOptions, ...
+ 'actionsToExecute', @actionsToExecute, ...
+ varargin{:});
+
+ if nargout == 0
+ makeTravisReport(status);
+ end
+
+ cd(cwd); % return to previous working directory
+end
+% ==============================================================================
+function status = actionsToExecute(status, ipp)
+ status = execute_plot_stage(status, ipp);
+
+ if status.skip
+ return
+ end
+
+ status = execute_tikz_stage(status, ipp);
+ status = execute_hash_stage(status, ipp);
+ status = execute_type_stage(status, ipp);
+
+ if ~status.closeall && ~isempty(status.plotStage.fig_handle)
+ try
+ close(status.plotStage.fig_handle);
+ catch
+ close('all');
+ end
+ else
+ close all;
+ end
+end
+% ==============================================================================
diff --git a/test/tex/Makefile b/test/tex/Makefile
index 481ae59..8980915 100644
--- a/test/tex/Makefile
+++ b/test/tex/Makefile
@@ -1,22 +1,13 @@
# ./Makefile
-# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
-
ECHOCMD:=/bin/echo -e
-PDFLATEX:=lualatex --shell-escape
-
-# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
-
+LATEX:=lualatex --shell-escape
TARGET:=acid
-# ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
-#
-
-default: main
-
-#main:
main:
- @$(PDFLATEX) $(TARGET)
+ cd ../data/reference/ && $(MAKE)
+ cd ../data/converted/ && $(MAKE)
+ @$(LATEX) $(TARGET)
.PHONY: clean
@@ -33,7 +24,10 @@ clean:
$(TARGET).ps \
missfont.log
@rm -f *~
+ cd ../data/reference/ && $(MAKE) clean
+ cd ../data/converted/ && $(MAKE) clean
distclean: clean
- @rm -f ../data/*
@rm -f $(TARGET).tex
+ cd ../data/reference/ && $(MAKE) distclean
+ cd ../data/converted/ && $(MAKE) distclean
diff --git a/version-0.4.7 b/version-0.4.7
deleted file mode 100644
index 5e89b8a..0000000
--- a/version-0.4.7
+++ /dev/null
@@ -1,6 +0,0 @@
-This file is there to make sure that the HTML page
-
- http://www.mathworks.de/matlabcentral/fileexchange/22022-matlab2tikz/all_files
-
-contains the version number in clear text. This is used
-by the automatic updater.
--
Alioth's /home/groups/pkg-octave/bin/git-commit-notice on /srv/git.debian.org/git/pkg-octave/matlab2tikz.git
More information about the Pkg-octave-commit
mailing list