changeset 22035:634fbedbfb5b

Additional functionality for isosurface.m (bug #46946) * isosurface.m: Change input argument checking to be Matlab compatible. Add support for option "noshare" (default behavior changed for Matlab compatibility). * __calc_isovalue_from_data__.m: Add private function to get "good" iso value from data. * __unite_shared_vertices__.m: Add private funtion to find and unite shared vertices for a patch.
author Markus Muetzel <markus.muetzel@gmx.de>
date Sun, 03 Jul 2016 23:39:36 +0200
parents 8df31c24dce3
children a2c29df93df7
files scripts/plot/draw/isosurface.m scripts/plot/draw/module.mk scripts/plot/draw/private/__calc_isovalue_from_data__.m scripts/plot/draw/private/__unite_shared_vertices__.m
diffstat 4 files changed, 504 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/draw/isosurface.m	Mon Jul 04 07:39:02 2016 -0700
+++ b/scripts/plot/draw/isosurface.m	Sun Jul 03 23:39:36 2016 +0200
@@ -18,12 +18,15 @@
 
 ## -*- texinfo -*-
 ## @deftypefn  {} {[@var{fv}] =} isosurface (@var{val}, @var{iso})
+## @deftypefnx {} {[@var{fv}] =} isosurface (@var{val})
 ## @deftypefnx {} {[@var{fv}] =} isosurface (@var{x}, @var{y}, @var{z}, @var{val}, @var{iso})
-## @deftypefnx {} {[@var{fv}] =} isosurface (@dots{}, "noshare", "verbose")
+## @deftypefnx {} {[@var{fv}] =} isosurface (@var{x}, @var{y}, @var{z}, @var{val})
 ## @deftypefnx {} {[@var{fvc}] =} isosurface (@dots{}, @var{col})
-## @deftypefnx {} {[@var{f}, @var{v}] =} isosurface (@var{x}, @var{y}, @var{z}, @var{val}, @var{iso})
-## @deftypefnx {} {[@var{f}, @var{v}, @var{c}] =} isosurface (@var{x}, @var{y}, @var{z}, @var{val}, @var{iso}, @var{col})
-## @deftypefnx {} {} isosurface (@var{x}, @var{y}, @var{z}, @var{val}, @var{iso}, @var{col}, @var{opt})
+## @deftypefnx {} {[@var{fv}] =} isosurface (@dots{}, "noshare")
+## @deftypefnx {} {[@var{fv}] =} isosurface (@dots{}, "verbose")
+## @deftypefnx {} {[@var{f}, @var{v}] =} isosurface (@dots{})
+## @deftypefnx {} {[@var{f}, @var{v}, @var{c}] =} isosurface (@dots{})
+## @deftypefnx {} {} isosurface (@dots{})
 ##
 ## Calculate isosurface of 3-D data.
 ##
@@ -31,32 +34,41 @@
 ## @var{val} is a three-dimensional array that contains the data of an
 ## isosurface geometry and the second input argument @var{iso} keeps the
 ## isovalue as a scalar value then return a structure array @var{fv}
-## that contains the fields @var{Faces} and @var{Vertices} at computed
-## points @command{[x, y, z] = meshgrid (1:l, 1:m, 1:n)}.  The output
+## that contains the fields @var{faces} and @var{vertices} at computed
+## points @command{[x, y, z] = meshgrid (1:l, 1:m, 1:n)}. The output
 ## argument @var{fv} can directly be taken as an input argument for the
 ## @command{patch} function.
 ##
-## If called with further input arguments @var{x}, @var{y} and @var{z}
-## which are three--dimensional arrays with the same size than @var{val}
-## then the volume data is taken at those given points.
+## If @var{iso} is omitted or empty, a "good" value for an isosurface is
+## determined from @var{val}.
 ##
-## The string input argument @qcode{"noshare"} is only for compatibility and
-## has no effect.  If given the string input argument
-## @qcode{"verbose"} then print messages to the command line interface about
-## the current progress.
+## If called with further input arguments @var{x}, @var{y} and @var{z}
+## which are three--dimensional arrays with the same size as @var{val} or
+## vectors with lengths corresponding to the dimensions of @var{val}, then the
+## volume data is taken at those given points. If @var{x}, @var{y} or @var{z}
+## are empty, the grid corresponds to the indices in the respective direction
+## (see @command{meshgrid}).
 ##
 ## If called with the input argument @var{col} which is a
-## three-dimensional array of the same size than @var{val} then take
+## three-dimensional array of the same size as @var{val}, take
 ## those values for the interpolation of coloring the isosurface
-## geometry.  Add the field @var{FaceVertexCData} to the structure
-## array @var{fv}.
+## geometry. In this case, the structure array @var{fv} has the additional field
+## @var{facevertexcdata}.
 ##
-## If called with two or three output arguments then return the
-## information about the faces @var{f}, vertices @var{v} and color data
-## @var{c} as separate arrays instead of a single structure array.
+## If given the string input argument @qcode{"noshare"}, vertices might be
+## returned multiple times for different faces. The default behavior is to
+## search vertices shared by adjacent faces with @command{unique} which might be
+## time consuming.  
+## The string input argument @qcode{"verbose"} is only for compatibility and
+## has no effect.  
+## The string input arguments must be passed after the other arguments.
 ##
-## If called with no output argument then directly process the
-## isosurface geometry with the @command{patch} command.
+## If called with two or three output arguments, return the information about
+## the faces @var{f}, vertices @var{v} and color data @var{c} as separate arrays
+## instead of a single structure array.
+##
+## If called with no output argument, the isosurface geometry is directly
+## processed with the @command{patch} command.
 ##
 ## For example,
 ##
@@ -70,6 +82,7 @@
 ##
 ## @noindent
 ## will directly draw a random isosurface geometry in a graphics window.
+##
 ## Another example for an isosurface geometry with different additional
 ## coloring
 ## @c Set example in small font to prevent overfull line
@@ -79,40 +92,44 @@
 ## iso = .4;  # Change isovalue to .1 to display a sphere
 ## lin = linspace (0, 2, N);
 ## [x, y, z] = meshgrid (lin, lin, lin);
-## c = abs ((x-.5).^2 + (y-.5).^2 + (z-.5).^2);
+## val = abs ((x-.5).^2 + (y-.5).^2 + (z-.5).^2);
 ## figure (); # Open another figure window
 ##
 ## subplot (2,2,1); view (-38, 20);
-## [f, v] = isosurface (x, y, z, c, iso);
+## [f, v] = isosurface (x, y, z, val, iso);
 ## p = patch ("Faces", f, "Vertices", v, "EdgeColor", "none");
 ## set (gca, "PlotBoxAspectRatioMode", "manual", ...
 ##           "PlotBoxAspectRatio", [1 1 1]);
-## # set (p, "FaceColor", "green", "FaceLighting", "phong");
-## # light ("Position", [1 1 5]); # Available with the JHandles package
+## isonormals (x, y, z, val, p)
+## set (p, "FaceColor", "green", "FaceLighting", "gouraud");
+## light ("Position", [1 1 5]);
 ##
 ## subplot (2,2,2); view (-38, 20);
 ## p = patch ("Faces", f, "Vertices", v, "EdgeColor", "blue");
 ## set (gca, "PlotBoxAspectRatioMode", "manual", ...
 ##           "PlotBoxAspectRatio", [1 1 1]);
-## # set (p, "FaceColor", "none", "FaceLighting", "phong");
-## # light ("Position", [1 1 5]);
+## isonormals (x, y, z, val, p)
+## set (p, "FaceColor", "none", "EdgeLighting", "gouraud");
+## light ("Position", [1 1 5]);
 ##
 ## subplot (2,2,3); view (-38, 20);
-## [f, v, c] = isosurface (x, y, z, c, iso, y);
+## [f, v, c] = isosurface (x, y, z, val, iso, y);
 ## p = patch ("Faces", f, "Vertices", v, "FaceVertexCData", c, ...
 ##            "FaceColor", "interp", "EdgeColor", "none");
 ## set (gca, "PlotBoxAspectRatioMode", "manual", ...
 ##           "PlotBoxAspectRatio", [1 1 1]);
-## # set (p, "FaceLighting", "phong");
-## # light ("Position", [1 1 5]);
+## isonormals (x, y, z, val, p)
+## set (p, "FaceLighting", "gouraud");
+## light ("Position", [1 1 5]);
 ##
 ## subplot (2,2,4); view (-38, 20);
 ## p = patch ("Faces", f, "Vertices", v, "FaceVertexCData", c, ...
 ##            "FaceColor", "interp", "EdgeColor", "blue");
 ## set (gca, "PlotBoxAspectRatioMode", "manual", ...
 ##           "PlotBoxAspectRatio", [1 1 1]);
-## # set (p, "FaceLighting", "phong");
-## # light ("Position", [1 1 5]);
+## isonormals (x, y, z, val, p)
+## set (p, "FaceLighting", "gouraud");
+## light ("Position", [1 1 5]);
 ## @end smallexample
 ##
 ## @seealso{isonormals, isocolors}
@@ -120,40 +137,18 @@
 
 ## Author: Martin Helm <martin@mhelm.de>
 
+## FIXME: Add support for string input argument "verbose" (needs changes to __marching_cube__.m)
+
 function varargout = isosurface (varargin)
 
-  if (nargin < 2 || nargin > 8 || nargout > 3)
+  if (nargin < 1 || nargin > 8 || nargout > 3)
     print_usage ();
   endif
 
-  calc_colors = false;
-  f = v = c = [];
-  verbose = false;
-  noshare = false;
-  if (nargin >= 5)
-    x = varargin{1};
-    y = varargin{2};
-    z = varargin{3};
-    val = varargin{4};
-    iso = varargin{5};
-    if (nargin >= 6 && isnumeric (varargin{6}))
-      colors = varargin{6};
-      calc_colors = true;
-    endif
-  else
-    val = varargin{1};
-    [n2, n1, n3] = size (val);
-    [x, y, z] = meshgrid (1:n1, 1:n2, 1:n3);
-    iso = varargin{2};
-    if (nargin >= 3 && isnumeric (varargin{3}))
-        colors = varargin{3};
-        calc_colors = true;
-    endif
-  endif
+  [x, y, z, val, iso, colors, noshare, verbose] = __get_check_isosurface_args__ (nargout, varargin{:});
+
+  calc_colors = ! isempty (colors);
   if (calc_colors)
-    if (nargout == 2)
-      warning ("isosurface: colors will be calculated, but no output argument to receive it.");
-    endif
     [fvc.faces, fvc.vertices, fvc.facevertexcdata] = ...
       __marching_cube__ (x, y, z, val, iso, colors);
   else
@@ -164,10 +159,18 @@
     warning ("isosurface: triangulation is empty");
   endif
 
+  if (!noshare)
+    [fvc.faces, fvc.vertices, J] = __unite_shared_vertices__ (fvc.faces, fvc.vertices);
+
+    if (calc_colors)
+      #fvc.facevertexcdata = fvc.facevertexcdata(vertices_idx);
+      fvc.facevertexcdata(J) = []; # share very close vertices
+    endif
+  endif
+
   switch (nargout)
     case 0
       ## plot the calculated surface
-      hax = newplot ();
       if (calc_colors)
         pa = patch ("Faces", fvc.faces, "Vertices", fvc.vertices,
                     "FaceVertexCData", fvc.facevertexcdata,
@@ -177,64 +180,328 @@
                     "FaceColor", "g", "EdgeColor", "k");
       endif
       if (! ishold ())
-        set (hax, "view", [-37.5, 30], "box", "off");
+        set (gca (), "View", [-37.5, 30], "Box", "off");
       endif
     case 1
       varargout = {fvc};
     case 2
       varargout = {fvc.faces, fvc.vertices};
-    case 3
+    otherwise ## 3
       varargout = {fvc.faces, fvc.vertices, fvc.facevertexcdata};
+  endswitch
+
+endfunction
+
+function [x, y, z, data, iso, colors, noshare, verbose] = __get_check_isosurface_args__ (nout, varargin)
+## get arguments from input and check values
+
+  x = [];
+  y = [];
+  z = [];
+  data = [];
+  iso = [];
+  colors = [];
+
+  ## default values
+  noshare = false;
+  verbose = false;
+
+  nin = length (varargin);
+  num_string_inputs = 0;
+  for i_arg = (nin:-1:nin-1)
+    ## check whether last maximum 2 input arguments are strings and assign parameters
+    if (!ischar (varargin{i_arg}) || i_arg < 1)
+      ## string arguments must be at the end
+      break
+    end
+    switch (tolower (varargin{i_arg}))
+      case {"v", "verbose"}
+        verbose = true;
+        num_string_inputs++;
+      case {"n", "noshare"}
+        noshare = true;
+        num_string_inputs++;
+      otherwise
+        error ("isosurface: parameter '%s' not supported", varargin{i_arg})
+    endswitch
+  endfor
+
+  ## assign arguments
+  switch (nin - num_string_inputs)
+    case 1 ## isosurface (val, ...)
+      data = varargin{1};
+    case 2 ## isosurface (val, iso, ...) or isosurface (val, col, ...) 
+      data = varargin{1};
+      if (isscalar (varargin{2}) || isempty (varargin{2}))
+        iso = varargin{2};
+      else
+        colors = varargin{2};
+      endif
+    case 3 ## isosurface (val, iso, col, ...) 
+      data = varargin{1};
+      iso = varargin{2};
+      colors = varargin{3};
+    case 4 ## isosurface (x, y, z, val, ...)
+      x = varargin{1};
+      y = varargin{2};
+      z = varargin{3};
+      data = varargin{4};
+    case 5 ## isosurface (x, y, z, val, iso, ...) or isosurface (x, y, z, val, col, ...)
+      x = varargin{1};
+      y = varargin{2};
+      z = varargin{3};
+      data = varargin{4};
+      if (isscalar (varargin{5}) || isempty (varargin{5}))
+        iso = varargin{5};
+      else
+        colors = varargin{5};
+      endif
+    case 6 ## isosurface (x, y, z, val, iso, col, ...)
+      x = varargin{1};
+      y = varargin{2};
+      z = varargin{3};
+      data = varargin{4};
+      iso = varargin{5};
+      colors = varargin{6};
     otherwise
-      print_usage ();
+      error ("isosurface: wrong number of input arguments")
   endswitch
 
+  ## check dimensions of data
+  data_size = size (data);
+  if (ndims (data) != 3 || any (data_size(1:3) < 2))
+    error ("isosurface: VAL must be a non-singleton 3-dimensional matrix");
+  endif
+
+  if (isempty (x))
+    x = 1:size (data, 2);
+  endif
+  if (isempty (y))
+    y = 1:size (data, 1);
+  endif
+  if (isempty (z))
+    z = 1:size (data, 3);
+  endif
+
+  ## check x
+  if (isvector (x) && length (x) == data_size(2))
+    x = repmat (x(:)', [data_size(1) 1 data_size(3)]);
+  elseif (! size_equal (data, x))
+    error ("isosurface: X must match the size of VAL");
+  endif
+
+  ## check y
+  if (isvector (y) && length (y) == data_size(1))
+    y = repmat (y(:), [1 data_size(2) data_size(3)]);
+  elseif (! size_equal (data, y))
+    error ("isosurface: Y must match the size of VAL");
+  endif
+
+  ## check z
+  if (isvector (z) && length (z) == data_size(3))
+    z = repmat (reshape (z(:), [1 1 length(z)]), [data_size(1) data_size(2) 1]);
+  elseif (! size_equal (data, z))
+    error ("isosurface: Z must match the size of VAL");
+  endif
+
+  ## check iso
+  if (isempty (iso))
+    ## calculate "good" iso value from data
+    iso = __calc_isovalue_from_data__ (data);
+  endif
+
+  if ~isscalar (iso)
+    error ("isosurface: ISO must be a scalar")
+  endif
+
+  ## check colors
+  if (! isempty (colors))
+    if (! size_equal (data, colors))
+      error ("isosurface: COL must match the size of VAL")
+    endif
+    if (nout == 2)
+      warning ("isosurface: colors will be calculated, but no output argument to receive it.");
+    endif
+  elseif (nout == 3)
+    error ("isosurface: COL must be passed to return C")
+  endif
+
 endfunction
 
 
 %!demo
 %! clf;
 %! [x,y,z] = meshgrid (-2:0.5:2, -2:0.5:2, -2:0.5:2);
-%! v = x.^2 + y.^2 + z.^2;
-%! isosurface (x, y, z, v, 1);
+%! val = x.^2 + y.^2 + z.^2;
+%! isosurface (x, y, z, val, 1);
 %! axis equal;
 %! title ('isosurface of a sphere');
 
-%!shared x, y, z, val
-%! [x, y, z]  = meshgrid (0:1, 0:1, 0:1); # Points for single
-%! val        = [0, 0; 0, 0];             # cube and a 3-D
-%! val(:,:,2) = [0, 0; 1, 0];             # array of values
+%!demo
+%! x = 0:2;
+%! y = 0:3;
+%! z = 0:1;
+%! [xx, yy, zz]  = meshgrid (x, y, z);
+%! val        = [0, 0, 0; 0, 0, 0; 0, 0, 1; 0, 0, 1];
+%! val(:,:,2) = [0, 0, 0; 0, 0, 1; 0, 1, 2; 0, 1, 2];
+%! iso = 0.8;
+%% three arguments, no output
+%! figure;
+%! subplot (2, 2, 1); isosurface (val, iso, yy); view(3)
+%% six arguments, no output (x, y, z are vectors)
+%! subplot (2, 2, 2); isosurface (x, y, z, val, iso, yy); view (3)
+%% six arguments, no output (x, y, z are matrices)
+%! subplot (2, 2, 3); isosurface (xx, yy, zz, val, iso, yy); view (3)
+%% six arguments, no output (mixed x, y, z) and option "noshare"
+%! subplot (2, 2, 4); isosurface (x, yy, z, val, iso, yy, "noshare"); view (3)
+%! annotation("textbox", [0 0.95 1 0.1], ...
+%!    "String", ["Apart from the first plot having a different scale, " ...
+%!               "all four plots must look the same.\n" ...
+%!               "The last plot might have different colors but must have " ...
+%!               "the same shape."], ...
+%!    "HorizontalAlignment", "left");
 
+%!shared x, y, z, xx, yy, zz, val, iso
+%! x = 0:2;
+%! y = 0:3;
+%! z = 0:1;
+%! [xx, yy, zz]  = meshgrid (x, y, z);
+%! val        = [0, 0, 0; 0, 0, 0; 0, 0, 1; 0, 0, 1];
+%! val(:,:,2) = [0, 0, 0; 0, 0, 1; 0, 1, 2; 0, 1, 2];
+%! iso = 0.8;
+
+## one argument, one output
 %!test
-%! fv = isosurface (x, y, z, val, 0.3);
+%! fv = isosurface (val);
 %! assert (isfield (fv, "vertices"), true);
 %! assert (isfield (fv, "faces"), true);
-%! assert (size (fv.vertices), [3 3]);
-%! assert (size (fv.faces), [1 3]);
+%! assert (size (fv.vertices), [5 3]);
+%! assert (size (fv.faces), [3 3]);
+
+## two arguments (second is ISO), one output
+%!test
+%! fv = isosurface (val, iso);
+%! assert (isfield (fv, "vertices"), true);
+%! assert (isfield (fv, "faces"), true);
+%! assert (size (fv.vertices), [11 3]);
+%! assert (size (fv.faces), [10 3]);
 
+## two arguments (second is COL), one output
 %!test
-%! fvc = isosurface (x, y, z, val, .3, y);
+%! fvc = isosurface (val, yy);
+%! assert (isfield (fvc, "vertices"), true);
+%! assert (isfield (fvc, "faces"), true);
+%! assert (isfield (fvc, "facevertexcdata"), true);
+%! assert (size (fvc.vertices), [5 3]);
+%! assert (size (fvc.faces), [3 3]);
+%! assert (size (fvc.facevertexcdata), [5 1]);
+
+## three arguments, one output
+%!test
+%! fvc = isosurface (val, iso, yy);
 %! assert (isfield (fvc, "vertices"), true);
 %! assert (isfield (fvc, "faces"), true);
 %! assert (isfield (fvc, "facevertexcdata"), true);
-%! assert (size (fvc.vertices), [3 3]);
-%! assert (size (fvc.faces), [1 3]);
-%! assert (size (fvc.facevertexcdata), [3 1]);
+%! assert (size (fvc.vertices), [11 3]);
+%! assert (size (fvc.faces), [10 3]);
+%! assert (size (fvc.facevertexcdata), [11 1]);
 
+## four arguments, one output
+%!test
+%! fv = isosurface (x, [], z, val);
+%! assert (isfield (fv, "vertices"), true);
+%! assert (isfield (fv, "faces"), true);
+%! assert (size (fv.vertices), [5 3]);
+%! assert (size (fv.faces), [3 3]);
+
+## five arguments (fifth is ISO), one output
+%!test
+%! fv = isosurface (xx, y, [], val, iso);
+%! assert (isfield (fv, "vertices"), true);
+%! assert (isfield (fv, "faces"), true);
+%! assert (size (fv.vertices), [11 3]);
+%! assert (size (fv.faces), [10 3]);
+
+## five arguments (fifth is COL), one output
 %!test
-%! [f, v] = isosurface (x, y, z, val, .3);
-%! assert (size (f), [1 3]);
-%! assert (size (v), [3 3]);
+%! fvc = isosurface ([], yy, z, val, yy);
+%! assert (isfield (fvc, "vertices"), true);
+%! assert (isfield (fvc, "faces"), true);
+%! assert (isfield (fvc, "facevertexcdata"), true);
+%! assert (size (fvc.vertices), [5 3]);
+%! assert (size (fvc.faces), [3 3]);
+%! assert (size (fvc.facevertexcdata), [5 1]);
 
+## six arguments, one output
+%!test
+%! fvc = isosurface (xx, yy, zz, val, iso, yy);
+%! assert (isfield (fvc, "vertices"), true);
+%! assert (isfield (fvc, "faces"), true);
+%! assert (isfield (fvc, "facevertexcdata"), true);
+%! assert (size (fvc.vertices), [11 3]);
+%! assert (size (fvc.faces), [10 3]);
+%! assert (size (fvc.facevertexcdata), [11 1]);
+
+## five arguments (fifth is ISO), two outputs
+%!test
+%! [f, v] = isosurface (x, y, z, val, iso);
+%! assert (size (f), [10 3]);
+%! assert (size (v), [11 3]);
+
+## six arguments, three outputs
 %!test
-%! [f, v, c] = isosurface (x, y, z, val, .3, y);
-%! assert (size (f), [1 3]);
-%! assert (size (v), [3 3]);
-%! assert (size (c), [3 1]);
+%! [f, v, c] = isosurface (x, y, z, val, iso, yy);
+%! assert (size (f), [10 3]);
+%! assert (size (v), [11 3]);
+%! assert (size (c), [11 1]);
 
+## two arguments (second is ISO) and one string, one output
+%!test
+%! fv = isosurface (val, iso, "verbose");
+%! assert (isfield (fv, "vertices"), true);
+%! assert (isfield (fv, "faces"), true);
+%! assert (size (fv.vertices), [11 3]);
+%! assert (size (fv.faces), [10 3]);
+
+## six arguments and two strings, one output
+%!test
+%! fvc = isosurface (xx, yy, zz, val, iso, yy, "v", "noshare");
+%! assert (isfield (fvc, "vertices"), true);
+%! assert (isfield (fvc, "faces"), true);
+%! assert (isfield (fvc, "facevertexcdata"), true);
+%! assert (size (fvc.vertices), [20 3]);
+%! assert (size (fvc.faces), [10 3]);
+%! assert (size (fvc.facevertexcdata), [20 1]);
+
+## five arguments (fifth is COL) and two strings (different order), one output
 %!test
-%! [f, v, c] = isosurface (val, .3, y);
-%! assert (size (f), [1 3]);
-%! assert (size (v), [3 3]);
-%! assert (size (c), [3 1]);
+%! fvc = isosurface (xx, yy, zz, val, yy, "n", "v");
+%! assert (isfield (fvc, "vertices"), true);
+%! assert (isfield (fvc, "faces"), true);
+%! assert (isfield (fvc, "facevertexcdata"), true);
+%! assert (size (fvc.vertices), [7 3]);
+%! assert (size (fvc.faces), [3 3]);
+%! assert (size (fvc.facevertexcdata), [7 1]);
 
+## test for each error and warning
+%!test
+%!error <X must match the size of VAL> x = 1:2:24; fvc = isosurface (x, y, z, val, iso);
+%!error <Y must match the size of VAL> y = -14:6:11; fvc = isosurface (x, y, z, val, iso);
+%!error <Z must match the size of VAL> z = linspace (16, 18, 5); fvc = isosurface (x, y, z, val, iso);
+%!error <X must match the size of VAL> x = 1:2:24; [xx, yy, zz] = meshgrid (x, y, z); fvc = isosurface (xx, yy, zz, val, iso);
+%!error <X must match the size of VAL> y = -14:6:11; [xx, yy, zz] = meshgrid (x, y, z); fvc = isosurface (xx, yy, zz, val, iso);
+%!error <X must match the size of VAL> z = linspace (16, 18, 3); [xx, yy, zz] = meshgrid (x, y, z); fvc = isosurface (xx, yy, zz, val, iso);
+%!error <VAL must be a non-singleton 3-dimensional matrix> val = reshape(1:6*8, [6 8]); fvc = isosurface (val, iso);
+%!error <VAL must be a non-singleton 3-dimensional matrix> val = reshape(1:6*8, [6 1 8]); fvc = isosurface (val, iso);
+%!error <ISO must be a scalar> fvc = isosurface (val, [iso iso], yy);
+%!error <COL must match the size of VAL> fvc = isosurface (val, [iso iso]);
+%!warning <colors will be calculated, but no output argument to receive it.> [f, v] = isosurface (val, iso, yy);
+%!error <COL must be passed to return C> [f, v, c] = isosurface (val, iso);
+%!error <Invalid call to isosurface> fvc = isosurface ();
+%!error <Invalid call to isosurface> [f, v, c, a] = isosurface (val, iso);
+%!error <wrong number of input arguments> fvc = isosurface (xx, yy, zz, val, iso, yy, 5);
+%!error <parameter 'test_string' not supported> fvc = isosurface (val, iso, "test_string");
+
+## test for __calc_isovalue_from_data__
+%!test
+%! assert (__calc_isovalue_from_data__ (1:5), 3.02);
--- a/scripts/plot/draw/module.mk	Mon Jul 04 07:39:02 2016 -0700
+++ b/scripts/plot/draw/module.mk	Sun Jul 03 23:39:36 2016 +0200
@@ -5,6 +5,7 @@
 scripts_plot_draw_PRIVATE_FCN_FILES = \
   scripts/plot/draw/private/__add_datasource__.m \
   scripts/plot/draw/private/__bar__.m \
+  scripts/plot/draw/private/__calc_isovalue_from_data__.m \
   scripts/plot/draw/private/__contour__.m \
   scripts/plot/draw/private/__errplot__.m \
   scripts/plot/draw/private/__ezplot__.m \
@@ -16,7 +17,8 @@
   scripts/plot/draw/private/__plt__.m \
   scripts/plot/draw/private/__quiver__.m \
   scripts/plot/draw/private/__scatter__.m \
-  scripts/plot/draw/private/__stem__.m
+  scripts/plot/draw/private/__stem__.m \
+  scripts/plot/draw/private/__unite_shared_vertices__.m
 
 scripts_plot_draw_FCN_FILES = \
   scripts/plot/draw/area.m \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/draw/private/__calc_isovalue_from_data__.m	Sun Jul 03 23:39:36 2016 +0200
@@ -0,0 +1,60 @@
+## Copyright (C) 2016 Markus Muetzel
+##
+## This file is part of Octave.
+##
+## Octave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## Undocumented internal function.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{iso} =} __calc_isovalue_from_data__ (@var{data})
+## Undocumented internal function.
+## @end deftypefn
+
+## calculate a "good" iso value from histogram of data
+## called from isocaps, isosurface
+
+
+function iso = __calc_isovalue_from_data__ (data)
+
+  ## use a maximum of 10000-20000 samples to limit run-time of hist
+  step = 1;
+  data_numel = numel (data);
+  if (data_numel > 20000)
+    step = floor (data_numel / 10000);
+    data = data(1:step:end);
+    data_numel = numel (data);
+  endif
+
+  num_bins = 100;
+  [bin_count, bin_centers] = hist (data(:), num_bins);
+
+  ## if one of the first two bins contains more than 10 times the count as
+  ## compared to equally distributed data, remove both (zero-padded + noise)
+  if (any (bin_count(1:2) > 10 * (data_numel / num_bins)))
+    bin_count(1:2) = [];
+    bin_centers(1:2) = [];
+  endif
+
+  ## if bins have low counts, remove them (but keep them if we would loose more than 90 % of bins)
+  bins_to_remove = find (bin_count < max (bin_count)/50);
+  if length (bins_to_remove) < .9 * num_bins
+    bin_centers(bins_to_remove) = [];
+  endif
+
+  ## select middle bar of histogram with previous conditions
+  iso = bin_centers(floor (length (bin_centers) / 2));
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/draw/private/__unite_shared_vertices__.m	Sun Jul 03 23:39:36 2016 +0200
@@ -0,0 +1,86 @@
+## Copyright (C) 2016 Markus Muetzel
+##
+## This file is part of Octave.
+##
+## Octave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*- 
+## @deftypefn {} {[@var{faces}, @var{vertices}, @var{J}] =} __unite_shared_vertices__ (@var{faces}, @var{vertices})
+##
+## Detect and unite shared vertices in patches
+##
+## Vertices of neighboring faces are detected and united to shared vertices. For
+## this, the mutual squared distances between all vertices are calculated. If
+## all coordinates are closer than @command{2 * eps (max (abs (vertices(:))))},
+## the vertices are united to one.
+##
+## @var{J} holds the indices of the deleted vertices.
+##
+## @seealso{isosurface, reducepatch}
+## @end deftypefn
+
+## Author: mmuetzel
+
+function [faces, vertices, J] = __unite_shared_vertices__ (faces, vertices)
+  ### unite shared vertices
+
+  J = [];
+  ## Calculate the mutual differences of all vertex coordinates
+  close_points = zeros (0, 2);
+  num_vertices = size (vertices, 1);
+  skip_point = false (num_vertices, 1);
+  for (i_point1 = 1:num_vertices - 1)
+    if (skip_point(i_point1))
+      ## points already detected as duplicates can be skipped
+      continue
+    endif
+
+    diff = vertices(i_point1,:) - vertices(i_point1 + 1:end,:);
+    is_close_point = all (abs (diff) <= sqrt(3) * eps * ...
+        (max (abs (vertices(i_point1,:)), abs (vertices(i_point1 + 1:end,:)))), 2);
+
+    if (any (is_close_point))
+      close_points_idx = find (is_close_point) + i_point1;
+      new_close_points_num = size (close_points_idx, 1);
+      close_points(end + 1:end + new_close_points_num,1) = i_point1;
+      close_points(end - new_close_points_num + 1:end,2) = close_points_idx;
+      skip_point(close_points_idx) = true;
+    endif
+  endfor
+
+  if (! isempty (close_points))
+    vertices(close_points(:,2),:) = []; # delete multiple shared vertices
+    ## renumber deleted vertices in faces to the one it is replaced by
+    vertex_renum = 1:num_vertices;
+    vertex_renum(close_points(:,2)) = close_points(:,1);
+    faces = vertex_renum(faces);
+    ## renumber vertices in faces with subsequent numbers
+    vertex_renum2 = ones (1, num_vertices);
+    vertex_renum2(close_points(:,2)) = 0;
+    vertex_renum2 = cumsum (vertex_renum2);
+    faces = vertex_renum2(faces);
+
+    ## eliminate identical faces
+    faces = sort (faces, 2);
+    faces = unique (faces, "rows");
+
+    ## eliminate faces with zero area
+    is_zero_area = (faces(:,1) == faces(:,2)) | (faces(:,2) == faces(:,3)); # vertices in faces are sorted
+    faces = faces(!is_zero_area,:);
+
+    J = close_points(:,2);
+  endif
+
+endfunction