function STAT = LodImage( ACTION, TAGBASE, LEVEL, ARGUMENT )
% STAT = LodImage ACTION TAGBASE LEVEL ARGUMENT
%
% mrcat callback functions for loading, zooming, and brightening images.
%
%	ACTION is the action to perform, which may be one of 'load',
% 'create' (to re-load an image menu), 'brighten', 'darken', 'zoomout',
% or 'zoomdown'.
%
%	TAGBASE: If you are performing an action on the main editable
% image, TAGBASE is 'Image'.  Actions on the sagittal image pass the
% word 'Sag', and actions on the coronal/axial locator image pass the
% word 'CorA'.
%
%	LEVEL: For the 'create' function, this specifies the name of
% the menu whose entries you wish to fill.  For example, if you wish to
% fill entries in the SagPlane menu, you would pass TAGBASE='Sag',
% LEVEL='Plane'.
%
%	ARGUMENT is an optional argument.  Right now, it is only used
% to specify an increment for the 'load' action.
%
% Example: 
%
% STAT = LodImage load Image
%
%    This loads the file specified by strings in the menus whose tags are
% ImageSeries, ImagePlane, ImageType, and ImageFile.
%
% STAT = LodImage zoomdown Sag
%
%    Zoom in on the image displayed in the axes whose tag is SagAxes.
%
% STAT = LodImage brighten CorA
%
%    Brighten the image whose image data is stored in the object CorADisplay.
%
% STAT = LodImage create CorA Plane
%
%    Re-load the pulldown menu whose tag is CorAPlane, then proceed to 
%  re-load all menus dependent on this one, and finally re-load the image
%  file specified by these menus.
%
% Written by Mark Hasegawa-Johnson and Jul Setsu Cha
% Revision 1.24, March 30, 1999
%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Parameter defaults
if nargin<4,
  ARGUMENT='0';
  if nargin<3,
    LEVEL='File';
    if nargin<2,
      TAGBASE='Image';
    end
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Determine which image we're acting on, and whether or not it already exists.
% If it exists, get the UserData; if not, create the UserData.
ImageHdl = findobj(gcbf,'Tag', [TAGBASE 'Display']);
if isempty(ImageHdl),
  UserData = struct('brightness', 1, 'corners', zeros(4,3), 'slice_thickness', 0, ...
      'unclipped', zeros(16,16), 'xyz', [1:3]);
else,      
  UserData = get(ImageHdl, 'UserData');
  if ~isstruct(UserData) | isempty(UserData),
    UserData = struct('brightness', 1, 'corners', zeros(4,3), 'slice_thickness', 0, ...
	'unclipped', zeros(16,16), 'xyz', [1:3]);
  end;
end;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Find out what ACTION is, and act appropriately
%
switch ACTION,
  case 'brighten',
    LodImage_brightness( ImageHdl, UserData, TAGBASE, 1.2 );
  case 'darken'
    LodImage_brightness( ImageHdl, UserData, TAGBASE, 1/1.2 );
  case 'zoomvisible',
    LodImage_locator_visibility( TAGBASE );
  case {'zoomdown', 'zoomout'},
    MRIZoom(ACTION, ImageHdl, UserData);
  case 'create',
    LodImage_create( TAGBASE, LEVEL, ARGUMENT );
  case 'load',
    LodImage_load( TAGBASE, ARGUMENT, ImageHdl, UserData );
  case 'save',
    LodImage_save( TAGBASE, ARGUMENT, ImageHdl, UserData );
end;

% Set the return status, and return
STAT =  0;
return;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function LodImage_brightness( ImageHdl, UserData, TAGBASE, scale )
% LodImage_brightness( ImageHdl, UserData, TAGBASE, scale )

