Code covered by the BSD License  

Highlights from
Mouse based dynamic 2D Graph Annotating Utility

from Mouse based dynamic 2D Graph Annotating Utility by Hrishi Shah
Add arrows/circles/rotated ellipses 2D graphs with mouse clicks. Preview the shape while dragging.

Annotate_2D(varargin)
function varargout = Annotate_2D(varargin)
% ANNOTATE_2D M-file for Annotate_2D.fig
%      ANNOTATE_2D, by itself, creates a new ANNOTATE_2D or raises the existing
%      singleton*.
%
%      H = ANNOTATE_2D returns the handle to a new ANNOTATE_2D or the handle to
%      the existing singleton*.
%
%      ANNOTATE_2D('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in ANNOTATE_2D.M with the given input arguments.
%
%      ANNOTATE_2D('Property','Value',...) creates a new ANNOTATE_2D or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before Annotate_2D_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to Annotate_2D_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help Annotate_2D

% Last Modified by GUIDE v2.5 21-Jun-2009 05:18:56

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @Annotate_2D_OpeningFcn, ...
                   'gui_OutputFcn',  @Annotate_2D_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

function Annotate_2D_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = hObject;
set(gcf,'DeleteFcn',@RemoveAppData); % To remove all temp vars from workspace
setappdata(0,'Clicked',0);
t     = 0:0.01:2*pi;
cos_t = cos(t)     ;
sin_t = sin(t)     ;
setappdata(0,'cos_t'   ,cos_t  );
setappdata(0,'sin_t'   ,sin_t  );
setappdata(0,'handles' ,handles);
setappdata(0,'DrawMode',0      );
setappdata(0,'points',[0,0,1;1,0,1]');
if isempty(findall(0,'Type','axes')) % if no axes, create one
    figure(1);
    axis([-1 1 -1 1]);
end
if ~isempty(findall(0,'Type','axes'))
    set(handles.Axes_Menu,'Enable','On');
    set(handles.Axes_Menu,'String',findall(0,'Type','axes'));
else
    set(handles.Axes_Menu,'Enable','Off');
end
set(handles.HelpWindow,'Visible','off');
set(handles.AspectRatioText     ,'Visible','off');
set(handles.EllipseAngleText    ,'Visible','off');
set(handles.Aspect_Slider       ,'Visible','off');
set(handles.Ellipse_Angle_Slider,'Visible','off');
guidata(hObject, handles);

function varargout = Annotate_2D_OutputFcn(hObject, eventdata, handles) 
varargout{1} = handles.output;

function Axes_Menu_Callback(hObject, eventdata, handles)
axes1=findall(0,'type','axes');
axes(axes1(get(hObject,'Value')));

function Axes_Menu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function Axes_Menu_Refresh_Callback(hObject, eventdata, handles)
if ~isempty(findall(0,'Type','axes'))
    set(handles.Axes_Menu,'Enable','On');
    set(handles.Axes_Menu,'String',findall(0,'Type','axes'));
else
    set(handles.Axes_Menu,'Enable','Off');
end

function Start_Annotate_Callback(hObject, eventdata, handles)
set(gcf,'WindowButtonDownFcn',@Button_Down);
set(gcf,'WindowButtonUpFcn'  ,@Button_Up  );
set(gcf,'DeleteFcn'    ,  @Deleting);
set(gcf,'WindowButtonMotionFcn',@Button_Motion);
set(gcf,'KeyPressFcn',@Key_Press);
axes(gca);
hold on;

function End_Annotate_Callback(hObject, eventdata, handles)
Deleting(hObject,eventdata);

function Aspect_Slider_Callback(hObject, eventdata, handles)
if ~isempty(getappdata(0,'drawhandle'))&&get(handles.PostProcess,'value')
    Button_Up(hObject,hObject); % junk arguments, not required
end

function Aspect_Slider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function LineWidth_Slider_Callback(hObject, eventdata, handles)
if get(handles.PostProcess,'value')
    drawhandle=getappdata(0,'drawhandle');
    if ~isempty(drawhandle)
        set(drawhandle,'LineWidth',get(hObject,'Value'));
    end
end

function LineWidth_Slider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function Ellipse_Angle_Slider_Callback(hObject, eventdata, handles)
if ~isempty(getappdata(0,'drawhandle'))&&get(handles.PostProcess,'value')
    Button_Up(hObject,hObject); % junk arguments, not required
end

function Ellipse_Angle_Slider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function LineStyle_Menu_Callback(hObject, eventdata, handles)
if get(handles.PostProcess,'value')
    drawhandle=getappdata(0,'drawhandle');
    if ~isempty(drawhandle)
        linestyles=[' -';'--';' :';'-.'];
        set(drawhandle,'LineStyle',linestyles(get(handles.LineStyle_Menu,'value'),:));
    end
end

function LineStyle_Menu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function LineColor_Menu_Callback(hObject, eventdata, handles)
if get(handles.PostProcess,'value')
    drawhandle=getappdata(0,'drawhandle');
    if ~isempty(drawhandle)
        linecolors='rbkgcymw';
        set(drawhandle,'Color',linecolors(get(handles.LineColor_Menu,'value')));
    end
end

function LineColor_Menu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function HelpButton_Callback(hObject, eventdata, handles)
if strcmp(get(handles.HelpWindow,'Visible'),'off')
    set(handles.HelpWindow,'Visible','on');
else
    set(handles.HelpWindow,'Visible','off');
end

function Key_Press(src,evnt) % to handle the delete functionality
Clicked=getappdata(0,'Clicked');
if Clicked==0 % check if the mouse is clicked, if yes, dont proceed
    if strcmp(evnt.Key,'delete') % check if the delete key is pressed
        drawhandle=getappdata(0,'drawhandle');
        if(~isempty(drawhandle))
            delete(drawhandle); % to erase the last plotted annotation
            setappdata(0,'drawhandle',[]); % to indicate that no plot is available
        end
    end
end

function Button_Down(src,evnt) 
handles=getappdata(0,'handles');
point1=get(gca,'CurrentPoint');
linestyles=[' -';'--';' :';'-.'];
linecolors='rbkgcymw';
txt=[linestyles(get(handles.LineStyle_Menu,'value'),:),linecolors(get(handles.LineColor_Menu,'value'))];
linewidth=get(handles.LineWidth_Slider,'value');
drawhandle=plot(point1(1),point1(2),txt,'EraseMode','xor','LineWidth',linewidth,'Clipping','on');
setappdata(0,'PointDown' ,point1);
setappdata(0,'PointUp'   ,point1);
setappdata(0,'drawhandle',drawhandle);
setappdata(0,'Clicked'   ,1);

function Button_Motion(src,evnt)
DrawMode=getappdata(0,'DrawMode');
handles=getappdata(0,'handles');
Clicked=getappdata(0,'Clicked');
if Clicked
    point1=getappdata(0,'PointDown');
    point2=get(gca,'CurrentPoint');
    setappdata(0,'PointUp',point2);
    drawhandle=getappdata(0,'drawhandle');
    rad=sqrt((point2(1,1)-point1(1,1))^2+(point2(1,2)-point1(1,2))^2);
    if DrawMode
        aspect=get(handles.Aspect_Slider,'value');
        ell_an=get(handles.Ellipse_Angle_Slider,'value')*pi/180;
        cos_t=getappdata(0,'cos_t');
        sin_t=getappdata(0,'sin_t');
        set(drawhandle,'Xdata',point1(1,1)+aspect*rad*cos_t*cos(ell_an)+rad*sin_t*sin(ell_an),...
                       'YData',point1(1,2)+rad*sin_t*cos(ell_an)-aspect*rad*cos_t*sin(ell_an));
    else
        al=atan2(point2(1,2)-point1(1,2),...
                 point2(1,1)-point1(1,1));
        T=[rad*cos(al) -rad*sin(al) point1(1,1);
           rad*sin(al)  rad*cos(al) point1(1,2);
                     0            0           1];
        points=getappdata(0,'points');
        points=T*points;
        set(drawhandle,'Xdata',points(1,:),...
                       'YData',points(2,:));
    end
end

function Button_Up(src,evnt)
handles=getappdata(0,'handles');
point1=getappdata(0,'PointDown');
point2=getappdata(0,'PointUp'  );
rad=sqrt((point2(1,1)-point1(1,1))^2+(point2(1,2)-point1(1,2))^2);
DrawMode=getappdata(0,'DrawMode');
drawhandle=getappdata(0,'drawhandle');
if DrawMode
    aspect=get(handles.Aspect_Slider,'value');
    ell_an=get(handles.Ellipse_Angle_Slider,'value')*pi/180;
    cos_t=getappdata(0,'cos_t');
    sin_t=getappdata(0,'sin_t');
    set(drawhandle,'Xdata',point1(1,1)+aspect*rad*cos_t*cos(ell_an)+rad*sin_t*sin(ell_an),...
                   'YData',point1(1,2)+rad*sin_t*cos(ell_an)-aspect*rad*cos_t*sin(ell_an),...
                   'EraseMode','normal');
else
    al=atan2(point2(1,2)-point1(1,2),...
             point2(1,1)-point1(1,1));
    T=[rad*cos(al) -rad*sin(al) point1(1,1);
       rad*sin(al)  rad*cos(al) point1(1,2);
                 0            0           1];
    points=getappdata(0,'points');
    points=T*points;
    set(drawhandle,'Xdata',points(1,:),...
                   'YData',points(2,:),...
                   'EraseMode','normal');
end
setappdata(0,'Clicked',0);
setappdata(0,'drawhandle',drawhandle);

function Deleting(src,evnt) % important function, deletes the temporary variables from MATLAB workspace
drawhandle=getappdata(0,'drawhandle');
if ~isempty(drawhandle)
    set(drawhandle,'EraseMode','normal');
    setappdata(0,'drawhandle',[]);
end
fig=get(gca,'parent');
set(fig,'WindowButtonDownFcn'  ,[]);
set(fig,'WindowButtonUpFcn'    ,[]);
set(fig,'DeleteFcn'            ,[]);
set(fig,'WindowButtonMotionFcn',[]);
set(fig,'KeyPressFcn'          ,[]);

function RemoveAppData(src,evnt) % removes temporary variables in workspace
fig=get(gca,'parent');
if isempty(get(fig,'parent'))
    set(fig,'WindowButtonDownFcn'  ,[]);
    set(fig,'WindowButtonUpFcn'    ,[]);
    set(fig,'DeleteFcn'            ,[]);
    set(fig,'WindowButtonMotionFcn',[]);
    set(fig,'KeyPressFcn'          ,[]);
end
if ~isempty(getappdata(0,'drawhandle'))
    rmappdata(0,'PointDown' );
    rmappdata(0,'PointUp'   );
    rmappdata(0,'drawhandle');
end
rmappdata(0,'Clicked' );
rmappdata(0,'cos_t'   );
rmappdata(0,'sin_t'   );
rmappdata(0,'handles' );
rmappdata(0,'DrawMode');
rmappdata(0,'points'  );

function ShapePanel_SelectionChangeFcn(hObject, eventdata, handles)
switch get(hObject,'Tag');
    case 'DrawArrow'
    setappdata(0,'DrawMode',0);
    set(handles.ArrowType           ,'Visible','on' );
    set(handles.ArrowTypeText       ,'Visible','on' );
    set(handles.AspectRatioText     ,'Visible','off');
    set(handles.EllipseAngleText    ,'Visible','off');
    set(handles.Aspect_Slider       ,'Visible','off');
    set(handles.Ellipse_Angle_Slider,'Visible','off');
    otherwise
    setappdata(0,'DrawMode',1);
    set(handles.ArrowType           ,'Visible','off');
    set(handles.ArrowTypeText       ,'Visible','off');
    set(handles.AspectRatioText     ,'Visible','on' );
    set(handles.EllipseAngleText    ,'Visible','on' );
    set(handles.Aspect_Slider       ,'Visible','on' );
    set(handles.Ellipse_Angle_Slider,'Visible','on' );
end

function ArrowType_Callback(hObject, eventdata, handles)
% define the normalized length and breadth of arrowhead
l=0.2; b=0.05;
switch get(hObject,'value')
    case 1
        laxes=[0 1];
        waxes=[0 0];
    case 2
        laxes=[0 1-l 1-l 1 1-l 1-l];
        waxes=[0   0   b 0  -b   0];
    case 3
        laxes=[l  l 0 l l 1];
        waxes=[0 -b 0 b 0 0];
    otherwise
        laxes=[l  l 0 l l 1-l 1-l 1 1-l 1-l];
        waxes=[0 -b 0 b 0   0   b 0  -b   0];
end
points=[laxes;waxes;ones(1,length(laxes))];
setappdata(0,'points',points);
if ~isempty(getappdata(0,'drawhandle'))&&get(handles.PostProcess,'value')
    Button_Up(hObject,hObject); % junk arguments, not required
end

function ArrowType_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function PostProcess_Callback(hObject, eventdata, handles)
switch get(hObject,'value')
    case 0
        set(hObject,'string','Disabled');
    otherwise
        set(hObject,'string','Enabled');
end

function DeleteLast_Callback(hObject, eventdata, handles)
Clicked=getappdata(0,'Clicked');
if Clicked==0 % check if the mouse is clicked, if yes, dont proceed
    drawhandle=getappdata(0,'drawhandle');
    if(~isempty(drawhandle))
        delete(drawhandle); % to erase the last plotted annotation
        setappdata(0,'drawhandle',[]); % to indicate that no plot is available
    end
end

Contact us at files@mathworks.com