[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