UserData.brightness = UserData.brightness * scale;
set(ImageHdl, 'CData', get(ImageHdl,'CData') * scale, 'UserData', UserData);
set(get(ImageHdl, 'Parent'), 'Tag', [ TAGBASE 'Axes' ]);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function LodImage_locator_visibility( TAGBASE );
%
% 'zoomvisible':  Toggle visibility of the ZoomLines and LocatorLines
tags = { [TAGBASE 'LocatorLine'], [TAGBASE 'ZoomLines'], [TAGBASE 'Lines'] };
h = [];
for i=1:2, 
  hi = findobj(gcbf,'Tag', tags{i}); 
  if ~isempty(hi), 
    h = [h hi]; 
    v = get(h(i),'Visible');
  end;
end;
set(findobj(gcbf,'Tag',tags{3}),'String', v);
if strcmp(v,'on'), v='off'; else, v='on'; end
for i=1:length(h), 
  set(h(i),'Visible', v); 
end
return;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function LodImage_create( TAGBASE, LEVEL, ARGUMENT );
%
% 'create':  Assume that the directory we are given is correct, and load a list of files or
% dirs from it

[ DIRECTORY, Names, LEVELS, CurLvl ] = get_directory_from_gui( TAGBASE, LEVEL );
[ String, Files, Dirs ] = filemenu(DIRECTORY);
hMENU = findobj(gcbf,'Tag', compound_tag( TAGBASE, LEVEL ));

% If there are no files or subdirectories, try a few heuristics
if length(String) < 3 & CurLvl > 3,
  % Look up the other possible options for [TAGBASE 'Series']
  SeriesMenu = findobj(gcbf, 'Tag', compound_tag(TAGBASE, 'Series'));
  SeriesDirs = get(SeriesMenu,'String');
  
  % If there is a '_loc' option, put that first
  iseries = strmatch('_loc', SeriesDirs, 'exact');
  if length(iseries)==0, iseries = strmatch('_LOC', SeriesDirs, 'exact'); end
  if length(iseries) > 0,
    TESTDIR = fullfile(Names{1}, Names{2}, SeriesDirs{iseries(1)}, Names{4});
    [ String, Files, Dirs ] = filemenu(TESTDIR);
  end
 
  % If '_loc' doesn't work, try the other Series options
  if length(String) < 3, iseries=0; end
  while length(String) < 3 & iseries < length(SeriesDirs),
    iseries = iseries+1;
    TESTDIR = fullfile(Names{1}, Names{2}, SeriesDirs{iseries}, Names{4})
    [ String, Files, Dirs ] = filemenu(TESTDIR);
  end
 
  if length(String) >= 3, 
    disp(sprintf('No files found in directory %s, using %s instead.',DIRECTORY, TESTDIR));
    set(SeriesMenu, 'Value', iseries);
  else
    % If no locator images found anywhere, return an empty image
    disp(sprintf('No files found in directory %s; no image loaded.',DIRECTORY));
    set(hMENU, 'Value', 1);
    set(hMENU, 'String', { 'nofiles' });
    set(hMENU, 'UserData',{ 'nofiles' });
    return;
  end
  
end;

% Check to see if the menu being filled already has a selection defined, and try to keep it
PathTail=get_menu_selection( compound_tag( TAGBASE, LEVEL ));
String = String(3:length(String));
Value = strmatch(PathTail, String, 'exact');
if length(Value)==0, Value=strmatch(upper(PathTail), String, 'exact'); end
if length(Value)==0, Value=strmatch(lower(PathTail), String, 'exact'); end    

% Write String and Value to the Menu object
set(hMENU, 'String', String);
set(hMENU, 'UserData', String);
if Value,
  set(hMENU, 'Value', Value(1));
else,
  set(hMENU, 'Value', round(length(String)/2));
end;

if strcmp(LEVEL, 'File'),
  % If LEVEL is 'File', then continue by loading the image
  LodImage('load', TAGBASE, 'File', '0');
else,
  % Otherwise, update the subsidiary directory
  LodImage('create', TAGBASE, LEVELS{CurLvl+1} );
end;
    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function  LodImage_load( TAGBASE, ARGUMENT, ImageHdl, UserData );
%
% Operations to actually load an image onto the screen
%
% If we are loading an image, first thing is to get the Increment, if it is defined
if nargin>=4, 
  Increment = str2num(ARGUMENT);
