Code covered by the BSD License  

Highlights from
draggable

draggable

by

 

20 Nov 2003 (Updated )

Allows graphical objects to be dragged in a figure.

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

dragdemo.m
function dragdemo(demotitle)

% DRAGDEMO
%   This function is a demo for the draggable.m function and should be
%   distributed along with it. It basically presents some of draggable.m's
%   features: users of draggable.m are invited to read dragdemo's source.
%
%   >> dragdemo(demotitle);
%
%   runs the demo which title is contained in the string demotitle.
%   Available demos are:
%
%   'crosshair'     A draggable crosshair is drawn; its movements are
%                   limited so that its center never leaves the axes. This
%                   is the default demo.
%
%   'dragtest'      Some graphical objects (such as rectangles, lines,
%                   patches and plots) are drawn, following various
%                   constraints. For more details, please read the comments
%                   in the source code.
%
%   'snapgrid'      An example on how to use draggable's "motionfcn"
%                   argument so that a draggable object snaps to a grid.
%
%   'polymove'      A polygon with draggable vertices is drawn;
%                   draggable's "motionfcn" argument is used to redraw the
%                   polygon each time a vertex is moved.
%
%   'sliders'       A set of vertical sliders are created; a single call
%                   to draggable is used for all sliders since they share
%                   the same parameters. A "motionfcn" argument is used to
%                   update the displayed slider values.

% (C) Copyright 2004-2012
% Franois Bouffard
% fbouffard@gmail.com

if nargin == 0
    demotitle = 'crosshair';
end

