function out = zoom(varargin)
%ZOOM   Zoom in and out on a 2-D plot.
%   ZOOM with no arguments toggles the zoom state.
%   ZOOM(FACTOR) zooms the current axis by FACTOR.
%       Note that this does not affect the zoom state.
%   ZOOM ON turns zoom on for the current figure.  
%   ZOOM OFF turns zoom off in the current figure.
%   ZOOM OUT returns the plot to its initial (full) zoom.
%   ZOOM XON or ZOOM YON turns zoom on for the x or y axis only.
%   ZOOM RESET clears the zoom out point.
%
%   When zoom is on, click the left mouse button to zoom in on the
%   point under the mouse.  Click the right mouse button to zoom out
%   (shift-click on the Macintosh).  Each time you click, the axes
%   limits will be changed by a factor of 2 (in or out).  You can also
%   click and drag to zoom into an area.  Double clicking zooms out to
%   the point at which zoom was first turned on for this figure.  Note
%   that turning zoom on, then off does not reset the zoom point.
%   This may be done explicitly with ZOOM RESET.
%   
%   ZOOM(FIG,OPTION) applies the zoom command to the figure specified
%   by FIG. OPTION can be any of the above arguments.

%   ZOOM FILL scales a plot such that it is as big as possible
%   within the axis position rectangle for any azimuth and elevation.

%   Clay M. Thompson 1-25-93
%   Revised 11 Jan 94 by Steven L. Eddins
%   Copyright (c) 1984-98 by The MathWorks, Inc.
%   $Revision: 5.42 $  $Date: 1998/09/30 13:43:56 $

%   Note: zoom uses the figure buttondown and buttonmotion functions
%
%   ZOOM XON zooms x-axis only
%   ZOOM YON zooms y-axis only

switch nargin,
   
   %%%%%%%%%%%%%%%%%%%%%%%%%%
   %%% No Input Arguments %%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%
case 0,
   fig=get(0,'currentfigure');
   if isempty(fig), return, end
   zoomCommand='toggle';
   
   %%%%%%%%%%%%%%%%%%%%%%%%%%
   %%% One Input Argument %%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%