else,
  Increment = 0;
end;

%
% Get the path information from appropriate menus
[ DIRECTORY, Names ] = get_directory_from_gui( TAGBASE, 'File' );
PathTail = get_menu_selection(compound_tag(TAGBASE, 'File'), Increment);
FName = fullfile(DIRECTORY, PathTail);

% If file doesn't exist, return
if exist(FName) ~= 2, 
  disp(sprintf('File %s not found', FName))
  return;
end;

%
% Modify the String to only include files 18 above and 18 below the selected file
ImageFile = findobj(gcbf, 'Tag', compound_tag(TAGBASE, 'File'));
AllStrings = get(ImageFile,'UserData');
whichstring = strmatch(PathTail, AllStrings);
newstrings = { AllStrings{max(1,whichstring-18):min(length(AllStrings),whichstring+18)} };
newval = strmatch(PathTail, newstrings);
set(ImageFile, 'Value', newval);
set(ImageFile, 'String', newstrings);

%
% Read in the image, and the corner locations if possible
if findstr(lower(FName), '.mr'),
  [h,UserData.unclipped] = geread(FName,'m');
  UserData.corners = h.mri.corners;
  UserData.slice_thickness = h.mri.slice_thickness;
else,
  UserData.unclipped = double(imread(FName));
  
  % Read slice_thickness from the otl file, if available
  [X, H] = otlread( strcat(otlfile_from_names({ Names{:} PathTail }), '.otl') );
  if isfield(H, 'slice_thickness'), 
    UserData.slice_thickness = H.slice_thickness; 
  else
    UserData.slice_thickness = 3;  % Dummy default value
  end

  % Read corners from the otl file, if available
  UserData.corners = zeros(4,3);
  if isfield(H, 'top_left_hand_corner'), UserData.corners(1,:) = H.top_left_hand_corner; end
  if isfield(H, 'top_right_hand_corner'), UserData.corners(2,:) = H.top_right_hand_corner; end
  if isfield(H, 'bottom_right_hand_corner'), UserData.corners(4,:) = H.bottom_right_hand_corner; end
  lrsame = (UserData.corners(1,:)==UserData.corners(2,:));
  UserData.corners(3,lrsame==1) = UserData.corners(4,lrsame==1);
  UserData.corners(3,lrsame==0) = UserData.corners(1,lrsame==0);
end;

%
% Create a grayscale image for display purposes
if length(size(UserData.unclipped)) > 2, UserData.unclipped = mean(UserData.unclipped, 3); end;

%
% Figure out what is the correspondence between absolute dimensions (R,A,S)
% and image-based dimensions 'X' (left-right), 'Y' ('up-down'), and 'Z'
% (orthogonal to the plane of the image).
%
[Dlr, Ilr] = max(abs(UserData.corners(2,:)-UserData.corners(1,:)));
[Dud, Iud] = max(abs(UserData.corners(3,:)-UserData.corners(1,:)));
Iorth = find([1:3] ~= Ilr & [1:3] ~= Iud);
UserData.xyz = [ Ilr Iud Iorth ];

% Heuristic guess about the best brightness scale to start with.
% Scale should be set so that 1-3 percent of pixels are clipped, except that
% clipping threshold should never be above image max, or below 30% of image max.
% User can adjust this using brightness controls if desired.
%
maxval = max(UserData.unclipped(:));
apparent_scale = max(0.3*maxval, min(maxval, ...
    mean(UserData.unclipped(:))+2.5*std(UserData.unclipped(:))));
UserData.unclipped = UserData.unclipped ./ apparent_scale;

%
% Get axis handle, and set it to add an image
AxesHdl = findobj(gcbf,'Tag',[TAGBASE 'Axes']);
set(AxesHdl,'NextPlot','add');

