view scripts/plot/plotyy.m @ 16950:b34202b24212

fplot.m: Overhaul function for Matlab compatibility and performance (bug #38961). * scripts/plot/fplot.m: Add ability to specify n,tol,fmt in any order and simultaneously. Return data rather than plotting it if asked. Use additional test on progress of algorithm to decide whether to quit. Add %!demo and %!tests.
author Rik <rik@octave.org>
date Thu, 11 Jul 2013 09:25:54 -0700
parents ddac88d32d6a
children 140d50ed8f22
line wrap: on
line source

## Copyright (C) 2007-2012 David Bateman
##
## 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  {Function File} {} plotyy (@var{x1}, @var{y1}, @var{x2}, @var{y2})
## @deftypefnx {Function File} {} plotyy (@dots{}, @var{fun})
## @deftypefnx {Function File} {} plotyy (@dots{}, @var{fun1}, @var{fun2})
## @deftypefnx {Function File} {} plotyy (@var{h}, @dots{})
## @deftypefnx {Function File} {[@var{ax}, @var{h1}, @var{h2}] =} plotyy (@dots{})
## Plot two sets of data with independent y-axes.  The arguments @var{x1} and
## @var{y1} define the arguments for the first plot and @var{x1} and @var{y2}
## for the second.
##
## By default the arguments are evaluated with
## @code{feval (@@plot, @var{x}, @var{y})}.  However the type of plot can be
## modified with the @var{fun} argument, in which case the plots are
## generated by @code{feval (@var{fun}, @var{x}, @var{y})}.  @var{fun} can be
## a function handle, an inline function or a string of a function name.
##
## The function to use for each of the plots can be independently defined
## with @var{fun1} and @var{fun2}.
##
## If given, @var{h} defines the principal axis in which to plot the @var{x1}
## and @var{y1} data.  The return value @var{ax} is a two element vector with
## the axis handles of the two plots.  @var{h1} and @var{h2} are handles to
## the objects generated by the plot commands.
##
## @example
## @group
## x = 0:0.1:2*pi;
## y1 = sin (x);
## y2 = exp (x - 1);
## ax = plotyy (x, y1, x - 1, y2, @@plot, @@semilogy);
## xlabel ("X");
## ylabel (ax(1), "Axis 1");
## ylabel (ax(2), "Axis 2");
## @end group
## @end example
## @seealso{plot}
## @end deftypefn

function [Ax, H1, H2] = plotyy (varargin)

  ## Don't use __plt_get_axis_arg__ here as ax is a two vector for plotyy
  if (nargin > 1 && length (varargin{1}) == 2 && ishandle (varargin{1}(1))
      && ishandle (varargin{1}(2))
      && all (floor (varargin{1}) != varargin{1}))
    obj1 = get (varargin{1}(1));
    obj2 = get (varargin{1}(2));
    if (strcmp (obj1.type, "axes") || strcmp (obj2.type, "axes"))
      ax = [obj1, obj2];
      varargin(1) = [];
      if (isempty (varargin))
        varargin = {};
      endif
    else
      error ("plotyy: expecting first argument to be axes handle");
    endif
    oldh = gca ();
  else
    f = get (0, "currentfigure");
    if (isempty (f))
      f = figure ();
    endif
    ca = get (f, "currentaxes");
    if (isempty (ca))
      ax = [];
    elseif (ishandle (ca) && isprop (ca, "__plotyy_axes__"))
      ax = get (ca, "__plotyy_axes__");
    else
      ax = ca;
    endif
    if (length (ax) > 2)
      for i = 3 : length (ax)
        delete (ax (i));
      endfor
      ax = ax(1:2);
    elseif (length (ax) == 1)
      ax(2) = axes ();
      set (ax(2), "nextplot", get (ax(1), "nextplot"));
    elseif (isempty (ax))
      ax(1) = axes ();
      ax(2) = axes ();
      ca = ax(2);
    endif
    if (nargin < 2)
      varargin = {};
    endif
    oldh = ca;
  endif

  if (nargin < 4)
    print_usage ();
  endif

  unwind_protect
    [ax, h1, h2] = __plotyy__ (ax, varargin{:});
  unwind_protect_cleanup
    ## Only change back to the old axis if we didn't delete it
    if (ishandle (oldh) && strcmp (get (oldh, "type"), "axes"))
      axes (oldh);
    endif
  end_unwind_protect

  if (nargout > 0)
    Ax = ax;
    H1 = h1;
    H2 = h2;
  endif

endfunction

function [ax, h1, h2] = __plotyy__ (ax, x1, y1, x2, y2, varargin)
  if (nargin > 5)
    fun1 = varargin{1};
  else
    fun1 = @plot;
  endif
  if (nargin > 6)
    fun2 = varargin{2};
  else
    fun2 = fun1;
  endif

  xlim = [min([x1(:); x2(:)]), max([x1(:); x2(:)])];

  if (ishandle (ax(1)) && strcmp (get (ax(1), "type"), "axes"))
    axes (ax(1));
  else
    ax(1) = axes ();
  endif
  newplot ();
  h1 = feval (fun1, x1, y1);

  set (ax(1), "ycolor", getcolor (h1(1)));
  set (ax(1), "xlim", xlim);
  set (ax(1), "color", "none");

  cf = gcf ();
  set (cf, "nextplot", "add");

  if (ishandle (ax(2)) && strcmp (get (ax(2), "type"), "axes"))
    axes (ax(2));
  else
    ax(2) = axes ();
    set (ax(2), "nextplot", get (ax(1), "nextplot"));
  endif
  newplot ();

  colors = get (ax(1), "colororder");
  set (ax(2), "colororder", [colors(2:end,:); colors(1,:)]);

  if (strcmp (get (ax(1), "autopos_tag"), "subplot"))
    set (ax(2), "autopos_tag", "subplot");
  else
    set (ax, "activepositionproperty", "position");
  endif

  h2 = feval (fun2, x2, y2);
  set (ax(2), "yaxislocation", "right");
  set (ax(2), "ycolor", getcolor (h2(1)));

  if (strcmp (get(ax(1), "activepositionproperty"), "position"))
    set (ax(2), "position", get (ax(1), "position"));
  else
    set (ax(2), "outerposition", get (ax(1), "outerposition"));
    set (ax(2), "looseinset", get (ax(1), "looseinset"));
  endif

  set (ax(2), "xlim", xlim);
  set (ax(2), "color", "none");
  set (ax(2), "box", "off");

  ## Add invisible text objects that when destroyed,
  ## also remove the other axis
  t1 = text (0, 0, "", "parent", ax(1), "tag", "plotyy",
             "handlevisibility", "off", "visible", "off",
             "xliminclude", "off", "yliminclude", "off");
  t2 = text (0, 0, "", "parent", ax(2), "tag", "plotyy",
             "handlevisibility", "off", "visible", "off",
             "xliminclude", "off", "yliminclude", "off");

  set (t1, "deletefcn", {@deleteplotyy, ax(2), t2});
  set (t2, "deletefcn", {@deleteplotyy, ax(1), t1});

  addlistener (ax(1), "position", {@update_position, ax(2)});
  addlistener (ax(2), "position", {@update_position, ax(1)});
  addlistener (ax(1), "outerposition", {@update_position, ax(2)});
  addlistener (ax(2), "outerposition", {@update_position, ax(1)});
  addlistener (ax(1), "looseinset", {@update_position, ax(2)});
  addlistener (ax(2), "looseinset", {@update_position, ax(1)});
  addlistener (ax(1), "view", {@update_position, ax(2)});
  addlistener (ax(2), "view", {@update_position, ax(1)});
  addlistener (ax(1), "plotboxaspectratio", {@update_position, ax(2)});
  addlistener (ax(2), "plotboxaspectratio", {@update_position, ax(1)});
  addlistener (ax(1), "plotboxaspectratiomode", {@update_position, ax(2)});
  addlistener (ax(2), "plotboxaspectratiomode", {@update_position, ax(1)});
  addlistener (ax(1), "nextplot", {@update_nextplot, ax(2)});
  addlistener (ax(2), "nextplot", {@update_nextplot, ax(1)});

  ## Store the axes handles for the sister axes.
  if (ishandle (ax(1)) && ! isprop (ax(1), "__plotyy_axes__"))
    addproperty ("__plotyy_axes__", ax(1), "data", ax);
  elseif (ishandle (ax(1)))
    set (ax(1), "__plotyy_axes__", ax);
  else
    error ("plotyy.m: This shouldn't happen. File a bug report.");
  endif
  if (ishandle (ax(2)) && ! isprop (ax(2), "__plotyy_axes__"))
    addproperty ("__plotyy_axes__", ax(2), "data", ax);
  elseif (ishandle (ax(2)))
    set (ax(2), "__plotyy_axes__", ax);
  else
    error ("plotyy.m: This shouldn't happen. File a bug report.");
  endif
endfunction


%!demo
%! clf;
%! x = 0:0.1:2*pi;
%! y1 = sin (x);
%! y2 = exp (x - 1);
%! ax = plotyy (x,y1, x-1,y2, @plot, @semilogy);
%! xlabel ('X');
%! ylabel (ax(1), 'Axis 1');
%! ylabel (ax(2), 'Axis 2');
%! axes (ax(1));
%! text (0.5, 0.5, 'Left Axis', ...
%!       'color', [0 0 1], 'horizontalalignment', 'center');
%! axes (ax(2));
%! text (4.5, 80, 'Right Axis', ...
%!       'color', [0 0.5 0], 'horizontalalignment', 'center');

%!demo
%! clf;
%! colormap ('default');
%! x = linspace (-1, 1, 201);
%! subplot (2,2,1);
%!  plotyy (x,sin(pi*x), x,10*cos(pi*x));
%! subplot (2,2,2);
%!  surf (peaks (25));
%! subplot (2,2,3);
%!  contour (peaks (25));
%! subplot (2,2,4);
%!  plotyy (x,10*sin(2*pi*x), x,cos(2*pi*x));
%! axis square;

%!demo
%! clf;
%! x = linspace (-1, 1, 201);
%! hax = plotyy (x, sin (pi*x), x, cos (pi*x));
%! ylabel (hax(1), 'Blue on the Left');
%! ylabel (hax(2), 'Green on the Right');
%! xlabel ('xlabel');

%!demo
%! clf
%! hold on
%! t = (0:0.1:9);
%! x = sin (t);
%! y = 5 * cos (t);
%! [hax, h1, h2] = plotyy (t, x, t, y);
%! [~, h3, h4] = plotyy (t+1, x, t+1, y);
%! set ([h3, h4], 'linestyle', '--');
%! xlabel (hax(1), 'xlabel');
%! title (hax(2), 'title');
%! ylabel (hax(1), 'Left axis is Blue');
%! ylabel (hax(2), 'Right axis is Green');

function deleteplotyy (h, d, ax2, t2)
  if (ishandle (ax2) && strcmp (get (ax2, "type"), "axes")
      && (isempty (gcbf ()) || strcmp (get (gcbf (), "beingdeleted"),"off"))
      && strcmp (get (ax2, "beingdeleted"), "off"))
    set (t2, "deletefcn", []);
    delete (ax2);
  endif
endfunction

function update_nextplot (h, d, ax2)
  persistent recursion = false;
  prop = "nextplot";
  if (! recursion)
    unwind_protect
      recursion = true;
      set (ax2, prop, get (h, prop));
    unwind_protect_cleanup
      recursion = false;
    end_unwind_protect
  endif
endfunction

function update_position (h, d, ax2)
  persistent recursion = false;

  ## Don't allow recursion
  if (! recursion)
    unwind_protect
      recursion = true;
      view = get (h, "view");
      oldview = get (ax2, "view");
      plotboxaspectratio = get (h, "plotboxaspectratio");
      oldplotboxaspectratio = get (ax2, "plotboxaspectratio");
      plotboxaspectratiomode = get (h, "plotboxaspectratiomode");
      oldplotboxaspectratiomode = get (ax2, "plotboxaspectratiomode");

      if (strcmp (get(h, "activepositionproperty"), "position"))
        position = get (h, "position");
        oldposition = get (ax2, "position");
        if (! (isequal (position, oldposition) && isequal (view, oldview)))
          set (ax2, "position", position, "view", view);
        endif
      else
        outerposition = get (h, "outerposition");
        oldouterposition = get (ax2, "outerposition");
        if (! (isequal (outerposition, oldouterposition) && isequal (view, oldview)))
          set (ax2, "outerposition", outerposition, "view", view);
        endif
      endif

      if (! (isequal (plotboxaspectratio, oldplotboxaspectratio)
             && isequal (plotboxaspectratiomode, oldplotboxaspectratiomode)))
        set (ax2, "plotboxaspectratio", plotboxaspectratio);
        set (ax2, "plotboxaspectratiomode", plotboxaspectratiomode);
      endif
    unwind_protect_cleanup
      recursion = false;
    end_unwind_protect
  endif
endfunction

function color = getcolor (ax)
  obj = get (ax);
  if (isfield (obj, "color"))
    color = obj.color;
  elseif (isfield (obj, "facecolor") && ! ischar (obj.facecolor))
    color = obj.facecolor;
  elseif (isfield (obj, "edgecolor") && !  ischar (obj.edgecolor))
    color = obj.edgecolor;
  else
    color = [0, 0, 0];
  endif
endfunction