function ginterp(action)
%GINTERP  Display interpolating function through given points
%
%  Call simply as ginterp with no arguments.
%
%  Usage is pretty self-explanatory, and help is available.
%
% author: Douglas N. Arnold, 9/96, 10/96

% This routine establishes a bunch of callbacks which are all
% to ginterp(action) for different actions.  A lot of variables
% have to be declared global to be shared by the different calls.

global ginterpfig ginterpaxes
global xminbox xmaxbox yminbox ymaxbox xmin xmax ymin ymax
global helpfig helpfigup morehelpfig morehelpfigup n x y nplot gridbutton extrapbutton
global typepopup interptype xpmin xpmax extrapolate loadbox loadfile

if nargin == 0,
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  initialize
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % start new figure
  ginterpfig = figure(...
    'Position',[100 400 560 420],...
    'Name',[interptype ' interpolation'],...
    'NumberTitle','off',...
    'DefaultUicontrolFontSize',12);
  ginterpaxes = axes(...,
    'Position',[.1 .2 .8 .7],...
    'Units','normalized',...
    'Box','on',...
    'Color','black',...
    'XColor',[.5 .5 .5],...
    'YColor',[.5 .5 .5],...
    'GridLineStyle','-',...
    'XLim',[-1 1],...
    'YLim',[-1 1],...
    'XLimMode','manual',...
    'YLimMode','manual',...
    'XGrid','on',...
    'YGrid','on',...
    'DefaultLineLineWidth',2);
  interptype = 'polynomial';
  % set up control widgets
  % plot limits
  frame = uicontrol(ginterpfig,...
    'Style','frame',...
    'Position',[5 7 350 27]);
  xminprompt = uicontrol(ginterpfig,...
    'Style','text',...
    'String','xmin:',...
    'Position',[10 10 40 20]);
  xminbox = uicontrol(ginterpfig,...
    'Style','edit',...
    'String','-1.0',...
    'Position',[50 10 40 20],...
    'CallBack','ginterp(''setxmin'')');
  xmaxprompt = uicontrol(ginterpfig,...
    'Style','text',...
    'String','xmax:',...
    'Position',[95 10 40 20]);
  xmaxbox = uicontrol(ginterpfig,...
    'Style','edit',...
    'String',' 1.0',...
    'Position',[135 10 40 20],...
    'CallBack','ginterp(''setxmax'')');
  yminprompt = uicontrol(ginterpfig,...
    'Style','text',...
    'String','ymin:',...
    'Position',[180 10 40 20]);
  yminbox = uicontrol(ginterpfig,...
    'Style','edit',...
    'String','-1.0',...
    'Position',[220 10 40 20],...
    'CallBack','ginterp(''setymin'')');
  ymaxprompt = uicontrol(ginterpfig,...
    'Style','text',...
    'String','ymax:',...
    'Position',[265 10 40 20]);
  ymaxbox = uicontrol(ginterpfig,...
    'Style','edit',...
    'String',' 1.0',...
    'Position',[305 10 40 20],...
    'CallBack','ginterp(''setymax'')');
  
  %  "Interpolate", "Reset", "Help", and "Quit" buttons
  ibutton = uicontrol(ginterpfig,...
     'Style','push',...
     'String','Interpolate',...
    'Position',[10 40 80 20],...
     'CallBack','ginterp(''interpolate'')',...
     'BackgroundColor',[0 0 .8],'ForegroundColor','yellow');
  rbutton  = uicontrol(ginterpfig,...
     'Style','push',...
     'String','Reset',...
    'Position',[95 40 80 20],...
     'BackgroundColor',[0 0 .8],'ForegroundColor','yellow',...
     'CallBack','ginterp(''reset'')'); 
  hbutton = uicontrol(ginterpfig,...
     'Style','push',...
     'String','Help',...
     'Position',[180 40 80 20],...
     'BackgroundColor',[0 0 .8],'ForegroundColor','yellow',...
     'CallBack','ginterp(''help'')');
  qbutton = uicontrol(ginterpfig,...
      'Style','push',...
      'String','Quit',...
      'Position',[265 40 80 20],... 
      'BackgroundColor',[0 0 .8],'ForegroundColor','yellow',...
      'CallBack','ginterp(''quit'')');
  lframe = uicontrol(ginterpfig,...
    'Style','frame',...
    'Position',[360 30 190 30]);
  loadprompt = uicontrol(ginterpfig,...
    'Style','text',...
    'String','load:',...
    'Position',[365 35 40 20]);
  loadbox = uicontrol(ginterpfig,...
    'Style','edit',...
    'Position',[405 35 120 20],...
    'HorizontalAlignment','left',...
    'CallBack','ginterp(''load'')');
  typepopup = uicontrol(ginterpfig,...
    'Style','popup',...
    'String','poly.|spline|pw lin.',...
    'Position',[360 7 70 20],...
    'CallBack','ginterp(''interptype'')');
  gridbutton = uicontrol(ginterpfig,...
    'Style','checkbox',...
    'String','grid',...
    'Position',[435 7 55 20],...
    'Value',1,...
    'CallBack','ginterp(''grid'')');
  extrapbutton = uicontrol(ginterpfig,...
    'Style','checkbox',...
    'String','extr.',...
    'Position',[495 7 55 20],...
    'CallBack','ginterp(''extrap'')');
    
  %  Set up mouse button to read and display input points
  set(ginterpfig,'WindowButtonDownfcn',['ginterp(''buttondown'');'])

  % initialize variables  
  xmin = -1;
  xmax = 1;
  ymin = -1;
  ymax = 1;
  n=0;
  x=[];
  y=[];
  nplot=500;
  helpfigup = 0;
  morehelpfigup = 0;
  extrapolate = 0;

