changeset 17098:33037eddecd2

Overhaul colorbar function. Colorbar now accepts an input colorbar handle or an input axis and will act appropriately. Updated docstring. Added Matlab compatible options "hide" and "delete". Stopped random failures by deleting all listeners when cbar is deleted. Added listener to monitor SIZE of colormap. Cleaned up coding style. Used less vague internal variable name __cbar_hax__ instead of __my_handle__ for out-of-band communication with __actual_axis_position__.m. * scripts/plot/colorbar.m: Colorbar now accepts an input colorbar handle or an input axis and will act appropriately. Updated docstring. Added Matlab compatible options "hide" and "delete". Stopped random failures by deleting all listeners when cbar is deleted. Added listener to monitor SIZE of colormap. Cleaned up coding style. Used less vague internal variable name __cbar_hax__ instead of __my_handle__ for out-of-band communication with __actual_axis_position__.m. * scripts/plot/private/__actual_axis_position__.m: Rename __my_handle__ to __cbar_hax__ for clarity.
author Rik <rik@octave.org>
date Fri, 26 Jul 2013 13:15:07 -0700
parents e38820d1124c
children e7a059a9a644
files scripts/plot/colorbar.m scripts/plot/private/__actual_axis_position__.m
diffstat 2 files changed, 177 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/colorbar.m
+++ b/scripts/plot/colorbar.m
@@ -17,9 +17,22 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {} colorbar (@var{s})
-## @deftypefnx {Function File} {} colorbar ("peer", @var{h}, @dots{})
-## Add a colorbar to the current axes.  Valid values for @var{s} are
+## @deftypefn  {Function File} {} colorbar
+## @deftypefnx {Function File} {} colorbar (@var{loc})
+## @deftypefnx {Function File} {} colorbar (@var{delete_option})
+## @deftypefnx {Function File} {} colorbar (@var{hcb}, @dots{})
+## @deftypefnx {Function File} {} colorbar (@var{hax}, @dots{})
+## @deftypefnx {Function File} {} colorbar (@dots{}, "peer", @var{hax}, @dots{})
+## @deftypefnx {Function File} {} colorbar (@dots{}, "location", @var{loc}, @dots{})
+## @deftypefnx {Function File} {} colorbar (@dots{}, @var{prop}, @var{val}, @dots{})
+## @deftypefnx {Function File} {@var{h} =} colorbar (@dots{})
+## Add a colorbar to the current axes.
+##
+## A colorbar displays the current colormap along with numerical rulings
+## so that the color scale can be interpreted.
+##
+## The optional input @var{loc} determines the location of the colorbar.
+## Valid values for @var{loc} are
 ##
 ## @table @asis
 ## @item "EastOutside"
@@ -45,52 +58,105 @@
 ##
 ## @item "South"
 ## Place the colorbar at the bottom of the plot.
-##
-## @item "Off", "None"
-## Remove any existing colorbar from the plot.
 ## @end table
 ##
+## To remove a colorbar from a plot use any one of the following keywords for
+## the @var{delete_option}: "delete", "hide", "off".
+## 
 ## If the argument "peer" is given, then the following argument is treated
-## as the axes handle on which to add the colorbar.
+## as the axes handle in which to add the colorbar.  Alternatively, 
+## If the first argument @var{hax} is an axes handle, then the colorbar is
+## added to this axis, rather than the current axes returned by @code{gca}.
+##
+## If the first argument @var{hcb} is a handle to a colorbar object, then
+## operate on this colorbar directly.
+##
+## Additional property/value pairs are passed directly to the underlying axes
+## object.
+##
+## The optional return value @var{h} is a graphics handle to the created
+## colorbar object.
+##
+## Implementation Note: A colorbar is created as an additional axes to the
+## current figure with the "tag" property set to "colorbar".  The created
+## axes object has the extra property "location" which controls the positioning
+## of the colorbar.
+## @seealso{colormap}
 ## @end deftypefn
 
 function h = colorbar (varargin)
-  ax = [];
-  loc = "eastoutside";
+
+  [hcb, varargin, nargin] = __plt_get_axis_arg__ ("colorbar", varargin{:});
+
+  if (hcb && ! strcmp (get (hcb, "tag"), "colorbar"))
+    ax = hcb;
+    hcb = [];
+  else
+    ax = [];
+  endif
+  loc = "";
   args = {};
   deleting = false;
 
   i = 1;
   while (i <= nargin)
