function demo_uniform_subdivision
% graphical input of a control polygon
% and uniform subdivision

% Initialise data
global AMB_Data;
AMB_Data=[];
AMB_Data.degree=3;
AMB_Data.c=zeros(0,2);
AMB_Data.closed=0;
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','Uniform Subdivision',...
	'Style','text');

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

% Input for degree
AMB_Data.uicontrols(1)=uicontrol('Units','normalized', ...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','right', ...
	'Position',[.15,.85,.03,.05],...
	'String',sprintf('%1d',AMB_Data.degree),...
	'Style','edit',...
	'Callback',@degree_update);

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

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

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

% Info text 
AMB_Data.uicontrols(5)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'HorizontalAlignment','right', ...
	'FontWeight','bold', ...
	'Position',[.05,.01,.9,.05],...
	'String','Select controlpolygon', ...
	'Style','text');

% Button for open/close
AMB_Data.uicontrols(6)=uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Close', ...
	'Position',[.2,.85,.1,.05], ...
	'Callback',@close_curve,...
	'Style','pushbutton');

% Button for new curve
uicontrol('Units','normalized', ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Restart', ...
	'Position',[.84,.85,.1,.05], ...
	'Callback',@restart,...
	'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 %%%
function degree_update(varargin)
global AMB_Data;
degree= str2double(get(AMB_Data.uicontrols(1),'String'));
if degree < 1 | degree > 9
  set(AMB_Data.uicontrols(1),'String',sprintf('%1d',AMB_Data.degree));
else
  AMB_Data.degree=degree;
  restart;
end

% new polygon
function restart(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(2),'Enable','off');  
set(AMB_Data.uicontrols(3),'Enable','off');  
set(AMB_Data.uicontrols(4),'Enable','off');  
set(AMB_Data.uicontrols(5),'String','Select controlpolygon');  
AMB_Data.closed=0;
set(AMB_Data.uicontrols(6),'String','Close');  

% toggle open/closed
function close_curve(varargin) 
global AMB_Data;

if AMB_Data.closed==1
  AMB_Data.closed=0;
set(AMB_Data.uicontrols(6),'String','Close');  
else
  AMB_Data.closed=1;
  set(AMB_Data.uicontrols(6),'String','Open');  
end
myplot;

% begin/proceed/end subdivision
function next(varargin) 
global AMB_Data;
if AMB_Data.step < AMB_Data.degree+1
  AMB_Data.step =AMB_Data.step+1; 
  myplot;
end
set(AMB_Data.uicontrols(3),'Enable','on');  
set(AMB_Data.uicontrols(5),'String','');  
set(AMB_Data.uicontrols(2),'String','Next');  
if AMB_Data.step == AMB_Data.degree+1
  set(AMB_Data.uicontrols(2),'Enable','off');  
  set(AMB_Data.uicontrols(4),'Enable','on');  
  set(AMB_Data.uicontrols(5),'String','Press refine to proceed with new points');  
end

% previous step
function prev(varargin) 
global AMB_Data;
if AMB_Data.step > 0
  AMB_Data.step=AMB_Data.step-1;
  myplot;
end
set(AMB_Data.uicontrols(2),'Enable','on');  
set(AMB_Data.uicontrols(4),'Enable','off');  
if AMB_Data.step == 0
  set(AMB_Data.uicontrols(3),'Enable','off');  
  set(AMB_Data.uicontrols(5),'String','Select point or press start');  
  set(AMB_Data.uicontrols(2),'String','Start');  
end

% refine polygon
function refine(varargin) 
global AMB_Data;
xd=get(AMB_Data.handles(3),'Xdata');xd=xd(:);
yd=get(AMB_Data.handles(3),'Ydata');yd=yd(:);
closed= AMB_Data.closed;
restart;
if closed
  AMB_Data.c=[xd(1:end-1),yd(1:end-1)];
  AMB_Data.closed=1;
  set(AMB_Data.uicontrols(6),'String','Open');  
else
  AMB_Data.c=[xd,yd];
end
myplot;

if AMB_Data.step > 0
  AMB_Data.step=AMB_Data.step-1;
  myplot;
end
set(AMB_Data.uicontrols(2),'Enable','on');  
set(AMB_Data.uicontrols(4),'Enable','off');  
if AMB_Data.step == 0
  set(AMB_Data.uicontrols(3),'Enable','off');  
  set(AMB_Data.uicontrols(5),'String','Select point or press start');  
  set(AMB_Data.uicontrols(2),'String','Start');  
end

% mouse pressed
function myDownfct(varargin)
global AMB_Data;

if strcmp(get(gcf,'SelectionType'),'normal') % Left mouse: new point 
  if AMB_Data.step == 0
    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(n+1)=0;
    end
    myplot;
  end
end

% mouse released
function myUpfct(varargin)
global AMB_Data;
AMB_Data.movetype=0;
AMB_Data.moveind=0;

if size(AMB_Data.c,1)> 2 & AMB_Data.step == 0
  set(AMB_Data.uicontrols(2),'Enable','on');  
  set(AMB_Data.uicontrols(5),'String','Select point or press start');  
end

% 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;

panz=300;
subplot(AMB_Data.axis(1));
n=size(AMB_Data.c,1);

% delete curve and 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
  return;
end

c=AMB_Data.c;
if AMB_Data.closed
  c=[c;c(1,:)];
end

if n>0
  % draw polygon
  if AMB_Data.handles(1)==0  
    AMB_Data.handles(1)=plot(c(:,1),c(:,2),'-ok');
  else
    set(AMB_Data.handles(1),'Xdata',c(:,1));
    set(AMB_Data.handles(1),'Ydata',c(:,2));
  end
end

c=AMB_Data.c;
if AMB_Data.step
  % doubling of control points
  c = c(ceil([1:2*size(c,1)]/2),:);
  if AMB_Data.closed
    c=[c;c(1,:)];
  end

  % repeated averages
  for k = 1:AMB_Data.step-1
    c = (c(1:end-1,:) + c(2:end,:))/2;
    if AMB_Data.closed
      c=[c;c(1,:)];
    end
  end

  if AMB_Data.handles(3)==0  
    AMB_Data.handles(3)=plot(c(:,1),c(:,2),'.-','color',[0,.6,0]);
  else
    set(AMB_Data.handles(3),'Xdata',c(:,1));
    set(AMB_Data.handles(3),'Ydata',c(:,2));
  end
  set(AMB_Data.handles(3),'Markersize',30);
else
  if AMB_Data.handles(3)~=0
    delete(AMB_Data.handles(3));
    AMB_Data.handles(3)=0;
  end
end

