changeset 17464:4a6a4657fdf2

Overhaul errorbar family of functions. * scripts/plot/errorbar.m: Add %!error tests for input validation. * scripts/plot/private/__errcomm__.m: Correctly validate that numeric inputs are numeric. More precise error reporting indicating the problematic argument. * scripts/plot/private/__errplot__.m: Fix automatic color cycling for matrices with multiple columns of data. Check "yerr" case first in if/elseif trees since it is the most common. Pull static computations out of for loop for performance.
author Rik <rik@octave.org>
date Wed, 18 Sep 2013 16:54:37 -0700
parents c39fa414b5ab
children 47269b03a946
files scripts/plot/errorbar.m scripts/plot/private/__errcomm__.m scripts/plot/private/__errplot__.m
diffstat 3 files changed, 143 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/errorbar.m
+++ b/scripts/plot/errorbar.m
@@ -197,3 +197,24 @@
 %! legend ("X-Y errbars", "X-Y errboxes");
 %! title ('errorbar() with X-Y errorbars and error boxes');
 
+## Invisible figure used for tests
+%!shared hf, hax
+%! hf = figure ("visible", "off");
+%! hax = axes;
+
+%!error errorbar ()
+%!error errorbar (1)
+%!error <data argument 1 must be numeric> errorbar (hax, {1}, 2)
+%!error <data argument 2 must be numeric> errorbar (hax, 1, {2})
+%!error <size of argument 2 does not match others> errorbar (hax, 1, 1:2)
+%!error <size of argument 3 does not match others> errorbar (hax, 1, 2, 3:4)
+%!error <too many arguments to plot> errorbar (1,2,3,4,5,6,7)
+
+%!error <2 column errorplot is only valid for xerr> errorbar (1,2, "~>")
+%!error <6 columns only valid for xyerr and boxxy> errorbar (1,2,3,4,5,6, "~")
+%!error <error plot requires 2, 3, 4, or 6 arguments> errorbar (1,2,3,4,5)
+
+## Close figure used for testing
+%!test
+%! close (hf);
+
--- a/scripts/plot/private/__errcomm__.m
+++ b/scripts/plot/private/__errcomm__.m
@@ -31,45 +31,45 @@
     print_usage (caller);
   endif
 
-  nargs = length (varargin);
   retval = [];
+  data = cell (6,1);
+  nargs = numel (varargin);
   k = 1;
-  data = cell (6,1);
   while (k <= nargs)
-    a = varargin{k++};
-    if (isvector (a))
-      a = a(:);
-    elseif (ismatrix (a))
-      ;
-    else
-      usage ("%s (...)", caller);
+    arg = varargin{k++};
+    if (! ismatrix (arg))
+      error ("%s: data argument %d must be numeric", caller, k-1);
     endif
-    sz = size (a);
+    if (isvector (arg))
+      arg = arg(:);
+    endif
+    sz = size (arg);
     ndata = 1;
-    data{ndata} = a;
+    data{ndata} = arg;
     while (k <= nargs)
-      a = varargin{k++};
-      if (ischar (a) || iscellstr (a))
-        retval = [retval; __errplot__(a, hax, data{1:ndata})];
+      arg = varargin{k++};
+      if (ischar (arg) || iscellstr (arg))
+        retval(end+1,1) = __errplot__(arg, hax, data{1:ndata});
         break;
-      elseif (isvector (a))
-        a = a(:);
-      elseif (ismatrix (a))
-        ;
-      else
-        error ("%s: wrong argument types", caller);
+      endif
+      if (! ismatrix (arg))
+        error ("%s: data argument %d must be numeric", caller, k-1);
       endif
-      if (size (a) != sz)
-        error ("%s: argument sizes do not match", caller);
+      if (isvector (arg))
+        arg = arg(:);
       endif
-      data{++ndata} = a;
+      if (any (size (arg) != sz))
+        error ("%s: size of argument %d does not match others", caller, k-1);
+      endif
+      data{++ndata} = arg;
       if (ndata > 6)
         error ("%s: too many arguments to plot", caller);
       endif
     endwhile
   endwhile
 
