function mrotate(h,c,v,constraint)
% MROTATE manually rotate an object with the mouse
% h : handle of the object to rotate
% c : rotation point
% v : rotation vector
% constraint : vertical or horizontal constraint (not implemented yet)
% adapted from Francois Bouffard's draggable.m function
% TO DO : add keyboard input in order to avoid conflicts with other objects on screen
% 06/07 T. Mervin
% ==============================================================================
% Input arguments management
% ==============================================================================
% Initialization of some default arguments
%constraint = 'none';
% At least the handle to the object must be given
Narg = nargin;
if Narg == 0
error('Not engough input arguments');
elseif prod(size(h))>1
error('Only one object at a time can be rotated');
end;
% Fetching informations about the parent axes
axh = get(h,'Parent');
if iscell(axh)
axh = axh{1};
end;
fgh = get(axh,'Parent');
ax_xlim = get(axh,'XLim');
ax_ylim = get(axh,'YLim');
ax_zlim = get(axh,'ZLim');
% Assigning defaults for constraint
switch lower(constraint)
case {'h','horizontal'}
disp('Horizontal constraint type, up & down rotation');
%if isempty(p); p = ax_xlim; end;
case {'v','vertical'}
disp('Vertical constraint type, left & right rotation');
%if isempty(p); p = ax_ylim; end;
otherwise
%error('Unknown constraint type');
disp('Unknown constraint type');
end;
% ==============================================================================
% Saving initial state and parameters, setting up the object callback
% ==============================================================================
% Saving object's and parent figure's initial state
setappdata(h,'initial_userdata',get(h,'UserData'));
setappdata(h,'initial_objbdfcn',get(h,'ButtonDownFcn'));
setappdata(h,'initial_wbdfcn',get(fgh,'WindowButtonDownFcn'));
setappdata(h,'initial_wbufcn',get(fgh,'WindowButtonUpFcn'));
setappdata(h,'initial_wbmfcn',get(fgh,'WindowButtonMotionFcn'));
% Saving parameters
setappdata(h,'constraint_type',constraint);
setappdata(h,'coordinates',c);
setappdata(h,'rotation_vector',v);
% Setting the object's ButtonDownFcn
set(h,'ButtonDownFcn',@click_object);
% ==============================================================================
% FUNCTION click_object
% Executed when the object is clicked
% ==============================================================================
% REMARQUE : l'objet ne devrait normalement pas tre cliqu
% pour pouvoir initier la rotation
function click_object(obj,eventdata);
disp('click_object');
% obj here is the object to be rotated and gcf is the object's parent
% figure since the user clicked on the object
h = obj;
position_type = getappdata(h,'position_type');
if strcmp(position_type,'xydata')
setappdata(h,'initial_xdata',get(h,'XData'));
setappdata(h,'initial_ydata',get(h,'YData'));
else
% setappdata(h,'initial_position',get(h,'Position'));
end;
setappdata(h,'initial_point',get(gca,'CurrentPoint'));
set(gcf,'WindowButtonDownFcn',{@activate_rotatefcn,h});
set(gcf,'WindowButtonUpFcn',{@deactivate_rotatefcn,h});
activate_rotatefcn(gcf,eventdata,h);
% ==============================================================================
% FUNCTION activate_rotatefcn
% Activates the WindowButtonMotionFcn for the figure
% ==============================================================================
function activate_rotatefcn(obj,eventdata,h);
set(obj,'WindowButtonMotionFcn',{@rotatefcn,h});
% ==============================================================================
% FUNCTION deactivate_rotatefcn
% Deactivates the WindowButtonMotionFcn for the figure
% ==============================================================================
function deactivate_rotatefcn(obj,eventdata,h);
% obj here is the figure containing the object
set(obj,'WindowButtonMotionFcn',getappdata(h,'initial_wbmfcn'));
set(obj,'WindowButtonDownFcn',getappdata(h,'initial_wbdfcn'));
set(obj,'WindowButtonUpFcn',getappdata(h,'initial_wbufcn'));
disp('deactivate_rotatefcn');
% deactivate mrotate completely
%set_initial_state(h);
% ==============================================================================
% FUNCTION set_initial_state
% Returns the object to its initial state
% ==============================================================================
function set_initial_state(h);
initial_objbdfcn = getappdata(h,'initial_objbdfcn');
initial_userdata = getappdata(h,'initial_userdata');
set(h,'ButtonDownFcn',initial_objbdfcn);
set(h,'UserData',initial_userdata);
% ==============================================================================
% FUNCTION rotatefcn
% Actual code for dragging the object
% ==============================================================================
function rotatefcn(obj,eventdata,h);
% obj here is the figure containing the object
% Getting current point
current_point = get(gca,'CurrentPoint');
initial_point = getappdata(h,'initial_point');
% Retrieving (x,y) couple for current and initial points
cpt = current_point(1,1:2);
ipt = initial_point(1,1:2);
% Computing movement
dpt = cpt - ipt;
% Computing movement range and imposing movement constraints
% (p is always [xmin xmax ymin ymax])
constraint = getappdata(h,'constraint_type');
switch lower(constraint)
case {'n','none'};
%range = p;
case {'h','horizontal'}
dpt(2) = 0;
%range = [p -inf inf];
case {'v','vertical'}
dpt(1) = 0;
%range = [-inf inf p];
end;
old_dpt = getappdata(h,'old_dpt');
setappdata(h,'old_dpt',dpt);
c = getappdata(h,'coordinates');
v = getappdata(h,'rotation_vector');
disp('rotatefcn');
switch lower(constraint) % TO DO : really add constraints
case {'n','none'};
%range = p;
case {'h','horizontal'}
if dpt(1) > old_dpt(1),
rotate(h,c-v,1,c);
else
rotate(h,c-v,-1,c);
end;
case {'v','vertical'}
if dpt(2) > old_dpt(2),
rotate(h,c-v,1,c);
else
rotate(h,c-v,-1,c);
end;
end;