%
% Use previous axis limits, unless they are larger than the new image size
ImgSize = size(UserData.unclipped);
xlim = get(AxesHdl,'XLim');
xlim = [ max(0.5, xlim(1)), min(ImgSize(2)+0.5, xlim(2)) ];
ylim = get(AxesHdl,'YLim');
ylim = [ max(0.5, ylim(1)), min(ImgSize(1)+0.5, ylim(2)) ];
if((xlim(2)-xlim(1))>1e3)
   xlim = [ max(0.5, xlim(1)), min(ImgSize(2)+0.5, xlim(2)) ];
   ylim = [ max(0.5, ylim(1)), min(ImgSize(1)+0.5, ylim(2)) ];
else
   xlim = [ min(0.5, xlim(1)), max(ImgSize(2)+0.5, xlim(2)) ];
   ylim = [ min(0.5, ylim(1)), max(ImgSize(1)+0.5, ylim(2)) ];
end

%
% Plot the image with the previous image's XLim and YLim, and store the UserData
ImageHdl = imagesm(UserData.brightness*UserData.unclipped, xlim, ylim, [], '', AxesHdl);
set(ImageHdl, 'UserData', UserData, 'Tag', [TAGBASE 'Display']);
set(get(ImageHdl,'parent'),'Tag', [TAGBASE 'Axes']);

%
% If TAGBASE is 'Image', then change the SvEdOtl object
%
if strcmp( TAGBASE, 'Image' ),
  here = fliplr(findstr(PathTail,'.'));
  OtlName = PathTail(1:(here(1)-1));
  set(findobj(gcbf,'Tag','SvEdOtl'), 'String', OtlName );
end;

%
% If ImageDisplay corners are defined, try to draw locator lines on SagDisplay and CorADisplay
%    
% First, see if we can find 'corners' and 'xyz' header information in main image
ImageDisplay = findobj(gcbf,'Tag','ImageDisplay');
UserData = get(ImageDisplay, 'UserData');
if ~isfield(UserData, 'corners') | ~isfield(UserData, 'xyz'), return; end;
if isempty(UserData.corners) | sum(abs(UserData.corners(:)))==0, return; end;
MRICorners = UserData.corners;

% Next, we get corners for each additional image, and set locator lines appropriately
ImageList = {'CorADisplay','SagDisplay'};
LocatorList = {'CorALocatorLine','SagLocatorLine'};
for loc=1:2,
  LocImage = findobj(gcbf,'Tag',ImageList{loc});
  UserData = get(LocImage, 'UserData');
  if isfield(UserData, 'corners') & isfield(UserData, 'xyz') & isfield(UserData,'unclipped'),
    LocCorners = UserData.corners;
    LocSize = size(UserData.unclipped);
    if ~isempty(LocCorners) & sum(abs(LocCorners(:))) > 0,
      
      % XPos is the positions of MRICorners on the dimension UserData.xyz(1)
      % The shift of 0.5 is because the left edge of pixel (1) is displayed at coord 0.5.
      X = UserData.xyz(1);
      XPos = LocSize(2)*(MRICorners([1 2 4 3 1],X) - LocCorners(1,X)) / ...
	  diff(LocCorners([1 2],X)) + 0.5;
      
      % YPos is the positions of MRICorners on the dimension UserData.Xyz(2)
      Y = UserData.xyz(2);
      YPos = LocSize(1)*(MRICorners([1 2 4 3 1],Y) - LocCorners(1,Y)) / ...
	  diff(LocCorners([1 3],Y)) + 0.5;
      
      % Plot the LocatorLine on axes which are Parent of LocImage
      LocAxes = get(LocImage, 'Parent');
      Locator = plot_tagged(LocatorList{loc}, XPos, YPos, 'y-', LocAxes);
    end;
  end;
end;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function  LodImage_save( TAGBASE, FORMAT, ImageHdl, UserData );
%
% Save an image in the specified FORMAT,
% with pixel intensities scaled by the currently selected image brightness.
%

% Get the image, scaled by the currently selected brightness
IMG = UserData.unclipped * UserData.brightness;

% Clip the image at 0 and 1, so we don't get aliasing
IMG = min(1, max(0, IMG));

% Get the path information from appropriate menus
[ DIRECTORY, Names ] = get_directory_from_gui(TAGBASE, 'File');
PathTail = get_menu_selection(compound_tag(TAGBASE, 'File'), 0);
FName = fullfile(DIRECTORY, PathTail);

