function polyH = drawfreehand(action,varargin)
% Draw a freehand line using the mouse.
%
% SYNTAX:  h = drawfreehand(axeshandle,options)
%
% The user clicks down to initiate the drawing and holds the button while
% drawing the line.
%
% h is a handle to the line object generated.  If there are no arguments,
% the current axes are used.  Options are parameter, value pairs.
% Parameters can be 'LineWidth', 'Color', and 'LineStyle'.  Case is ignored.
%
% This can be used as or within a callback function for an axes object.  If
% gcbo is not empty, the current position of the pointer is used as the
% starting line.  Otherwise drawfreehand waits for the next mouse click.
%
% EXAMPLES:
%
% h = drawfreehand(gca,'Color','r','LineWidth',3)
%
% set(gca,'ButtonDownFcn','drawfreehand(gcbo,''LineStyle'',''--'')')
%
% NOTE:
%
% Error checking is inadequate.  Probably ought to pass line options
% directly to the line command, which would enable abbreviations to be used.
% The animation is slow when the plot is complicated; I haven't tried much
% optimization.  This is 2-D only.  If there are no other children of the
% axes and autoscaling is allowed, things get a bit messy.  (Perhaps I
% ought to turn scaling to manual.)
%
% AUTHOR:
% Steven Howell, University of Hawaii
% showell@soest.hawaii.edu
%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Main routine is mostly a switchyard
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if nargin<1				% Assume current axes if no
  action = gca;				% axes given.
end

if ischar(action)			% This is a callback
  try
    feval(action, varargin{:});      	% Switchyard
  catch
    disp(lasterr);
  end
else					% Initial call.  Start routine
  %Simple error check.
  if ~(ishandle(action) & strcmp('axes',get(action,'Type')))
    error('drawfreehand.m requires an axes handle')
  end
  dp_init(action,varargin{:});
  aud = get(action,'UserData');		% Save axes UserData.
  set(action,'UserData','pending');
  waitfor(action,'UserData');		% Block execution until polygon
  if nargout > 0			% is done.
    polyH = get(action,'UserData');	% Polygon handle is passed back
  end					% via axes UserData.
  set(action,'UserData',aud);		% Restore UserData.
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% SUBROUTINES
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function dp_init(axH,varargin)
figH = get(axH,'Parent');
set(figH,'DoubleBuffer','on', 'Renderer','painters');
set(axH,'Drawmode','fast');
% Set up defaults
dp.linewidth = 1;
dp.color = 'k';
dp.linestyle = '-';
dp.threshold = 5;

%Parse input arguments
for ii=1:2:length(varargin)
  switch(lower(varargin{ii}))
   case {'linewidth'}
    dp.linewidth = varargin{ii+1};
   case {'color'}
    dp.color = varargin{ii+1};
   case {'linestyle'}
    dp.linestyle = varargin{ii+1};
  end
end
% The variable dp stores data required for drawing the polygon
setappdata(figH,'drawfreehand',dp);
% dpa contains data required to restore figure and axes properties once
% drawing is done.
dpa.axesbuttondownfcn = get(axH,'ButtonDownFcn');
setappdata(figH,'DPAuxdata',dpa);

% If this is called as a callback function, then drawing should begin
% immediately. Oterwise set the axes so the next click will start the
% drawing process.
if isempty(gcbo)
  set(axH,'ButtonDownFcn',[mfilename '(''dp_start'',gcbo)']);
else
  dp_start(axH);
end

%---------------------------------------------------------------------

function dp_start(axH)			% First button press
figH = get(axH,'Parent');
axes(axH);				% Make sure drawing is in
                                        % the correct axes.
dp = getappdata(figH,'drawfreehand');
dpa = getappdata(figH,'DPAuxdata');
% dpa contains data required to restore figure and axes properties once
% drawing is done.
dpa.windowbuttondownfcn = get(figH,'WindowButtonDownFcn');
dpa.windowbuttonmotionfcn = get(figH,'WindowButtonMotionFcn');
dpa.windowbuttonupfcn = get(figH,'WindowButtonUpFcn');
dpa.pointer = get(figH,'Pointer');
setappdata(figH,'DPAuxdata',dpa);

dp.drawaxes = axH;
%curptfig = get(figH,'CurrentPoint');
curptax = get(axH,'Currentpoint');
dp.poly.x = curptax(1,1);
dp.poly.y = curptax(1,2);
dp.polyH = line(dp.poly.x, dp.poly.y, ...
		'LineWidth',dp.linewidth, ...
		'Color', dp.color', ...
		'LineStyle', dp.linestyle, ...
		'EraseMode', 'none');
setappdata(figH,'drawfreehand',dp);

set(figH,'WindowButtonMotionFcn', [mfilename '(''dp_movemouse'',gcbf)'], ...
	 'WindowButtonUpFcn',[mfilename '(''dp_winbuttonup'',gcbf)'], ...
	 'Pointer','custom', ...
	 'PointerShapeCData',xcursor, ...
	 'PointerShapeHotSpot',xcursor('hotspot'));

%---------------------------------------------------------------------

function dp_movemouse(figH)		% Mouse moved--change the segment
dp = getappdata(figH,'drawfreehand');	% being displayed.
%curptfig = get(figH,'CurrentPoint');	% Get the current point
curptax = get(dp.drawaxes,'Currentpoint');
dp.poly.x(end+1,1) = curptax(1,1);
dp.poly.y(end+1,1) = curptax(1,2);
set(dp.polyH, 'XData', dp.poly.x, 'YData', dp.poly.y);
drawnow
setappdata(figH,'drawfreehand',dp);

%---------------------------------------------------------------------

function dp_winbuttonup(figH)		% Button released.  Done.
dp = getappdata(figH,'drawfreehand');
axH = dp.drawaxes;

dpa = getappdata(figH,'DPAuxdata');
set(axH,'ButtonDownFcn',dpa.axesbuttondownfcn);
set(figH,'WindowButtonDownFcn',dpa.windowbuttondownfcn, ...
	 'WindowButtonMotionFcn',dpa.windowbuttonmotionfcn, ...
	 'WindowButtonUpFcn',dpa.windowbuttonupfcn, ...
	 'Pointer', dpa.pointer);
rmappdata(figH,'DPAuxdata');
rmappdata(figH,'drawfreehand');
set(axH,'UserData',dp.polyH);		% Signal main routine that the
                                        % line is complete.

%---------------------------------------------------------------------

function [x,y] = figpos2data(figH,axH,figpt);
% Given a mouse position [x y] in figure coordinates, calculate position
% in axes data units. The new x and y are returned separately because
% they will be put into XData and YData separately.
% For the moment, assume that figure position is in pixels and axes
% position is normalized.  I'm not positive this routine will work
% otherwise. 
% This would be easy to vectorize, but it will never be called with more
% than 1 point and speed is critical.
figpos = get(figH,'position');
fracfigpt = figpt./figpos(3:4);
axespos = get(axH,'position');
fracaxespt = (fracfigpt - axespos(1:2))./axespos(3:4);
xlim = get(axH,'XLim');
ylim = get(axH,'YLim');

if(strcmp('log',get(axH,'XScale')))
  xlog = 1;
  xlim = log10(xlim);
else
  xlog=0;
end
if(strcmp('log',get(axH,'YScale')))
  ylog = 1;
  ylim = log10(ylim);
else
  ylog=0;
end

x = xlim(1) + fracaxespt(1).*diff(xlim);
y = ylim(1) + fracaxespt(2).*diff(ylim);

if xlog; x = 10.^x; end
if ylog; y = 10.^y; end