case 1,
   
   % If the argument is a string, the argument is a zoom command
   % (i.e. (on, off, down, xdown, etc.).  Otherwise, the argument is
   % assumed to be a figure handle, in which case all we do is
   % toggle the zoom status.
   
   if isstr(varargin{1}),
      fig=get(0,'currentfigure');
      if isempty(fig), return, end
      
      zoomCommand=varargin{1};
   else
      scale_factor=varargin{1};
      zoomCommand='scale';
      fig = gcf;
   end % if
   
   %%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%% Two Input Arguments %%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%
case 2,
   fig=varargin{1};
   zoomCommand=varargin{2};
   
otherwise,
   error(nargchk(0, 2, nargin));
   
end % switch nargin

%
% handle 'off' commands first
%
if strcmp(zoomCommand,'off'),
   %
   % turn off zoom, and take a hike
   %
   set(findall(fig,'Tag','figToolZoomIn'),'State','off');
   set(findall(fig,'Tag','figToolZoomOut'),'State','off');   
   if ~isempty(getappdata(fig,'ZoomFigureMode'))
      rmappdata(fig,'ZOOMFigureMode');
   end

   state = getuprop(fig,'ZOOMFigureState');
   if ~isempty(state),
      uirestore(state);
      clruprop(fig,'ZOOMFigureState');
   end
   return
end % if

ax=get(fig,'currentaxes');

rbbox_mode = 0;
zoomx = 1; zoomy = 1; % Assume no constraints

zoomCommand=lower(zoomCommand);

if ~isempty(ax) & any(get(ax,'view')~=[0 90]) ...
           & ~(strcmp(zoomCommand,'scale') | ...
           strcmp(zoomCommand,'fill')),
   fZoom3d = 1;
   % set(findall(fig,'Tag','figToolZoom'),'State','off');   
   % warning('Zoom is only supported for 2D plots.');
   % return % Do nothing
else
   fZoom3d = 0;
end

if strcmp(zoomCommand,'toggle'),
   state = getuprop(fig,'ZOOMFigureState');
   if isempty(state)
      zoom(fig,'on');
   else
      zoom(fig,'off');
   end
   return
end % if

% Catch constrained zoom
if strcmp(zoomCommand,'xdown'),
   zoomy = 0; zoomCommand = 'down'; % Constrain y
elseif strcmp(zoomCommand,'ydown')
   zoomx = 0; zoomCommand = 'down'; % Constrain x
end


switch zoomCommand
case 'down',
   % Activate axis that is clicked in
   allAxes = findobj(datachildren(fig),'flat','type','axes');
   ZOOM_found = 0;
   
   % this test may be causing failures for 3d axes
   for i=1:length(allAxes),
      ax=allAxes(i);
      ZOOM_Pt1 = get(ax,'CurrentPoint');
      xlim = get(ax,'xlim');
      ylim = get(ax,'ylim');
      if (xlim(1) <= ZOOM_Pt1(1,1) & ZOOM_Pt1(1,1) <= xlim(2) & ...
            ylim(1) <= ZOOM_Pt1(1,2) & ZOOM_Pt1(1,2) <= ylim(2))
         ZOOM_found = 1;
         set(fig,'currentaxes',ax);
         break
      end % if
   end % for

   if ZOOM_found==0, return, end
   
   % Check for selection type
   selection_type = get(fig,'SelectionType');
   zoomMode = getappdata(fig,'ZOOMFigureMode');

   axz = get(ax,'ZLabel');
   
      if fZoom3d

      viewData = getappdata(axz,'ZOOMAxesView');
      if isempty(viewData)
         viewProps = { 'CameraTarget'...
                    'CameraTargetMode'...
                    'CameraViewAngle'...
                    'CameraViewAngleMode'};
         setappdata(axz,'ZOOMAxesViewProps', viewProps);
         setappdata(axz,'ZOOMAxesView', get(ax,viewProps));
      end
      
      if isempty(zoomMode) | strcmp(zoomMode,'in');
         zoomLeftFactor = 1.5;
         zoomRightFactor = .75;         
      elseif strcmp(zoomMode,'out');
         zoomLeftFactor = .75;
         zoomRightFactor = 1.5;
      end
      
      switch selection_type
         case 'open'
            set(ax,getappdata(axz,'ZOOMAxesViewProps'),...
                    getappdata(axz,'ZOOMAxesView'));
         case 'normal'
            newTarget = mean(get(ax,'CurrentPoint'),1);
            set(ax,'CameraTarget',newTarget);
            camzoom(ax,zoomLeftFactor);
         otherwise
            newTarget = mean(get(ax,'CurrentPoint'),1);
            set(ax,'CameraTarget',newTarget);
            camzoom(ax,zoomRightFactor);
      end

      return
   end

   if isempty(zoomMode) | strcmp(zoomMode,'in');
      switch selection_type
         case 'normal'
            % Zoom in
            m = 1;
            scale_factor = 2; % the default zooming factor
         case 'open'
            % Zoom all the way out
            zoom(fig,'out');
            return;
         otherwise
            % Zoom partially out
            m = -1;
            scale_factor = 2;
      end
   elseif strcmp(zoomMode,'out')
      switch selection_type
         case 'normal'
            % Zoom partially out
            m = -1;
            scale_factor = 2;
         case 'open'
            % Zoom all the way out
            zoom(fig,'out');
            return;
         otherwise
            % Zoom in
            m = 1;
            scale_factor = 2; % the default zooming factor
      end
   else % unrecognized zoomMode
      return
   end
   
   
   ZOOM_Pt1 = get_currentpoint(ax);
   ZOOM_Pt2 = ZOOM_Pt1;
   center = ZOOM_Pt1;
   
   if (m == 1)
      % Zoom in
      units = get(fig,'units'); set(fig,'units','pixels')
      rbbox([get(fig,'currentpoint') 0 0],get(fig,'currentpoint'));
      ZOOM_Pt2 = get_currentpoint(ax);
      set(fig,'units',units)
      
      % Note the currentpoint is set by having a non-trivial up function.
      if min(abs(ZOOM_Pt1-ZOOM_Pt2)) >= ...
            min(.01*[diff(get_xlim(ax)) diff(get_ylim(ax))]),
         % determine axis from rbbox 
         a = [ZOOM_Pt1;ZOOM_Pt2]; a = [min(a);max(a)];
         
         % Undo the effect of get_currentpoint for log axes
         if strcmp(get(ax,'XScale'),'log'),
            a(1:2) = 10.^a(1:2);
         end
         if strcmp(get(ax,'YScale'),'log'),
            a(3:4) = 10.^a(3:4);
         end
         rbbox_mode = 1;
      end
   end
   limits = zoom(fig,'getlimits');
   
case 'scale',
   if all(get(ax,'view')==[0 90]), % 2D zooming with scale_factor
      
      % Activate axis that is clicked in
      ZOOM_found = 0;
      ax = gca;
      xlim = get(ax,'xlim');
      ylim = get(ax,'ylim');
      ZOOM_Pt1 = [sum(xlim)/2 sum(ylim)/2];
      ZOOM_Pt2 = ZOOM_Pt1;
      center = ZOOM_Pt1;
      
      if (xlim(1) <= ZOOM_Pt1(1,1) & ZOOM_Pt1(1,1) <= xlim(2) & ...
            ylim(1) <= ZOOM_Pt1(1,2) & ZOOM_Pt1(1,2) <= ylim(2))
         ZOOM_found = 1;
      end % if
      
      if ZOOM_found==0, return, end
      
      if (scale_factor >= 1)
         m = 1;
      else
         m = -1;
      end
      
   else % 3D
      old_CameraViewAngle = get(ax,'CameraViewAngle')*pi/360;
      ncva = atan(tan(old_CameraViewAngle)*(1/scale_factor))*360/pi;
      set(ax,'CameraViewAngle',ncva);
      return;
   end
   
   limits = zoom(fig,'getlimits');

case 'getmode'
   state = getuprop(fig,'ZOOMFigureState');
   if isempty(state)
      out = 'off';
   else
      mode = getappdata(fig,'ZOOMFigureMode');
      if isempty(mode)
         out = 'on';
      else
         out = mode;
      end
   end
   return

   
case 'on',
   
   set(findall(fig,'Tag','figToolZoomIn'),'State','on');
   
   state = getuprop(fig,'ZOOMFigureState');
   if isempty(state),
      state = uiclearmode(fig,'zoom',fig,'off');
      setuprop(fig,'ZOOMFigureState',state);
   end
   set(fig,'windowbuttondownfcn','zoom down', ...
      'windowbuttonupfcn','ones;', ...
      'windowbuttonmotionfcn','','buttondownfcn','', ...
      'interruptible','on');
   set(ax,'interruptible','on')
   return
   
case 'inmode'
   zoom(fig,'on');
   set(findall(fig,'Tag','figToolZoomIn'),'State','on');   
   set(findall(fig,'Tag','figToolZoomOut'),'State','off');      
   setappdata(fig,'ZOOMFigureMode','in');
   return   
   
case 'outmode'
   zoom(fig,'on');
   set(findall(fig,'Tag','figToolZoomIn'),'State','off');   
   set(findall(fig,'Tag','figToolZoomOut'),'State','on');      
   setappdata(fig,'ZOOMFigureMode','out');
   return
   
case 'reset',
   axz = get(ax,'ZLabel');
   if isappdata(axz,'ZoomAxesData')
     rmappdata(axz,'ZOOMAxesData');
   end
   return
   
case 'xon',
   zoom(fig,'on') % Set up userprop
   set(fig,'windowbuttondownfcn','zoom xdown', ...
      'windowbuttonupfcn','ones;', ...
      'windowbuttonmotionfcn','','buttondownfcn','',...
      'interruptible','on');
   set(ax,'interruptible','on')
   return
   
case 'yon',
   zoom(fig,'on') % Set up userprop
   set(fig,'windowbuttondownfcn','zoom ydown', ...
      'windowbuttonupfcn','ones;', ...
      'windowbuttonmotionfcn','','buttondownfcn','',...
      'interruptible','on');
   set(ax,'interruptible','on')
   return
   
case 'out',
   limits = zoom(fig,'getlimits');
   center = [sum(get_xlim(ax))/2 sum(get_ylim(ax))/2];
   m = -inf; % Zoom totally out
   
case 'getlimits', % Get axis limits
   axz = get(ax,'ZLabel');
   limits = getappdata(axz,'ZOOMAxesData');
   % Do simple checking of userdata
   if size(limits,2)==4 & size(limits,1)<=2, 
      if all(limits(1,[1 3])<limits(1,[2 4])), 
         getlimits = 0; out = limits(1,:); return   % Quick return
      else
         getlimits = -1; % Don't munge data
      end
   else
      if isempty(limits), getlimits = 1; else getlimits = -1; end
   end
   
   % If I've made it to here, we need to compute appropriate axis
   % limits.
   
   if isempty(getappdata(axz,'ZOOMAxesData')),
      % Use quick method if possible
      xlim = get_xlim(ax); xmin = xlim(1); xmax = xlim(2); 
      ylim = get_ylim(ax); ymin = ylim(1); ymax = ylim(2); 
      
   elseif strcmp(get(ax,'xLimMode'),'auto') & ...
         strcmp(get(ax,'yLimMode'),'auto'),
      % Use automatic limits if possible
      xlim = get_xlim(ax); xmin = xlim(1); xmax = xlim(2); 
      ylim = get_ylim(ax); ymin = ylim(1); ymax = ylim(2); 
      
   else
      % Use slow method only if someone else is using the userdata
      h = get(ax,'Children');
      xmin = inf; xmax = -inf; ymin = inf; ymax = -inf;
      for i=1:length(h),
         t = get(h(i),'Type');
         if ~strcmp(t,'text'),
            if strcmp(t,'image'), % Determine axis limits for image
               x = get(h(i),'Xdata'); y = get(h(i),'Ydata');
               x = [min(min(x)) max(max(x))];
               y = [min(min(y)) max(max(y))];
               [ma,na] = size(get(h(i),'Cdata'));
               if na>1, dx = diff(x)/(na-1); else dx = 1; end
               if ma>1, dy = diff(y)/(ma-1); else dy = 1; end
               x = x + [-dx dx]/2; y = y + [-dy dy]/2;
            end
            xmin = min(xmin,min(min(x)));
            xmax = max(xmax,max(max(x)));
            ymin = min(ymin,min(min(y)));
            ymax = max(ymax,max(max(y)));
         end
      end
      
      % Use automatic limits if in use (override previous calculation)
      if strcmp(get(ax,'xLimMode'),'auto'),
         xlim = get_xlim(ax); xmin = xlim(1); xmax = xlim(2); 
      end
      if strcmp(get(ax,'yLimMode'),'auto'),
         ylim = get_ylim(ax); ymin = ylim(1); ymax = ylim(2); 
      end
   end
   
   limits = [xmin xmax ymin ymax];
   if getlimits~=-1, % Don't munge existing data.
      % Store limits ZOOMAxesData
      % store it with the ZLabel, so that it's cleared if the 
      % user plots again into this axis.  If that happens, this
      % state is cleared
      axz = get(ax,'ZLabel');
      setappdata(axz,'ZOOMAxesData',limits);
   end
   
   out = limits;
   return
   
case 'getconnect', % Get connected axes
   axz = get(ax,'ZLabel');
   limits = getappdata(axz,'ZOOMAxesData');
   if all(size(limits)==[2 4]), % Do simple checking
      out = limits(2,[1 2]);
   else
      out = [ax ax];
   end
   return
   
case 'fill',
   old_view = get(ax,'view');
   view(45,45);
   set(ax,'CameraViewAngleMode','auto');
   set(ax,'CameraViewAngle',get(ax,'CameraViewAngle'));
   view(old_view);
   return
   
otherwise
   error(['Unknown option: ',zoomCommand,'.']);
end

%
% Actual zoom operation
%

%========================================================
%  Part of modification for ADDAXIS commands
ystart = get(gca,'ylim');
%========================================================

if ~rbbox_mode,
   xmin = limits(1); xmax = limits(2);   
   ymin = limits(3); ymax = limits(4);   
   
   if m==(-inf),
      dx = xmax-xmin;
      dy = ymax-ymin;
   else
      dx = diff(get_xlim(ax))*(scale_factor.^(-m-1)); dx = min(dx,xmax-xmin);
      dy = diff(get_ylim(ax))*(scale_factor.^(-m-1)); dy = min(dy,ymax-ymin);
   end
   
   % Limit zoom.
   center = max(center,[xmin ymin] + [dx dy]);
   center = min(center,[xmax ymax] - [dx dy]);
   a = [max(xmin,center(1)-dx) min(xmax,center(1)+dx) ...
         max(ymin,center(2)-dy) min(ymax,center(2)+dy)];
   
   % Check for log axes and return to linear values.
   if strcmp(get(ax,'XScale'),'log'),
      a(1:2) = 10.^a(1:2);
   end
   if strcmp(get(ax,'YScale'),'log'),
      a(3:4) = 10.^a(3:4);
   end
   
end

% Check for axis equal and update a as necessary
if strcmp(get(ax,'plotboxaspectratiomode'),'manual') & ...
   strcmp(get(ax,'dataaspectratiomode'),'manual')
   ratio = get(ax,'plotboxaspectratio')./get(ax,'dataaspectratio');
   dx = a(2)-a(1);
   dy = a(4)-a(3);
   [kmax,k] = max([dx dy]./ratio(1:2));
   if k==1
      dy = kmax*ratio(2);
      a(3:4) = mean(a(3:4))+[-dy dy]/2;
   else
     dx = kmax*ratio(1);
     a(1:2) = mean(a(1:2))+[-dx dx]/2;
   end
end

% Update circular list of connected axes
list = zoom(fig,'getconnect'); % Circular list of connected axes.
if zoomx,
   if a(1)==a(2), return, end % Short circuit if zoom is moot.
   set(ax,'xlim',a(1:2))
   h = list(1);
   while h ~= ax,
      set(h,'xlim',a(1:2))
      % Get next axes in the list
      hz = get(h,'ZLabel');
      next = getappdata(hz,'ZOOMAxesData');
      if all(size(next)==[2 4]), h = next(2,1); else h = ax; end
   end
end
if zoomy,
   if a(3)==a(4), return, end % Short circuit if zoom is moot.
   set(ax,'ylim',a(3:4))
   h = list(2);
   while h ~= ax,
      set(h,'ylim',a(3:4))
      % Get next axes in the list
      hz = get(h,'ZLabel');
      next = getappdata(hz,'ZOOMAxesData');
      if all(size(next)==[2 4]), h = next(2,2); else h = ax; end
   end
end

%====================================================================
%  This is a modification to accomodate the ADDAXIS commands.  
%  Check to see if add axis has been used and update the axes using 
%  the same scale factors.  (also put ystart = get(gca,'ylim'); 
%  before the zoom is started above)

yend = get(gca,'ylim');

%  ystart and yend are the starting and ending yaxis limits
%  now go through all of the added axes and scale their limits
%axh = get(gca,'userdata');
axh = getaddaxisdata(gca,'axisdata');
if ~isempty(axh)
  for I = 1:length(axh)
    axhan = axh{I}(1);
    axyl = get(axhan,'ylim');
    axylnew(1) = axyl(1)+(yend(1)-ystart(1))/(ystart(2)-ystart(1)).*...
	                 (axyl(2)-axyl(1));
    axylnew(2) = axyl(2)-(ystart(2)-yend(2))/(ystart(2)-ystart(1)).*...
	                 (axyl(2)-axyl(1));
    set(axhan,'ylim',axylnew);
  end
end

%  END of modification
%================================================================

function p = get_currentpoint(ax)
%GET_CURRENTPOINT Return equivalent linear scale current point
p = get(ax,'currentpoint'); p = p(1,1:2);
if strcmp(get(ax,'XScale'),'log'),
   p(1) = log10(p(1));
end
if strcmp(get(ax,'YScale'),'log'),
   p(2) = log10(p(2));
end

function xlim = get_xlim(ax)
%GET_XLIM Return equivalent linear scale xlim
xlim = get(ax,'xlim');
if strcmp(get(ax,'XScale'),'log'),
   xlim = log10(xlim);
end

function ylim = get_ylim(ax)
%GET_YLIM Return equivalent linear scale ylim
ylim = get(ax,'ylim');
if strcmp(get(ax,'YScale'),'log'),
   ylim = log10(ylim);
end
