function varargout = getligne(varargin)
%getligne Select polyline with mouse.
%   [X,Y] = getligne(FIG) lets you select a polyline in the
%   current axes of figure FIG using the mouse.  Coordinates of
%   the polyline are returned in X and Y.  Use normal button
%   clicks to add points to the polyline.  A shift-, right-, or
%   double-click adds a final point and ends the polyline
%   selection.  Pressing RETURN or ENTER ends the polyline
%   selection without adding a final point.  Pressing BACKSPACE
%   or DELETE removes the previously selected point from the
%   polyline.
%
%   [X,Y] = getligne(AX) lets you select a polyline in the axes
%   specified by the handle AX.
%
%   [X,Y] = getligne is the same as [X,Y] = getligne(GCF).
%
%   [X,Y] = getligne(...,'closed') animates and returns a closed
%   polygon.
%
%   See also GETRECT, GETPTS.

%   Callback syntaxes:
%        getligne('KeyPress')
%        getligne('FirstButtonDown')
%        getligne('NextButtonDown')
%        getligne('ButtonMotion')

%   Grandfathered syntaxes:
%   XY = getligne(...) returns output as M-by-2 array; first
%   column is X; second column is Y.

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

global getligne_FIG getligne_AX getligne_H1 getligne_H2
global getligne_X getligne_Y
global getligne_ISCLOSED

xlimorigmode = xlim('mode');
ylimorigmode = ylim('mode');
xlim('manual');
ylim('manual');

if ((nargin >= 1) & (isstr(varargin{end})))
    str = varargin{end};
    if (str(1) == 'c')
        % getligne(..., 'closed')
        getligne_ISCLOSED = 1;
        varargin = varargin(1:end-1);
    end
else
    getligne_ISCLOSED = 0;
end

if ((length(varargin) >= 1) & isstr(varargin{1}))
    % Callback invocation: 'KeyPress', 'FirstButtonDown',
    % 'NextButtonDown', or 'ButtonMotion'.
    feval(varargin{:});
    return;
end

getligne_X = [];
getligne_Y = [];

if (length(varargin) < 1)
    getligne_AX = gca;
    getligne_FIG = get(getligne_AX, 'Parent');
else
    if (~ishandle(varargin{1}))
        CleanUp(xlimorigmode,ylimorigmode);
        error('First argument is not a valid handle');
    end
    
    switch get(varargin{1}, 'Type')
    case 'figure'
        getligne_FIG = varargin{1};
        getligne_AX = get(getligne_FIG, 'CurrentAxes');
        if (isempty(getligne_AX))
            getligne_AX = axes('Parent', getligne_FIG);
        end

    case 'axes'
        getligne_AX = varargin{1};
        getligne_FIG = get(getligne_AX, 'Parent');

    otherwise
        CleanUp(xlimorigmode,ylimorigmode);
        error('First argument should be a figure or axes handle');
    end
end

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

% Set up initial callbacks for initial stage
set(getligne_FIG, ...
    'Pointer', 'crosshair', ...
    'WindowButtonDownFcn', 'getligne(''FirstButtonDown'');',...
    'KeyPressFcn', 'getligne(''KeyPress'');', ...
    'DoubleBuffer', 'on');

% Bring target figure forward
figure(getligne_FIG);

% Initialize the lines to be used for the drag
getligne_H1 = line('Parent', getligne_AX, ...
                  'XData', getligne_X, ...
                  'YData', getligne_Y, ...
                  'Visible', 'off', ...
                  'Clipping', 'off', ...
                  'Color', 'k', ...
                  'LineStyle', '-');

getligne_H2 = line('Parent', getligne_AX, ...
                  'XData', getligne_X, ...
                  'YData', getligne_Y, ...
                  'Visible', 'off', ...
                  'Clipping', 'off', ...
                  'Color', 'w', ...
                  'LineStyle', ':');

% We're ready; wait for the user to do the drag
% Wrap the call to waitfor in try-catch so we'll
% have a chance to clean up after ourselves.
errCatch = 0;
try
    waitfor(getligne_H1, 'UserData', 'Completed');
catch
    errCatch = 1;
end

% After the waitfor, if getligne_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(getligne_H1) | ...
            ~strcmp(get(getligne_H1, 'UserData'), 'Completed'))
    errStatus = 'unknown';
    
else
    errStatus = 'ok';
    x = getligne_X(:);
    y = getligne_Y(:);
    % If no points were selected, return rectangular empties.
    % This makes it easier to handle degenerate cases in
    % functions that call getligne.
    if (isempty(x))
        x = zeros(0,1);
    end
    if (isempty(y))
        y = zeros(0,1);
    end
end

% Delete the animation objects
if (ishandle(getligne_H1))
    delete(getligne_H1);
end
if (ishandle(getligne_H2))
    delete(getligne_H2);
end

% Restore the figure's initial state
if (ishandle(getligne_FIG))
   uirestore(state);
   set(getligne_FIG, 'DoubleBuffer', old_db);
end

CleanUp(xlimorigmode,ylimorigmode);

% Depending on the error status, return the answer or generate
% an error message.
switch errStatus
case 'ok'
    % Return the answer
    if (nargout >= 2)
        varargout{1} = x;
        varargout{2} = y;
    else
        % Grandfathered output syntax
        varargout{1} = [x(:) y(:)];
    end
    
