function rect = getrectangle(varargin)
%getrectangle Select rectangle with mouse.
%   RECT = getrectangle(FIG) lets you select a rectangle in the
%   current axes of figure FIG using the mouse.  Use the mouse to
%   click and drag the desired rectangle.  RECT is a four-element
%   vector with the form [xmin ymin width height].  To constrain
%   the rectangle to be a square, use a shift- or right-click to
%   begin the drag.
%
%   RECT = getrectangle(AX) lets you select a rectangle in the axes
%   specified by the handle AX.
%
%   See also GETLINE, GETPTS.

%   Callback syntaxes:
%        getrectangle('ButtonDown')
%        getrectangle('ButtonMotion')
%        getrectangle('ButtonUp')

%   Copyright 1993-2003 The MathWorks, Inc.
%   $Revision: 5.23 $  $Date: 2003/01/17 16:27:31 $

global getrectangle_FIG getrectangle_AX getrectangle_H1 getrectangle_H2
global getrectangle_PT1 getrectangle_TYPE

if ((nargin >= 1) & (ischar(varargin{1})))
    % Callback invocation: 'ButtonDown', 'ButtonMotion', or 'ButtonUp'
    feval(varargin{:});
    return;
end

if (nargin < 1)
    getrectangle_AX = gca;
    getrectangle_FIG = get(getrectangle_AX, 'Parent');
else
    if (~ishandle(varargin{1}))
        CleanGlobals;
        error('First argument is not a valid handle');
    end
    
    switch get(varargin{1}, 'Type')
    case 'figure'
        getrectangle_FIG = varargin{1};
        getrectangle_AX = get(getrectangle_FIG, 'CurrentAxes');
        if (isempty(getrectangle_AX))
            getrectangle_AX = axes('Parent', getrectangle_FIG);
        end

    case 'axes'
        getrectangle_AX = varargin{1};
        getrectangle_FIG = get(getrectangle_AX, 'Parent');

    otherwise
        CleanGlobals;
        error('First argument should be a figure or axes handle');
    end
end

% Remember initial figure state
old_db = get(getrectangle_FIG, 'DoubleBuffer');
state = uisuspend(getrectangle_FIG);

% Set up initial callbacks for initial stage
set(getrectangle_FIG, ...
    'Pointer', 'crosshair', ...
    'WindowButtonDownFcn', 'getrectangle(''ButtonDown'');', ...
    'DoubleBuffer', 'on');

% Set axes limit modes to manual, so that the presence of lines used to
% draw the rectangles doesn't change the axes limits.
original_modes = get(getrectangle_AX, {'XLimMode', 'YLimMode', 'ZLimMode'});
set(getrectangle_AX,'XLimMode','manual', ...
               'YLimMode','manual', ...
               'ZLimMode','manual');

% Bring target figure forward
figure(getrectangle_FIG);

% Initialize the lines to be used for the drag
getrectangle_H1 = line('Parent', getrectangle_AX, ...
                  'XData', [0 0 0 0 0], ...
                  'YData', [0 0 0 0 0], ...
                  'Visible', 'off', ...
                  'Clipping', 'off', ...
                  'Color', 'k', ...
                  'LineStyle', '-');

getrectangle_H2 = line('Parent', getrectangle_AX, ...
                  'XData', [0 0 0 0 0], ...
                  'YData', [0 0 0 0 0], ...
                  'Visible', 'off', ...
                  'Clipping', 'off', ...
                  'Color', 'w', ...
                  'LineStyle', ':');


% We're ready; wait for the user to do the drag
% Wrap the waitfor call in try-catch so
% that if the user Ctrl-C's we get a chance to
% clean up the figure.
errCatch = 0;
try
    waitfor(getrectangle_H1, 'UserData', 'Completed');
catch
    errCatch = 1;
end

% After the waitfor, if getrectangle_H1 is still valid
% and its UserData is 'Completed', then the user
% completed the drag.  If not, the user interrupted
% the action somehow, perhaps by a Ctrl-C in the
% command window or by closing the figure.

if (errCatch == 1)
    errStatus = 'trap';
    
elseif (~ishandle(getrectangle_H1) | ...
            ~strcmp(get(getrectangle_H1, 'UserData'), 'Completed'))
    errStatus = 'unknown';
    
else
    errStatus = 'ok';
    x = get(getrectangle_H1, 'XData');
    y = get(getrectangle_H1, 'YData');
end

% Delete the animation objects
if (ishandle(getrectangle_H1))
    delete(getrectangle_H1);
end
if (ishandle(getrectangle_H2))
    delete(getrectangle_H2);
end

% Restore the figure state
if (ishandle(getrectangle_FIG))
   uirestore(state);
   set(getrectangle_FIG, 'DoubleBuffer', old_db);
   
   if ishandle(getrectangle_AX)
       set(getrectangle_AX, {'XLimMode','YLimMode','ZLimMode'}, original_modes);
   end
end

CleanGlobals;