-    arg = varargin {i++};
+    arg = varargin{i++};
     if (ischar (arg))
-      if (strcmpi (arg, "peer"))
-        if (i > nargin)
-          error ("colorbar: missing axes handle after \"peer\"");
-        else
-          ax = varargin{i++};
-          if (!isscalar (ax) || ! ishandle (ax)
-              || ! strcmp (get (ax, "type"), "axes"))
-            error ("colorbar: expecting an axes handle following \"peer\"");
+      switch (tolower (arg))
+        case "peer"
+          if (i > nargin)
+            error ('colorbar: missing axes handle after "peer"');
+          else
+            ax = varargin{i++};
+            if (! isscalar (ax) || ! ishandle (ax)
+                || ! strcmp (get (ax, "type"), "axes"))
+              error ('colorbar: expecting an axes handle following "peer"');
+            endif
           endif
-        endif
-      elseif (strcmpi (arg, "north") || strcmpi (arg, "south")
-              || strcmpi (arg, "east") || strcmpi (arg, "west")
-              || strcmpi (arg, "northoutside") || strcmpi (arg, "southoutside")
-              || strcmpi (arg, "eastoutside") || strcmpi (arg, "westoutside"))
-        loc = tolower (arg);
-      elseif (strcmpi (arg, "location") && i <= nargin)
-        loc = tolower (varargin{i++});
-      elseif (strcmpi (arg, "off") || strcmpi (arg, "none"))
-        deleting = true;
-      else
-        args{end+1} = arg;
-      endif
+        case {"north", "south", "east", "west",
+              "northoutside", "southoutside", "eastoutside", "westoutside"}
+          loc = tolower (arg);
+        case "location"
+          if (i > nargin)
+            error ('colorbar: missing value after "location"');
+          else
+            loc = tolower (varargin{i++});
+          endif
+        case {"delete", "hide", "off", "none"}
+          deleting = true;
+        otherwise
+          args{end+1} = arg;
+      endswitch
     else
       args{end+1} = arg;
     endif
   endwhile
 
+  ## Handle changes to existing colorbar
+  if (! isempty (hcb))
+    if (deleting)
+      delete (hcb);
+      if (nargout > 0)
+        h = hcb;
+      endif
+      return;
+    else
+      ## FIXME: No listener on location property so have to re-create
+      ##        colorbar whenever an option changes.
+      ##        re-instate this code if listener is developed.
+      # if (! isempty (loc))
+      #   set (hcb, "location", loc);
+      # endif
+      # if (! isempty (args))
+      #   set (hcb, args{:});
+      # endif
+      ax = get (get (hcb, "parent"), "currrentaxes");      
+    endif
+  endif
+    
+  if (isempty (loc))
+    loc = "eastoutside";
+  endif
   if (isempty (ax))
     ax = gca ();
   endif
@@ -107,14 +173,16 @@
   end_unwind_protect
 
   if (! deleting)
-    ## FIXME - Matlab does not require the "position" property to be active.
-    ##         Is there a way to determine the plotbox position for the
-    ##         gnuplot graphics toolkit with the outerposition is active?
+    ## FIXME: Matlab does not require the "position" property to be active.
+    ##        Is there a way to determine the plotbox position for the
+    ##        gnuplot graphics toolkit with the outerposition is active?
     set (ax, "activepositionproperty", "position");
     obj = get (ax);
-    obj.__my_handle__ = ax;
+    obj.__cbar_hax__ = ax;
     position = obj.position;
-    clen = rows (get (get (ax, "parent"), "colormap"));
+    ## FIXME: Should this be ancestor to accommodate hggroups?
+    hpar = get (ax, "parent");  
+    clen = rows (get (hpar, "colormap"));
     cext = get (ax, "clim");
     cdiff = (cext(2) - cext(1)) / clen / 2;
     cmin = cext(1) + cdiff;
@@ -124,10 +192,10 @@
         __position_colorbox__ (loc, obj, ancestor (ax, "figure"));
     set (ax, "position", pos);
 
-    cax = __go_axes__ (get (ax, "parent"), "tag", "colorbar",
-                       "handlevisibility", "on",
-                       "activepositionproperty", "position",
-                       "position", cpos);
+    cax = __go_axes__ (hpar, "tag", "colorbar",
+                             "handlevisibility", "on",
+                             "activepositionproperty", "position",
+                             "position", cpos);
     addproperty ("location", cax, "radio",
                  "eastoutside|east|westoutside|west|northoutside|north|southoutside|south",
                  loc);
