function nlintool(x,y,model,beta0,alpha,xname,yname)
%NLINTOOL Interactive graphical tool for nonlinear fitting and prediction.
%   NLINTOOL(X,Y,MODEL,BETA0,ALPHA) is a prediction plot that provides a 
%   nonlinear curve fit to (x,y) data. It plots a 100(1 - ALPHA) percent
%   global confidence interval for predictions as two red curves. BETA0 is
%   a vector containing initial guesses for the parameters. 
%   The default value for ALPHA is 0.05, which produces 95% confidence
%   intervals. 
%
%   NLINTOOL(X,Y,MODEL,BETA0,ALPHA,XNAME,YNAME) The optional inputs XNAME  
%   and YNAME contain strings for the X and Y variables respectively.
%
%   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.
%   Use the pop-up menu labeled Export to move specified variables to
%   the base workspace.
   
%   B.A. Jones 1-30-94
%   Copyright (c) 1993-96 by The MathWorks, Inc.
%   $Revision: 2.2 $  $Date: 1996/02/13 23:26:08 $

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
   
   nlin_fig = findobj(0,'Tag','nlinfig');
   if strcmp(action,'down')
       set(nlin_fig,'WindowButtonMotionFcn','nlintool(''motion'',1)');
    end
   
   k = gcf;
   idx = find(nlin_fig == k);
   if isempty(idx)
      return
   end
   nlin_fig = nlin_fig(idx);      
   ud = get(nlin_fig,'Userdata');
   
   crit          = ud(1);
   rmse          = ud(2);
   popup         = ud(3);
   
   Rinv          = get(ud(4),'Userdata');
   beta          = get(ud(5),'Userdata');
   model         = get(ud(6),'Userdata');
   x             = get(ud(7),'Userdata');
   y             = get(ud(8),'Userdata');
   xfit          = get(ud(9),'Userdata');
   residuals     = get(ud(10),'Userdata');
   x_field       = get(ud(11),'Userdata');
   nlin_axes     = get(ud(12),'Userdata');
   xsettings     = get(ud(13),'Userdata');
   y_field1      = ud(14);
   y_field2      = ud(15);
   y_field3      = ud(16);
   
   [m,n]         = size(x);

    fitline        = zeros(3,n);
    reference_line = zeros(n,2);
   xrange         = zeros(n,2);
   yrange         = zeros(n,2);
   newx           = zeros(n,1);
   newy           = zeros(1,1);
    last_axes      = zeros(n,1);

    for k = 1:n      
      axes_ud = get(nlin_axes(k),'UserData');
       fitline(1:3,k)      = axes_ud(1:3)';
       reference_line(k,:)   = axes_ud(4:5);
      last_axes(k)        = axes_ud(6);
   
        xrange(k,1:2) = get(nlin_axes(k),'XLim');
        yrange(k,1:2) = get(nlin_axes(k),'YLim');

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

if strcmp(action,'start')

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