% Depending on the error status, return the answer or generate
% an error message.
switch errStatus
case 'ok'
    % Return the answer
    xmin = min(x);
    ymin = min(y);
    rect = [xmin ymin max(x)-xmin max(y)-ymin];
    
case 'trap'
    % An error was trapped during the waitfor
    error('Interruption during mouse selection.');
    
case 'unknown'
    % User did something to cause the rectangle drag to
    % terminate abnormally.  For example, we would get here
    % if the user closed the figure in the drag.
    error('Interruption during mouse selection.');
end

%--------------------------------------------------
% Subfunction ButtonDown
%--------------------------------------------------
function ButtonDown

global getrectangle_FIG getrectangle_AX getrectangle_H1 getrectangle_H2
global getrectangle_PT1 getrectangle_TYPE

set(getrectangle_FIG, 'Interruptible', 'off', ...
                 'BusyAction', 'cancel');

%[x1, y1] = getcurpt(getrectangle_AX);
pt = get(getrectangle_AX, 'CurrentPoint');
x1 = pt(1,1);
y1 = pt(1,2);

getrectangle_PT1 = [x1 y1];
getrectangle_TYPE = get(getrectangle_FIG, 'SelectionType');
x2 = x1;
y2 = y1;
xdata = [x1 x2 x2 x1 x1];
ydata = [y1 y1 y2 y2 y1];

set(getrectangle_H1, 'XData', xdata, ...
                'YData', ydata, ...
                'Visible', 'on');
set(getrectangle_H2, 'XData', xdata, ...
                'YData', ydata, ...
                'Visible', 'on');

% Let the motion functions take over.
set(getrectangle_FIG, 'WindowButtonMotionFcn', 'getrectangle(''ButtonMotion'');', ...
                 'WindowButtonUpFcn', 'getrectangle(''ButtonUp'');');


%-------------------------------------------------
% Subfunction ButtonMotion
%-------------------------------------------------
function ButtonMotion

global getrectangle_FIG getrectangle_AX getrectangle_H1 getrectangle_H2
global getrectangle_PT1 getrectangle_TYPE

%[x2,y2] = getcurpt(getrectangle_AX);
pt = get(getrectangle_AX, 'CurrentPoint');
x2 = pt(1,1);
y2 = pt(1,2);

x1 = getrectangle_PT1(1,1);
y1 = getrectangle_PT1(1,2);
xdata = [x1 x2 x2 x1 x1];
ydata = [y1 y1 y2 y2 y1];

if (~strcmp(getrectangle_TYPE, 'normal'))
    [xdata, ydata] = Constrain(xdata, ydata);
end

set(getrectangle_H1, 'XData', xdata, ...
                'YData', ydata);
set(getrectangle_H2, 'XData', xdata, ...
                'YData', ydata);

%--------------------------------------------------
% Subfunction ButtonUp
%--------------------------------------------------
function ButtonUp

global getrectangle_FIG getrectangle_AX getrectangle_H1 getrectangle_H2
global getrectangle_PT1 getrectangle_TYPE

% Kill the motion function and discard pending events
set(getrectangle_FIG, 'WindowButtonMotionFcn', '', ...
                 'Interruptible', 'off');

% Set final line data
%[x2,y2] = getcurpt(getrectangle_AX);
pt = get(getrectangle_AX, 'CurrentPoint');
x2 = pt(1,1);
y2 = pt(1,2);

x1 = getrectangle_PT1(1,1);
y1 = getrectangle_PT1(1,2);
xdata = [x1 x2 x2 x1 x1];
ydata = [y1 y1 y2 y2 y1];
if (~strcmp(getrectangle_TYPE, 'normal'))
    [xdata, ydata] = Constrain(xdata, ydata);
end

set(getrectangle_H1, 'XData', xdata, ...
                'YData', ydata);
set(getrectangle_H2, 'XData', xdata, ...
                'YData', ydata);

% Unblock execution of the main routine
set(getrectangle_H1, 'UserData', 'Completed');

%-----------------------------------------------
% Subfunction Constrain
% 
% constrain rectangle to be a square in
% axes coordinates
%-----------------------------------------------
function [xdata_out, ydata_out] = Constrain(xdata, ydata)

x1 = xdata(1);
x2 = xdata(2);
y1 = ydata(1);
y2 = ydata(3);
ydis = abs(y2 - y1);
xdis = abs(x2 - x1);

if (ydis > xdis)
   x2 = x1 + sign(x2 - x1) * ydis;
else
   y2 = y1 + sign(y2 - y1) * xdis;
end

xdata_out = [x1 x2 x2 x1 x1];
ydata_out = [y1 y1 y2 y2 y1];

%---------------------------------------------------
% Subfunction CleanGlobals
%--------------------------------------------------
function CleanGlobals

% Clean up the global workspace
clear global getrectangle_FIG getrectangle_AX getrectangle_H1 getrectangle_H2
clear global getrectangle_PT1 getrectangle_TYPE



