function demo_casteljau_algorithm
% graphical input of a control polygon
% and visualization of de Casteljau's algorithm

% Initialise data
global AMB_Data;
AMB_Data=[];
AMB_Data.plotpoints=300;
AMB_Data.movepoints=50;
AMB_Data.c=zeros(0,2);
AMB_Data.t=.5;
AMB_Data.step=0;
AMB_Data.handles=[0 0 0 0 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','Algorithm of de Casteljau',...
	'Style','text');

% Button for next
AMB_Data.uicontrols(1)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Next', ...
	'Enable','Off', ...
	'Position',[.72,.85,.1,.05], ...
	'Callback',@next,...
	'Style','pushbutton');

% Button for prev
AMB_Data.uicontrols(2)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Prev.', ...
	'Enable','Off', ...
	'Position',[.6,.85,.1,.05], ...
	'Callback',@prev,...
	'Style','pushbutton');

% Text for t
AMB_Data.uicontrols(3)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'HorizontalAlignment','left', ...
	'Position',[.45,.84,.14,.05],...
	'String','t=0.50', ...
	'Style','text');

% Slider for t
AMB_Data.uicontrols(4)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'Position',[.05,.85,.4,.05], ...
	'Min',0,'Max',1,'Value',AMB_Data.t,...
	'Sliderstep',[.05,.05],...
	'Callback',@slider_update,...
	'Style','slider');

% Info text 
uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'HorizontalAlignment','right', ...
	'FontWeight','bold', ...
	'Position',[.05,.01,.9,.05],...
	'String','Mouse: left=insert, right=move, middle = delete point ', ...
	'Style','text');

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

% Axis for curve
AMB_Data.axis(1)=subplot('position',[.05,.07,.9,.75]);
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 %%%

% reset button pressed
function reset(varargin) 
global AMB_Data;
AMB_Data.c=zeros(0,2);
AMB_Data.step=0;
AMB_Data.movetype=0;
AMB_Data.moveind=0;
for k=AMB_Data.handles
  if k,delete(k);end
end
AMB_Data.handles=[0,0,0,0,0 0];
set(AMB_Data.uicontrols(1),'Enable','off');  
set(AMB_Data.uicontrols(2),'Enable','off');  


function next(varargin) 
global AMB_Data;
if AMB_Data.step < size(AMB_Data.c,1)-1
  AMB_Data.step =AMB_Data.step+1; 
  myplot;
end
set(AMB_Data.uicontrols(2),'Enable','on');  
if AMB_Data.step == size(AMB_Data.c,1)-1
  set(AMB_Data.uicontrols(1),'Enable','off');  
end

function prev(varargin) 
global AMB_Data;
if AMB_Data.step > 0
  AMB_Data.step=AMB_Data.step-1;
  myplot;
end
set(AMB_Data.uicontrols(1),'Enable','on');  
if AMB_Data.step == 0
  set(AMB_Data.uicontrols(2),'Enable','off');  
end

function slider_update(varargin) 
global AMB_Data;
% Read t value
AMB_Data.t = round(get(AMB_Data.uicontrols(4),'Value')*20)/20;
set(AMB_Data.uicontrols(4),'Value',AMB_Data.t);
set(AMB_Data.uicontrols(3),'String',sprintf('t=%4.2f',AMB_Data.t));
myplot;

% 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;
    AMB_Data.handles(1+n)=0;
  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 
  p=get(AMB_Data.axis(1),'CurrentPoint');
  p=p(1,1:2);  
  if min(p) >= 0 & max(p) <=1 % Middle mouse: delete
    [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;
n=size(AMB_Data.c,1);
if AMB_Data.step < n-1
  set(AMB_Data.uicontrols(1),'Enable','on');  
elseif AMB_Data.step == n-1
  set(AMB_Data.uicontrols(1),'Enable','off');  
else
  AMB_Data.step = n-1;
  if AMB_Data.handles(1+n)
    delete(AMB_Data.handles(1+n)); 
    AMB_Data.handles(1+n)=0;
  end
end
if AMB_Data.step == 0
  set(AMB_Data.uicontrols(2),'Enable','off');  
end
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 Functions %%%
function myplot
global AMB_Data;

if AMB_Data.movetype
  panz = AMB_Data.movepoints;
else
  panz = AMB_Data.plotpoints;
end  
  
subplot(AMB_Data.axis(1));
n=size(AMB_Data.c,1);

% delete curve, control polygon, and c-steps 
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 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 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

c=AMB_Data.c;
for k=1:AMB_Data.step
  c=(1-AMB_Data.t)*c(1:end-1,:)+AMB_Data.t*c(2:end,:);
  if AMB_Data.handles(2+k)==0  
    AMB_Data.handles(2+k)=plot(c(:,1),c(:,2),'.-','color',[0,.6,0]);
  else
    set(AMB_Data.handles(2+k),'Xdata',c(:,1));
    set(AMB_Data.handles(2+k),'Ydata',c(:,2));
  end
  set(AMB_Data.handles(2+k),'Markersize',30);
  if k > 1
    set(AMB_Data.handles(1+k),'Markersize',20);
  end
end

for k=AMB_Data.step+1:size(AMB_Data.c,1)-1
  if AMB_Data.handles(2+k)
    delete(AMB_Data.handles(2+k)); 
    AMB_Data.handles(2+k)=0;
  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

