function h = polytool(x,y,n,alpha,xname,yname)
%POLYTOOL Fits a polynomial to (x,y) data and displays an interactive graph.
%   POLYTOOL(X,Y,N,ALPHA) is a prediction plot that provides a Nth degree
%   polynomial curve fit to (x,y) data. It plots a 100(1 - ALPHA) percent
%   global confidence interval for predictions as two red curves. The
%   default value for N is 1 and the default value for ALPHA is 0.05,
%   which produces a linear model and 95% confidence intervals respectively. 
%
%   POLYTOOL(X,Y,N,ALPHA,XNAME,YNAME) The optional inputs XNAME and 
%   YNAME contain strings for the X and Y variables respectively.
%
%   H = POLYTOOL(X,Y,N,ALPHA,XNAME,YNAME) outputs a vector of handles, H,
%   to the line objects in the plot.
%
%   You can drag the dotted white reference line and watch the predicted
%   values update simultaneously. Alternatively, you can get a specific
%   prediction by typing the "X" value into an editable text field.
%   A pop-up menu at the top allows you to change the degree of the 
%   polynomial fit. Use the pop-up menu labeled Export to move 
%   specified variables to the base workspace.

   
%   B.A. Jones 3-15-93
%   Copyright (c) 1993-96 by The MathWorks, Inc.
%   $Revision: 2.3 $  $Date: 1996/02/13 23:31:56 $

if ~isstr(x) 
    action = 'start';
else
    action = x;
end

%On recursive calls get all necessary handles and data.
if ~strcmp(action,'start')   

   if nargin == 2,
      flag = y;
   end
   
   poly_fig = findobj(0,'Tag','polyfig');
   k = gcf;
   idx = find(poly_fig == k);
   if isempty(idx)
      return
   end
   poly_fig = poly_fig(idx);      
   f = get(poly_fig,'Userdata');

   x_field          = f(1);
   y_field1         = f(2);
   y_field2         = f(3);
   y_field3         = f(4);
   xtext            = f(5);
   ytext            = f(6);
   degreetext       = f(7);
   betastore        = f(8);
   structurestore   = f(9);
   critstore        = f(10);
   poly_axes        = f(11);
   outputpopup      = f(12);

   
   x              = get(xtext,'UserData');
   y              = get(ytext,'UserData');
   fitline        = get(y_field2,'UserData');
   beta           = get(betastore,'UserData');
   structure      = get(structurestore,'UserData');
   crit           = get(critstore,'UserData');
   degree         = str2num(get(degreetext,'String'));
   reference_line = get(poly_axes,'UserData');
   
    xrange = get(poly_axes,'XLim');
    yrange = get(poly_axes,'YLim');

    newx = str2num(get(x_field,'String'));  
    newy = str2num(get(y_field1,'String'));  
    deltay = str2num(get(y_field3,'String'));
end

if strcmp(action,'start')

if nargin < 3, 
    degree = 1;
else
    if isempty(n)
      n = 1;
   end
    degree = n;
end

if degree == 1
    model = 'Linear';
elseif degree == 2
    model = 'Quadratic';
elseif degree == 3
    model = 'Cubic';
elseif degree == 4
    model = 'Quartic';
elseif degree == 5
    model = 'Quintic';
else
    model = [num2str(degree),'th Order'];
end   

if nargin < 4, 
    alpha = 0.05;
end
if isempty(alpha)
   alpha = 0.05;
end

obsm1 = max(size(y)) - 1;
crit = tinv(1 - alpha/2,obsm1-(1:obsm1-1));

% Set positions of graphic objects
axisp   = [.21 .18 .75 .68];

maxx = max(x);
minx = min(x);
xrange = maxx - minx;
maxx = maxx + 0.025 * xrange;
minx = minx - 0.025 * xrange;

xfieldp = [.50 .07 .15 .05];
yfieldp = [.01 .45 .13 .04];

bcolor = [0.5 0.5 0.5];

poly_fig = figure('Visible','off');
set(poly_fig,'Units','Normalized','Color','k','Tag','polyfig');

gray_axes = axes;
set(gray_axes,'XTick',[],'YTick',[],'Units','Normalized','Position',[0 0 1 1]);
set(gray_axes,'Color',bcolor,'UserData','gray_axes','Xlim',[0 1],'Ylim',[0 1]);
set(gray_axes,'ButtonDownFcn','polytool(''stray_click'')');