switch lower(demotitle)
    
    case 'crosshair'
        
        % CROSSHAIR demo
        %   We first build the cross, using plot() with a NaN y-element so
        %   that it is non-continuous. The cross spans 0.2 units:
        
        figure;
        p = plot([0.5 0.5 0.4 0.4 0.6],[0.4 0.6 NaN 0.5 0.5]);
        
        %   Some cosmetic adjustments, and adjusting the axes limits:
        
        set(p,'Color','k','LineWidth',2);
        axis equal;
        set(gca,'DataAspectRatio',[1 1 1],'Xlim',[0 1],'YLim',[0 1]);
        title('CROSSHAIR demo for draggable.m');
        
        %   Call to draggable(), using no movement constraint and limits
        %   corresponding to the axes limits, plus half the size of the
        %   cross, so that its center always stays in the axes:
        
        draggable(p,'n',[-0.1 1.1 -0.1 1.1]); 
        
    case 'dragtest'
        
        % DRAGTEST demo
        %   This demo is used in draggable's development to test some of
        %   its features. Users interested in modifying draggable.m should
        %   ensure that it works with this demo in order to verify backward
        %   compatibility.
          
        %   Figure creation; we set up an initial WindowButtonDownFcn in
        %   order to test proper figure properties recovery after the
        %   object is dragged:
        
        f = figure;
        set(f,'WindowButtonDownFcn','disp(''Window Click!'')');
        axis equal; box on;
        set(gca,'DataAspectRatio',[1 1 1],'XLim',[0.3 1],'YLim',[0 1]);
        title('DRAGTEST demo for draggable.m');
        
        %   Drawing various test case objects...
        
        hold on;
        
        %   The Green Rectangle tests default behavior:
        
        greenrect = rectangle('Position',[0.7 0.1 0.1 0.2]);
        set(greenrect,'FaceColor','g');
        draggable(greenrect);
        
        %   The Red Square will demonstrates 'off' mode and object
        %   properties recovery afterwards, so that the 'beep' function is
        %   assigned as its ButtonDownFcn.
        
        redsquare = rectangle('Position',[0.5 0.6 0.1 0.1]);
        set(redsquare,'FaceColor','r','ButtonDownFcn','beep');
        draggable(redsquare);
        draggable(redsquare,'off');
        
        %   The Blue Rectangle will demonstrate a case in which the
        %   'painters' renderer is used, and in which the object cannot be
        %   dragged past the axis limits on the right, left and bottom.
        %   However, the object can be dragged past the axis limit on top;
        %   furthermore, the object starts off-limits to the left, so that
        %   it can be dragged inside the limits, but cannot be dragged back
        %   off-limits afterwards.
        
        bluerect = rectangle('Position',[0.2 0.4 0.2 0.2]); 
        set(bluerect,'FaceColor','b');
        draggable(bluerect,'none',[0.3 1 0 inf]);
        
        %   The Magenta Line will demonstrates a line object being dragged
        %   with horizontal movement constraint with default parameters.
        
        magline = line([0.7 0.9],[0.7 0.9]);
        set(magline,'Color','m','LineWidth',2);
        draggable(magline,'h');
        
        %   The Cyan Cross will demonstrates a plot object being dragged,
        %   its center always forced to be in the axes (as in the
        %   'crosshair' demo).
        
        cyancross = plot([0.7 0.7 0.6 0.6 0.8],[0.4 0.6 NaN 0.5 0.5]); 
        set(cyancross,'Color','c','LineWidth',2);
        draggable(cyancross,'n',[0.2 1.1 -0.1 1.1]); 
        
        %   The Yellow Triangle demonstrates a patch object being dragged
        %   with vertical movement constraint and the opengl renderer.
        
        yellowtri = patch([0.4 0.6 0.6],[0.1 0.1 0.2],'y');
        draggable(yellowtri,'v',[0.1 0.7]);
        
        %   The Gray Text demonstrate diagonal constraint operating on
        %   text objects. We test a negative, gentle slope crossing the
        %   horizontal range limits and a positive, large slope crossing
        %   the vertical range limits but in which the object will be 
        %   nonetheless stopped by the leftmost limit.
        
        gray = [0.5 0.5 0.5];
        
        % first text position parameters
        x1 = 0.4; y1 = 0.9; m1 = -1/3;
        b1 = y1 - m1*x1;
       
        % second text position parameters
        x2 = 0.7; y2 = 0.35; m2 = 3;
        b2 = y2 - m2*x2;
        
        % guide lines
        plot(xlim,m1*xlim+b1,'LineStyle','--','Color',gray);
        plot(xlim,m2*xlim+b2,'LineStyle','--','Color',gray);
        
        % text objects
        graytext1 = text(x1,y1,'This is a test');
        graytext2 = text(x2,y2,'Another test');
        set([graytext1 graytext2],'Color',gray,'EdgeColor',gray, ...
                                  'BackgroundColor','w','LineStyle',':')
        draggable(graytext1,'d',m1);
        draggable(graytext2,'d',m2);
               
    case 'snapgrid'
        
        % SNAPGRID demo
        %   The 'motionfcn' argument of draggable.m is used here to set up
        %   a grid on which the object movement is constrained.
        %
        %   Furthermore, we use 'motionfcn' to display a "fleur" figure
        %   pointer while the object moves, and the figure's
        %   WindowButtonMotionFcn to display a standard "arrow" figure
        %   pointer while the mouse moves but the object is not dragged.
        
        %   First we set up the figure and axes. 
        
        f = figure;
        set(f,'WindowButtonMotionFcn','set(gcf,''Pointer'',''arrow'')');
        axis equal; box on;
        set(gca,'DataAspectRatio',[1 1 1],'XLim',[0 10],'YLim',[0 10],...
            'XTick',0:10,'YTick',0:10);
        grid on;
        title('SNAPGRID demo for draggable.m');
        
        %   Now we create a cross which will snap on a grid with 1-unit
        %   spacing. This is done by giving the handle to the move_cross
        %   function (see below) as the 'movefcn' argument of draggable.m.
        
        hold on;
        cross = plot([5 5 4.5 4.5 5.5],[4.5 5.5 NaN 5 5]); 
        set(cross,'Color','r','LineWidth',3);
        draggable(cross,'n',[-0.5 10.5 -0.5 10.5],@move_cross);

    case 'polymove'

        % POLYMOVE demo
        %   The 'motionfcn' argument of draggable.m is used here to
        %   redraw the polygon each time one of its vertex is moved.

        %   Setting up the figure and axes.

        figure;
        axis equal; box on;
        set(gca,'DataAspectRatio',[1 1 1],'Xlim',[-2 2],'YLim',[-2 2]);
        title('POLYMOVE demo for draggable.m')

        % Creating the polygon vertices

        hold on;
        v1 = plot(-1,0);
        v2 = plot(0,-1);
        v3 = plot(1,0);
        v4 = plot(0,1);
        vv = [v1 v2 v3 v4];
        % For visible vertices:
        set(vv,'Marker','o','MarkerSize',10,'MarkerFaceColor','b');
        % For invisible vertices:
        %set(vv,'Marker','o','MarkerSize',10,'MarkerFaceColor','none', ...
        %       'MarkerEdgeColor','none');

        % Saving the vertex vector as application data
        % in the current axes (along with empty element p which will
        % later hold the handle to the polygon itself)

        setappdata(gca,'vv',vv);
        setappdata(gca,'p',[]);

        % Calling draggable on each of the vertices, passing as an
        % argument the handle to the redraw_poly fucntion (see below)

        draggable(v1,@redraw_poly);
        draggable(v2,@redraw_poly);
        draggable(v3,@redraw_poly);
        draggable(v4,@redraw_poly);

        % Finally we draw the polygon itself using the redraw_poly
        % function, which can be found below

        redraw_poly;
        
    case 'sliders'
        
        % SLIDERS demo
        %   Creates five sliders that share the same draggable parameters,
        %   and which are enable using only one draggable call. Slider
        %   values are displayed and updated in real time.
        
        % Figure layout
        figure;
        axis equal; box on;
        set(gca,'DataAspectRatio',[1 1 1], ...
                'Xlim',[0 6],'Ylim',[-0.25 1.5], ...
                'XTick',1:5,'YTick',[]);
        title('SLIDERS demo for draggable.m')
        hold on;
        
        % Creating the objects
        w = 0.6; h = 0.1;
        sliders = zeros(5);
        values = zeros(5);
        for x = 1:5
            plot([x x],[0 1],':k');
            values(x) = text(x,1.2,'','HorizontalAlignment','center');
            sliders(x) = rectangle('Position',[x-w/2,0.5-h/2,w,h], ...
                                   'Curvature',[0.05 0.05], ...
                                   'EdgeColor',[0 0 0.7], ...
                                   'FaceColor',[0 0 0.9]);
            % Storing the handle to the value text object for each slider
            setappdata(sliders(x),'value_handle',values(x));
            % This will be also used as the motionfcn argument. See
            % the function definition later in the file.
            update_value(sliders(x));
        end

        % Calling draggable on handle vector sliders with a vertical 
        % constraint. We must take into account the height of the sliders
        % when setting range limits. We use update_value as the 'motionfcn'
        % argument, and add an 'endfcn' argument which makes the slider
        % and slider value blink when the user stops dragging.
        draggable(sliders,'v',[0-h/2 1+h/2],  ...
                  @update_value,'endfcn',@blink_value);

    otherwise
        
        disp(['Demo ''' demotitle ''' is not available.']);
        
end

% -----------------------------------------------------------------------
% Function MOVE_CROSS
%   This function is passed as the 'motionfcn' argument to draggable.m in
%   the SNAPGRID demo. It recieves the handle to the object being dragged
%   as its only argument.

function move_cross(h)

% We first set up the figure pointer to "fleur"

set(gcf,'Pointer','fleur');

% Then we retrieve the current cross position

cross_xdata = get(h,'XData');
cross_ydata = get(h,'YData');
cross_center = [cross_xdata(1) cross_ydata(5)];

% Computing the new position of the cross

new_position = round(cross_center);

% Updating the cross' XData and YData properties

delta = new_position - cross_center;
set(h,'XData',cross_xdata+delta(1),'YData',cross_ydata+delta(2));

% -----------------------------------------------------------------------
% Function REDRAW_POLY
%   This function is passed as the 'motionfcn' argument to draggable.m in
%   the POLYMOVE demo. It recieves the handle to the object being dragged
%   as its only argument, but it is not actually used in this function.

function redraw_poly(h)

% Deleting the previous polygon

delete(getappdata(gca,'p'));

% Retrieving the vertex vector and corresponding xdata and ydata

vv = getappdata(gca,'vv');
xdata = cell2mat(get(vv,'xdata'));
ydata = cell2mat(get(vv,'ydata'));

% Plotting the new polygon and saving its handle as application data

p = plot([xdata' xdata(1)],[ydata' ydata(1)]);
setappdata(gca,'p',p);

% Putting the vertices on top of the polygon so that they are easier
% to drag (or else, the polygone line get in the way)

set(gca,'Children',[vv p]);

% -----------------------------------------------------------------------
% Function UPDATE_VALUE
%   This function is passed as the 'motionfcn' argument to draggable.m in
%   the SLIDERS demo. It recieves the handle to the slider being dragged
%   as its only argument and uses that to retrieve the handle to a text
%   object which was stored with the slider object.

function update_value(slider)

value_handle = getappdata(slider,'value_handle');
slider_position = get(slider,'Position');
value = slider_position(2) + slider_position(4)/2;
set(value_handle,'String',sprintf('%0.2f',value));

% -----------------------------------------------------------------------
% Function BLINK_VALUE
%   This makes the displayed value blink; just to show usage of the
%   'endfcn' argument.

function blink_value(slider)

value_handle = getappdata(slider,'value_handle');
initedgecolor = get(slider,'EdgeColor');
initfacecolor = get(slider,'FaceColor');
set(value_handle,'FontWeight','bold');
set(slider,'EdgeColor',[0 0 0.8],'FaceColor',[0 0 0.5]);
pause(0.2)
set(value_handle,'FontWeight','normal');
set(slider,'EdgeColor',initedgecolor,'FaceColor',initfacecolor);


Contact us