case 'trap'
    % An error was trapped during the waitfor
    error('Interruption during mouse selection.');
    
case 'unknown'
    % User did something to cause the polyline selection to
    % terminate abnormally.  For example, we would get here
    % if the user closed the figure in the middle of the selection.
    error('Interruption during mouse selection.');
end

%--------------------------------------------------
% Subfunction KeyPress
%--------------------------------------------------
function KeyPress

global getligne_FIG getligne_AX getligne_H1 getligne_H2
global getligne_PT1 
global getligne_ISCLOSED
global getligne_X getligne_Y

key = get(getligne_FIG, 'CurrentCharacter');
switch key
case {char(8), char(127)}  % delete and backspace keys
    % remove the previously selected point
    switch length(getligne_X)
    case 0
        % nothing to do
    case 1
        getligne_X = [];
        getligne_Y = [];
        % remove point and start over
        set([getligne_H1 getligne_H2], ...
                'XData', getligne_X, ...
                'YData', getligne_Y);
        set(getligne_FIG, 'WindowButtonDownFcn', ...
                'getligne(''FirstButtonDown'');', ...
                'WindowButtonMotionFcn', '');
    otherwise
        % remove last point
        if (getligne_ISCLOSED)
            getligne_X(end-1) = [];
            getligne_Y(end-1) = [];
        else
            getligne_X(end) = [];
            getligne_Y(end) = [];
        end
        set([getligne_H1 getligne_H2], ...
                'XData', getligne_X, ...
                'YData', getligne_Y);
    end
    
case {char(13), char(3)}   % enter and return keys
    % return control to line after waitfor
    set(getligne_H1, 'UserData', 'Completed');     

end

%--------------------------------------------------
% Subfunction FirstButtonDown
%--------------------------------------------------
function FirstButtonDown

global getligne_FIG getligne_AX getligne_H1 getligne_H2
global getligne_ISCLOSED
global getligne_X getligne_Y

%[x,y] = getcurpt(getligne_AX);
pt = get(getligne_AX, 'CurrentPoint');
x = pt(1,1);
y = pt(1,2);
% check if getligne_X,getligne_Y is inside of axis
xlim = get(getligne_AX,'xlim');
ylim = get(getligne_AX,'ylim');
if (x>=xlim(1)) & (x<=xlim(2)) & (y>=ylim(1)) & (y<=ylim(2))
    % inside axis limits
    getligne_X = x;
    getligne_Y = y;
else
    % outside axis limits, ignore this FirstButtonDown
    return
end

if (getligne_ISCLOSED)
    getligne_X = [getligne_X getligne_X];
    getligne_Y = [getligne_Y getligne_Y];
end

set([getligne_H1 getligne_H2], ...
        'XData', getligne_X, ...
        'YData', getligne_Y, ...
        'Visible', 'on');

if (~strcmp(get(getligne_FIG, 'SelectionType'), 'normal'))
    % We're done!
    set(getligne_H1, 'UserData', 'Completed');
else
    % Let the motion functions take over.
    set(getligne_FIG, 'WindowButtonMotionFcn', 'getligne(''ButtonMotion'');', ...
            'WindowButtonDownFcn', 'getligne(''NextButtonDown'');');
end

%--------------------------------------------------
% Subfunction NextButtonDown
%--------------------------------------------------
function NextButtonDown

global getligne_FIG getligne_AX getligne_H1 getligne_H2
global getligne_ISCLOSED
global getligne_X getligne_Y

selectionType = get(getligne_FIG, 'SelectionType');
if (~strcmp(selectionType, 'open'))
    % We don't want to add a point on the second click
    % of a double-click

    %[x,y] = getcurpt(getligne_AX);
    pt = get(getligne_AX, 'CurrentPoint');
    x = pt(1,1);
    y = pt(1,2);

    if (getligne_ISCLOSED)
        getligne_X = [getligne_X(1:end-1) x getligne_X(end)];
        getligne_Y = [getligne_Y(1:end-1) y getligne_Y(end)];
    else
        getligne_X = [getligne_X x];
        getligne_Y = [getligne_Y y];
    end
    
    set([getligne_H1 getligne_H2], 'XData', getligne_X, ...
            'YData', getligne_Y);
    
end

if (~strcmp(get(getligne_FIG, 'SelectionType'), 'normal'))
    % We're done!
    set(getligne_H1, 'UserData', 'Completed');
end

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

global getligne_FIG getligne_AX getligne_H1 getligne_H2
global getligne_ISCLOSED
global getligne_X getligne_Y

%[newx, newy] = getcurpt(getligne_AX);
pt = get(getligne_AX, 'CurrentPoint');
newx = pt(1,1);
newy = pt(1,2);
if (getligne_ISCLOSED & (length(getligne_X) >= 3))
    x = [getligne_X(1:end-1) newx getligne_X(end)];
    y = [getligne_Y(1:end-1) newy getligne_Y(end)];
else
    x = [getligne_X newx];
    y = [getligne_Y newy];
end

set([getligne_H1 getligne_H2], 'XData', x, 'YData', y);

%---------------------------------------------------
% Subfunction CleanUp
%--------------------------------------------------
function CleanUp(xlimmode,ylimmode)

xlim(xlimmode);
ylim(ylimmode);
% Clean up the global workspace
clear global getligne_FIG getligne_AX getligne_H1 getligne_H2
clear global getligne_X getligne_Y
clear global getligne_ISCLOSED
