view m-toolbox/classes/@ao/.#iplot.m.1.138 @ 44:409a22968d5e default

Add unit tests
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Tue, 06 Dec 2011 18:42:11 +0100
parents f0afece42f48
children
line wrap: on
line source

% IPLOT provides an intelligent plotting tool for LTPDA.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DESCRIPTION: IPLOT provides an intelligent plotting tool for LTPDA.
%
% CALL:               hfig = iplot (a,pl)
%              [hfig, hax] = iplot (a,pl)
%         [hfig, hax, hli] = iplot (a,pl)
%
% INPUTS:      pl   - a parameter list
%              a    - input analysis object
%
% OUTPUTS:     hfig - handles to figures
%              hax  - handles to axes
%              hli  - handles to lines
%
% AO Plot Info
% ------------
%
% If an input AO has a filled plotinfo plist, then the options contained in
% therein will overide any other options. The recognised keys are:
%
%   'linestyle', 'linewidth', 'color', 'marker', 'legend_on'
%
% The possible values are all those accepted by plot.
%
%
% Notes on Parameters
% -------------------
%
%        Many of the properties take cell-array values. If the length of
%        the cell array is shorter than the number of lines to plot, the
%        remaining lines will be plotted with the default options. If the
%        cell array is of length 2 and the first cell contains the string
%        'all', then the second cell is used to set the propery of all
%        lines.
%
%
% Error parameters: If you give more than one input AO then you must
%                   specify the following parameter values in a cell-array,
%                   one cell for each input AO. Leave the cell empty to
%                   plot no errors. Each error can be a value or a vector
%                   the same length as the data vector. If you give and
%                   upper limit but not lower limit, then the errors are
%                   assumed to be symmetric (and vice versa)
%
%
% EXAMPLES:
%
% 1) Plot two time-series AOs with different colors, line styles, and widths
%
%   pl = plist('Linecolors', {'g', 'k'}, 'LineStyles', {'None', '--'}, 'LineWidths', {1, 4});
%   iplot(tsao1, tsao2, pl);
%
% 2) Plot two time-series AOs in subplots. Also override the second legend
%    text and the first line style.
%
%   pl = plist('Arrangement', 'subplots', 'LineStyles', {'--'}, 'Legends', {'', 'My Sine Wave'});
%   iplot(tsao1, tsao2, pl);
%
%
% 3) Plot two frequency-series AOs on subplots with the same Y-scales and
%    Y-ranges
%
%   pl1 = plist('Yscales', {'All', 'lin'});
%   pl2 = plist('arrangement', 'subplots', 'YRanges', {'All', [1e-6 100]});
%   iplot(fsd1, fsd2, pl1, pl2)
%
% <a href="matlab:utils.helper.displayMethodInfo('ao', 'iplot')">Parameters Description</a>
%
% VERSION:     $Id: iplot.m,v 1.138 2011/05/13 13:49:12 hewitson Exp $
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% DEPRECATED xmaths and ymaths in release 2.4

% 3) Plot two time-series AOs taking the square of the y-values of the
%    first AO and the log of the x-values of the second AO.
%
%   pl = plist('Arrangement', 'subplots', 'YMaths', 'y.^2', 'XMaths', {'', 'log(x)'});
%   iplot(tsao1, tsao2, pl);

% Math operations: You can specify rudimentary math operations to be
%                  performed on the X and Y data prior to plotting. The
%                  'all' keyword is also supported by these parameters.
%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% TODO:
%    1) Add XRange, YRange, ZRange to xyzdata
%