poly_axes = axes;
set(poly_axes,'UserData','poly_axes','XLim',[minx maxx],'Box','on');
set(poly_axes,'NextPlot','add','DrawMode','fast','Position',axisp,'Color','k');

set(poly_fig,'CurrentAxes',poly_axes,'Name',['Prediction Plot of ',model,' Model']);

[beta structure] = polyfit(x,y,degree);
xfit = linspace(minx,maxx,40)';
[yfit deltay]=polyval(beta,xfit,structure);
deltay = deltay*crit(degree);   

fitline = plot(x,y,'y+',xfit,yfit,'g-',xfit,yfit-deltay,'r--',xfit,yfit+deltay,'r--');
yrange = get(gca,'YLim');
xrange = get(gca,'XLim');
xvert = ones(size(yrange)) * (maxx+minx)/2;
newx  = xvert(1);

[newy deltay]=polyval(beta,newx,structure);
deltay = deltay*crit(degree);

x_field = uicontrol('Style','edit','Position',xfieldp,...
         'Units','normalized','String',num2str(newx),...
         'BackgroundColor','white','UserData',newx,...
         'CallBack','polytool(''edittext'')');
if nargin >= 5
   xstr = xname;
   if isempty(xname)
      xstr = 'X Values';
   end  
else
   xstr = 'X Values';
end

y_field1= uicontrol('Style','text','Units','normalized',...
         'Position',yfieldp + [0 .14 0 0],'String',num2str(NaN),...
         'ForegroundColor','w','BackgroundColor',bcolor);

y_field2= uicontrol('Style','text','Units','normalized',...
         'Position',yfieldp + [0 .07 -0.01 0],'String',' +/-','UserData',fitline,...
         'ForegroundColor','w','BackgroundColor',bcolor);

y_field3= uicontrol('Style','text','Units','normalized',...
         'Position',yfieldp,'String',num2str(NaN),...
         'ForegroundColor','w','BackgroundColor',bcolor);

xtext=uicontrol('Style','text','Position',xfieldp - [0 0.07 0 0],...
        'Units','normalized','BackgroundColor',bcolor,...
        'ForegroundColor','w','String',xstr,'UserData',x);

if nargin == 6
   ystr = yname;
else
   ystr = 'Y Values';
end
        
ytext=uicontrol('Style','text','Position',yfieldp + [0 0.20 0 0],...
        'Units','normalized','BackgroundColor',bcolor,...
        'ForegroundColor','w','String',ystr,'UserData',y);

if strcmp(computer,'MAC2')
  offset = -0.01;
else
  offset = 0;
end

degreelabel=uicontrol('Style','Text','String','Degree','BackgroundColor',bcolor,...
        'Position',xfieldp + [-0.03 0.87+offset -0.05 0],'Units','normalized','ForegroundColor','w');

degreetext=uicontrol('Style','Edit','String',num2str(degree),...
        'Userdata',degree,'BackgroundColor','white',...
        'Position',xfieldp + [0.07 0.87 -0.10 0],'Units','normalized',...
        'CallBack','polytool(''change_degree'')');

outputpopup=uicontrol('Style','Popup','String',...
        'Export|Parameters|Parameter CI|Prediction|Prediction CI|Residuals|All','Value',1,...
        'Units','normalized','Position',[0.01 0.07 0.17 0.05],...
      'CallBack','polytool(''output'')');

close_button = uicontrol('Style','Pushbutton','Units','normalized',...
               'Position',[0.01 0.01 0.17 0.05],'Callback','close','String','Close');

betastore=uicontrol('Style','text','Position',[0.01 0.01 0.1 0.05],...
        'Units','normalized','Visible','off','String','beta','UserData',beta);

structurestore=uicontrol('Style','text','Position',[0.01 0.06 0.1 0.05],...
        'Units','normalized','Visible','off','String','structure','UserData',structure);

critstore=uicontrol('Style','text','Position',[0.01 0.11 0.1 0.05],...
        'Units','normalized','Visible','off','String','crit','UserData',crit);

set(x_field,'String',num2str(newx));
set(y_field1,'String',num2str(newy));
set(y_field3,'String',num2str(deltay));

%   Create Reference Line
reference_line(1) = plot(xvert,yrange,'w--','Erasemode','xor');
reference_line(2) = plot(xrange,[newy newy],'w:','Erasemode','xor');
set(reference_line(1),'ButtonDownFcn','polytool(''down'')');
set(poly_fig,'Backingstore','off','WindowButtonMotionFcn','polytool(''motion'',0)');
set(poly_fig,'WindowButtonDownFcn','polytool(''down'')');
set(poly_fig,'UserData',[x_field;y_field1;y_field2;y_field3;xtext;ytext;degreetext;...
    betastore;structurestore;critstore;poly_axes;outputpopup],'Visible','on');