@@ -137,38 +205,41 @@
       hi = image (cax, [0,1], [cmin, cmax], [1 : clen]');
       if (mirror)
         set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
-             "ylim", cext, "ylimmode", "manual",
-             "yaxislocation", "right", args{:});
+                  "ylim", cext, "ylimmode", "manual",
+                  "yaxislocation", "right", args{:});
       else
         set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
-             "ylim", cext, "ylimmode", "manual",
-             "yaxislocation", "left", args{:});
+                  "ylim", cext, "ylimmode", "manual",
+                  "yaxislocation", "left", args{:});
       endif
     else
       hi = image (cax, [cmin, cmax], [0,1], [1 : clen]);
       if (mirror)
         set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
-             "xlim", cext, "xlimmode", "manual",
-             "xaxislocation", "top", args{:});
+                  "xlim", cext, "xlimmode", "manual",
+                  "xaxislocation", "top", args{:});
       else
         set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
-             "xlim", cext, "xlimmode", "manual",
-             "xaxislocation", "bottom", args{:});
+                  "xlim", cext, "xlimmode", "manual",
+                  "xaxislocation", "bottom", args{:});
       endif
     endif
 
-    ctext = text (0, 0, "", "tag", "colorbar","visible", "off",
-                  "handlevisibility", "off", "xliminclude", "off",
-                  "yliminclude", "off", "zliminclude", "off",
+    ## Dummy object placed in axis to delete colorbar when axis is deleted.
+    ctext = text (0, 0, "", "tag", "colorbar",
+                  "visible", "off", "handlevisibility", "off",
+                  "xliminclude", "off", "yliminclude", "off",
+                  "zliminclude", "off",
                   "deletefcn", {@deletecolorbar, cax, obj});
 
     set (cax, "deletefcn", {@resetaxis, ax, obj});
 
+    addlistener (hpar, "colormap", {@update_colorbar_cmap, hi, vertical, clen});
     addlistener (ax, "clim", {@update_colorbar_clim, hi, vertical});
+    addlistener (ax, "dataaspectratio", {@update_colorbar_axis, cax, obj});
+    addlistener (ax, "dataaspectratiomode", {@update_colorbar_axis, cax, obj});
     addlistener (ax, "plotboxaspectratio", {@update_colorbar_axis, cax, obj});
     addlistener (ax, "plotboxaspectratiomode", {@update_colorbar_axis, cax, obj});
-    addlistener (ax, "dataaspectratio", {@update_colorbar_axis, cax, obj});
-    addlistener (ax, "dataaspectratiomode", {@update_colorbar_axis, cax, obj});
     addlistener (ax, "position", {@update_colorbar_axis, cax, obj});
 
   endif
@@ -176,42 +247,55 @@
   if (nargout > 0)
     h = cax;
   endif
+
 endfunction
 
 function deletecolorbar (h, d, hc, orig_props)
   ## Don't delete the colorbar and reset the axis size if the
   ## parent figure is being deleted.
   if (ishandle (hc) && strcmp (get (hc, "type"), "axes")
-      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"),"off")))
+      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off")))
     if (strcmp (get (hc, "beingdeleted"), "off"))
       delete (hc);
     endif
     if (!isempty (ancestor (h, "axes"))
         && strcmp (get (ancestor (h, "axes"), "beingdeleted"), "off"))
-      set (ancestor (h, "axes"), "position", orig_props.position, ...
-                            "outerposition", orig_props.outerposition, ...
-                    "activepositionproperty", orig_props.activepositionproperty);
+      ax = ancestor (h, "axes");
+      units = get (ax, "units");
+      set (ax, "units", orig_props.units);
+      set (ancestor (h, "axes"), "position", orig_props.position,
+                            "outerposition", orig_props.outerposition,
+                   "activepositionproperty", orig_props.activepositionproperty);
+      set (ax, "units", units);
     endif
   endif
 endfunction
 
 function resetaxis (cax, d, ax, orig_props)
   if (ishandle (ax) && strcmp (get (ax, "type"), "axes"))
+    ## FIXME: Probably don't want to delete everyone's listeners on colormap.
+    dellistener (get (ax, "parent"), "colormap");
+    dellistener (ax, "clim");
+    dellistener (ax, "dataaspectratio");
+    dellistener (ax, "dataaspectratiomode");
+    dellistener (ax, "plotboxaspectratio");
+    dellistener (ax, "plotboxaspectratiomode");
     dellistener (ax, "position");
+
     units = get (ax, "units");
     set (ax, "units", orig_props.units);
-    set (ax, "position", orig_props.position, ...
-             "outerposition", orig_props.outerposition, ...
+    set (ax, "position", orig_props.position,
+             "outerposition", orig_props.outerposition,
              "activepositionproperty", orig_props.activepositionproperty);
     set (ax, "units", units);
   endif
 endfunction
 
-function update_colorbar_clim (h, d, hi, vert)
-  if (ishandle (h) && strcmp (get (h, "type"), "image")
-      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"),"off")))
-    clen = rows (get (get (h, "parent"), "colormap"));
-    cext = get (h, "clim");
+function update_colorbar_clim (hax, d, hi, vert)
+  if (ishandle (hax) && strcmp (get (hax, "type"), "axes")
+      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off")))
+    clen = rows (get (get (hax, "parent"), "colormap"));
+    cext = get (hax, "clim");
     cdiff = (cext(2) - cext(1)) / clen / 2;
     cmin = cext(1) + cdiff;
     cmax = cext(2) - cdiff;
@@ -226,13 +310,32 @@
   endif
 endfunction
 
+function update_colorbar_cmap (hf, d, hi, vert, init_sz)
+  persistent sz = init_sz;
+
+  if (ishandle (hf) && strcmp (get (hf, "type"), "figure")
+      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"), "off")))
+    clen = rows (get (hf, "colormap"));
+    if (clen != sz)
+      if (vert)
+        set (hi, "cdata", [1:clen]');
+      else
+        set (hi, "cdata", [1:clen]);
+      endif
+      sz = clen;
+      ## Also update limits on axis or there will be white gaps
+      update_colorbar_clim (get (hi, "parent"), d, hi, vert);
+    endif
+  endif
+endfunction
+
 function update_colorbar_axis (h, d, cax, orig_props)
 
   if (ishandle (cax) && strcmp (get (cax, "type"), "axes")
       && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"),"off")))
     loc = get (cax, "location");
     obj = get (h);
-    obj.__my_handle__ = h;
+    obj.__cbar_hax__ = h;
     obj.position = orig_props.position;
     obj.outerposition = orig_props.outerposition;
     [pos, cpos, vertical, mirror] =  ...
@@ -241,18 +344,18 @@
     if (vertical)
       if (mirror)
         set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
-             "yaxislocation", "right", "position", cpos);
+                  "yaxislocation", "right", "position", cpos);
       else
         set (cax, "xtick", [], "xdir", "normal", "ydir", "normal",
-             "yaxislocation", "left", "position", cpos);
+                  "yaxislocation", "left", "position", cpos);
       endif
     else
       if (mirror)
         set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
-             "xaxislocation", "top", "position", cpos);
+                  "xaxislocation", "top", "position", cpos);
       else
         set (cax, "ytick", [], "xdir", "normal", "ydir", "normal",
-             "xaxislocation", "bottom", "position", cpos);
+                  "xaxislocation", "bottom", "position", cpos);
       endif
     endif
 
@@ -265,8 +368,8 @@
   pos = obj.position;
   sz = pos(3:4);
 
-  if (strcmpi (obj.plotboxaspectratiomode, "manual")
-      || strcmpi (obj.dataaspectratiomode, "manual"))
+  if (strcmp (obj.plotboxaspectratiomode, "manual")
+      || strcmp (obj.dataaspectratiomode, "manual"))
     if (isempty (strfind (cbox, "outside")))
       scale = 1.0;
     else
@@ -340,8 +443,8 @@
 
   cpos = [origin, sz];
 
-  if (strcmpi (obj.plotboxaspectratiomode, "manual")
-      || strcmpi (obj.dataaspectratiomode, "manual"))
+  if (strcmp (obj.plotboxaspectratiomode, "manual")
+      || strcmp (obj.dataaspectratiomode, "manual"))
     obj.position = pos;
     actual_pos = __actual_axis_position__ (obj);
     if (strfind (cbox, "outside"))
--- a/scripts/plot/private/__actual_axis_position__.m
+++ b/scripts/plot/private/__actual_axis_position__.m
@@ -30,7 +30,7 @@
     axis_obj = get (h);
   elseif (isstruct (h))
     axis_obj = h;
-    h = axis_obj.__my_handle__;
+    h = axis_obj.__cbar_hax__;
   endif
 
   ## Get figure size in pixels