-  if (! (ischar (a) || iscellstr (a)))
+  ## No format code found, use yerrorbar
+  if (! (ischar (arg) || iscellstr (arg)))
     retval = [retval; __errplot__("~", hax, data{1:ndata})];
   endif
 
--- a/scripts/plot/private/__errplot__.m
+++ b/scripts/plot/private/__errplot__.m
@@ -27,119 +27,124 @@
 
 function h = __errplot__ (fstr, hax, varargin)
 
-  [fmt, valid] = __pltopt__ ("__errplot__", fstr);
-
-  [len, nplots] = size (varargin{1});
-  h = [];
+  fmt = __pltopt__ ("__errplot__", fstr);
 
-  for i = 1:nplots
-    ## Set the plot type based on linestyle.
-
-    if (strcmp (fmt.errorstyle, "~"))
+  ## Set the plot type based on linestyle.
+  switch (fmt.errorstyle) 
+    case "~"
       ifmt = "yerr";
-    elseif (strcmp (fmt.errorstyle, ">"))
+    case ">"
       ifmt = "xerr";
-    elseif (strcmp (fmt.errorstyle, "~>"))
+    case "~>"
       ifmt = "xyerr";
-    elseif (strcmp (fmt.errorstyle, "#"))
+    case "#"
       ifmt = "box";
-    elseif (strcmp (fmt.errorstyle, "#~"))
+    case "#~"
       ifmt = "boxy";
-    elseif (strcmp (fmt.errorstyle, "#~>"))
+    case "#~>"
       ifmt = "boxxy";
+    otherwise
+      ifmt = "yerr";
+  endswitch
+
+  h = [];
+  nplots = columns (varargin{1});
+  for i = 1:nplots
+
+    if (isempty (fmt.color))
+      lc = __next_line_color__ ();
     else
-      ifmt = "yerr";
+      lc = fmt.color ();
+    endif
+    if (isempty (fmt.marker) && isempty (fmt.linestyle))
+      [ls, mk] = __next_line_style__ ();
+    else
+      ls = fmt.linestyle;
+      mk = fmt.marker;
     endif
 
+    ## Must occur after __next_line_color__ in order to work correctly.
     hg = hggroup ("parent", hax);
     h = [h; hg];
     args = __add_datasource__ ("__errplot__", hg,
                                {"x", "y", "l", "u", "xl", "xu"});
 
-    if (isempty (fmt.color))
-      fmt.color = __next_line_color__ ();
-    endif
-    if (isempty (fmt.marker) && isempty (fmt.linestyle))
-      [fmt.linestyle, fmt.marker] = __next_line_style__ ();
-    endif
-    hl = [(__line__ (hg, "linestyle", fmt.linestyle, "marker", fmt.marker,
-                   "color", fmt.color)),
-          (__line__ (hg, "linestyle", "-", "marker", "none",
-                   "color", fmt.color))];
+    hl = [(__line__ (hg, "color", lc, "linestyle", ls, "marker", mk)),
+          (__line__ (hg, "color", lc, "linestyle", "-", "marker", "none"))];
 
     switch (numel (varargin))
       case 2
         ydata = varargin{1}(:,i);
         xdata = 1:numel (ydata);