set(poly_axes,'UserData',reference_line);

% Finished with plot startup function.

elseif strcmp(action,'motion'),
   maxx = xrange(2);
   minx = xrange(1);
     
    if flag == 0,
      cursorstate = get(gcf,'Pointer');
        cp = get(gca,'CurrentPoint');
        cx = cp(1,1);
        cy = cp(1,2);
        fuzz = 0.01 * (maxx - minx);
        online = cy > yrange(1) & cy < yrange(2) & cx > newx - fuzz & cx < newx + fuzz;
        if online & strcmp(cursorstate,'arrow'),
            set(gcf,'Pointer','crosshair');
        elseif ~online & strcmp(cursorstate,'crosshair'),
            set(gcf,'Pointer','arrow');
        end
    elseif flag == 1,
        cp = get(gca,'CurrentPoint');
        newx=cp(1,1);
        if newx > maxx
            newx = maxx;
        end
        if newx < minx
            newx = minx;
        end
        [newy deltay]=polyval(beta,newx,structure);
        deltay = deltay*crit(degree);
        set(x_field,'String',num2str(newx));
        set(x_field,'Userdata',newx);
        set(y_field1,'String',num2str(newy));
        set(y_field3,'String',num2str(deltay));
       set(reference_line(1),'XData',newx*ones(size(yrange)));
       set(reference_line(2),'YData',[newy newy]);
    end

elseif strcmp(action,'down'),
   p = get(poly_fig,'CurrentPoint');
   if p(1) < .21 | p(1) > .96 | p(2) > 0.86 | p(2) < 0.18
      return
   end

    set(gcf,'Pointer','crosshair');
    cp = get(gca,'CurrentPoint');

    newx=cp(1,1);
    if newx > maxx
       newx = maxx;
    end
    if newx < minx
       newx = minx;
    end
    [newy deltay]=polyval(beta,newx,structure);
    deltay = deltay*crit(degree);
    set(x_field,'String',num2str(newx));
    set(x_field,'Userdata',newx);
    set(y_field1,'String',num2str(newy));
    set(y_field3,'String',num2str(deltay));
    set(reference_line(1),'XData',newx*ones(size(yrange)));
    set(reference_line(2),'YData',[newy newy]);
    set(gcf,'WindowButtonMotionFcn','polytool(''motion'',1)');
    set(gcf,'WindowButtonUpFcn','polytool(''up'')');

elseif strcmp(action,'up'),
    set(gcf,'WindowButtonMotionFcn','polytool(''motion'',0)');
    set(gcf,'WindowButtonUpFcn','');

elseif strcmp(action,'edittext'),
    if isempty(newx) 
      newx = get(x_field,'Userdata');
      set(x_field,'String',num2str(newx));
      warndlg('The X value must be a number. Reseting to previous value.');
       return;
   end
    maxx = xrange(2);
   minx = xrange(1);

    if newx > maxx
        newx = maxx;
        set(x_field,'String',num2str(newx));
    end
    if newx < minx
        newx = minx;
        set(x_field,'String',num2str(newx));
    end

    [newy deltay]=polyval(beta,newx,structure);
    deltay = deltay*crit(degree);
   set(x_field,'UserData',newx);
    set(y_field1,'String',num2str(newy));
    set(y_field3,'String',num2str(deltay));
    set(reference_line(1),'XData',newx*ones(size(yrange)),'Ydata',yrange);
    set(reference_line(2),'YData',[newy newy],'XData',xrange);