function varargout = iplot(varargin)
  
  import utils.const.*
  
  %% Check if this is a call for parameters
  if utils.helper.isinfocall(varargin{:})
    varargout{1} = getInfo(varargin{3});
    return
  end
  
  utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
  
  % Collect input variable names
  in_names = cell(size(varargin));
  for ii = 1:nargin,in_names{ii} = inputname(ii);end
  
  % Collect all AOs and plists
  [as, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
  [upl, pl_invars] = utils.helper.collect_objects(varargin(:), 'plist', in_names);
  if numel(upl)>1, upl = combine(upl); end
  
  %% Go through AOs and collect them into similar types
  
  tsAOs  = [];
  fsAOs  = [];
  xyAOs  = [];
  xyzAOs = [];
  cAOs   = [];
  
  consistent = 1;
  for jj = 1:numel(as)
    % Check if AOs are consistent (all containing data of the same class):
    if ~strcmpi(class(as(jj).data) , class(as(1).data) ), consistent = 0; end;
    switch class(as(jj).data)
      case 'tsdata'
        if isempty(as(jj).y)
          warning('AO %s has no data and will not be plotted', as(jj).name);
        else
          tsAOs = [tsAOs as(jj)]; %#ok<*AGROW>
        end
      case 'fsdata'
        if isempty(as(jj).y)
          warning('AO %s has no data and will not be plotted', as(jj).name);
        else
          fsAOs = [fsAOs as(jj)];
        end
      case 'xydata'
        if isempty(as(jj).y)
          warning('AO %s has no data and will not be plotted', as(jj).name);
        else
          xyAOs = [xyAOs as(jj)];
        end
      case 'xyzdata'
        if isempty(as(jj).y)
          warning('AO %s has no data and will not be plotted', as(jj).name); %#ok<*WNTAG>
        else
          xyzAOs = [xyzAOs as(jj)];
        end
      case 'cdata'
        if isempty(as(jj).y)
          warning('AO %s has no data and will not be plotted', as(jj).name);
        else
          cAOs = [cAOs as(jj)];
        end
      otherwise
        warning('!!! Unknown data type %s', class(as(jj).data));
    end
  end
  
  
  %% Now plot all the objects on separate figures
  %  (unless they're consistent and a figure handle was passed)
  
  if consistent && ~isempty(upl), fig2plot = find(upl,'Figure'); else fig2plot = []; end
  
  hfig = [];
  hax  = [];
  hli  = [];
  
  %----------- TSDATA
  if ~isempty(tsAOs)
    % get default plist
    dpl = getDefaultPlist('Time-series plot');
    % combine the plists
    pl = parse(upl, dpl);
    % Call x-y plot
    [hf, ha, hl] = xy_plot(tsAOs, pl, fig2plot);
    hfig = [hfig hf];
    hax  = [hax ha];
    hli  = [hli hl];
  end
  %----------- XYDATA
  if ~isempty(xyAOs)
    % get default plist
    dpl = getDefaultPlist('X-Y data plot');
    % combine the plists
    pl = parse(upl, dpl);
    % Call x-y plot
    [hf, ha, hl] = xy_plot(xyAOs, pl, fig2plot);
    hfig = [hfig hf];
    hax  = [hax ha];
    hli  = [hli hl];
  end
  %----------- XYZDATA
  if ~isempty(xyzAOs)
    % get default plist
    dpl = getDefaultPlist('3D plot');
    % combine the plists
    pl = parse(upl, dpl);
    % Call x-y-z plot
    [hf, ha, hl] = xyz_plot(xyzAOs, pl, fig2plot);
    hfig = [hfig hf];
    hax  = [hax ha];
    hli  = [hli hl];
  end
  %----------- CDATA
  if ~isempty(cAOs)
    % get default plist
    dpl = getDefaultPlist('Y data plot');
    % combine the plists
    pl = parse(upl, dpl);
    % Call x-y plot
    [hf, ha, hl] = y_plot(cAOs, pl, fig2plot);
    hfig = [hfig hf];
    hax  = [hax ha];
    hli  = [hli hl];
  end
  %----------- FSDATA
  if ~isempty(fsAOs)
    % get default plist
    dpl = getDefaultPlist('Frequency-series plot');
    % combine the plists
    pl = parse(upl, dpl);
    % Call fsdata plot
    [hf, ha, hl] = fs_plot(fsAOs, pl, fig2plot);
    hfig = [hfig hf];
    hax  = [hax ha];
    hli  = [hli hl];
  end
  
  %% Deal with outputs
  if nargout == 1
    varargout{1} = hfig;
  end
  if nargout == 2
    varargout{1} = hfig;
    varargout{2} = hax;
  end
  if nargout == 3
    varargout{1} = hfig;
    varargout{2} = hax;
    varargout{3} = hli;
  end
  
  if nargout > 3
    error('### Incorrect number of outputs');
  end
  
end

%--------------------------------------------------------------------------
% Plot fsdata objects
%
function varargout = fs_plot(varargin)
  
  aos = varargin{1};
  pl  = varargin{2};
  fig2plot = varargin{3};
  
  UseLatex = find(pl, 'LatexLabels');
  UseLatex = find(pl, 'LatexLabels');
  if ischar(UseLatex)
    UseLatex = eval(UseLatex);
  end
  
  % Extract parameters
  arrangement     = find(pl, 'Arrangement');
  colors          = find(pl, 'Colors');
  linecolors      = find(pl, 'LineColors');
  linestyles      = find(pl, 'LineStyles');
  markers         = find(pl, 'Markers');
  linewidths      = find(pl, 'LineWidths');
  legends         = find(pl, 'Legends');
  legendsFont     = find(pl, 'LegendFontSize');
  ylabels         = find(pl, 'YLabels');
  xlabels         = find(pl, 'XLabels');
  yscales         = find(pl, 'YScales');
  xscales         = find(pl, 'XScales');
  yranges         = find(pl, 'YRanges');
  xranges         = find(pl, 'XRanges');
  xmaths          = find(pl, 'XMaths');
  ymaths          = find(pl, 'YMaths');
  type            = find(pl, 'Function');
  legendLoc       = find(pl, 'LegendLocation');
  complexPlotType = find(pl, 'complexPlotType');
  autoErrors      = find(pl, 'AUTOERRORS');
  
  % Convert the colour if it is a character to a cell-string
  if ischar(colors)
    colors = cellstr(colors);
  end
  
  % get errors
  XerrL       = find(pl, 'XerrL');
  XerrU       = find(pl, 'XerrU');
  YerrL       = find(pl, 'YerrL');
  YerrU       = find(pl, 'YerrU');
  if ~iscell(XerrU), XerrU = {XerrU}; end
  if ~iscell(XerrL), XerrL = {XerrL}; end
  if ~iscell(YerrU), YerrU = {YerrU}; end
  if ~iscell(YerrL), YerrL = {YerrL}; end
  if (numel(XerrL) > 1 && numel(XerrL) ~= numel(aos)) || ...
      (numel(YerrL) > 1 && numel(YerrL) ~= numel(aos)) || ...
      (numel(XerrU) > 1 && numel(XerrU) ~= numel(aos)) || ...
      (numel(YerrU) > 1 && numel(YerrU) ~= numel(aos))
    error('### Please specify 1 set of errors for all AOs, or a set of errors for each AO.');
  end
  
  % check whether we want legends or not
  if iscell(legends)
    legendsOn = 1;
  else
    if strcmpi(legends, 'off')
      legendsOn = 0;
    else
      legendsOn = 1;
      legends = [];
    end
  end
  
  if ~isempty(ymaths) || ~isempty(xmaths)
    warning('The use of the ''ymaths'' and ''xmaths'' parameters is deprecated. Please perform any calculations before calling iplot.');
  end
  
  if ~iscell(linewidths), linewidths = {linewidths}; end
  if ~iscell(linestyles), linestyles = {linestyles}; end
  if ~iscell(linecolors), linecolors = {linecolors}; end
  if ~iscell(markers), markers = {markers}; end
  if ~iscell(legends), legends = {legends}; end
  if ~iscell(ylabels), ylabels = {ylabels}; end
  if ~iscell(xlabels), xlabels = {xlabels}; end
  if ~iscell(xmaths), xmaths = {xmaths}; end
  if ~iscell(ymaths), ymaths = {ymaths}; end
  if ~iscell(xscales), xscales = {xscales}; end
  if ~iscell(yscales), yscales = {yscales}; end
  if ~iscell(xranges), xranges = {xranges}; end
  if ~iscell(yranges), yranges = {yranges}; end
  
  % collect figure handles
  fsfig = []; fsax  = []; fsli  = [];
  % Legend holder
  legendStrR = [];
  legendStrI = [];
  % Helper variables
  ymin = Inf;
  ymax = -Inf;
  xmin = Inf;
  xmax = -Inf;
  complexFig   = [];
  complexAxes  = [];
  
  if ~isempty(aos)
    % Now loop over AOs
    Na = length(aos);
    % First to check if any are complex y data including any Y maths at the same
    % time.
    haveComplex = 0;
    for jj = 1:Na
      % Get data
      y = aos(jj).data.getY;
      % Do any math operations
      ymath = parseOptions(jj, ymaths, 'y');
      eval(sprintf('y = %s;', ymath));
      % Is this a complex plot?
      if ~isreal(y)
        haveComplex = 1;
      end
    end
    
    % Do we want to use a unit placeholder on the yaxis?
    yunits = aos(1).data.yunits;
    yunitPlaceholder = '[Mixed]';
    useYunitPlaceholder = false;
    if strcmpi(arrangement, 'stacked')
      for jj = 1:Na
        if yunits ~= aos(jj).data.yunits
          useYunitPlaceholder = true;
          break;
        end
      end
    end
    ylabeli = '';
    % Do we want to use a unit placeholder on the xaxis?
    xunits = aos(1).data.xunits;
    xunitPlaceholder = '[Mixed]';
    useXunitPlaceholder = false;
    if strcmpi(arrangement, 'stacked')
      for jj = 1:Na
        if xunits ~= aos(jj).data.xunits
          useXunitPlaceholder = true;
          break;
        end
      end
    end
    
    % No plot
    for jj = 1:Na
      
      if useYunitPlaceholder
        yunits = yunitPlaceholder;
      else
        yunits = aos(jj).data.yunits;
      end
      if useXunitPlaceholder
        xunits = xunitPlaceholder;
      else
        xunits = aos(jj).data.xunits;
      end      
      % set real and imag subplot handles to empty
      fsax_r = [];
      fsax_i = [];
      % Get data
      x = aos(jj).data.getX; y = aos(jj).data.getY;
      % Do any math operations
      ymath = parseOptions(jj, ymaths, 'y'); eval(sprintf('y = %s;', ymath));
      xmath = parseOptions(jj, xmaths, 'x'); eval(sprintf('x = %s;', xmath));
      % what figures do we need?
      switch arrangement
        case 'single'
          fsfig = [fsfig figure];
          col = colors{1};
          % check if this data set is real or complex
          if ~isreal(y)
            % complex means we use two subplots
            fsax_r = subplot(2,1,1); fsax_i = subplot(2,1,2);
            fsax   = [fsax fsax_r fsax_i];
            complexFig   = [complexFig get(fsax_r, 'Parent')];
            complexAxes  = [complexAxes fsax_r fsax_i];
          else
            % real means we use a single subplot
            fsax_r = subplot(1, 1, 1);
            fsax = [fsax fsax_r];
          end
          % Make sure we reset the helper variables in this case
          ymin = Inf; ymax = -Inf; xmin = Inf; xmax = -Inf;
        case 'stacked'
          if ~isempty(fig2plot), fsfig = fig2plot;
          elseif jj == 1, fsfig = figure;
          end
          % if at least one of the input fsdata AOs is complex, we need to
          % allow for subplots
          if haveComplex
            fsax_r = subplot(2,1,1,'Parent',fsfig); fsax_i = subplot(2,1,2,'Parent',fsfig);
            fsax   = [fsax_r fsax_i];
            if jj == 1
              complexFig   = [complexFig fsfig];
              complexAxes  = [complexAxes fsax_r fsax_i];
            end
          else
            fsax_r = subplot(1, 1, 1,'Parent',fsfig);
            fsax = fsax_r;
          end
          col = colors{mod(jj-1,length(colors))+1};
          hold(fsax_r, 'on');
          if ishandle(fsax_i)
            hold(fsax_i, 'on');
          end
        case 'subplots'
          if ~isempty(fig2plot), fsfig = fig2plot;
          elseif jj == 1, fsfig = figure;
          end
          c = 1+(jj-1)*2;
          sx = Na;
          sy = 2;
          % Now we have one or two subplots per input object.
          if ~isreal(y)
            fsax_r = subplot(sx, sy,c); fsax_i = subplot(sx, sy,c+1);
            fsax   = [fsax fsax_r fsax_i];
          else
            fsax_r = subplot(sx, sy, c:c+1);
            fsax   = [fsax fsax_r];
          end
          col = colors{1};
          % Make sure we reset the helper variables in this case
          ymin = Inf; ymax = -Inf; xmin = Inf; xmax = -Inf;
        otherwise
          error('### Unknown plot arrangement');
      end
      
      % Process errors
      [fcn, xu, xl, yu, yl] = process_errors(jj, size(y), type, XerrU, XerrL, YerrU, YerrL, aos(jj), autoErrors);
      
      %------- Plot the data
      
      % plot real or complex data and setup default values for scale and
      % labels as we go.
      if isreal(y)
        % if the data are real, then we don't expect negative error bars
        idx = find(yl>abs(y));
        yl(idx) = 0.999*abs(y(idx));
        switch fcn
          case 'errorbar'
            li = errorbar(fsax_r, x, y, yl, yu);
            le = false;
          case type
            li   = feval(type, fsax_r, x, y);
            le   = false; % we have no error plots
          case 'errorbarxy'
            lhs = errorbarxy(fsax_r, x, y, xu, yu, xl, yl);
            li = lhs(1);
            le = lhs(2);
        end
        fsli = [fsli li];
        ylabelr = ''; ylabeli = 'imag';
        yscaleR = 'log'; yscaleI = 'lin';
        xscaleR = 'log'; xscaleI = 'log';
      else
        switch complexPlotType
          case 'realimag'
            switch fcn
              case 'errorbar'
                ry = real(y);
                ferr = yl./abs(y);
                yl = ry.*ferr;
                yu = ry.*ferr;
                li = errorbar(fsax_r, x, ry, yl, yu);
                le = false;
              case type
                li   = feval(type, fsax_r, x, real(y));
                le   = false; % we have no error plots
              case 'errorbarxy'
                lhs = errorbarxy(fsax_r, x, real(y), xu, yu, xl, yl);
                li = lhs(1);
                le = lhs(2);
            end
            switch fcn
              case 'errorbar'
                iy = imag(y);
                ferr = yl./abs(y);
                yl = iy.*ferr;
                yu = iy.*ferr;
                li = [li errorbar(fsax_i, x, iy, yl, yu)];
                le = false;
              case type
                li   = [li feval(type, fsax_i, x, imag(y))];
                le   = false; % we have no error plots
              case 'errorbarxy'
                lhs = errorbarxy(fsax_i, x, imag(y), xu, yu, xl, yl);
                li = [li lhs(1)];
                le = lhs(2);
            end
            fsli = [fsli li];
            ylabelr = 'real'; ylabeli = 'imag';
            yscaleR = 'lin'; yscaleI = 'lin';
            xscaleR = 'log'; xscaleI = 'log';
          case 'absdeg'
            a = abs(y);
            p = utils.math.phase(y);
            % if the data are absolute values, then we don't expect
            % negative error bars
            idx = find(yl>abs(y));
            yl(idx) = 0.999*abs(y(idx));
            switch fcn
              case 'errorbar'
                li = errorbar(fsax_r, x, a, yl, yu);
                le = false;
              case type
                li   = feval(type, fsax_r, x, abs(y));
                le   = false; % we have no error plots
              case 'errorbarxy'
                lhs = errorbarxy(fsax_r, x, abs(y), xu, yu, xl, yl);
                li = lhs(1); le = lhs(2);
            end
            switch fcn
              case 'errorbar'
                ferr = yl./a;
                yl = 360.*ferr;
                yu = 360.*ferr;
                li = [li errorbar(fsax_i, x, p, yl, yu)];
                le = false;
              case type
                li   = [li feval(type, fsax_i, x, utils.math.phase(y))];
                le   = false; % we have no error plots
              case 'errorbarxy'
                lhs = errorbarxy(fsax_i, x, utils.math.phase(y), xu, yu, xl, yl);
                li = [li lhs(1)]; le = lhs(2);
            end
            fsli = [fsli li];
            ylabelr = 'Amplitude'; ylabeli = 'Phase';
            yscaleR = 'log'; yscaleI = 'lin';
            xscaleR = 'log'; xscaleI = 'log';
          case 'absrad'
            % if the data are absolute values, then we don't expect
            % negative error bars
            idx = find(yl>abs(y));
            yl(idx) = 0.999*abs(y(idx));
            switch fcn
              case 'errorbar'
                li = errorbar(fsax_r, x, abs(y), yl, yu);
                le = false; %#ok<*NASGU>
              case type
                li   = feval(type, fsax_r, x, abs(y));
                le   = false; % we have no error plots
              case 'errorbarxy'
                lhs = errorbarxy(fsax_r, x, abs(y), xu, yu, xl, yl);
                li = lhs(1); le = lhs(2);
            end
            switch fcn
              case 'errorbar'
                ferr = yl./abs(y);
                yl = pi.*ferr;
                yu = pi.*ferr;
                li = [li errorbar(fsax_i, x, angle(y), yl, yu)];
                le = false;
              case type
                li   = [li feval(type, fsax_i, x, angle(y))];
                le   = false; % we have no error plots
              case 'errorbarxy'
                lhs = errorbarxy(fsax_i, x, angle(y), xu, yu, xl, yl);
                li = [li lhs(1)];
                le = lhs(2);
            end
            fsli = [fsli li];
            ylabelr = 'Amplitude'; ylabeli = 'Phase';
            yscaleR = 'log'; yscaleI = 'lin';
            xscaleR = 'log'; xscaleI = 'log';
          otherwise
            error('### Unknown plot type for complex data');
        end
      end
      
      %------- Axis properties
      % axis counter
      c = 1+(jj-1)*2;
      
      % Set real axis ylabel
      ylstrR = parseOptions(c, ylabels, ylabelr);
      ylstrR = prepareAxisLabel(yunits, ymath, ylstrR, 'y', UseLatex);
      ylstrR = fixlabel(ylstrR);
      if UseLatex
        ylabel(fsax_r, ylstrR, 'interpreter', 'latex');
      else
        ylabel(fsax_r, ylstrR);
      end
      
      % Set imag axis ylabel
      if ishandle(fsax_i)
        ylstrI = parseOptions(c+1, ylabels, ylabeli);
        switch complexPlotType
          case 'realimag'
            ylstrI = prepareAxisLabel(yunits, ymath, ylstrI, 'y', UseLatex);
          case 'absdeg'
            ylstrI = prepareAxisLabel(unit('deg'), [], ylstrI, 'y', UseLatex);
          case 'absrad'
            ylstrI = prepareAxisLabel(unit('rad'), [], ylstrI, 'y', UseLatex);
          otherwise
        end
        ylstrI = fixlabel(ylstrI);
        if UseLatex
          ylabel(fsax_i, ylstrI, 'interpreter', 'latex');
        else
          ylabel(fsax_i, ylstrI);
        end
      end
      
      % Set xlabel
      xlstr = parseOptions(jj, xlabels, find(pl, 'XLabels'));
      xlstr = prepareAxisLabel(xunits, xmath, xlstr, 'x', UseLatex);
      xlstr = fixlabel(xlstr);
      if isreal(y)
        if UseLatex
          xlabel(fsax_r, xlstr, 'interpreter', 'latex');
        else
          xlabel(fsax_r, xlstr);
        end
      else
        % Do not draw Xlabel and XTicklabel on the real plot
        set(fsax_r, 'XTickLabel',[]);
      end
      if ~isempty(fsax_i) && ishandle(fsax_i)
        if UseLatex
          xlabel(fsax_i, xlstr, 'interpreter', 'latex');
        else
          xlabel(fsax_i, xlstr);
        end
      end
      
      % Set grid on or off
      grid(fsax_r, 'on');
      if ~isempty(fsax_i) && ishandle(fsax_i), grid(fsax_i, 'on'); end
      
      % Set Y scale
      yscaleR = parseOptions(c, yscales, yscaleR);
      yscaleI = parseOptions(c+1, yscales, yscaleI);
      set(fsax_r, 'YScale', yscaleR);
      if ~isempty(fsax_i) && ishandle(fsax_i), set(fsax_i, 'YScale', yscaleI); end
      
      % Set X scale
      xscaleR = parseOptions(c, xscales, xscaleR);
      xscaleI = parseOptions(c+1, xscales, xscaleI);
      set(fsax_r, 'XScale', xscaleR);
      if ~isempty(fsax_i) && ishandle(fsax_i)
        set(fsax_i, 'XScale', xscaleI);
      end
      
      % Set Y range
      yrange = parseOptions(c, yranges, []);
      if ~isempty(yrange)
        set(fsax_r, 'YLim', yrange);
      elseif strcmpi(yscaleR, 'log')
        [tcks,ymin,ymax] = getRealYDataTicks(y, ymin, ymax, complexPlotType, yscaleR);
        nticks = numel(tcks);
        if nticks>0 && nticks < 10
          yrange = [tcks(1) tcks(end)];
          set(fsax_r, 'YLim', yrange);
          set(fsax_r, 'Ytickmode', 'manual');
          set(fsax_r, 'Ytick', tcks);
        end
      end
      yrange = parseOptions(c+1, yranges, []);
      if ~isempty(fsax_i) && ishandle(fsax_i)
        if ~isempty(yrange)
          set(fsax_i, 'YLim', yrange);
        elseif strcmpi(yscaleI, 'log')
          
          % This doesn't really make sense since the imaginary part or
          % phase or angle will always contain negative parts. Would the
          % user really choose a log scale in that case?
          %           tcks = getImagYDataTicks(y, ymin, ymax, complexPlotType, yscaleI);
          %           if ~isempty(tcks)
          %             yrange = [tcks(1) tcks(end)];
          %             set(fsax_i, 'YLim', yrange);
          %             set(fsax_i, 'Ytickmode', 'manual');
          %             set(fsax_i, 'Ytick', tcks);
          %           end
        end
      end
      
      % Set X range
      xrange = parseOptions(c, xranges, []);
      if ~isempty(xrange)
        set(fsax_r, 'XLim', xrange);
      elseif strcmpi(xscaleR, 'log')
        xmin = min(xmin,  floor(log10(min(x(x>0)))));
        xmax = max(xmax, ceil(log10(max(x(x>0)))));
        tcks = logspace(xmin, xmax, xmax - xmin +1);
        xrange = [tcks(1) tcks(end)];
        set(fsax_r, 'XLim', xrange);
        set(fsax_r, 'Xtickmode', 'manual');
        set(fsax_r, 'Xtick', tcks);
      end
      xrange = parseOptions(c+1, xranges, []);
      if ~isempty(fsax_i) && ishandle(fsax_i)
        if ~isempty(xrange)
          set(fsax_i, 'XLim', xrange);
        elseif strcmpi(xscaleR, 'log')
          xmin = min(xmin, floor(log10(min(x(x>0)))));
          xmax = max(xmax, ceil(log10(max(x(x>0)))));
          tcks = logspace(xmin, xmax, xmax - xmin +1);
          xrange = [tcks(1) tcks(end)];
          set(fsax_i, 'XLim', xrange);
          set(fsax_i, 'Xtickmode', 'manual');
          set(fsax_i, 'Xtick', tcks);
        end
      end
      
      %------- line properties
      [col, lstyle, lwidth, mkr] = parseLineProps(jj, aos(jj).plotinfo, ...
        linecolors, col, ...
        linestyles, '-', ...
        linewidths, get(0,'DefaultLineLineWidth'), ...
        markers, 'None');
      
      % set props
      set(li, 'Color', col);
      set(li, 'LineStyle', lstyle);
      set(li, 'LineWidth', lwidth);
      if numel(x) == 1 && numel(y) == 1 && strcmp(mkr, 'None')
        mkr = '.';
      end
      set(li, 'Marker', mkr);
      
      % Set legend string
      if legendsOn
        if ~isempty(aos(jj).plotinfo)            && ...
            aos(jj).plotinfo.isparam('LEGEND_ON') && ...
            ~aos(jj).plotinfo.find('LEGEND_ON')
          for kk=1:numel(li)
            set(get(get(li(kk),'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); % Exclude line from legend
          end
        else
          lstr = parseOptions(jj, legends, makeLegendStr(aos(jj)));
          legendStrR = [legendStrR cellstr(lstr)];
          if ~isreal(y)
            legendStrI = [legendStrI cellstr(lstr)];
          end
          if strcmp(arrangement, 'single') || strcmp(arrangement, 'subplots')
            legend(fsax_r, fixlabel(legendStrR{end}), 'Location', legendLoc);
            if ~isempty(fsax_i) && ishandle(fsax_i)
              h = legend(fsax_i, fixlabel(legendStrI), 'Location', legendLoc);
            end
          end
        end
      end
      
    end % End loop over AOs
    
    % Make sure the plots are refreshed
    drawnow();
    % Trim the size of complex plots
    for jj = 1:length(complexFig)
      p_r = get(complexAxes(2*jj-1), 'Position');
      p_i = get(complexAxes(2*jj), 'Position');
      dh = (p_r(2) - (p_i(2)+p_i(4)))/3;
      set(complexAxes(2*jj-1), 'Position', [p_r(1) p_r(2)-dh p_r(3) p_r(4)+dh]);
      set(complexAxes(2*jj), 'Position', [p_i(1) p_i(2) p_i(3) p_i(4)+dh]);
    end
    
    % Process legends for stacked plots
    if legendsOn
      if strcmp(arrangement, 'stacked')
        if ~isempty(legendStrR)
          h = legend(fsax_r, fixlabel(legendStrR), 'Location', legendLoc);
          set(h, 'FontSize', legendsFont);
          if ~isempty(fsax_i) && ishandle(fsax_i)
            h = legend(fsax_i, fixlabel(legendStrI), 'Location', legendLoc);
            set(h, 'FontSize', legendsFont);
          end
        end
      end
    end
  end % End ~isempty AOs
  
  % Apply plot settings to the figure
  applyPlotSettings(fsax, fsli);
  
  % Set outputs
  if nargout > 0
    varargout{1} = fsfig;
  end
  if nargout > 1
    varargout{2} = fsax;
  end
  if nargout == 3
    varargout{3} = fsli;
  end
  if nargout > 3
    error('### Too many output arguments');
  end
end % End fs_plot

%--------------------------------------------------------------------------
% Plot tsdata and xydata objects
%
function varargout = xy_plot(varargin)
  
  aos = varargin{1};
  pl  = varargin{2};
  fig2plot = varargin{3};
  Na  = length(aos);
  
  UseLatex = find(pl, 'LatexLabels');
  if ischar(UseLatex)
    UseLatex = eval(UseLatex);
  end
  
  % Extract parameters
  arrangement = find(pl, 'Arrangement');
  linecolors  = find(pl, 'LineColors');
  colors      = find(pl, 'Colors');
  linestyles  = find(pl, 'LineStyles');
  markers     = find(pl, 'Markers');
  linewidths  = find(pl, 'LineWidths');
  legends     = find(pl, 'Legends');
  legendsFont = find(pl, 'LegendFontSize');
  ylabels     = find(pl, 'YLabels');
  xlabels     = find(pl, 'XLabels');
  xmaths      = find(pl, 'XMaths');
  ymaths      = find(pl, 'YMaths');
  yranges     = find(pl, 'YRanges');
  xranges     = find(pl, 'XRanges');
  yscales     = find(pl, 'YScales');
  xscales     = find(pl, 'XScales');
  type        = find(pl, 'Function');
  legendLoc   = find(pl, 'LegendLocation');
  xunits      = find(pl, 'Xunits');
  autoErrors      = utils.prog.yes2true(find(pl, 'AUTOERRORS'));
  
  % Convert the colour if it is a character to a cell-string
  if ischar(colors)
    colors = cellstr(colors);
  end
  
  % get errors
  XerrL       = find(pl, 'XerrL');
  XerrU       = find(pl, 'XerrU');
  YerrL       = find(pl, 'YerrL');
  YerrU       = find(pl, 'YerrU');
  if ~iscell(XerrU), XerrU = {XerrU}; end
  if ~iscell(XerrL), XerrL = {XerrL}; end
  if ~iscell(YerrU), YerrU = {YerrU}; end
  if ~iscell(YerrL), YerrL = {YerrL}; end
  if (numel(XerrL) > 1 && numel(XerrL) ~= numel(aos)) || ...
      (numel(YerrL) > 1 && numel(YerrL) ~= numel(aos)) || ...
      (numel(XerrU) > 1 && numel(XerrU) ~= numel(aos)) || ...
      (numel(YerrU) > 1 && numel(YerrU) ~= numel(aos))
    error('### Please specify 1 set of errors for all AOs, or a set of errors for each AO.');
  end
  
  torigin     = [];
  
  % check whether we want legends or not
  if iscell(legends)
    legendsOn = 1;
  else
    if strcmpi(legends, 'off')
      legendsOn = 0;
    else
      legendsOn = 1;
      legends = [];
    end
  end
  
  if ~isempty(ymaths) || ~isempty(xmaths)
    warning('The use of the ''ymaths'' and ''xmaths'' parameters is deprecated. Please perform any calculations before calling iplot.');
  end
  
  if ~iscell(linewidths), linewidths = {linewidths}; end
  if ~iscell(linestyles), linestyles = {linestyles}; end
  if ~iscell(linecolors), linecolors = {linecolors}; end
  if ~iscell(markers), markers = {markers}; end
  if ~iscell(legends), legends = {legends}; end
  if ~iscell(ylabels), ylabels = {ylabels}; end
  if ~iscell(xlabels), xlabels = {xlabels}; end
  if ~iscell(xmaths), xmaths = {xmaths}; end
  if ~iscell(ymaths), ymaths = {ymaths}; end
  if ~iscell(xranges), xranges = {xranges}; end
  if ~iscell(yranges), yranges = {yranges}; end
  if ~iscell(xscales), xscales = {xscales}; end
  if ~iscell(yscales), yscales = {yscales}; end
  if ~iscell(xunits), xunits = {xunits}; end
  
  % collect figure handles
  tsfig = []; tsax  = []; tsli  = [];
  % Legend holder
  legendStr = [];
  if ~isempty(aos)
    % Now loop over AOs to get earliest start time
    T0 = 0;
    if strcmp(arrangement, 'stacked')
      T0 = 1e50;
      for jj = 1:Na
        % Get this AO
        if isa(aos(jj).data, 'tsdata') && aos(jj).data.t0.utc_epoch_milli/1000 < T0
          T0 = floor(aos(jj).data.t0.utc_epoch_milli/1000);
        end
      end
    end
    
    % Do we want to use a unit placeholder on the yaxis?
    yunits = aos(1).data.yunits;
    yunitPlaceholder = '[Mixed]';
    useYunitPlaceholder = false;
    if strcmpi(arrangement, 'stacked')
      for jj = 1:Na
        if yunits ~= aos(jj).data.yunits
          useYunitPlaceholder = true;
          break;
        end
      end
    end
    
    % Do we want to use a unit placeholder on the xaxis?
    firstXunits = aos(1).data.xunits;
    xunitPlaceholder = '[Mixed]';
    useXunitPlaceholder = false;
    if strcmpi(arrangement, 'stacked')
      for jj = 1:Na
        if firstXunits ~= aos(jj).data.xunits
          useXunitPlaceholder = true;
          break;
        end
      end
    end
    
    % Now loop over AOs
    for jj = 1:Na
      % Get this AO
      toff = 0;
      
      if useYunitPlaceholder
        yunits = yunitPlaceholder;
      else
        yunits = aos(jj).data.yunits;
      end
      
      % what figures do we need?
      switch arrangement
        case 'single'
          tsfig = [tsfig figure];
          tsax = subplot(1,1,1);
          col = colors{1};
          if isa(aos(jj).data, 'tsdata')
            torigin = aos(jj).data.t0;
          end
        case 'stacked'
          if ~isempty(fig2plot), tsfig = fig2plot;
          elseif jj==1, tsfig = figure;
          end
          tsax = subplot(1,1,1,'Parent',tsfig);
          col = colors{mod(jj-1,length(colors))+1};
          hold on;
          % deal with time-stamps here
          if isa(aos(jj).data, 'tsdata')
            toff = aos(jj).data.t0.utc_epoch_milli/1000 - T0;
          else
            toff = 0;
          end
          if isa(aos(jj).data, 'tsdata')
            torigin = time(T0);
          end
        case 'subplots'
          if ~isempty(fig2plot), tsfig = fig2plot;
          elseif jj==1, tsfig = figure;
          end
          tsax = [tsax subplot(Na, 1, jj,'Parent',tsfig)];
          col = colors{1};
          if isa(aos(jj).data, 'tsdata')
            torigin = aos(jj).data.t0;
          end
        otherwise
          error('### Unknown plot arrangement');
      end
      
      %------- Apply math functions
      
      % Get data and add t0 offset for this time-series
      x = aos(jj).data.getX + toff;
      y = aos(jj).data.getY;
      
      % Apply any math operations
      ymath = parseOptions(jj, ymaths, 'y'); eval(sprintf('y = %s;', ymath));
      xmath = parseOptions(jj, xmaths, 'x'); eval(sprintf('x = %s;', xmath));
      
      % Process X units
      if useXunitPlaceholder
        xunit = xunitPlaceholder;
        dateTicSpec = false;
      else
        if isa(aos(jj).data, 'tsdata')
          xunitIn  = char(aos(jj).data.xunits);
          xunit    = parseOptions(jj, xunits, xunitIn);
          [x, xunit, dateTicSpec] = convertXunits(x, torigin, xunit, xunitIn);
        elseif isa(aos(jj).data, 'xydata')
          xunitIn  = char(aos(jj).data.xunits);
          xunit    = parseOptions(jj, xunits, xunitIn);
          dateTicSpec = false;
        else
          xunit = '';
          dateTicSpec = false;
        end
      end

      % Process errors
      [fcn, xu, xl, yu, yl] = process_errors(jj, size(y), type, XerrU, XerrL, YerrU, YerrL, aos(jj), autoErrors);
      
      %------- Plot the data
      
      switch fcn
        case 'errorbar'
          li = errorbar(tsax(end), x, y, yl, yu);
          le = false;
        case type
          li   = feval(type, tsax(end), x, y);
          le   = false; % we have no error plots
        case 'errorbarxy'
          lhs = errorbarxy(tsax(end), x, y, xu, yu, xl, yl);
          li = lhs(1);
          le = lhs(2);
      end
      tsli = [tsli li];
      if isa(aos(jj).data, 'tsdata')
        title(sprintf('Time origin: %s', char(torigin)));
      end
      
      %------- Add time origin to the axis handle
      
      if isempty(torigin)
        torigin = time();
      end
      
      set(tsax(end), 'UserData', torigin)
      try
        dcm_obj = datacursormode(get(tsfig(end),'Parent'));
      catch
        dcm_obj = datacursormode(tsfig(end));
      end
      set(dcm_obj, 'UpdateFcn', @utils.plottools.datacursormode)
      
      %---------- Call datetic
      if dateTicSpec
        datetick(tsax(end), 'x', xunit(2:end-1), 'keeplimits');
      end
      
      %------- Axis properties
      
      % Set ylabel
      ylstr = parseOptions(jj, ylabels, find(pl, 'YLabels'));
      ylstr = prepareAxisLabel(yunits, ymath, ylstr, 'y', UseLatex);
      if UseLatex
        ylabel(ylstr, 'interpreter', 'latex');
      else
        ylabel(ylstr);
      end
      
      % Set xlabel
      xlstr = parseOptions(jj, xlabels, find(pl, 'XLabels'));
      xlstr = prepareAxisLabel(xunit, xmath, xlstr, 'x', UseLatex);
      if UseLatex
        xlabel(xlstr, 'interpreter', 'latex');
      else
        xlabel(xlstr);
      end
      
      % Set Y range
      yrange = parseOptions(jj, yranges, []);
      if ~isempty(yrange), set(tsax(end), 'YLim', yrange); end
      
      % Set X range
      xrange = parseOptions(jj, xranges, []);
      if ~isempty(xrange), set(tsax(end), 'XLim', xrange); end
      
      % Set Y scale
      yscale = parseOptions(jj, yscales, 'lin');
      set(tsax(end), 'YScale', yscale);
      
      % Set X scale
      xscale = parseOptions(jj, xscales, 'lin');
      set(tsax(end), 'XScale', xscale);
      
      % Set grid on or off
      grid(tsax(end), 'on');
      
      
      %------- line properties
      [col, lstyle, lwidth, mkr] = parseLineProps(jj, aos(jj).plotinfo, ...
        linecolors, col, ...
        linestyles, '-', ...
        linewidths, get(0,'DefaultLineLineWidth'), ...
        markers, 'None');
      
      
      % Set line color
      set(li, 'Color', col);
      if ~isempty(le) && ishandle(le), set(le, 'Color', col); end
      % Set line style
      set(li, 'LineStyle', lstyle);
      if ishandle(le), set(le, 'LineStyle', lstyle); end
      % Set markers
      if numel(x) == 1 && numel(y) == 1 && strcmp(mkr, 'None')
        mkr = '.';
      end
      set(li, 'Marker', mkr);
      % Set line widths
      set(li, 'LineWidth', lwidth);
      if ~isempty(le) && ishandle(le), set(le, 'LineWidth', lwidth); end
      
      % Set legend string
      if legendsOn
        if ~isempty(aos(jj).plotinfo)            && ...
            aos(jj).plotinfo.isparam('LEGEND_ON') && ...
            ~aos(jj).plotinfo.find('LEGEND_ON')
          for kk=1:numel(li)
            set(get(get(li(kk),'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); % Exclude line from legend
          end
        else
          lstr = parseOptions(jj, legends, makeLegendStr(aos(jj)));
          legendStr = [legendStr cellstr(lstr)];
          % Set the legend now if we can
          if strcmp(arrangement, 'single') || strcmp(arrangement, 'subplots')
            legend(fixlabel(legendStr{end}), 'Location', legendLoc);
          end
        end
      end
    end
    
    % Process legends for stacked plots
    if legendsOn
      if strcmp(arrangement, 'stacked')
        if ~isempty(legendStr)
          h = legend(fixlabel(legendStr), 'Location', legendLoc);
          set(h, 'FontSize', legendsFont);
        end
      end
    end
  end % End if empty AOs
  
  % Apply plot settings to the figure
  applyPlotSettings(tsax, tsli);
  
  % Set outputs
  if nargout > 0
    varargout{1} = tsfig;
  end
  if nargout > 1
    varargout{2} = tsax;
  end
  if nargout == 3
    varargout{3} = tsli;
  end
  if nargout > 3
    error('### Too many output arguments');
  end
end % end xy_plot


%--------------------------------------------------------------------------
% Plot cdata objects
%
function varargout = y_plot(varargin)
  
  aos = varargin{1};
  pl  = varargin{2};
  fig2plot = varargin{3};
  
  UseLatex = find(pl, 'LatexLabels');
  UseLatex = find(pl, 'LatexLabels');
  if ischar(UseLatex)
    UseLatex = eval(UseLatex);
  end
  
  % Extract parameters
  arrangement = find(pl, 'Arrangement');
  linecolors  = find(pl, 'LineColors');
  colors      = find(pl, 'Colors');
  linestyles  = find(pl, 'LineStyles');
  markers     = find(pl, 'Markers');
  linewidths  = find(pl, 'LineWidths');
  legends     = find(pl, 'Legends');
  legendsFont = find(pl, 'LegendFontSize');
  ylabels     = find(pl, 'YLabels');
  xlabels     = find(pl, 'XLabels');
  xmaths      = find(pl, 'XMaths');
  ymaths      = find(pl, 'YMaths');
  yranges     = find(pl, 'YRanges');
  xranges     = find(pl, 'XRanges');
  yscales     = find(pl, 'YScales');
  xscales     = find(pl, 'XScales');
  type        = find(pl, 'Function');
  legendLoc   = find(pl, 'LegendLocation');
  autoErrors      = find(pl, 'AUTOERRORS');
  
  % Convert the colour if it is a character to a cell-string
  if ischar(colors)
    colors = cellstr(colors);
  end
  
  % get errors
  YerrL       = find(pl, 'YerrL');
  YerrU       = find(pl, 'YerrU');
  if ~iscell(YerrU), YerrU = {YerrU}; end
  if ~iscell(YerrL), YerrL = {YerrL}; end
  if (numel(YerrL) > 1 && numel(YerrL) ~= numel(aos)) || ...
      (numel(YerrU) > 1 && numel(YerrU) ~= numel(aos))
    error('### Please specify 1 set of errors for all AOs, or a set of errors for each AO.');
  end
  
  % check whether we want legends or not
  if iscell(legends)
    legendsOn = 1;
  else
    if strcmp(legends, 'off')
      legendsOn = 0;
    else
      legendsOn = 1;
      legends = [];
    end
  end
  
  if ~isempty(ymaths) || ~isempty(xmaths)
    warning('The use of the ''ymaths'' and ''xmaths'' parameters is deprecated. Please perform any calculations before calling iplot.');
  end
  
  if ~iscell(linewidths), linewidths = {linewidths}; end
  if ~iscell(linestyles), linestyles = {linestyles}; end
  if ~iscell(linecolors), linecolors = {linecolors}; end
  if ~iscell(markers), markers = {markers}; end
  if ~iscell(legends), legends = {legends}; end
  if ~iscell(ylabels), ylabels = {ylabels}; end
  if ~iscell(xlabels), xlabels = {xlabels}; end
  if ~iscell(xmaths), xmaths = {xmaths}; end
  if ~iscell(ymaths), ymaths = {ymaths}; end
  if ~iscell(xranges), xranges = {xranges}; end
  if ~iscell(yranges), yranges = {yranges}; end
  if ~iscell(xscales), xscales = {xscales}; end
  if ~iscell(yscales), yscales = {yscales}; end
  
  % collect figure handles
  cfig = []; cax  = []; cli  = [];
  % Legend holder
  legendStr = [];
  if ~isempty(aos)
    
    % Now loop over AOs
    Na = length(aos);
    
    % Do we want to use a unit placeholder on the yaxis?
    yunits = aos(1).data.yunits;
    yunitPlaceholder = '[Mixed]';
    useYunitPlaceholder = false;
    if strcmpi(arrangement, 'stacked')
      for jj = 1:Na
        if yunits ~= aos(jj).data.yunits
          useYunitPlaceholder = true;
          break;
        end
      end
    end
    
    for jj = 1:Na
      if useYunitPlaceholder
        yunits = yunitPlaceholder;
      else
        yunits = aos(jj).data.yunits;
      end
      
      % what figures do we need?
      switch arrangement
        case 'single'
          cfig = [cfig figure];
          cax = subplot(1,1,1);
          col = colors{1};
        case 'stacked'
          if ~isempty(fig2plot), cfig = fig2plot;
          elseif jj==1, cfig = figure;
          end
          %           if jj==1, cfig = figure; end
          cax = subplot(1,1,1,'Parent',cfig);
          col = colors{mod(jj-1,length(colors))+1};
          hold on;
        case 'subplots'
          if ~isempty(fig2plot), cfig = fig2plot;
          elseif jj==1, cfig = figure;
          end
          %           if jj == 1, cfig = figure; end
          cax = [cax subplot(Na, 1, jj)];
          col = colors{1};
        otherwise
          error('### Unknown plot arrangement');
      end
      
      % Get data
      if isreal(aos(jj).data.getY)
        x = 1:length(aos(jj).data.getY);
        y = aos(jj).data.getY;
      else
        x = real(aos(jj).data.getY);
        y = imag(aos(jj).data.getY);
      end
      
      %------- Apply math functions
      ymath = parseOptions(jj, ymaths, 'y'); eval(sprintf('y = %s;', ymath));
      xmath = parseOptions(jj, xmaths, 'x'); eval(sprintf('x = %s;', xmath));
      % Process errors
      [fcn, xu, xl, yu, yl] = process_errors(jj, size(y), type, {[]}, {[]}, YerrU, YerrL, aos(jj), autoErrors);
      
      %------- Plot the data
      switch fcn
        case 'errorbar'
          lhs = errorbarxy(cax(end), x, y,zeros(size(yl)),yu,zeros(size(yl)),yl);
          idcs = lhs(1);
          le = lhs(2);
        case type
          idcs   = feval(type, cax(end), x, y);
          le   = false; % we have no error plots
        case 'errorbarxy'
          lhs = errorbarxy(cax(end), x, y, xu, yu, xl, yl);
          idcs = lhs(1);
          le = lhs(2);
      end
      %------- Plot the data
      %       idcs = feval(type, cax(end), x, y);
      %       cli = [cli idcs(1:end).'];
      
      %------- Axis properties
      
      % Set ylabel
      ylstr = parseOptions(jj, ylabels, find(pl, 'YLabels'));
      ylstr = prepareAxisLabel(yunits, ymath, ylstr, 'y', UseLatex);
      if UseLatex
        ylabel(ylstr, 'interpreter', 'latex');
      else
        ylabel(ylstr);
      end
      
      % Set xlabel
      xlstr = parseOptions(jj, xlabels, find(pl, 'XLabels'));
      xlstr = prepareAxisLabel(unit('Index'), xmath, xlstr, 'x', UseLatex);
      if UseLatex
        xlabel(xlstr, 'interpreter', 'latex');
      else
        xlabel(xlstr);
      end
      
      % Set Y scale
      yscale = parseOptions(jj, yscales, 'lin');
      set(cax(end), 'YScale', yscale);
      
      % Set X scale
      xscale = parseOptions(jj, xscales, 'lin');
      set(cax(end), 'XScale', xscale);
      
      % Set Y range
      yrange = parseOptions(jj, yranges, []);
      if ~isempty(yrange), set(cax(end), 'YLim', yrange); end
      
      % Set X range
      xrange = parseOptions(jj, xranges, []);
      if ~isempty(xrange), set(cax(end), 'XLim', xrange); end
      
      % Set grid on or off
      grid(cax(end), 'on');
      
      %------- line properties
      
      [col, lstyle, lwidth, mkr] = parseLineProps(jj, aos(jj).plotinfo, ...
        linecolors, col, ...
        linestyles, '-', ...
        linewidths, get(0,'DefaultLineLineWidth'), ...
        markers, 'None');
      
      % Overide line colors with user defined colors
      set(idcs, 'Color', col);
      if ~isempty(le) && ishandle(le), set(le, 'Color', col); end
      
      % Set line style
      set(idcs, 'LineStyle', lstyle);
      if ishandle(le), set(le, 'LineStyle', lstyle); end
      % Set Markers
      if numel(x) == 1 && numel(y) == 1 && strcmp(mkr, 'None')
        mkr = '.';
      end
      set(idcs, 'Marker', mkr);
      % Set line widths
      set(idcs, 'LineWidth', lwidth);
      if ~isempty(le) && ishandle(le), set(le, 'LineWidth', lwidth); end
      
      % Set legend string
      if legendsOn
        if ~isempty(aos(jj).plotinfo)            && ...
            aos(jj).plotinfo.isparam('LEGEND_ON') && ...
            ~aos(jj).plotinfo.find('LEGEND_ON')
          for kk=1:numel(li)
            set(get(get(li(kk),'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); % Exclude line from legend
          end
        else
          lstr = parseOptions(jj, legends, makeLegendStr(aos(jj)));
          legendStr = [legendStr cellstr(lstr)];
          % Set the legend now if we can
          if strcmp(arrangement, 'single') || strcmp(arrangement, 'subplots')
            legend(fixlabel(legendStr{end}), 'Location', legendLoc);
          end
        end
      end
    end % End AO loop
    
    % Process legends for stacked plots
    if legendsOn
      if strcmp(arrangement, 'stacked')
        if ~isempty(legendStr)
          h = legend(fixlabel(legendStr), 'Location', legendLoc);
          set(h, 'FontSize', legendsFont);
        end
      end
    end
    
  end
  
  % Apply plot settings to the figure
  applyPlotSettings(cax, cli);
  
  % Set outputs
  if nargout > 0
    varargout{1} = cfig;
  end
  if nargout > 1
    varargout{2} = cax;
  end
  if nargout == 3
    varargout{3} = cli;
  end
  if nargout > 3
    error('### Too many output arguments');
  end
end % End y_plot

%--------------------------------------------------------------------------
% Plot xyzdata objects
%
function varargout = xyz_plot(varargin)
  
  aos = varargin{1};
  pl  = varargin{2};
  fig2plot = varargin{3};
  
  UseLatex = find(pl, 'LatexLabels');
  if ischar(UseLatex)
    UseLatex = eval(UseLatex);
  end
  
  % Extract parameters
  arrangement = find(pl, 'Arrangement');
  legends     = find(pl, 'Legends');
  legendsFont = find(pl, 'LegendFontSize');
  zlabels     = find(pl, 'ZLabels');
  ylabels     = find(pl, 'YLabels');
  xlabels     = find(pl, 'XLabels');
  xmaths      = find(pl, 'XMaths');
  ymaths      = find(pl, 'YMaths');
  zmaths      = find(pl, 'ZMaths');
  legendLoc   = find(pl, 'LegendLocation');
  yranges     = find(pl, 'YRanges');
  xranges     = find(pl, 'XRanges');
  zranges     = find(pl, 'ZRanges');
  zscales     = find(pl, 'ZScales');
  yscales     = find(pl, 'YScales');
  xscales     = find(pl, 'XScales');
  invertY     = find(pl, 'InvertY');
  
  % check whether we want legends or not
  if iscell(legends)
    legendsOn = 1;
  else
    if strcmp(legends, 'off')
      legendsOn = 0;
    else
      legendsOn = 1;
      legends = [];
    end
  end
  
  if ~isempty(ymaths) || ~isempty(xmaths)
    warning('The use of the ''ymaths'' and ''xmaths'' parameters is deprecated. Please perform any calculations before calling iplot.');
  end
  
  if ~iscell(legends), legends = {legends}; end
  if ~iscell(ylabels), ylabels = {ylabels}; end
  if ~iscell(xlabels), xlabels = {xlabels}; end
  if ~iscell(zlabels), zlabels = {zlabels}; end
  if ~iscell(xmaths), xmaths = {xmaths}; end
  if ~iscell(ymaths), ymaths = {ymaths}; end
  if ~iscell(zmaths), zmaths = {zmaths}; end
  if ~iscell(xranges), xranges = {xranges}; end
  if ~iscell(yranges), yranges = {yranges}; end
  if ~iscell(zranges), zranges = {zranges}; end
  if ~iscell(xscales), xscales = {xscales}; end
  if ~iscell(yscales), yscales = {yscales}; end
  if ~iscell(zscales), zscales = {zscales}; end
  
  
  % collect figure handles
  tdfig = [];
  tdax  = [];
  tdli  = [];
  
  % Legend holder
  legendStr = [];
  
  if ~isempty(aos)
    
    % Now loop over AOs
    Na = length(aos);
    for jj = 1:Na
      % what figures do we need?
      switch arrangement
        case 'single'
          tdfig = [tdfig figure];
          tdax = subplot(1,1,1);
        case 'subplots'
          if ~isempty(fig2plot), tdfig = fig2plot;
          elseif jj==1, tdfig = figure;
          end
          %           if jj == 1, tdfig = figure; end
          tdax = [tdax subplot(Na, 1, jj)];
        otherwise
          warning('!!! Plot arrangement ''%s'' not supported on XYZ plots. Using ''single'' instead.', arrangement);
          arrangment = 'single';
          tdfig = [tdfig figure];
          tdax = subplot(1,1,1);
      end
      
      %------- Apply math functions
      x = aos(jj).data.x;
      y = aos(jj).data.getY;
      z = aos(jj).data.z;
      
      ymath = parseOptions(jj, ymaths, 'y'); eval(sprintf('y = %s;', ymath));
      xmath = parseOptions(jj, xmaths, 'x'); eval(sprintf('x = %s;', xmath));
      zmath = parseOptions(jj, zmaths, 'z'); eval(sprintf('z = %s;', zmath));
      
      %------- Plot the data
      
      idcs = pcolor(x,y,z);
      tdli = [tdli idcs(1:end).'];
      
      % plot properties
      set(idcs, 'EdgeColor', 'none');
      
      %------- Axis properties
      
      % Reverse y-direction for spectrograms
      if invertY
        set(tdax(end), 'YDir', 'reverse');
      end
      
      % Set ylabel
      ylstr = parseOptions(jj, ylabels, find(pl, 'YLabels'));
      ylstr = prepareAxisLabel(aos(jj).data.yunits, ymath, ylstr, 'y', UseLatex);
      if UseLatex
        ylabel(ylstr, 'interpreter', 'latex');
      else
        ylabel(ylstr);
      end
      
      % Set xlabel
      xlstr = parseOptions(jj, xlabels, find(pl, 'XLabels'));
      xlstr = prepareAxisLabel(aos(jj).data.xunits, xmath, xlstr, 'x', UseLatex);
      if UseLatex
        xlabel(xlstr, 'interpreter', 'latex');
      else
        xlabel(xlstr);
      end
      
      % Set grid on or off
      grid(tdax(end), 'on');
      
      % Set title string
      if legendsOn
        if ~isempty(aos(jj).plotinfo)            && ...
            aos(jj).plotinfo.isparam('LEGEND_ON') && ...
            ~aos(jj).plotinfo.find('LEGEND_ON')
          for kk=1:numel(li)
            set(get(get(li(kk),'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); % Exclude line from legend
          end
        else
          lstr = parseOptions(jj, legends, makeLegendStr(aos(jj)));
          legendStr = [legendStr cellstr(lstr)];
          % Set the legend now if we can
          title(legendStr{end});
        end
      end
      
      % Set colorbars
      hc = colorbar('peer', tdax(end));
      zlstr = parseOptions(jj, zlabels, find(pl, 'Zlabels'));
      zlstr = prepareAxisLabel(aos(jj).data.zunits, zmath, zlstr, 'z', UseLatex);
      ylh = get(hc, 'YLabel');
      set(ylh, 'String', zlstr);
      set(ylh, 'Fontsize', get(tdax(end), 'Fontsize'))
      set(ylh, 'FontName', get(tdax(end), 'FontName'))
      set(ylh, 'FontAngle', get(tdax(end), 'FontAngle'))
      set(ylh, 'FontWeight', get(tdax(end), 'FontWeight'))
      
      
      % Set Y scale
      yscale = parseOptions(jj, yscales, 'lin');
      set(tdax(end), 'YScale', yscale);
      
      % Set X scale
      xscale = parseOptions(jj, xscales, 'lin');
      set(tdax(end), 'XScale', xscale);
      
      % Set Z scale
      zscale = parseOptions(jj, zscales, 'lin');
      set(tdax(end), 'ZScale', zscale);
      
      % Set Y range
      yrange = parseOptions(jj, yranges, []);
      if ~isempty(yrange), set(tdax(end), 'YLim', yrange); end
      
      % Set X range
      xrange = parseOptions(jj, xranges, []);
      if ~isempty(xrange), set(tdax(end), 'XLim', xrange); end
      
      % Set Z range
      zrange = parseOptions(jj, zranges, []);
      if ~isempty(zrange), set(tdax(end), 'CLim', zrange); end
    end
  end
  
  % Apply plot settings to the figure
  applyPlotSettings(tdax, tdli);
  
  % Set outputs
  if nargout > 0
    varargout{1} = tdfig;
  end
  if nargout > 1
    varargout{2} = tdax;
  end
  if nargout == 3
    varargout{3} = tdli;
  end
  if nargout > 3
    error('### Too many output arguments');
  end
end % end xyz_plot

%--------------------------------------------------------------------------
% Get Info Object
%--------------------------------------------------------------------------
function ii = getInfo(varargin)
  if nargin == 1 && strcmpi(varargin{1}, 'None')
    sets = {};
    pl   = [];
  elseif nargin == 1&& ~isempty(varargin{1}) && ischar(varargin{1})
    sets{1} = varargin{1};
    pl = getDefaultPlist(sets{1});
  else
    sets = {'Time-series Plot', 'Frequency-series Plot', 'Y Data Plot', 'X-Y Data Plot', '3D Plot'};
    % get plists
    pl(size(sets)) = plist;
    for k = 1:numel(sets)
      pl(k) =  getDefaultPlist(sets{k});
    end
  end
  % Build info object
  ii = minfo(mfilename, 'ao', 'ltpda', utils.const.categories.output, '$Id: iplot.m,v 1.138 2011/05/13 13:49:12 hewitson Exp $', sets, pl);
  ii.setModifier(false);
  ii.setOutmin(0);
end

% Parse line properties from plist, or defaults
function [col, lstyle, lwidth, mkr] = parseLineProps(jj, pli, ...
    linecolors, dcol, ...
    linestyles, dlstyle, ...
    linewidths, dlwidth, ...
    markers, dmkr)
  
  if isempty(pli)
    pli = plist;
  end
  
  % Set line color but overide with user colors
  col = pli.find('color');
  if isempty(col)
    col = parseOptions(jj, linecolors, dcol);
  end
  
  % Set line style
  lstyle = pli.find('linestyle');
  if isempty(lstyle)
    lstyle = parseOptions(jj, linestyles, dlstyle);
  end
  
  % Set line widths
  lwidth = pli.find('linewidth');
  if isempty(lwidth)
    lwidth = parseOptions(jj, linewidths, dlwidth);
  end
  
  % Set markers
  mkr = pli.find('marker');
  if isempty(mkr)
    mkr = parseOptions(jj, markers, dmkr);
  end
  
end


%--------------------------------------------------------------------------
% Get Default Plist
%--------------------------------------------------------------------------
function plout = getDefaultPlist(set)
  persistent pl;
  persistent lastset;
  if exist('pl', 'var')==0 || isempty(pl) || ~strcmp(lastset, set)
    pl = buildplist(set);
    lastset = set;
  end
  pl.pset('LEGENDFONTSIZE', LTPDAprefs.legendFontSize);
  plout = pl;
end

function out = buildplist(set)
  
  % Get the LTPDA color set for lines
  colors = getappdata(0,'ltpda_default_plot_colors');
  
  out = plist();
  
  % Figure
  p = param({'Figure',['The handle of the figure to plot in to. This will be ignored if the AOs to plot are inconsistent,<br>'...
    'containing different class of data (such as tsdata and fsdata), or if the ''arrangement''<br>',...
    'parameter is passed as ''single''.']}, paramValue.EMPTY_DOUBLE);
  out.append(p);
  
  % Colors
  p = param({'Colors', 'A list of colors which will be cycled through for each line in a plot.'}, colors);
  out.append(p);
  
  % Arrangement
  p = param({'Arrangement',['Select the plot layout:<ul>',...
    '<li>''single''   - plot all AOs on individual figures</li>',...
    '<li>''stacked''  - plot all AOs on the same axes</li>',...
    '<li>''subplots'' - plot all AOs on subplots</li>'...
    '</ul>']}, {1, {'stacked', 'single', 'subplots'}, paramValue.SINGLE});
  out.append(p);
  
  % Function
  p = param({'Function',['Specify the plot function:<ul>',...
    '<li>''plot''</li>', ...
    '<li>''stairs''</li>',...
    '<li>''stem''</li>',...
    '</ul>'...
    '[*** doesn''t work for xyzdata AOs]']}, {1, {'plot', 'stairs', 'stem'}, paramValue.SINGLE});
  out.append(p);
  
  % LineColors
  p = param({'LineColors', ['A cell-array of color definitions, one for each trace.<br>'...
    'Give an empty string to use the default color.']}, ...
    {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % LineColors
  p = param({'LineStyles', ['A cell-array of line styles, one for each trace.<br>'...
    'Give an empty string to use the default style.']}, ...
    {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % Markers
  p = param({'Markers', ['A cell-array of markers, one for each trace.']}, ...
    {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % LineWidths
  p = param({'LineWidths', ['A cell-array of line widths, one for each trace.<br>'...
    'Give an empty string to use the default line width.']}, ...
    {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % Legends
  p = param({'Legends', ['Give a cell-array of strings to be used for<br>'...
    'the plot legends. If a cell contains an empty<br>'...
    'string, the default legend string is built.<br>'...
    'If a single string ''off'' is given instead of a<br>'...
    'cell-array, then the legends are all switched off.']}, ...
    {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % LegendLocation
  p = param({'LegendLocation','Choose the legend location.'}, ...
    {5, {'North', 'South', 'East', 'West', ...
    'NorthEast', 'NorthWest', 'SouthEast', 'SouthWest', ...
    'NorthOutside', 'SouthOutside', 'EastOutside', 'WestOutside', ...
    'NorthEastOutside', 'NorthWestOutside', 'SouthEastOutside', ...
    'SouthWestOutside', 'Best', 'BestOutside'}, paramValue.SINGLE});
  out.append(p);
  
  % LegendFontSize
  p = param({'LegendFontSize','Choose the legend font size.'}, ...
    {1, {LTPDAprefs.legendFontSize}, paramValue.SINGLE});
  out.append(p);
  
  % XerrL
  p = param({'XerrL','Lower bound error values for the X data points.'}, paramValue.EMPTY_DOUBLE);
  out.append(p);
  
  % XerrU
  p = param({'XerrU','Upper bound error values for the X data points.'}, paramValue.EMPTY_DOUBLE);
  out.append(p);
  
  % YerrL
  p = param({'YerrL','Lower bound error values for the Y data points.'}, paramValue.EMPTY_DOUBLE);
  out.append(p);
  
  % YerrU
  p = param({'YerrU','Upper bound error values for the Y data points.'}, paramValue.EMPTY_DOUBLE);
  out.append(p);
  
  % XScales
  p = param({'XScales', ['A cell-array specifying the scale to be used on each x-axis.<br>'...
    'For example, {''lin'', ''log''}']}, {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % YScales
  p = param({'YScales', ['A cell-array specifying the scale to be used on each y-axis.<br>'...
    'For example, {''lin'', ''log''}']}, {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % XRanges
  p = param({'XRanges', ['A cell-array specifying the ranges to be displayed on each x-axis.<br>'...
    'For example, {[0 1], [-4 4]}.']}, {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % YRanges
  p = param({'YRanges', ['A cell-array specifying the ranges to be displayed on each y-axis.<br>'...
    'For example, {[0 1], [-4 4]}.']}, {1, {''}, paramValue.OPTIONAL});
  out.append(p);
  
  % LatexLabels
  p = param({'LatexLabels','Use latex interpreter for axis labels.'}, paramValue.TRUE_FALSE);
  p.val.setValIndex(2);
  out.append(p);
  
  % YMaths
  p = param({'YMaths',['Specify math operations to perform on the data vector ''y''. [DEPRECATED]<br>',...
    'For example, <tt>plist(''Ymaths'', ''sqrt(y)'')</tt>.']}, paramValue.EMPTY_STRING);
  out.append(p);
  
  
  % No auto-errors
  p = param({'AUTOERRORS',['If the AO contains errors, they will be plotted. You can avoid plotting the <br>',...
    'errors by setting this to false.']}, paramValue.FALSE_TRUE);
  out.append(p);
  
  switch lower(set)
    case 'frequency-series plot'
      % ComplexPlotType
      p = param({'complexPlotType',['Specify how to plot complex data. Choose from:<ul>',...
        '<li>''realimag''</li>',...
        '<li>''absdeg''</li>',...
        '<li>''absrad''</li>'...
        '</ul>']}, {1, {'absdeg', 'realimag', 'absrad'}, paramValue.SINGLE});
      out.append(p);
      
      % Xlabel
      p = param({'XLabels',['Specify the labels to be used on the x-axes. The units are added from<br>',...
        'the data object ''xunits'' property.']}, paramValue.STRING_VALUE('Frequency'));
      out.append(p);
      % ylabels
      p = param({'YLabels',['Specify the labels to be used on the y-axes. The units are added from<br>',...
        'the data object ''yunits'' property.']}, paramValue.STRING_VALUE(''));
      out.append(p);
      
      % XMaths
      p = param({'XMaths',['Specify math operations to perform on the data vector ''x''. [DEPRECATED]<br>',...
        'For example, <tt>plist(''Xmaths'', ''abs(x)'')</tt>.']}, paramValue.EMPTY_STRING);
      out.append(p);
      
    case 'time-series plot'
      
      % Xlabel
      p = param({'XLabels',['Specify the labels to be used on the x-axes. The units are added from<br>',...
        'the data object ''xunits'' property.']}, paramValue.STRING_VALUE('Time'));
      out.append(p);
      
      % Ylabels
      p = param({'YLabels',['Specify the labels to be used on the y-axes. The units are added from<br>',...
        'the data object ''yunits'' property.']}, paramValue.STRING_VALUE('Amplitude'));
      out.append(p);
      
      % Xunits
      p = param({'Xunits', ['Specify the units of time on the x-axis as<ul>'...
        '<li>''us''        - microseconds<li>' ...
        '<li>''ms''        - milliseconds<li>' ...
        '<li>''s''         - seconds<li>' ...
        '<li>''m''         - minutes<li>' ...
        '<li>''h''         - hours<li>' ...
        '<li>''D''         - days<li>' ...
        '<li>''M''         - months<li>' ...
        '<li>''HH:MM:SS''  - using a date/time format</li>' ...
        '</ul>']}, {3, {'us', 'ms', 's', 'm', 'h', 'D', 'M', 'HH:MM:SS', 'yyyy-mm-dd HH:MM:SS'}, paramValue.OPTIONAL});
      out.append(p);
      
    case 'x-y data plot'
      % Xlabel
      p = param({'XLabels',['Specify the labels to be used on the x-axes. The units are added from<br>',...
        'the data object ''xunits'' property.']}, paramValue.STRING_VALUE('X-data'));
      out.append(p);
      
      % Ylabels
      p = param({'YLabels',['Specify the labels to be used on the y-axes. The units are added from<br>',...
        'the data object ''yunits'' property.']}, paramValue.STRING_VALUE('Y-data'));
      out.append(p);
      
      % XMaths
      p = param({'XMaths',['Specify math operations to perform on the data vector ''x''. [DEPRECATED]<br>',...
        'For example, <tt>plist(''Xmaths'', ''abs(x)'')</tt>.']}, paramValue.EMPTY_STRING);
      out.append(p);
      
    case '3d plot'
      
      out.pset('arrangement', 'single');
      
      % Xlabel
      p = param({'XLabels',['Specify the labels to be used on the x-axes. The units are added from<br>',...
        'the data object ''xunits'' property.']}, paramValue.STRING_VALUE('Time'));
      out.append(p);
      
      % Ylabels
      p = param({'YLabels',['Specify the labels to be used on the y-axes. The units are added from<br>',...
        'the data object ''yunits'' property.']}, paramValue.STRING_VALUE('Frequency'));
      out.append(p);
      
      % Zlabels
      p = param({'ZLabels',['Specify the labels to be used on the z-axes. The units are added from<br>',...
        'the data object ''zunits'' property.']}, paramValue.STRING_VALUE('Amplitude'));
      out.append(p);
      
      % XMaths
      p = param({'XMaths',['Specify math operations to perform on the data vector ''x''. [DEPRECATED]<br>',...
        'For example, <tt>plist(''Xmaths'', ''abs(x)'')</tt>.']}, paramValue.EMPTY_STRING);
      out.append(p);
      
      % ZMaths
      p = param({'ZMaths',['Specify math operations to perform on the data vector ''z''. [DEPRECATED]<br>',...
        'For example, <tt>plist(''Zmaths'', ''abs(z)'')</tt>.']}, paramValue.EMPTY_STRING);
      out.append(p);
      
      % ZScales
      p = param({'ZScales', ['A cell-array specifying the scale to be used on each z-axis.<br>'...
        'For example, {''lin'', ''log''}']}, {1, {''}, paramValue.OPTIONAL});
      out.append(p);
      
      % ZRanges
      p = param({'ZRanges', ['A cell-array specifying the ranges to be displayed on each z-axis.<br>'...
        'For example, {[0 1], [-4 4]}.']}, {1, {''}, paramValue.OPTIONAL});
      out.append(p);
      
      % Invert y-axis
      p = param({'InvertY', ['Invert the y-axis or not.']}, paramValue.TRUE_FALSE);
      out.append(p);
      
      out.remove('linestyles');
      out.remove('linewidths');
      out.remove('linecolors');
      out.remove('markers');
      
    case 'y data plot'
      % Xlabel
      p = param({'XLabels',['Specify the labels to be used on the x-axes. The units are added from<br>',...
        'the data object ''xunits'' property.']}, paramValue.STRING_VALUE('Sample'));
      out.append(p);
      
      % Ylabels
      p = param({'YLabels',['Specify the labels to be used on the y-axes. The units are added from<br>',...
        'the data object ''yunits'' property.']}, paramValue.STRING_VALUE('Value'));
      out.append(p);
    otherwise
      error('### Unknown set [%s]', set);
  end
end


function name = makeLegendStr(a)
  
  name = utils.plottools.label(a.name);
  desc = utils.plottools.label(a.description);
  
  if isempty(name)
    name = '?';
  end
  
  if ~isempty(desc) && LTPDAprefs.includeDescription
    name = [name ': ' desc];
  end
  
  
end

% Perform some substitutions on the labels
function ss = fixlabel(ss)
  
  MAX_LENGTH = 100;
  wasCell = true;
  if ~iscell(ss)
    ss = {ss};
    wasCell = false;
  end
  
  for kk = 1:numel(ss)
    s = ss{kk};
    if ~isempty(s)
      % Replace all ^(...) with ^{...}
      jj = 1;
      while jj < numel(s)
        if strcmp(s(jj:jj+1), '^(')
          % find next )
          for k = 1:numel(s)-jj+1
            if s(jj+k) == ')'
              s(jj+1) = '{';
              s(jj+k) = '}';
              break;
            end
          end
        end
        jj = jj + 1;
      end
      % Replace all .^ with ^
      s = strrep(s, '.^', '^');
      
      % reduce size
      if length(s) > MAX_LENGTH
        s = s(1:MAX_LENGTH-3);
        if s(end) == '\'
          s = s(1:end-1)
        end
        s = [ s '...' ];
      end
      
    end
    ss(kk) = {s};
  end

  if ~wasCell
    ss = ss{1};
  end
  
end

%-----------------------------------------------
% Change X data for time-series according to the specified xunits
function [x, xunit, dateTicSpec] = convertXunits(x, t0, xunit, xunitIn)
  
  dateTicSpec = false;
  
  xunit = strtrim(xunit);
  xunitIn = strtrim(xunitIn);
  if ~strcmpi(strtrim(xunitIn), '[s]')
    warning('### I can only convert from [s] to %s - ignoring requested Xunit', xunit);
    xunit = xunitIn;
  else
    switch strtrim(xunit)
      case {'[us]', 'us'}
        x = x .* 1e6;
      case {'[ms]', 'ms'}
        x = x .* 1e3;
      case {'[s]', 's'}
      case {'[m]', 'm'}
        x = x ./ 60;
      case {'[h]', 'h'}
        x = x ./ 3600;
      case {'[D]', 'D'}
        x = x ./ 86400;
      otherwise
        % then we have a datetic spec
        dateTicSpec = true;
        % first convert x data to serial date
        st = format(t0, 'yyyy-mm-dd hh:mm:ss');
        st = regexp(st, ' ', 'split');
        st = [st{1} ' ' st{2}];
        t0 = datenum(st); % get t0 as a serial date
        x = t0 + x./86400; % convert x to days
    end
  end
  if xunit(1) ~= '['
    xunit = ['[' xunit];
  end
  if xunit(end) ~= ']'
    xunit = [xunit ']'];
  end
  %                          'us'        - microseconds
  %                          'ms'        - milliseconds
  %                          's'         - seconds [default]
  %                          'm'         - minutes
  %                          'h'         - hours
  %                          'D'         - days
  %                          'M'         - months
  %                          'HH:MM:SS'  - using a date/time format
  
end

%----------------------------------------
% Prepare an axis label
function lstr = prepareAxisLabel(units, math, lstr, axis, UseLatex)
  if isa(units, 'unit')
    
    if ismac && UseLatex
      units = units.tolabel;
    else
      units = {fixlabel(char(units))};
    end
  else
    units = {units};
  end
  if ~isempty(math)
    if ~isempty(lstr)
      lstr = strrep(math, axis, lstr);
    end
    lstr = [fixlabel(lstr) '  ' units{1} ];
  else
    lstr = [fixlabel(lstr) '  ' units{1}];
  end
end

% Parse cell-array of options
function opt = parseOptions(varargin) %jj, opts, dopt
  
  jj    = varargin{1};
  opts = varargin{2};
  dopt = varargin{3};
  opt   = dopt;
  
  if ~iscell(opts)
    opts = {opts};
  end
  Nopts = numel(opts);
  
  % First look for the 'all' keyword
  if Nopts == 2 && strcmpi(opts{1}, 'all')
    opt = opts{2};
  else
    if jj <= Nopts && ~isempty(opts{jj})
      opt = opts{jj};
    end
  end
  
end



% ERRORBARXY Customizable error bar plot in X and Y direction
%
% This function allows the user to plot the graph of x against y, along with
% both x and y errorbars.
%
% With 4 numeric arguments (x,y,dx,dy), error bar are assumed to be of
% same magnitude in both direction.
%
% One can specify lower and upper error bar with 6 numeric arguments
% (x,y,dx_high,dy_high,dx_low,dy_low).
%
% x,y,dx,dy,... must be vectors of the same length
%
% [hp he] = errorbarxy(...) returns respectively the handle for the line
% plot object and the line error bar object.
%
% It is possible to customize the line properties of the error bars by
% adding pair of 'field/value' fields (such as 'LineWidth',2) that can be
% understood by line. See LineProperties for more information.
%
% --------
% EXAMPLES
% --------
% X = 10 * rand(7,1);
% Y = 10 * rand(7,1);
% dx = rand(7,1);
% dy = rand(7,1);
% errorbarxy(X,Y,dx,dy,'Color','k','LineStyle','none','Marker','o',...
% 'MarkerFaceColor','w','LineWidth',1,'MarkerSize',11);
%
% X = 10 * rand(7,1);
% Y = 10 * rand(7,1);
% dx = rand(7,1);
% dy = rand(7,1);
% dx2 = rand(7,1);
% dy2 = rand(7,1);
% errorbarxy(X,Y,dx,dy,dx2,dy2,'Color','B','LineStyle','--','Marker','s',...
% 'MarkerFaceColor','w','LineWidth',2,'MarkerSize',11);
%
% This is a rewrite of the m-file errorbarxy of James Rooney, to add
% customizable line properties.

% ------------------ INFO ------------------
%   Authors: Jean-Yves Tinevez
%   Work address: Max-Plank Insitute for Cell Biology and Genetics,
%   Dresden,  Germany.
%   Email: tinevez AT mpi-cbg DOT de
%   November 2007 - June 2008;
%   Permission is given to distribute and modify this file as long as this
%   notice remains in it. Permission is also given to write to the author
%   for any suggestion, comment, modification or usage.
% ------------------ BEGIN CODE ------------------

function out = errorbarxy(ax, x,y,varargin)
  
  
  nargs = length(varargin);
  
  for i = 1 : nargs
    
    if ~( isnumeric(varargin{i}) )
      break
    end
    errbaropt{i} = varargin{i};
    
  end
  
  
  if i+3 < nargin
    displayopt = varargin(i:end);
    if isstruct(displayopt{1})
      options = displayopt{1};
    else
      options = varargin2struct(displayopt);
    end
    erroroptions = options;
  else
    displayopt = [];
  end
  
  options.Color = 'k';
  erroroptions.LineStyle = '-';
  erroroptions.Marker = 'none';
  
  
  xw = (max(x)-min(x))/100;
  yw = (max(y)-min(y))/100;
  
  n = length(varargin) - length(displayopt);
  
  if n == 2
    % only 2 cells, so this is the same for lower and upper bar
    ux = errbaropt{1};
    lx = ux;
    uy = errbaropt{2};
    ly = uy;
    
  elseif n == 4
    % 4 cells, the user specified both upper and lower limit
    ux = errbaropt{1};
    lx = errbaropt{3};
    uy = errbaropt{2};
    ly = errbaropt{4};
    
  else
    errid = 'MATLAB:errorbarxy:BadArgumentNumber';
    errmsg = ['Must have 4 or 6 numeric arguments, got ' ,num2str(n+2),'.'];
    error(errid,errmsg);
    
  end
  
  
  %%
  
  holdstate = ishold(gca);
  X = [];
  Y = [];
  for t = 1:length(x)
    
    % x errorbars
    X = [ X     nan x(t)-lx(t) x(t)+ux(t)    nan    x(t)-lx(t) x(t)-lx(t) nan     x(t)+ux(t) x(t)+ux(t)         ];
    Y = [ Y     nan y(t) y(t)                nan    y(t)-yw y(t)+yw       nan     y(t)-yw y(t)+yw               ];
    
    % y errorbars
    X = [ X     nan x(t) x(t)                nan    x(t)-xw x(t)+xw       nan     x(t)-xw x(t)+xw               ];
    Y = [ Y     nan y(t)-ly(t) y(t)+uy(t)    nan    y(t)-ly(t) y(t)-ly(t) nan     y(t)+uy(t) y(t)+uy(t)         ];
    
  end
  
  hold on
  axes(ax);
  he = line(X,Y,erroroptions);
  hp = plot(ax, x,y,options);
  out = [hp he];
  
  % Return to initial hold state if needed
  if ~holdstate
    hold off
  end
  
  function out = varargin2struct(in)
    % I hould write help
    
    if ~iscell(in)
      errid = 'MATLAB:struct2varargin:BadInputType';
      errmsg = ['Input argument must be a cell, got a ' ,class(in),'.'];
      error(errid,errmsg);
    end
    
    n = length(in);
    
    if mod(n,2) ~= 0
      errid = 'MATLAB:struct2varargin:BadInputType';
      errmsg = ['Input argument must have an even number of elements, got ' ,num2str(n),'.'];
      error(errid,errmsg);
    end
    
    out = struct;
    
    for jj = 1 : n/2
      name = in{2*jj-1};
      value = in{2*jj};
      out.(name) = value;
    end
    
  end
  
end

% Get a suitable ymin and ymax (in logscale) for the given
% data.
function [ticks,ymin,ymax] = getRealYDataTicks(y, ymin, ymax, complexPlotType, scale)
  
  ticks = [];
  switch complexPlotType
    case 'realimag'
      if strcmpi(scale', 'log')
        
        % do nothing because it doesn't make sense since we will have
        % negative values on a log scale
        
      end
    case {'absdeg', 'absrad'}
      
      if strcmpi(scale, 'log')
        % This is the main case we want to cover.
        ay = abs(y);
        newymin = min(ymin, floor(log10(min(ay(ay>0)))));
        newymax = max(ymax, ceil(log10(max(ay(ay>0)))));
        
        if ~isempty(newymin) && ~isempty(newymax)
          ymin = newymin;
          ymax = newymax;
        
          if ymin == ymax
            ymin = floor(log10(min(ay)/10));
            ymax = ceil(log10(max(ay)*10));
          end
        
        else
          
          if ymin == inf
            ymin = -1;
          end
          
          if ymax == -inf
            ymax = 1;
          end
                  
        end
        nticks = ymax - ymin +1;
        % can we reduce this if they don't all fit?
        
        ticks = logspace(ymin, ymax, nticks);
      end
      
    otherwise
      error('### Unknown plot type for complex data');
  end
  
end

% Get a suitable ymin and ymax (in linscale) for the given
% data.
function ticks = getImagYDataTicks(y, ymin, ymax, complexPlotType, scale)
  
  
  ticks = [];
  switch complexPlotType
    case 'realimag'
      if strcmpi(scale', 'log')
        
        % do nothing because it doesn't make sense since we will have
        % negative values on a log scale
        
      end
    case 'absdeg'
      
      if strcmpi(scale', 'log')
        % do nothing because it doesn't make sense since we will have
        % negative values on a log scale
      end
      
    case 'absrad'
      
    otherwise
      error('### Unknown plot type for complex data');
  end
  
  
end


%-------------------
% Process errorbars
function [fcn, xu, xl, yu, yl] = process_errors(jj, dsize, ptype, XerrU, XerrL, YerrU, YerrL, a, auto)
  
  if numel(XerrL) == 1
    xl = XerrL{1};
  else
    xl = XerrL{jj};
  end
  if numel(XerrU) == 1
    xu = XerrU{1};
  else
    xu = XerrU{jj};
  end
  if numel(YerrL) == 1
    yl = YerrL{1};
  else
    yl = YerrL{jj};
  end
  if numel(YerrU) == 1
    yu = YerrU{1};
  else
    yu = YerrU{jj};
  end
  
  % Check if we have AOs
  if isa(xl, 'ao'), xl = xl.data.getY; end
  if isa(xu, 'ao'), xu = xu.data.getY; end
  if isa(yl, 'ao'), yl = yl.data.getY; end
  if isa(yu, 'ao'), yu = yu.data.getY; end
  
  if isempty(xl) && ~isempty(xu)
    xl = xu;
  end
  if isempty(xu) && ~isempty(xl)
    xu = xl;
  end
  if isempty(yl) && ~isempty(yu)
    yl = yu;
  end
  if isempty(yu) && ~isempty(yl)
    yu = yl;
  end
  
  
  
  % If the AO has errors, we use them
  if ~isempty(a.dy) && auto
    yl = a.dy;
    yu = a.dy;
    yu(yu==inf) = 0;
    yl(yl==inf) = 0;
  end
  
  if isempty(xl) && isempty(xu) && isempty(yu) && isempty(yl)
    fcn = ptype;
  elseif isempty(xl) && isempty(xu)
    fcn = 'errorbar';
  else
    fcn = 'errorbarxy';
  end
  
  if isempty(xl), xl = zeros(dsize); end
  if isempty(yl), yl = zeros(dsize); end
  if isempty(xu), xu = zeros(dsize); end
  if isempty(yu), yu = zeros(dsize); end
  if numel(xl) == 1, xl = xl.*ones(dsize); end
  if numel(xu) == 1, xu = xu.*ones(dsize); end
  if numel(yu) == 1, yu = yu.*ones(dsize); end
  if numel(yl) == 1, yl = yl.*ones(dsize); end
end

function applyPlotSettings(axesH, lineH)
  
  prefs = getappdata(0, 'LTPDApreferences');
  jPlotPrefs = prefs.getPlotPrefs();
  
  if jPlotPrefs.getPlotApplyPlotSettings.equals(mpipeline.ltpdapreferences.EnumPlotSetting.IPLOT_ONLY)
    
    % Set all axes properteis
    for ii =1:numel(axesH)
      set(axesH(ii), 'FontSize', double(jPlotPrefs.getPlotDefaultAxesFontSize));
      set(axesH(ii), 'LineWidth', double(jPlotPrefs.getPlotDefaultAxesLineWidth));
      set(axesH(ii), 'GridLineStyle', char(jPlotPrefs.getPlotDefaultAxesGridLineStyle));
      set(axesH(ii), 'MinorGridLineStyle', char(jPlotPrefs.getPlotDefaultAxesMinorGridLineStyle));
      switch char(jPlotPrefs.getPlotDefaultAxesFontWeight)
        case 'Plain'
          set(axesH(ii), 'FontWeight', 'normal');
        case 'Bold'
          set(axesH(ii), 'FontWeight', 'bold');
        case 'Italic'
          set(axesH(ii), 'FontWeight', 'light');
        case 'Bold Italic'
          set(axesH(ii), 'FontWeight', 'demi');
        otherwise
          error('### Unknown value (%s) for the default axes property ''FontWeight''', char(jPlotPrefs.getPlotDefaultAxesFontWeight));
      end
    end
    
    % Set all line properties
    for ii = 1:numel(lineH) 
      set(lineH(ii), 'LineWidth', double(jPlotPrefs.getPlotDefaultLineLineWidth));
      set(lineH(ii), 'MarkerSize', double(jPlotPrefs.getPlotDefaultLineMarkerSize));
    end
  end
end