% Change FName to the correct extension
where = findstr(FName, '.mr');
if length(where)==0, where = findstr(FName, '.MR'); end
if length(where)==0, where = length(FName); end
FName = [ FName(1:where(length(where))) FORMAT ];

% Write out the image in specified format
% 
if strcmp(FORMAT, 'bmp'),
  imwrite(repmat(IMG, [ 1 1 3 ]), FName, FORMAT);
else 
  imwrite(IMG, FName, FORMAT);
end 

% Send a message to the screen to tell the user where the file went
disp(sprintf('Saved image in the file %s', FName));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function MRIZoom(action, ImageDisplay, UserData)
% function MRIZoom(action, ImageDisplay, UserData)
%
% This callback function uses the matlab 'rbbox' function to choose a region of the
% main image axes to zoom in on, and then zooms the sagittal and CorA locator axes as well.
%

% First, look up the Axis and Image handles, and get UserData.unclipped size and selection_type
ImageAxes = get(ImageDisplay,'Parent');
[M, N] = size(UserData.unclipped);
selection_type = get(gcbf,'SelectionType');

% If action is 'zoomdown', find the XLim and YLim using an rbbox
if strcmp(action,'zoomdown') & any(strcmp(selection_type, { 'normal' 'alt' 'extend' })),
  
  % Find the XLim and YLim by looking at where FinalRect is relative to AxisRect
  set(ImageAxes,'Units','normalized');
  AxisRect = get(ImageAxes,'Position');
  oldXLim = get(ImageAxes,'XLim');
  oldYLim = get(ImageAxes,'YLim');
  
  set(gcbf,'Units','normalized');
  FinalRect = rbbox;
  LimAdjust(1:2) = (FinalRect(1:2) - AxisRect(1:2)) ./ AxisRect(3:4);
  LimAdjust(3:4) = (FinalRect(1:2)+FinalRect(3:4) - AxisRect(1:2)-AxisRect(3:4)) ./ AxisRect(3:4);
  
  if strcmp(selection_type, 'alt'),
    % If selection type is 'alt' (right-click), we zoom out by a factor of 2,
    % except that zoom-window boundaries can't exceed image boundaries
    XLim = [ oldXLim(1) + diff(oldXLim)*(LimAdjust(1) + [-1 1]) ];
    YLim = [ oldYLim(1) + diff(oldYLim)*(-LimAdjust(4) + [-1 1]) ];
  
  elseif strcmp(selection_type, 'extend'),
    % If selection type is 'extend' (shift-click), we just move the axes without zooming
    XLim = [ oldXLim(1) + diff(oldXLim)*(LimAdjust(1) + [-0.5 0.5]) ];
    YLim = [ oldYLim(1) + diff(oldYLim)*(-LimAdjust(4) + [-0.5 0.5]) ];
  
  elseif any(FinalRect(3:4) < 0.01),
    % If FinalRect spans less than one percent of the figure, just zoom by a factor of 50%,
    % with center of the zoom at center of FinalRect, unless that would put us off the edge of the image
    XLim = [ oldXLim(1) + diff(oldXLim)*(LimAdjust(1) + [-0.25 0.25]) ];
    YLim = [ oldYLim(1) + diff(oldYLim)*(-LimAdjust(4) + [-0.25 0.25]) ];

  else,
    % If selection type is 'normal' and rbbox is big enough, just zoom to it
    XLim = oldXLim + diff(oldXLim)*LimAdjust([1 3]);
    YLim = oldYLim - diff(oldYLim)*LimAdjust([4 2]);
  end;
  
  % If XLim or YLim extends beyond the edge of the image, shift it back into the image range
  dx = min(N, diff(XLim));
  dy = min(M, diff(YLim));
  XLim = max(0.5+[0 dx], min(N+0.5-[dx 0], XLim));
  YLim = max(0.5+[0 dy], min(M+0.5-[dy 0], YLim));
  
