function fh = mycursors(varargin)
% MYCURSORS - Create as many cursors as you want on an axes
% FH = MYCURSORS(VARARGIN) add cursor on an axes.
%
% VARARGIN can contain 0, 1 or 2 elements:
% 1) First input is the handle of the axes on which you want to create the
% cursor.
% Default value is the current axes (GCA)
% 2) Second input is the color of the cursor.
% Default value is red ('r' or [1 0 0])
%
% FH is a structure containing function handles that enable you to manipulate
% the cursor. FH structure looks like:
% FH.add(XPOS) : Adds a cursor on the same axes at the position x = XPOS.
% XPOS is facultative. Default value is the middle of the
% axes.
% FH.off(NUM) : Removes the NUMth cursor created
% If NUM is a character string then the function removes only
% the last cursor.
% FH.val(NUM) : Returns a vector containing the positions of the NUMth
% cursor.
% If you omit NUM then the function returns the position of
% all cursors.
% FH.hdl() : Returns a matrix containing the handles of the cursors in
% the first column and the handles of the associated labels
% in the second column.
% Default inputs
inArgs = {...
gca, ... % Current axes is used by default
'r' ... % Default color for cursors is red
};
% Replace default input arguments by input values
inArgs(1:nargin) = varargin;
% Initializations
axeH = inArgs{1};
figH = ancestor(axeH,'figure');
curH = [];
labH = [];
curC = [];
curL = [];
yLim = ylim(axeH);
curColor = inArgs{2};
% Add a cursor
CursorAdd();
% Assign Output Argument
fh = struct('add',@CursorAdd,'off',@CursorOff,'val',@CursorValue,'hdl',@CursorHandles);
% Drag Cursor shape
curCData = [...
NaN NaN NaN NaN NaN 2 2 2 2 2 NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN 2 1 2 1 2 NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN 2 1 2 1 2 NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN 2 2 1 2 1 2 2 NaN NaN NaN NaN NaN
NaN NaN NaN 2 1 2 1 2 1 2 1 2 NaN NaN NaN NaN
NaN NaN 2 1 1 2 1 2 1 2 1 1 2 NaN NaN NaN
NaN 2 1 1 1 1 1 2 1 1 1 1 1 2 NaN NaN
2 1 1 1 1 1 1 2 1 1 1 1 1 1 2 NaN
NaN 2 1 1 1 1 1 2 1 1 1 1 1 2 NaN NaN
NaN NaN 2 1 1 2 1 2 1 2 1 1 2 NaN NaN NaN
NaN NaN NaN 2 1 2 1 2 1 2 1 2 NaN NaN NaN NaN
NaN NaN NaN NaN 2 2 1 2 1 2 2 NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN 2 1 2 1 2 NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN 2 1 2 1 2 NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN 2 2 2 2 2 NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
];
curHotSpot = [8 8];
%----------------------------------------------------------
function CursorAdd(varargin)
% Adds a new cursor on the axes
% Check input to define default cursor position
if nargin
defPos = varargin{1};
else
defPos = diff(xlim(axeH)) / 2;
end
% Get axes limits
yLim2 = ylim(axeH);
% Create Cursor Line
curH(end+1) = line([defPos defPos],yLim2,'Color',curColor,'LineWidth',2,'Parent',axeH);
% Create associated label
labH(end+1) = text(defPos,yLim2(2),...
sprintf('%u',length(curH)),...
'BackgroundColor','w',...
'EdgeColor',curColor,...
'LineWidth',2,...
'Color',curColor,...
'FontSize',8,...
'FontWeight','bold');
% Set the WindowButtonDownFcn
set(curH(end),'ButtonDownFcn',@ButtonDownFcn)
end
%----------------------------------------------------------
function CursorOff(varargin)
% Remove one or all cursors
% Execute only if there is a cursor
if ~isempty(curH)
if nargin
if ischar(varargin{1})
ind2del = length(curH); % Remove last cursor if nothing is specified
else
ind2del = varargin{1};
ind2del(ind2del>length(curH)) = []; % Remove indices outside the range
ind2del(ind2del<1) = []; % Remove indices outside the range
end
% Delete specified cursor and associated label
delete(curH(ind2del));
delete(labH(ind2del));
% Remove cursor and label handles from vectors
curH(ind2del) = [];
labH(ind2del) = [];
% Update labels
for indL=1:length(labH)
set(labH(indL),'String',indL)
end
else
% Remove all cursors and all associated labels
delete([curH labH]);
end
end
end
%----------------------------------------------------------
function res = CursorValue(varargin)
% Returns the positions of each cursor in a vector
% Executes only if there is a cursor
if ~isempty(curH)
if nargin
% Ensure that number specified in input argument is correct
ind = varargin{1};
ind(ind>length(curH)) = []; % Remove indices outside the range
ind(ind<1) = []; % Remove indices outside the range
% Retrieve data
res = get(curH(ind),'XData');
else
% If no input specified then returns all positions
if ~iscell(get(curH,'XData'))
res = cell2mat({get(curH,'XData')});
else
res = cell2mat(get(curH,'XData'));
end
end
% Remove the second column
res(:,2) = [];
else
% If there is no cursor then returns an empty vector
res = [];
end
end
%----------------------------------------------------------
function res = CursorHandles()
% Returns a matrix with cursor handles in the first column and labels
% handles in the second
% Execute only if there is a cursor
if ~isempty(curH)
res = [curH(:) labH(:)];
end
end
%----------------------------------------------------------
function ButtonDownFcn(varargin)
% Update current cursor handle
curC = varargin{1};
if ~isempty(curC)
% Get label handle associated
curL = labH(curC==curH);
% Change cursor pointer
set(figH,'Pointer','custom','PointerShapeCData',curCData,'PointerShapeHotSpot',curHotSpot)
% Update EraseMode property for the current Cursor
set(curC,'EraseMode','xor')
% Update figure callbacks
set(figH,'WindowButtonMotionFcn',@ButtonMotionFcn)
set(figH,'WindowButtonUpFcn',@ButtonUpFcn)
end
end
%----------------------------------------------------------
function ButtonUpFcn(varargin)
% Change cursor pointer
set(figH,'Pointer','arrow')
% Update EraseMode property for the current Cursor
set(curC,'EraseMode','normal')
% Update Figure Callbacks
set(figH,'WindowButtonMotionFcn','')
% Unset current Cursor and label
curC = [];
curL = [];
end
%----------------------------------------------------------
function ButtonMotionFcn(varargin)
% Get current point
curP = get(axeH,'CurrentPoint');
% Get axes limits
yLim2 = ylim(axeH);
% Update cursor position
set(curC,'XData',[curP(1) curP(1)],'YData',[min(yLim(1),yLim2(1)) max(yLim(2),yLim2(2))])
% Move label
set(curL,'Position',[curP(1) yLim2(2)]);
% Refresh Display
drawnow;
end
end