% Fit nonlinear model
[beta, loss, J] = nlinfit(x,y,model,beta0);
if rcond(J'*J) < 1E-12
    warndlg('Your model fit is poor. Use discretion with these results.');
end

evalstr = [model,'(beta,x)'];
yhat = eval(evalstr);
residuals = y - yhat;

p = length(beta);
df = max(size(y)) - p;

rmse = sqrt(sum(residuals.*residuals)./df);
crit = sqrt(p*finv(1 - alpha,p,df))*rmse;

[Q R] = qr(J,0);
Rinv = R\eye(size(R));


% Set positions of graphic objects

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

yfieldp = [.01 .45 .10 .04];

bcolor = [0.5 0.5 0.5];

[m,n]          = size(x);

x_field        = zeros(n,1);
nlin_axes      = zeros(n,1);
fitline        = zeros(n,3);
reference_line = zeros(n,2);

xfit      = xrange(ones(41,1),:)./40;
xfit(1,:) = minx;
xfit      = cumsum(xfit);

avgx      = mean(x);
xsettings = avgx(ones(41,1),:);

nlin_fig = figure('Units','Normalized','Interruptible','yes',...
                'Position',[0.05 0.45 0.90 0.5],'Color','k',...
             'Name',['Nonlinear Fit of ',model,' Model'],'Tag','nlinfig');
set(nlin_fig,'WindowButtonMotionFcn','nlintool(''motion'',0)');
set(nlin_fig,'WindowButtonDownFcn','nlintool(''down'')');
set(nlin_fig,'WindowButtonUpFcn','nlintool(''up'')');

gray_axes = axes;
set(gray_axes,'XTick',[],'YTick',[],'Units','Normalized','Position',[0 0 1 1],...
  'Color',bcolor,'ButtonDownFcn','nlintool(''stray_click'')');

yextremes = zeros(k,2);

for k = 1:n
   % Create an axis for each input (x) variable
    
    xfieldp = [.18 + (k-0.5)*.80/n - 0.5*min(.5/n,.15) .09 min(.5/n,.15) .07];
    xtextp  = [.18 + (k-0.5)*.80/n - 0.5*min(.5/n,.18) .02 min(.5/n,.18) .05];
    axisp   = [.18 + (k-1)*.80/n .22 .80/n .68];

    nlin_axes(k) = axes;
    set(nlin_axes(k),'XLim',[minx(k) maxx(k)],'Box','on','NextPlot','add',...
        'DrawMode','fast','Position',axisp,'Color','k');
   if k>1
      set(nlin_axes(k),'Yticklabels',[]);
   end

   % Calculate y values for fitted line plot.
   ith_x      = xsettings;
   ith_x(:,k) = xfit(:,k);
   evalstr    = [model,'(beta,ith_x)'];
   yfit       = eval(evalstr);

   xi         = xfit(:,k);
   
   % Calculate y values for confidence interval lines.
   deltay = zeros(41,length(beta));
    kx = xsettings;
    kx(:,k) = xfit(:,k);

   for k1 = 1:length(beta),
      delta = zeros(size(beta));
      delta(k1) = sqrt(eps)*beta(k1);
      evalstr1 = [model,'(beta+delta,kx)'];
      evalstr = [model,'(beta,kx)'];
      yplus = eval(evalstr1);
      ypred = eval(evalstr);
      deltay(:,k1) = (yplus - ypred)/(sqrt(eps)*beta(k1));
   end

   E = deltay*Rinv;
   deltay = sqrt(sum((E.*E)')')*crit;

   % Plot prediction line with confidence intervals
   set(0,'CurrentFigure',nlin_fig);
   set(nlin_fig,'CurrentAxes',nlin_axes(k));
   fitline(1:3,k) = plot(xi,yfit,'g-',xi,yfit-deltay,'r--',xi,yfit+deltay,'r--');

   % Calculate data for vertical reference lines
   yextremes(k,:) = get(gca,'YLim');


   newx  = avgx(k);
   xrow  = xsettings(1,:);
   xrow(k) = newx;

   evalstr = [model,'(beta,xrow)'];
   newy = eval(evalstr);

   deltay = zeros(1,length(beta));
   for k1 = 1:length(beta)
      delta = zeros(size(beta));
      delta(k1) = sqrt(eps)*beta(k1);
      evalstr1 = [model,'(beta+delta,xrow)'];
      yplus = eval(evalstr1);
      deltay(:,k1) = (yplus - newy)/(sqrt(eps)*beta(k1));
   end

   E = deltay*Rinv;
   deltay = sqrt(sum((E.*E)')')*crit;


   set(nlin_fig,'Backingstore','off','Interruptible','yes',...
        'WindowButtonMotionFcn','nlintool(''motion'',0)');

    x_field(k)  = uicontrol(nlin_fig,'Style','edit','Position',xfieldp,...
         'Units','normalized','String',num2str(newx),...
         'BackgroundColor','white','CallBack',['nlintool(''edittext'',',int2str(k),')']);

    if nargin >= 6
      xstr = xname(k,:);
      if isempty(xname)
         xstr = ['X',int2str(k)];
      end
   else
      xstr = ['X',int2str(k)];
   end
    uicontrol(nlin_fig,'Style','text','Position',xtextp,...
        'Units','normalized','BackgroundColor',bcolor,...
        'ForegroundColor','w','String',xstr);

end  % End of the plotting loop over all the axes.

if nargin == 7
   ystr = yname;
else
   ystr = 'Predicted Y';
end

uicontrol(nlin_fig,'Style','text','Position',yfieldp + [0 0.21 0 0],...
        'Units','normalized','BackgroundColor',bcolor,...
        'ForegroundColor','w','String',ystr);

ymin = min(yextremes(:,1));
ymax = max(yextremes(:,2));

for k = 1:n
   set(nlin_axes(k),'YLim',[ymin ymax]);
   xlimits = get(nlin_axes(k),'XLim');
   %   Create Reference Lines
   xvert = ones(size(yextremes(k,:))) * avgx(k);
   set(nlin_fig,'CurrentAxes',nlin_axes(k));
   reference_line(k,1) = plot(xvert,[ymin ymax],'w--','Erasemode','xor');
   reference_line(k,2) = plot(xlimits,[newy newy],'w:','Erasemode','xor');
   set(reference_line(k,1),'ButtonDownFcn','nlintool(''down'')');
   set(nlin_axes(k),'UserData',[fitline(1:3,k)' reference_line(k,:) 0]);
end

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

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

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

popup        = uicontrol(nlin_fig,'Style','Popup','String',...
              'Export|Parameters|RMSE|Residuals|All','Value',1,...
              'Units','normalized','Position',[0.01 0.10 0.13 0.07],...
              'CallBack','nlintool(''output'')');

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

Rinvstore=uicontrol('Style','text','Visible','off','UserData',Rinv);

betastore=uicontrol('Style','text','Visible','off','UserData',beta);

modelstore=uicontrol('Style','text','Visible','off','UserData',model);

xstore=uicontrol('Style','text','Visible','off','UserData',x);

ystore=uicontrol('Style','text','Visible','off','UserData',y);

xfieldstore=uicontrol('Style','text','Visible','off','UserData',x_field);

xfitstore=uicontrol('Style','text','Visible','off','UserData',xfit);

residualstore=uicontrol('Style','text','Visible','off','UserData',residuals);

nlin_axesstore=uicontrol('Style','text','Visible','off','UserData',nlin_axes);

xsettingstore=uicontrol('Style','text','Visible','off','UserData',xsettings);

ud = [crit rmse popup Rinvstore betastore modelstore xstore ystore...
      xfitstore residualstore xfieldstore nlin_axesstore...
     xsettingstore y_field1 y_field2 y_field3];

set(nlin_fig,'UserData',ud);

% Finished with plot startup function.

elseif strcmp(action,'motion'),
    p = get(nlin_fig,'CurrentPoint');
   k = floor(1+n*(p(1)-0.18)/.80);
   if k < 1 | k > n
      return
   end
   maxx = xrange(k,2);
   minx = xrange(k,1);
     
    if flag == 0,
       % Check for data consistency in each axis
      yn = zeros(n,1);
      for idx = 1:n
         y = get(reference_line(idx,2),'Ydata');
         yn(idx) = y(1);
      end
      % If data is inconsistent update all the plots.
      if diff(yn) ~= zeros(n-1,1)
         nlintool('up');
      end
      % Change cursor to plus sign when mouse is on top of vertical line.
      cursorstate = get(nlin_fig,'Pointer');
        cp = get(nlin_axes(k),'CurrentPoint');
        cx = cp(1,1);
        cy = cp(1,2);
        fuzz = 0.02 * (maxx - minx);
        online = cx > newx(k) - fuzz & cx < newx(k) + fuzz;
        if online & strcmp(cursorstate,'arrow'),
            cursorstate = 'crosshair';
        elseif ~online & strcmp(cursorstate,'crosshair'),
            cursorstate = 'arrow';
        end
        set(nlin_fig,'Pointer',cursorstate);
      return
   elseif flag == 1,
       if last_axes(k) == 0
         return;
      end
        cp = get(nlin_axes(k),'CurrentPoint');
        cx=cp(1,1);
        if cx > maxx
            cx = maxx;
        end
        if cx < minx
            cx = minx;
        end

       xrow  = xsettings(1,:);
       xrow(k) = cx;

       evalstr = [model,'(beta,xrow)'];
       cy = eval(evalstr);
         
      for idx = 1:n
         deltay = zeros(1,length(beta));

        for k1 = 1:length(beta),
           delta = zeros(size(beta));
          delta(k1) = sqrt(eps)*beta(k1);
          evalstr1 = [model,'(beta+delta,xrow)'];
          evalstr = [model,'(beta,xrow)'];
          yplus = eval(evalstr1);
          ypred = eval(evalstr);
          deltay(:,k1) = (yplus - ypred)/(sqrt(eps)*beta(k1));
        end

        E = deltay*Rinv;
        dcy = sqrt(sum((E.*E)')')*crit;
      end

        set(ud(13),'Userdata',xsettings);            
       set(nlin_fig,'Userdata',ud);       
 
        set(x_field(k),'String',num2str(cx));
        set(reference_line(k,1),'XData',cx*ones(2,1));
         set(reference_line(k,2),'YData',[cy cy]);
      
       set(y_field1,'String',num2str(cy));
      set(y_field3,'String',num2str(dcy));

   end  % End of code for dragging reference lines

elseif strcmp(action,'down'),
       p = get(nlin_fig,'CurrentPoint');
       k = floor(1+n*(p(1)-0.18)/.80);
      if k < 1 | k > n | p(2) > 0.90 | p(2) < 0.22
         return
      end
      axud = get(nlin_axes(k),'UserData');
      axud(6) = 1;
      set(nlin_axes(k),'UserData',axud);
       set(nlin_fig,'Pointer','crosshair');
       cp = get(nlin_axes(k),'CurrentPoint');
       cx=cp(1,1);
       if cx > maxx
          cx = maxx;
       end
       if cx < minx
          cx = minx;
       end

      xrow  = xsettings(1,:);
      xrow(k) = cx;
       xsettings(:,k) = cx(ones(41,1));

      evalstr = [model,'(beta,xrow)'];
      cy = eval(evalstr);
         
       for idx = 1:n
          deltay = zeros(1,length(beta));

         for k1 = 1:length(beta),
            delta = zeros(size(beta));
           delta(k1) = sqrt(eps)*beta(k1);
           evalstr1 = [model,'(beta+delta,xrow)'];
           evalstr = [model,'(beta,xrow)'];
           yplus = eval(evalstr1);
           ypred = eval(evalstr);
           deltay(:,k1) = (yplus - ypred)/(sqrt(eps)*beta(k1));
         end

         E = deltay*Rinv;
         dcy = sqrt(sum((E.*E)')')*crit;
       end

       set(ud(13),'Userdata',xsettings);            
      set(nlin_fig,'Userdata',ud);       
 
       set(x_field(k),'String',num2str(cx));
       set(reference_line(k,1),'XData',cx*ones(2,1));
       set(reference_line(k,2),'YData',[cy cy]);
      
      set(y_field1,'String',num2str(cy));
      set(y_field3,'String',num2str(dcy));

       set(nlin_fig,'WindowButtonUpFcn','nlintool(''up'')');

elseif strcmp(action,'up'),
    p = get(nlin_fig,'CurrentPoint');
   k = floor(1+n*(p(1)-0.15)/.80);
   lk = find(last_axes == 1);
   if isempty(lk)
      return
   end
   if k < lk
      set(x_field(lk),'String',num2str(xrange(lk,1)));
   elseif k > lk
      set(x_field(lk),'String',num2str(xrange(lk,2)));
   end
       
   set(nlin_fig,'WindowButtonMotionFcn','nlintool(''motion'',0)');

    cx    = str2num(get(x_field(lk),'String'));
    xrow  = xsettings(1,:);
    xrow(lk) = cx;
  
    evalstr = [model,'(beta,xrow)'];
    cy = eval(evalstr);
      
    xsettings(:,lk) = cx(ones(41,1));

    set(reference_line(lk,1),'XData',cx*ones(2,1));
    set(reference_line(lk,2),'YData',[cy cy]);
    ymax = zeros(n,1);
    ymin = zeros(n,1);
       
    for idx = 1:n
       ith_x      = xsettings;
         ith_x(:,idx) = xfit(:,idx);
      evalstr    = [model,'(beta,ith_x)'];
      yfit       = eval(evalstr);

      xi         = xfit(:,idx);

      deltay = zeros(42,length(beta));

       xidx = xsettings;
      xidx(:,idx) = xfit(:,idx);
      xdelta = [xidx;xrow];

      for k1 = 1:length(beta),
          delta = zeros(size(beta));
        delta(k1) = sqrt(eps)*beta(k1);
        evalstr1 = [model,'(beta+delta,xdelta)'];
        evalstr = [model,'(beta,xdelta)'];
        yplus = eval(evalstr1);
        ypred = eval(evalstr);
        deltay(:,k1) = (yplus - ypred)/(sqrt(eps)*beta(k1));
      end

      E = deltay*Rinv;
      deltay = sqrt(sum((E.*E)')')*crit;
      dcy = deltay(42);
       deltay = deltay(1:41);
            
      set(ud(13),'Userdata',xsettings);            
      set(nlin_fig,'Userdata',ud);       
      set(fitline(1,idx),'Ydata',yfit);
      set(fitline(2,idx),'Ydata',yfit-deltay);
      set(fitline(3,idx),'Ydata',yfit+deltay);
      ymax(idx) = max(yfit+deltay);
      ymin(idx) = min(yfit-deltay);
    end         

    for ix = 1:n      
       axud = get(nlin_axes(ix),'Userdata');
       axud(6) = 0;
       set(nlin_axes(ix),'Ylim',[min(ymin) max(ymax)],'UserData',axud)
       set(reference_line(ix,1),'Ydata',[min(ymin) max(ymax)]);
         set(reference_line(ix,2),'YData',[cy cy]);
    end
         
    set(y_field1,'String',num2str(cy));
    set(y_field3,'String',num2str(dcy));

elseif strcmp(action,'edittext'),
   cx    = str2num(get(x_field(flag),'String'));
   
   if isempty(cx)
        set(x_field(flag),'String',num2str(xsettings(1,flag)));
       % Create Bad Settings Warning Dialog.
       warndlg('Please type only numbers in the editable text fields.');
      mval = get(modelpopup,'Userdata');
      set(modelpopup,'Value',mval);
       return
   end  
   xl = get(nlin_axes(flag),'Xlim');
   if cx < xl(1) | cx > xl(2)
       % Create Bad Settings Warning Dialog.
       warndlg('This number is outside the range of the data for this variable.');
      mval = get(modelpopup,'Userdata');
      set(modelpopup,'Value',mval);
       set(x_field(flag),'String',num2str(xsettings(1,flag)));
       return
   end
   
   xrow  = xsettings(1,:);
   xrow(flag) = cx;

   evalstr = [model,'(beta,xrow)'];
   cy = eval(evalstr);
      
   xsettings(:,flag) = cx(ones(41,1));

   set(reference_line(flag,1),'XData',cx*ones(2,1));
   set(reference_line(flag,2),'YData',[cy cy]);
   
   ymax = zeros(n,1);
   ymin = zeros(n,1);
      
   for idx = 1:n
      ith_x      = xsettings;
        ith_x(:,idx) = xfit(:,idx);
     evalstr    = [model,'(beta,ith_x)'];
     yfit       = eval(evalstr);

     xi         = xfit(:,idx);

     deltay = zeros(42,length(beta));

      xidx = xsettings;
     xidx(:,idx) = xfit(:,idx);
     xdelta = [xidx;xrow];

     for k1 = 1:length(beta),
         delta = zeros(size(beta));
       delta(k1) = sqrt(eps)*beta(k1);
       evalstr1 = [model,'(beta+delta,xdelta)'];
       evalstr = [model,'(beta,xdelta)'];
       yplus = eval(evalstr1);
       ypred = eval(evalstr);
       deltay(:,k1) = (yplus - ypred)/(sqrt(eps)*beta(k1));
     end

     E = deltay*Rinv;
     deltay = sqrt(sum((E.*E)')')*crit;
     dcy = deltay(42);
      deltay = deltay(1:41);
            
     set(ud(13),'Userdata',xsettings);            
     set(nlin_fig,'Userdata',ud);       
     set(fitline(1,idx),'Ydata',yfit);
     set(fitline(2,idx),'Ydata',yfit-deltay);
     set(fitline(3,idx),'Ydata',yfit+deltay);
     ymax(idx) = max(yfit+deltay);
     ymin(idx) = min(yfit-deltay);         
         
    end
   for ix = 1:n
      set(nlin_axes(ix),'Ylim',[min(ymin) max(ymax)]);
      set(reference_line(ix,1),'Ydata',[min(ymin) max(ymax)]);
       set(reference_line(ix,2),'YData',[cy cy]);
   end
   set(y_field1,'String',num2str(cy));
   set(y_field3,'String',num2str(dcy));


elseif strcmp(action,'output'),
    bmf = get(nlin_fig,'WindowButtonMotionFcn');
    bdf = get(nlin_fig,'WindowButtonDownFcn');
    set(nlin_fig,'WindowButtonMotionFcn','');
    set(nlin_fig,'WindowButtonDownFcn','');
   cases = get(popup,'Value');
   if cases == 2
      matqueue('put',beta);
      promptstr = 'Enter the variable name for the fitted parameters or use default:';
      matqdlg('buffer2ws','PromptString',promptstr,'EntryString','beta');
   elseif cases == 3
      matqueue('put',rmse);
      promptstr = 'Enter the variable name for the RMSE of the fit or use default:';
      matqdlg('buffer2ws','PromptString',promptstr,'EntryString','rmse');
   elseif cases == 4;
      matqueue('put',residuals);
      promptstr = 'Enter the variable name for the residuals or use default:';
      matqdlg('buffer2ws','PromptString',promptstr,'EntryString','residuals');
   elseif cases == 5;
      matqueue('put',beta);
      matqueue('put',rmse);
      matqueue('put',residuals);
      promptstr = 'Enter variable names or use the supplied names:';
      entrystr = 'beta,rmse,residuals';
      matqdlg('buffer2ws','PromptString',promptstr,'EntryString',entrystr);
   end
   set(popup,'Value',1);
    set(nlin_fig,'WindowButtonMotionFcn',bmf);
    set(nlin_fig,'WindowButtonDownFcn',bdf);

elseif strcmp(action,'stray_click'),
   for k = 1:n,
      set(nlin_fig,'CurrentAxes',nlin_axes(k));
   end

end