elseif (strcmp(action, 'zoomdown') & strcmp(selection_type, 'open')) | strcmp(action, 'zoomout'),
  % If selection type is 'open' or action is 'zoomout,' we zoom all the way out
  XLim = [0.5 N+0.5];
  YLim = [0.5 M+0.5];
end;

% Change the pointer to a watch, so user knows the zoom is being computed
set(gcbf,'Pointer','watch');
drawnow;

% Get the previous UserData, and re-plot the image with new axis limits
ImageDisplay = imagesm(UserData.brightness*UserData.unclipped, XLim, YLim, [], [], ImageAxes, ...
    'UserData', UserData);
set(get(ImageDisplay,'parent'),'Tag','ImageAxes');


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% After zooming the main axes, adjust axis limits on the two locator images.

% First, convert main-image XLim and YLim into absolute coordinates RASLim
MRICorners = UserData.corners;
MRIxyz = UserData.xyz;
RASLim = zeros(3,2);
RASLim(MRIxyz(1),1:2) = (1/M) * ((XLim-0.5) * MRICorners(2,MRIxyz(1)) + ...
    (M+0.5-XLim) * MRICorners(1,MRIxyz(1)));
RASLim(MRIxyz(2),1:2) = (1/N) * ((YLim-0.5) * MRICorners(3,MRIxyz(2)) + ...
    (N+0.5-YLim) * MRICorners(1,MRIxyz(2)));
RASLim(MRIxyz(3),1:2) = repmat(MRICorners(1,MRIxyz(3)), [1 2]);

% Define each of the two other images we would like to zoom
AxesList = {'CorAAxes','SagAxes'};
ImageList = {'CorADisplay','SagDisplay'};
ZoomList = {'CorAZoomLines','SagZoomLines'};
for loc=1:2,
  % Get the image and axes handles
  LocImage = findobj(gcbf,'Tag',ImageList{loc});
  if ~isempty(LocImage),
    UserData = get(LocImage, 'UserData');
    LocAxes = get(LocImage, 'Parent');
    
    % Check to see if the locator image has corners and xyz defined
    if isfield(UserData, 'corners') & isfield(UserData, 'xyz') & isfield(UserData, 'unclipped'),
      LocCorners = UserData.corners;
      LocSize = size(UserData.unclipped);
      XYRng = LocSize([2 1])';
      if ~isempty(LocCorners) & sum(abs(LocCorners(:))) > 0,
	
	% XPos is the projection of RASLim in the locator image X direction
	X = UserData.xyz(1);
	Pos(1,:) = LocSize(2)*(RASLim(X,:) - LocCorners(1,X)) / diff(LocCorners([1 2],X)) + 0.5;

	% YPos is the projection of RASLim in the locator image Y direction
	Y = UserData.xyz(2);
	Pos(2,:) = LocSize(1)*(RASLim(Y,:) - LocCorners(1,Y)) / diff(LocCorners([1 3],Y)) + 0.5;

	% Locator image zoom size is based on the maximum of XPos and YPos ranges
	[ rng, XorY ] = max(abs(diff(Pos, 1, 2)));
	
	% Add 1.5*rng to the dimension in which rng sits; add 2.5*rng to the other dimension
	Expand = 1.25 * rng * [-1 1; -1 1];
	Expand(XorY,:) = [ -0.75*rng 0.75*rng ];
	newlim = Pos + Expand;
	
	% If new limits extend beyond the edge of the image, shift it back into the image range
	dimrng = min(XYRng, diff(newlim,1,2));
	newlim = max(0.5+[[0;0] dimrng], min([XYRng+0.5-dimrng XYRng+0.5], newlim));

	% Re-plot using new axis limits
	LocImage = imagesm(UserData.brightness*UserData.unclipped, newlim(1,:), newlim(2,:), ...
	    [], [], LocAxes, 'UserData', UserData);
	set(get(LocImage,'parent'),'Tag',AxesList{loc});
	% set(LocAxes,'XLim',newlim(1,:));
	% set(LocAxes,'YLim',newlim(2,:));
	
	% Now create the zoom lines
	ZoomLines(XorY,1:4) = Pos(XorY,[1 1 2 2]);
	ZoomLines(3-XorY,:) = newlim(3-XorY,[1 2 2 1]);
	H=plot_tagged(ZoomList{loc}, ZoomLines(1,:), ZoomLines(2,:), 'c', LocAxes);
      end
    end
  end