-        if (strcmp (ifmt, "xerr") || strcmp (ifmt, "box"))
+        if (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy"))
+          ldata  = varargin{2}(:,i);
+          udata  = ldata;
+          xldata = [];
+          xudata = [];
+        elseif (strcmp (ifmt, "xerr") || strcmp (ifmt, "box"))
           xldata = varargin{2}(:,i);
           xudata = ldata;
-          ldata = [];
-          udata = [];
-        elseif (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy"))
-          ldata = varargin{2}(:,i);
-          udata = ldata;
-          xldata = [];
-          xudata = [];
+          ldata  = [];
+          udata  = [];
         else
           error ("errorbar: 2 column errorplot is only valid for xerr or yerr");
         endif
       case 3
-        if (strcmp (ifmt, "boxxy") || strcmp (ifmt, "xyerr"))
-          ydata = varargin{1}(:,i);
-          xdata = 1:numel (ydata);
+        if (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy"))
+          xdata  = varargin{1}(:,i);
+          ydata  = varargin{2}(:,i);
+          ldata  = varargin{3}(:,i);
+          udata  = ldata;
+          xldata = [];
+          xudata = [];
+        elseif (strcmp (ifmt, "xyerr") || strcmp (ifmt, "boxxy"))
+          ydata  = varargin{1}(:,i);
+          xdata  = 1:numel (ydata);
           xldata = varargin{2}(:,i);
           xudata = xldata;
-          ldata = varargin{3}(:,i);
-          udata = ldata;
-        elseif (strcmp (ifmt, "xerr") || strcmp (ifmt, "box"))
-          xdata = varargin{1}(:,i);
-          ydata = varargin{2}(:,i);
-          xldata = varargin{3}(:,i);
-          xudata = xldata;
-          ldata = [];
-          udata = [];
-        else # yerr or boxy
-          xdata = varargin{1}(:,i);
-          ydata = varargin{2}(:,i);
-          ldata = varargin{3}(:,i);
-          udata = ldata;
-          xldata = [];
-          xudata = [];
-        endif
-      case 4
-        if (strcmp (ifmt, "boxxy") || strcmp (ifmt, "xyerr"))
-          xdata = varargin{1}(:,i);
-          ydata = varargin{2}(:,i);
+          ldata  = varargin{3}(:,i);
+          udata  = ldata;
+        else  # xerr or box
+          xdata  = varargin{1}(:,i);
+          ydata  = varargin{2}(:,i);
           xldata = varargin{3}(:,i);
           xudata = xldata;
-          ldata = varargin{4}(:,i);
-          udata = ldata;
-        elseif (strcmp (ifmt, "xerr") || strcmp (ifmt, "box"))
-          xdata = varargin{1}(:,i);
-          ydata = varargin{2}(:,i);
+          ldata  = [];
+          udata  = [];
+        endif
+      case 4
+        if (strcmp (ifmt, "yerr") || strcmp (ifmt, "boxy"))
+          xdata  = varargin{1}(:,i);
+          ydata  = varargin{2}(:,i);
+          ldata  = varargin{3}(:,i);
+          udata  = varargin{4}(:,i);
+          xldata = [];
+          xudata = [];
+        elseif (strcmp (ifmt, "xyerr") || strcmp (ifmt, "boxxy"))
+          xdata  = varargin{1}(:,i);
+          ydata  = varargin{2}(:,i);
+          xldata = varargin{3}(:,i);
+          xudata = xldata;
+          ldata  = varargin{4}(:,i);
+          udata  = ldata;
+        else  # xerr or box
+          xdata  = varargin{1}(:,i);
+          ydata  = varargin{2}(:,i);
           xldata = varargin{3}(:,i);
           xudata = varargin{4}(:,i);
-          ldata = [];
-          udata = [];
-        else # yerr or boxy
-          xdata = varargin{1}(:,i);
-          ydata = varargin{2}(:,i);
-          ldata = varargin{3}(:,i);
-          udata = varargin{4}(:,i);
-          xldata = [];
-          xudata = [];
+          ldata  = [];
+          udata  = [];
         endif
-      case 6 # boxxy, xyerr
-        if (strcmp (ifmt, "boxxy") || strcmp (ifmt, "xyerr"))
-          xdata = varargin{1}(:,i);
-          ydata = varargin{2}(:,i);
+      case 6  # xyerr, boxxy
+        if (strcmp (ifmt, "xyerr") || strcmp (ifmt, "boxxy"))
+          xdata  = varargin{1}(:,i);
+          ydata  = varargin{2}(:,i);
           xldata = varargin{3}(:,i);
           xudata = varargin{4}(:,i);
-          ldata = varargin{5}(:,i);
-          udata = varargin{6}(:,i);
+          ldata  = varargin{5}(:,i);
+          udata  = varargin{6}(:,i);
         else
-          error ("errorbar: error plot with 6 columns only valid for boxxy and xyerr");
+          error ("errorbar: error plot with 6 columns only valid for xyerr and boxxy");
         endif
       otherwise
         error ("errorbar: error plot requires 2, 3, 4, or 6 arguments");
@@ -154,21 +159,22 @@
     addproperty ("format", hg, "string", ifmt);
 
     addproperty ("color", hg, "linecolor", get (hl(1), "color"));
+    addproperty ("linestyle", hg, "linelinestyle", get (hl(1), "linestyle"));
     addproperty ("linewidth", hg, "linelinewidth", get (hl(1), "linewidth"));
-    addproperty ("linestyle", hg, "linelinestyle", get (hl(1), "linestyle"));
     addproperty ("marker", hg, "linemarker", get (hl(1), "marker"));
+    addproperty ("markeredgecolor", hg, "linemarkerfacecolor",
+                 get (hl(1), "markeredgecolor"));
     addproperty ("markerfacecolor", hg, "linemarkerfacecolor",
                  get (hl(1), "markerfacecolor"));
-    addproperty ("markeredgecolor", hg, "linemarkerfacecolor",
-                 get (hl(1), "markeredgecolor"));
     addproperty ("markersize", hg, "linemarkersize",
                  get (hl(1), "markersize"));
 
     fcn = {@update_props, hl};
     addlistener (hg, "color", fcn);
+    addlistener (hg, "linestyle", fcn);
     addlistener (hg, "linewidth", fcn);
-    addlistener (hg, "linestyle", fcn);
     addlistener (hg, "marker", fcn);
+    addlistener (hg, "markeredgecolor", fcn);
     addlistener (hg, "markerfacecolor", fcn);
     addlistener (hg, "markersize", fcn);
 
@@ -250,7 +256,7 @@
     ylo = ydata/ry;
     yhi = ydata*ry;
   endif
-  nans = NaN + xdata(:);
+  nans = NaN + xdata(:);  # fast way to do NaN (size (xdata(:)))
   if (strcmp (ifmt, "yerr"))
     xdata = [xdata, xdata, nans, ...
              xlo, xhi, nans, ...
@@ -289,7 +295,7 @@
     ydata = [y1; y2];
     return;
   else
-    error ("errorbar: valid error bar types are xerr, yerr, boxxy, and xyerr");
+    error ("errorbar: valid error bar types are xerr, yerr, xyerr, box, boxy, boxxy");
   endif
 
   xdata = xdata.'(:);
@@ -297,17 +303,17 @@
 
 endfunction
 
-function update_props (hg, dummy, hl)
+function update_props (hg, ~, hl)
   set (hl, "color", get (hg, "color"),
-           "linewidth", get (hg, "linewidth"));,
+           "linewidth", get (hg, "linewidth"));
   set (hl(1), "linestyle", get (hg, "linestyle"),
               "marker", get (hg, "marker"),
-              "markersize", get (hg, "markersize"),
+              "markeredgecolor", get (hg, "markeredgecolor"),
               "markerfacecolor", get (hg, "markerfacecolor"),
-              "markeredgecolor", get (hg, "markeredgecolor"));
+              "markersize", get (hg, "markersize"));
 endfunction
 
-function update_data (hg, dummy, hl)
+function update_data (hg, ~, hl)
 
   if (strcmp (get (hg, "type"), "axes"))
     hax = hg;
@@ -318,10 +324,10 @@
   xscale = get (hax, "xscale");
   yscale = get (hax, "yscale");
 
-  xdata = get (hg, "xdata");
-  ydata = get (hg, "ydata");
-  ldata = get (hg, "ldata");
-  udata = get (hg, "udata");
+  xdata  = get (hg, "xdata");
+  ydata  = get (hg, "ydata");
+  ldata  = get (hg, "ldata");
+  udata  = get (hg, "udata");
   xldata = get (hg, "xldata");
   xudata = get (hg, "xudata");
   ifmt = get (hg, "format");