classdef fanchart < report.series
% fanchart  Add fancharts to graphs.
%
% Syntax
% =======
%
%     P.fanchart(CAP,X,STD,PROB,...)
%
% Input arguments
% ================
%
% * `P` [ struct ] - Report object created by the
% [`report.new`](report/new) function.
%
% * `CAP` [ char ] - Caption used as a legend entry for the line (mean of
% fanchart)
%
% * `X` [ tseries ] - Tseries object with input data to be displayed.
%
% * `STD` [ tseries ] - Tseries object with standard deviations of input
% data.
%
% * `PROB` [ numeric ] - Confidence porbabilities of intervals to be
% displayed.
%
% Options for fancharts
% ======================
%
% * `'asym='` [ numeric | *1* ] - Ratio of asymmetry (area of upper part to
% one of lower part).
%
% * `'exclude='` [ numeric | true | *false* ] - Exclude some of the
% confidence intervals.
%
% * `'factor='` [ numeric | *`1`* ] - factor to increase or decrease input
% standard deviations
%
% * `'fanlegend='` [ cell | `NaN` | *`Inf`* ] -  Legend entries used instead of
% confidence interval values; Inf means all confidence intervals values
% will be used to construct legend entries; NaN means the intervals will be
% exluded from legend; `NaN` in cellstr means the intervals of respective
% fancharts will be exluded from legend.
%
% See help on [`report/series`](report/series) for other options available.
%
% Description
% ============
%
% The confidence intervals are based on normal distributions with standard
% deviations supplied by the user. Optionally, the user can also specify
% assumptions about asymmetry and/or common correction factors.
%
% Example
% ========
%

% -IRIS Toolbox.
% -Copyright (c) 2007-2012 Jaromir Benes & Sergey Plotnikov.
  
    properties
        std = [];
        prob = [];
    end
    
    methods
        
        function this = fanchart(varargin)
            this = this@report.series(varargin{:});
            this.default = [this.default,{...
                'asym',1,@(x) isnumeric(x) && all(x >= 0),false,...
                'exclude',false,@(x) (isnumeric(x)...
                    && all((x == 1) + (x == 0))) || islogical(x),false, ...
                'factor',1,@(x) isnumeric(x) && all(x >= 0),false,...
                'fanlegend',Inf,@(x) isempty(x) ...
                    || (isnumericscalar(x) && (isnan(x) || isinf(x))) ...
                    || iscellstrwithnans(x) || ischar(x),false, ...
                }];
        end
        
        function [this,varargin] = specargin(this,varargin)
            [this,varargin] = specargin@report.series(this,varargin{:});
            if ~isempty(varargin)
                this.std = varargin{1};
                varargin(1) = [];
            end
            if ~isempty(varargin)
                this.prob = varargin{1};
                if isnumeric(this.prob)
                    this.prob = sort(this.prob(:));
                    i = 1;
                    while i<length(this.prob)
                        if this.prob(i) == this.prob(i+1)
                            this.prob(i+1) = [];
                        else
                            i = i + 1;
                        end;
                    end;
                end
                varargin(1) = [];
            end
        end
        
        function leg = plot(this,ax)
            par = this.parent;
            % Create the line plot first using the parent's method.
            [leg,h,time,cdata,grid] = ...
                plot@report.series(this,ax);
            grid = grid(:);
            stdata = this.std(time);
            probdata = this.prob;
            nint = size(probdata,1);
            nextplot = get(ax,'nextPlot');
            set(ax,'nextPlot','add');
            pt = nan(1,nint);
            stdata = stdata.*this.options.factor;
            asym = this.options.asym;
            lstdata = stdata.*(2/(1 + asym));
            hstdata = stdata.*(2*asym/(1+asym));
            leg = [cell(1,nint) leg];
            for i = 1 : nint
                whi = probdata(i);
                ldata = -norminv(0.5*probdata(i)+0.5)*lstdata;
                hdata = norminv(0.5*probdata(i)+0.5)*hstdata;
                vdata = [ldata;flipud(hdata)];
                vdata = vdata + [cdata;flipud(cdata)];
                pt(i) = patch([grid;flipud(grid)],vdata,'white');
                ch = get(ax,'children');
                ch(ch == pt(i)) = [];
                ch(end+1) = pt(i);
                set(ax,'children',ch);
                linecol = get(h,'color');
                facecol = whi*[1,1,1] + (1-whi)*linecol;
                if this.options.exclude(min([i,end]))
                    facecol = 'none';
                end
                set(pt(i),'faceColor',facecol, ...
                    'edgeColor','none', ...
                    'lineStyle','-', ...
                    'tag','fanchart', ...
                    'userData', whi);
                lgd = this.options.fanlegend;
                if isequal(lgd,Inf)
                    if this.options.exclude(min([i,end]))
                        grfun.excludefromlegend(pt(i));
                        leg(nint+1-i) = [];
                    else
                        leg{nint+1-i} = sprintf('%g%%',100*whi);
                    end;
                elseif iscell(lgd)
                    if ~all(isnan(lgd{i})) && ~this.options.exclude(min([i,end]))
                        leg{nint+1-i} = lgd{i};
                    else
                        grfun.excludefromlegend(pt(i));
                        leg(nint+1-i) = [];
                    end
                end
            end
            if isequalwithequalnans(this.options.fanlegend,NaN)
                grfun.excludefromlegend(pt(:));
                leg(1:nint) = [];
            end
            set(ax,'nextPlot',nextplot);
        end
        
    end
    
end