end  
  
% Finally, end MRIZoom by changing the pointer back to previous pointer, so user knows the zoom is done
set(gcbf,'Pointer','crosshair');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function plot_tagged: return the handle of the plot, so we can reset axis label
% We specify parent, so that the parent handle and tag remain unchanged!!  (also, it's faster)

function H = plot_tagged(tag, xdata, ydata, color, parent,varargin)

C = get(parent,'Children');
t = strmatch(tag, get(C,'Tag'));
if t,
  set(C(t),'XData',xdata,'YData',ydata,varargin{:});
  H = C(t);
else,
  H = plot(xdata,ydata,color,'Tag',tag,'Parent',parent,varargin{:});
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
% get_menu_selection: 
% a convenience routine for finding the currently selected string from a tagged menu

function selection = get_menu_selection(tag, inc)

if nargin<2, inc=0; end
Menu = findobj(gcbf,'Tag',tag);
if ~isempty(Menu),
  String = get(Menu,'String');
  Value = get(Menu,'Value');
  selection = String{max(1, min(length(String), Value+inc))};
else,
  selection = '';
end;

    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
% compound_tag:
% combine a TAGBASE descriptor (e.g. 'Image', 'Sag', or 'CorA') with a LEVEL descriptor
% (e.g. 'Type', 'Series', 'Plane', or 'Display') to give the Tag value of an object
% which we wish to find on screen

function TAG = compound_tag(TAGBASE, LEVEL)

if nargin<2, 
  TAGBASE='';
  if nargin<1, 
    LEVEL='';
  end
end

if length(TAGBASE)==0, TAGBASE='Image'; end
if length(LEVEL)==0, LEVEL='Display'; end

TAG = strcat(TAGBASE, LEVEL);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
% get_directory_from_gui:
% parse information on the various GUI pulldown menus to find the directory in which the current
% file should reside
%
function [ DIRECTORY, Names, LEVELS, CurLvl ] = get_directory_from_gui(TAGBASE, LEVEL)
global ROOTDIR;
    
% Determine current level
LEVELS = { 'Type' 'Series' 'Plane' 'File' };
CurLvl = strmatch(LEVEL, LEVELS);

if CurLvl <= 1,
  % If top level, the directory is just the ROOTDIR
  Names  = { ROOTDIR };
  DIRECTORY = ROOTDIR;
  
else
  % Otherwise, look for files in a DIRECTORY which depends on LEVEL
  Names = { ROOTDIR, get_menu_selection( compound_tag('Image', LEVELS{1})) };
  for lvl=3:CurLvl,
    Names = { Names{:}, get_menu_selection( compound_tag(TAGBASE, LEVELS{lvl-1})) };
  end;

  DIRECTORY = fullfile(Names{:});
end
  
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
% otlfile_from_names(Names):
% a convenience routine for finding the current OTL filename and directory
% from the GUI window
function [ Path, Directory, FName ] = otlfile_from_names(Names)
% 
% Construct the complete otl filename
FName = Names{5};
TName = get(findobj(gcbf,'Tag','OtlDir'),'String');
PName = Names{4};
SName = Names{3};
Root = Names{1};

% Next, if FName contains a string which equals the file type, remove it
where = findstr([ '.' Names{2} ], FName);
for i=length(where):-1:1,
  if length(FName) > where(i) + length(Names{2}) + 1,
    FName = strcat( FName(1:(where(i)-1)), FName((where(i)+length(Names{2})+2):length(FName)) );
  else
    FName = FName(1:(where(i)-1));
  end
end

% Finally, construct the path name  
Directory = fullfile(Root, TName, SName, PName);
Path = fullfile(Directory, FName);