elseif strcmp(action,'setxmin'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  setxmin callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  xmin = str2num(get(xminbox,'String'));
  set(gca,'XLim',[xmin,xmax]);
elseif strcmp(action,'setxmax'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  setxmax callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  xmax = str2num(get(xmaxbox,'String'));
  set(gca,'XLim',[xmin,xmax]);
elseif strcmp(action,'setymin'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  setymin callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  ymin = str2num(get(yminbox,'String'));
  set(gca,'YLim',[ymin,ymax]);
elseif strcmp(action,'setymax'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  setymax callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  ymax = str2num(get(ymaxbox,'String'));
  set(gca,'YLim',[ymin,ymax]);elseif strcmp(action,'setymax'),
elseif strcmp(action,'load'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  load callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% When the load callback is called, the plot is reset and the data
% emptied.  Then a user-supplied file is read in as Matlab code.
% This file should define vectors x and y of interpolation points.
% These points are taken as the new data and plotted.  The file
% may also define some of xmin, xmax, ymin, and ymax, in which
% case these are set accordingly.
  loadfile = get(loadbox,'String');
  ginterp('reset')
  x=[]; y=[];
  eval(loadfile);
  set(xminbox,'String',num2str(xmin));
  set(xmaxbox,'String',num2str(xmax));
  set(yminbox,'String',num2str(ymin));
  set(ymaxbox,'String',num2str(ymax));
  set(gca,'XLim',[xmin,xmax]);
  set(gca,'YLim',[ymin,ymax]);
  n=min(length(x),length(y));
  x=x(1:n); y=y(1:n);
  for j=1:n
    line(x(j),y(j),'Marker','.','MarkerSize',25,'Color','red',...
      'erasemode','background');
  end
elseif strcmp(action,'interpolate'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  interpolate callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  if n == 0
    disp('You must first supply points to interpolate.')
  else
    if extrapolate
      xpmin = xmin;
      xpmax = xmax;
    else
      xpmin = min(x(1:n));
      xpmax = max(x(1:n));
    end
    if (interptype(1:6) == 'polyno')
      % compute the interpolating polynomial using Newton's form:
      %  compute divided difference table
      d = y;
      for i = 1:n-1
        for j=n:-1:i+1
          d(j) = (d(j) - d(j-1)) / (x(j) - x(j-i));
        end
      end
      %  evaluate polynomial
      t = linspace(xpmin,xpmax,nplot);
      v = d(n)*ones(size(t));
      for i = n-1:-1:1
        v = v.*(t-x(i)) + d(i);
      end
      %  and plot it
      line(t,v,'erasemode','background','color','yellow');
    elseif (interptype(1:6) == 'spline')
      % interpolate a single point with a constant
      if n == 1
        line([xpmin,xpmax],[y(1),y(1)],'erasemode','background','color','green');
      else
      % evaluate spline interpolant using Matlab supplied spline.m
        t = linspace(xpmin,xpmax,nplot);
        line(t,spline(x,y,t),'erasemode','background','color','green');
      end
    elseif (interptype(1:6) == 'piecew')
      % interpolate a single point with a constant
      if n == 1
        line([xpmin,xpmax],[y(1),y(1)],'erasemode','background','color','cyan');
      else
      % sort points by increasing x coordinate, add points at beginning and
      % end to handle extrapolation, and connect the points
        [x,i] = sort(x);
        y = y(i);
        xx = [xpmin,x,xpmax];
        yy = [y(1)+(xpmin-x(1))*(y(2)-y(1))/(x(2)-x(1)), y,...
              y(n)+(xpmax-x(n))*(y(n)-y(n-1))/(x(n)-x(n-1))];
        line(xx,yy,'erasemode','background','color','cyan');
      end
    end
  end
elseif strcmp(action,'reset'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  reset callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  cla;
  n=0;
  x=[];
  y=[];
elseif strcmp(action,'help'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  help callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  helpfig = figure('Position',[660,420,420,210],...
       'Name','Help','NumberTitle','off',...
       'DefaultUicontrolFontSize',12);
   
  % Define the help string to display.
  helpstr=[
  '                                                                '
  '                            ginterp.m by Douglas N. Arnold, 9/96'
  '                                                                '
  '    This tool demonstrates Lagrange interpolation and cubic     '
  '    spline interpolation of a given set of data points. Enter   '
  '    points by clicking the mouse within the axes box. When      '
  '    the desired number of points have been entered, click on    '
  '    the "Interpolate" button to display the interpolating       '
  '    polynomial.  The plot can be cleared by clicking on         '
  '    "Reset".  Click on the quit button to exit the program.     '
  ];
   
  % Display it.
  uicontrol(...
     'Style','edit',...
     'Units','normalized',...
     'BackGroundColor',[1 1 .9],...
     'ForeGroundColor',[.2 0 .6],...
     'String',helpstr,...
     'Position',[0 0 1 1],'Max',20,...
     'FontSize',12);
   
  % Button for more help.
  uicontrol(...
     'Style','push',...
     'String','More...',...
     'Position',[220 5 60 20],...
     'CallBack','ginterp(''morehelp'')')
   
elseif strcmp(action,'morehelp'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  morehelp callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  morehelpfig = figure('Position',[660,420,420,350],...
       'Name','MoreHelp','NumberTitle','off',...
       'DefaultUicontrolFontSize',12);
   
  % Define the help string to display.
  morehelpstr=[
  '                                                                    '
  '    Use the "poly./spline/pw lin." popup to toggle between          '
  '    polynomial interpolation, cubic spline interpolation, and       '
  '    piecewise linear interpolation. (Not-a-knot end conditions      '
  '    are used for the cubic spline.) The plot limits can be          '
  '    changed at any time by typing into the boxes labelled           '
  '    xmin, xmax, ymin, and ymax. A grid can be toggled on            '
  '    and off with the button labelled "grid". Normally the           '
  '    interpolant is plotted between the first and last data          '
  '    points. It will instead be extended to the edges of the         '
  '    plotting box if the "extr." (extrapolate) toggle is on.         '
  '                                                                    '
  '    As an alternative to entering interpolation points with         '
  '    the mouse, you can create a Matlab M-file with commands         '
  '    defining vectors x and y of the same length. Typing the         '
  '    name of this file (without the .m suffix) will cause the        '
  '    file to be read and the interpolation points to be defined      '
  '    accordingly. The file may also define xmin, xmax, ymin,         '
  '    and/or ymax.                                                    '
  ];
   
  % Display it.
  uicontrol(...
     'Style','edit',...
     'Units','normalized',...
     'BackGroundColor',[1 1 .9],...
     'ForeGroundColor',[.2 0 .6],...
     'String',morehelpstr,...
     'Position',[0 0 1 1],'Max',20,...
     'FontSize',12);
   
elseif strcmp(action,'quit'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  quit callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  delete(ginterpfig);
  return;
elseif strcmp(action,'grid'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  grid callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  if get(gridbutton,'Value') == 1,
    set(ginterpaxes,'XGrid','on','YGrid','on');
  else
    set(ginterpaxes,'XGrid','off','YGrid','off');
  end
elseif strcmp(action,'extrap'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  extrap callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  if get(extrapbutton,'Value') == 1,
    extrapolate=1;
  else
    extrapolate=0;
  end
elseif strcmp(action,'interptype'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  interptype callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  if get(typepopup,'Value') == 1
    interptype = 'polynomial';
  elseif get(typepopup,'Value') == 2
    interptype = 'spline';
  else
    interptype = 'piecewise linear';
  end    
  set(ginterpfig,'Name',[interptype ' interpolation'])
elseif strcmp(action,'buttondown'),
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  ButtonDown function
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  pt=get(ginterpaxes,'CurrentPoint');
  if pt(1,1) >= xmin & pt(1,1) <= xmax & pt(1,2) >= ymin & pt(1,2) <= ymax
    line(pt(1,1),pt(1,2),'Marker','.','MarkerSize',25,...
      'Color','red','erasemode','background');
    n=n+1;
    x(n)=pt(1,1);
    y(n)=pt(1,2);
  end

end