elseif strcmp(action,'change_degree')
    df = length(crit);
   if degree >= df+1
      degree = get(degreetext,'Userdata');
      set(degreetext,'String',num2str(degree));
      warndlg('Cannot fit model. Reseting to previous value.');
       return;
   end   
    if isempty(degree) 
      degree = get(degreetext,'Userdata');
      set(degreetext,'String',num2str(degree));
      warndlg('The polynomial degree must be a positive integer. Reseting to previous value.');
       return;
   end
    if floor(degree) ~= degree | degree <= 0
      degree = get(degreetext,'Userdata');
      set(degreetext,'String',num2str(degree));
      warndlg('The polynomial degree must be a positive integer. Reseting to previous value.');
       return;
   end
   [beta structure] = polyfit(x,y,degree);
   set(betastore,'UserData',beta);
   set(structurestore,'UserData',structure);
   maxx = max(x);
   minx = min(x);
   xrange = maxx - minx;
   maxx = maxx + 0.025 * xrange;
   minx = minx - 0.025 * xrange;
   xfit = linspace(minx,maxx,40)';
   [yfit deltay]=polyval(beta,xfit,structure);
   deltay = deltay*crit(degree);   

   set(fitline(1),'XData',x,'YData',y,'Color','y','Linestyle','+');
   set(fitline(2),'XData',xfit,'YData',yfit,'Color','g','Linestyle','-');
   set(fitline(3),'XData',xfit,'YData',yfit-deltay,'Color','r','Linestyle','--');
   set(fitline(4),'XData',xfit,'YData',yfit+deltay,'Color','r','Linestyle','--');

   maxy = max(yfit+deltay);
   miny = min(yfit-deltay);
   yrange = maxy - miny;
   maxy = maxy + 0.025 * yrange;
   miny = miny - 0.025 * yrange;

   yrange = [miny maxy];
   set(gca,'YLim',yrange);
   set(reference_line(1),'YData',yrange);
   set(reference_line(2),'XData',[minx maxx]);
   
   [newy deltay]=polyval(beta,newx,structure);
    deltay = deltay*crit(degree);
    set(degreetext,'UserData',degree);
    set(y_field1,'String',num2str(newy));
    set(y_field3,'String',num2str(deltay));
   set(reference_line(2),'YData',[newy newy]);
   
    if degree == 1
        model = 'Linear';
    elseif degree == 2
        model = 'Quadratic';
    elseif degree == 3
        model = 'Cubic';
    elseif degree == 4
        model = 'Quartic';
    elseif degree == 5
        model = 'Quintic';
   else
       model = [num2str(degree),'th Order'];
    end   
    set(poly_fig,'Name',['Prediction Plot of ',model,' Model']);  
elseif strcmp(action,'output');
    bmf = get(poly_fig,'WindowButtonMotionFcn');
    bdf = get(poly_fig,'WindowButtonDownFcn');
    set(poly_fig,'WindowButtonMotionFcn','');
    set(poly_fig,'WindowButtonDownFcn','');
   R = structure(1:degree+1,1:degree+1);
    df = structure(degree+2,1);
    rmse = structure(degree+3,1);

   cases = get(outputpopup,'Value');

   if cases == 2
        matqueue('put',beta);
      entrystr = 'beta';
   elseif cases == 3
      Rinv = eye(size(R))/R;
      dxtxi = diag(Rinv).^2;
      dbeta = dxtxi*crit(degree)*rmse;
      matqueue('put',[beta-dbeta';beta+dbeta']);
      entrystr = 'betaci';
   elseif cases == 4 | cases == 5;
      prediction = str2num(get(y_field1,'String'));
      temp = str2num(get(y_field3,'String'));
      interval = [(prediction - temp) (prediction + temp)];
      if cases == 4
         matqueue('put',prediction);
          entrystr = 'yhat';
      else 
         matqueue('put',interval);
          entrystr = 'yci';
      end
   elseif cases == 6
      yhat = polyval(beta,x);
      matqueue('put',y - yhat);
      entrystr = 'residuals';
    elseif cases == 7
      Rinv = eye(size(R))/R;
      dxtxi = diag(Rinv).^2;
      dbeta = dxtxi*crit(degree)*rmse;
      prediction = str2num(get(y_field1,'String'));
      temp = str2num(get(y_field3,'String'));
      interval = [(prediction - temp) (prediction + temp)];
      yhat = polyval(beta,x);
        matqueue('put',beta);
      matqueue('put',[beta-dbeta';beta+dbeta']);
       matqueue('put',prediction);
      matqueue('put',interval);
      matqueue('put',y - yhat);
       entrystr = 'beta,betaci,yhat,yci,residuals';
   end
    if cases ~= 1
       promptstr = 'Enter names for the output variables or use the supplied default.';
       matqdlg('buffer2ws','PromptString',promptstr,'EntryString',entrystr);
   end

   set(outputpopup,'Value',1);
    set(poly_fig,'WindowButtonMotionFcn',bmf);
    set(poly_fig,'WindowButtonDownFcn',bdf);

   
elseif strcmp(action,'stray_click'),
   set(gcf,'CurrentAxes',poly_axes);

end

if nargout == 1
   h = fitline;
end
