function demo_bezier_curve
% graphical input of a control polygon
% for a Bezier curve

% Initialise data
global AMB_Data;
AMB_Data=[];
AMB_Data.plotpoints=300;
AMB_Data.movepoints=51;
AMB_Data.c=zeros(0,2);
AMB_Data.handles=[0 0];
AMB_Data.movetype=0;
AMB_Data.moveind=0;
AMB_Data.uicontrols=[];
AMB_Data.axis=0;

% Initialize GUI
clf reset;
set(gcf,'WindowButtonDownFcn',@myDownfct);
set(gcf,'WindowButtonMotionFcn',@myMovefct);
set(gcf,'WindowButtonUpFcn',@myUpfct);

% Text for title
uicontrol('Units','normalized', ...
  	'BackgroundColor',get(gcf,'Color'), ...
	'HorizontalAlignment','center', ...
        'FontWeight','bold',...
        'FontSize',20,...
	'Position',[.05,.91,.9,.07],...
	'String','Bezier Curve',...
	'Style','text');

% Text for degree
AMB_Data.uicontrols(1)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'HorizontalAlignment','left', ...
	'Position',[.05,.84,.14,.05],...
	'String','Degree: -1', ...
	'Style','text');

% Button for new curve
uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Restart', ...
	'Position',[.79,.85,.16,.05], ...
	'Callback',@restart,...
	'Style','pushbutton');

% Text for click
uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'Fontweight','bold', ...
	'HorizontalAlignment','right', ...
	'Position',[.18,.01,.77,.05],...
	'String','Mouse: left = insert, right = move, middle = delete point ', ...
	'Style','text');

% Axis for curve
AMB_Data.axis(1)=subplot('position',[.05,.07,.9,.77]);
axis([-.01,1.01,-.01,1.01]);
axis manual;
axis off
hold on;

% Frame 
fill([-.1,1.1,1.1,-.1],[-.1,-.1,1.1,1.1],[1,1,1],'edgecolor','none');
plot([0,1,1,0,0],[0,0,1,1,0],'-k');


%%% Callback Functions %%%

% button new curve pressed
function restart(varargin) 
global AMB_Data;
AMB_Data.c=zeros(0,2);
AMB_Data.movetype=0;
AMB_Data.moveind=0;
for k=AMB_Data.handles
  if k,delete(k);end
end
AMB_Data.handles=[0,0];
set(AMB_Data.uicontrols(1),'String','Degree: -1');    

% Mouse button pressed
function myDownfct(varargin)
global AMB_Data;

if strcmp(get(gcf,'SelectionType'),'normal') % left mouse: new point 
  p=get(AMB_Data.axis(1),'CurrentPoint');
  p=p(1,1:2);
  
  if min(p) >= 0 & max(p) <=1
    AMB_Data.c=[AMB_Data.c;p];
    n=size(AMB_Data.c,1);
    AMB_Data.movetype=1;
    AMB_Data.moveind=n;
  end
  myplot;
end

if strcmp(get(gcf,'SelectionType'),'alt') % right mousse: move point
  p=get(AMB_Data.axis(1),'CurrentPoint');
  p=p(1,1:2);  
  if min(p) >= 0 & max(p) <=1 
    % find nearest point
    [m,ind]=min((p(1)-AMB_Data.c(:,1)).^2+(p(2)-AMB_Data.c(:,2)).^2);
    if m < .01 % react only on "near" click
      AMB_Data.movetype=1; 
      AMB_Data.moveind=ind;
      AMB_Data.c(ind,:)=p;
      myplot;      
    end
  end
end

if strcmp(get(gcf,'SelectionType'),'extend') % middle mouse: delete point 
  p=get(AMB_Data.axis(1),'CurrentPoint');
  p=p(1,1:2);  
  if min(p) >= 0 & max(p) <=1 
    [m,ind]=min((p(1)-AMB_Data.c(:,1)).^2+(p(2)-AMB_Data.c(:,2)).^2);
    if m < .01 
      AMB_Data.c(ind,:)=[];
      myplot;      
    end
  end
end

% Mouse button released
function myUpfct(varargin)
global AMB_Data;
AMB_Data.movetype=0;
AMB_Data.moveind=0;
myplot;

% Mouse movement
function myMovefct(varargin)
global AMB_Data;
if AMB_Data.movetype
  p=get(AMB_Data.axis(1),'CurrentPoint');
  p=p(1,1:2);
  if min(p) >= 0 & max(p) <=1 
    AMB_Data.c(AMB_Data.moveind,:)=p;
    myplot;
  end 
end     
  
%%% Internal Funcions %%%

function myplot
global AMB_Data;

if AMB_Data.movetype
  panz = AMB_Data.movepoints;
else
  panz = AMB_Data.plotpoints;
end  
  
% Update text for degree 
subplot(AMB_Data.axis(1));
n=size(AMB_Data.c,1);
set(AMB_Data.uicontrols(1),'String',sprintf('Degree: %1d',n-1));

% delete curve and control polygon 
if n < 2 
  if AMB_Data.handles(2)~=0  
    delete(AMB_Data.handles(2));
    AMB_Data.handles(2)=0;
  end
end
if n < 1 
  if AMB_Data.handles(1)~=0  
    delete(AMB_Data.handles(1));
    AMB_Data.handles(1)=0;
  end
end

if n>0
  % draw or update polygon
  if AMB_Data.handles(1)==0  
    AMB_Data.handles(1)=plot(AMB_Data.c(:,1),AMB_Data.c(:,2),'-ok');
  else
    set(AMB_Data.handles(1),'Xdata',AMB_Data.c(:,1));
    set(AMB_Data.handles(1),'Ydata',AMB_Data.c(:,2));
  end
end
if n>1  
  % draw or update curve 
  t=linspace(0,1,panz);
  s=casteljau(AMB_Data.c,t');  
  if AMB_Data.handles(2)==0  
    AMB_Data.handles(2)=plot(s(:,1),s(:,2),'-b','Linewidth',2);  
  else
    set(AMB_Data.handles(2),'Xdata',s(:,1));
    set(AMB_Data.handles(2),'Ydata',s(:,2));
  end
end

%%% Functions from AMB_Programs (included for standalone use) %%%

function p = casteljau(c,t)
% CASTELJAU: de Casteljau's algorithm for Bezier curves 
% p = casteljau(c,t)
% points p(j,:) on a Bezier curve with control points c(k,:)
% for parameters t(j) 

% convert to column vector (if necessary)
t = t(:); 
 
[np1,dim] = size(c);
% de Casteljau's recursion 
if np1 == 1
   p = (1+0*t)*c;
else
   p0 = casteljau(c(1:end-1,:),t); 
   p1 = casteljau(c(2:end,:),t); 
   t = repmat(t,1,dim); 
   p = (1-t).*p0 + t.*p1;
end

