function splineplotter(P) % splineplotter: interactive spline plot % left mouse buuton: insert point % right mouse button: move point or knot % middle mouse button: move weights % Daten Initialisieren global SP_Data; if (nargin >0) SP_Data=P; SP_Data.handles=[0 0 0 0]; else SP_Data.plotpoints=100; SP_Data.movepoints=100; SP_Data.degree=3; SP_Data.c=zeros(0,2); SP_Data.PW=zeros(0,2); SP_Data.w=[]; SP_Data.tau=[]; SP_Data.innertau=[]; SP_Data.handles=[0 0 0 0]; SP_Data.movetype=0; SP_Data.moveind=0; SP_Data.movestart=NaN; SP_Data.uicontrols=[];; SP_Data.axis=[0,0]; end % Fenster oeffnen und Mausreaktionen einbinden clf; set(gcf,'WindowButtonDownFcn',@myDownfct); set(gcf,'WindowButtonMotionFcn',@myMovefct); set(gcf,'WindowButtonUpFcn',@myUpfct); set(gcf,'units','normalized'); % Schiebebalken und Knoepfe % Schiebebalken fuer Grad SP_Data.uicontrols(1)=uicontrol('Units','normalized', ... 'BackgroundColor',get(gcf,'Color'), ... 'Position',[.25,.05,.2,.05], ... 'Min',1,'Max',9,'Value',SP_Data.degree,... 'Sliderstep',[.125,.125],... 'Callback',@slider_update,... 'Style','slider'); % Text fuer Grad SP_Data.uicontrols(2)=uicontrol('Units','normalized', ... 'BackgroundColor',get(gcf,'Color'), ... 'HorizontalAlignment','left', ... 'ListboxTop',0, ... 'Position',[.05,.05,.2,.05],... 'String',sprintf('Grad: %1d',SP_Data.degree), ... 'Style','text'); % Knopf fuer Neuer Spline SP_Data.uicontrols(3)=uicontrol('Units','normalized', ... 'BackgroundColor',get(gcf,'Color'), ... 'String','Neuer Spline', ... 'Position',[.75,.05,.2,.05], ... 'Callback',@spline_reset,... 'Style','pushbutton'); % Knopf fuer Punkt loeschen SP_Data.uicontrols(3)=uicontrol('Units','normalized', ... 'BackgroundColor',get(gcf,'Color'), ... 'String','Punkt entfernen', ... 'Position',[.5,.05,.2,.05], ... 'Callback',@delete_point,... 'Style','pushbutton'); % Zwei Achsensysteme, eines fuer die Knoten, eines fuer den Spline % Knotenfenster SP_Data.axis(2)=subplot('position',[.05,.12,.9,.05]); axis([-.01,1.01,-.1,.1]); axis manual plot([0 1],[0,0],'-k'); axis off % SplineFenster SP_Data.axis(1)=subplot('position',[.05,.2,.9,.75]); axis([-.01,1.01,-.01,1.01]); axis manual; axis off hold on; % Rahmen fuer den Zeichenbereich plot([0,1,1,0,0],[0,0,1,1,0],'-k'); if (nargin >0) myplotspline(); end function spline_reset(varargin) % Werte zurücksetzen und Grafik loeschen global SP_Data; SP_Data.c=zeros(0,2); SP_Data.w=zeros(0,1); SP_Data.innertau=[]; SP_Data.movetype=0; SP_Data.moveind=0; SP_Data.movestart=NaN; for k=SP_Data.handles if k,delete(k);end end SP_Data.handles=[0,0,0,0]; myplottau; function delete_point(varargin) % Werte zurücksetzen und Grafik loeschen global SP_Data; SP_Data.c=SP_Data.c(1:end-1,:); SP_Data.w=SP_Data.w(1:end-1); if ~isempty(SP_Data.innertau) SP_Data.innertau=SP_Data.innertau(1:end-1)./SP_Data.innertau(end); end myplotspline; myplottau; function slider_update(varargin) % Gradaenderung durchfuehren global SP_Data; % Daten aus dem Schiebebalken holen set(SP_Data.uicontrols(1),'Value',round(get(SP_Data.uicontrols(1),'Value'))); olddegree=SP_Data.degree; SP_Data.degree=get(SP_Data.uicontrols(1),'Value'); set(SP_Data.uicontrols(2),'String',sprintf('Grad: %1d',SP_Data.degree)); % Ggf. Knotenvektor anpassen. n=size(SP_Data.c,1); % Graderhoehung: letzter Knoten weg, rest wieder auf altes Intervall skalieren if olddegree SP_Data.degree & n > SP_Data.degree+1 f= (n-SP_Data.degree-1)/(n-SP_Data.degree); SP_Data.innertau=[f*SP_Data.innertau;f]; end % Spline berechnen und zeichnen myplottau; myplotspline; function myDownfct(varargin) global SP_Data; if strcmp(get(gcf,'SelectionType'),'normal') % Linke Maus-Taste: Neuer Punkt p=get(SP_Data.axis(1),'CurrentPoint'); p=p(1,1:2); if min(p) >= 0 & max(p) <=1 SP_Data.c=[SP_Data.c;p]; SP_Data.w=[SP_Data.w;1]; n=size(SP_Data.c,1); % Ggf. Neuen Knoten bestimmen if n > SP_Data.degree+1 f= (n-SP_Data.degree-1)/(n-SP_Data.degree); SP_Data.innertau=[f*SP_Data.innertau;f]; end % Daten fuer Mausbewegung setzen SP_Data.movetype=1; SP_Data.moveind=n; end % Zeichnen myplottau; myplotspline; end if strcmp(get(gcf,'SelectionType'),'alt') % Rechte Maus-Taste: Knoten oder Punkt verschieben p=get(SP_Data.axis(1),'CurrentPoint'); p=p(1,1:2); if min(p) >= 0 & max(p) <=1 % Spline-Fenster: Punkt verschieben % Punkt mit kleinstem Abstand [m,ind]=min((p(1)-SP_Data.c(:,1)).^2+(p(2)-SP_Data.c(:,2)).^2); if m < .01 % Nur wenn der Klick nahe genug dran war SP_Data.movetype=1; % Punkt verschieben SP_Data.moveind=ind; SP_Data.c(ind,:)=p; myplotspline; end else p=get(SP_Data.axis(2),'CurrentPoint'); p=p(1,1:2); if p(1) >= 0 & p(1) <=1 & p(2)>-.1 & p(2) < .1 % Tau-Fenster: Knoten verschieben falls vorhanden if ~isempty(SP_Data.tau) % Knoten mit kleinstem Abstand [m,ind]=min(abs(p(1)-SP_Data.innertau)); if m < .05 % Nur wenn der Klick nahe genug dran war SP_Data.movetype=2; % Knoten verschieben SP_Data.innertau(ind)=p(1); [SP_Data.innertau,sind]=sort(SP_Data.innertau); SP_Data.moveind=sind(ind); myplotspline; myplottau; end end end end end if strcmp(get(gcf,'SelectionType'),'extend') % Mittlere Maus-Taste: Gewicht variieren p=get(SP_Data.axis(1),'CurrentPoint'); p=p(1,1:2); if min(p) >= 0 & max(p) <=1 & size(SP_Data.c,1) >1 % Spline-Fenster % Punkt mit kleinstem Abstand [m,ind]=min((p(1)-SP_Data.c(:,1)).^2+(p(2)-SP_Data.c(:,2)).^2); if m < .003 % Nur wenn der Klick nahe genug dran war SP_Data.movetype=3; % Gewicht verschieben SP_Data.moveind=ind; SP_Data.movestart=p(1); myplotspline; else % Abstand zu Gewichtsmarkern [m,ind]=min((p(1)-SP_Data.PW(:,1)).^2+(p(2)-SP_Data.PW(:,2)).^2); if m < .003 % Nur wenn der Klick nahe genug dran war SP_Data.movetype=4; % Gewichtsverhaeltnis verschieben SP_Data.moveind=ind; myplotspline; end end end end function myUpfct(varargin) global SP_Data; % Modifikation bei Mausbewegung abschalten SP_Data.movetype=0; SP_Data.moveind=0; SP_Data.movestart=NaN; % Roten Knoten wieder Schwarz malen myplottau; % GGf. Gewichtsmarkierungen loeschen if SP_Data.handles(4)~=0 set(SP_Data.handles(4),'Xdata',NaN); set(SP_Data.handles(4),'Ydata',NaN); myplotspline(SP_Data.plotpoints); end function myMovefct(varargin) global SP_Data; switch SP_Data.movetype case 1 % Punkt verschieben p=get(SP_Data.axis(1),'CurrentPoint'); p=p(1,1:2); if min(p) >= 0 & max(p) <=1 % Im Zeichenbereich % Punkt ersetzen und neu zeichnen SP_Data.c(SP_Data.moveind,:)=p; myplotspline; end case 2 p=get(SP_Data.axis(2),'CurrentPoint'); p=p(1,1); ind = SP_Data.moveind; tau=[0;SP_Data.innertau;1]; % Verschiebung nur innerhalb des Umgebenden Intervalls zulassen if p < tau(ind), p=tau(ind); end if p > tau(ind+2), p=tau(ind+2); end % Knoten ersetzen und neu zeichnen SP_Data.innertau(ind)=p; myplottau; myplotspline; case 3 p=get(SP_Data.axis(1),'CurrentPoint'); p=p(1,1); % Gewicht exponentiell mit Laenge der Mausbewegung skalieren f=10^(2*(p(1)-SP_Data.movestart)); SP_Data.w(SP_Data.moveind)=f*SP_Data.w(SP_Data.moveind); % Gewichte auf Summe n normieren SP_Data.w=SP_Data.w*size(SP_Data.w,1)/sum(SP_Data.w); % Neuen Anfangspunkt merken und neu zeichnen SP_Data.movestart=p; myplotspline; case 4 ind=SP_Data.moveind; P=SP_Data.c; p=get(SP_Data.axis(1),'CurrentPoint'); p=p(1,1:2); tw=((p-P(ind,:))*diff(P(ind:ind+1,:))')/norm(diff(P(ind:ind+1,:)))^2; if tw < .05, tw=.05;end if tw > .95, tw=.95;end SP_Data.w(ind+1:end)=tw/(1-tw)*SP_Data.w(ind)/SP_Data.w(ind+1)*... SP_Data.w(ind+1:end); % Gewichte auf Summe n normieren SP_Data.w=SP_Data.w*size(SP_Data.w,1)/sum(SP_Data.w); % Neu zeichnen myplotspline; end function myplotspline(panz) global SP_Data; % Zeichenfunktion if ~exist('panz','var') || isempty(panz) panz=SP_Data.movepoints; end % Spline-Bereich subplot(SP_Data.axis(1)); n=size(SP_Data.c,1); % Falls Punktreduktion durch loeschen, Spline und Gewichte entfernen if n < 2 if SP_Data.handles(2)~=0 delete(SP_Data.handles(2)); SP_Data.handles(2)=0; end if SP_Data.handles(3)~=0 delete(SP_Data.handles(3)); SP_Data.handles(3)=0; end end if n < 1 if SP_Data.handles(1)~=0 delete(SP_Data.handles(1)); SP_Data.handles(1)=0; end end if n>0 % Kontrollpolygon zeichnen bzw. Daten erneuern if SP_Data.handles(1)==0 SP_Data.handles(1)=plot(SP_Data.c(:,1),SP_Data.c(:,2),'-ok'); else set(SP_Data.handles(1),'Xdata',SP_Data.c(:,1)); set(SP_Data.handles(1),'Ydata',SP_Data.c(:,2)); end end if n>1 % Spline auswerten und zeichnen bzw. Daten erneuern t=linspace(0,1,panz); % t=linspace(eps,1-eps,100); [s,PW]=rspl_val(SP_Data,t'); SP_Data.PW=PW; if SP_Data.handles(2)==0 SP_Data.handles(2)=plot(s(:,1),s(:,2),'-b','Linewidth',2); else set(SP_Data.handles(2),'Xdata',s(:,1)); set(SP_Data.handles(2),'Ydata',s(:,2)); end % Relative Gewichtspositionen bestimmen und zeichnen bzw. Daten erneuern if SP_Data.handles(3)==0 SP_Data.handles(3)= ... plot(SP_Data.PW(:,1),SP_Data.PW(:,2),'sm'); else set(SP_Data.handles(3),'Xdata',SP_Data.PW(:,1)); set(SP_Data.handles(3),'Ydata',SP_Data.PW(:,2)); end % Wenn gerade eine Gewichtsveraenderung aktiv ist, Markierungen updaten if SP_Data.movetype==3 % Erster Punkt -> nur ein Gewichtsmarker if SP_Data.moveind == 1 ind=1; % Letzter Punkt -> nur ein Gewichtsmarker elseif SP_Data.moveind == n ind=SP_Data.moveind-1; else % Normalfall -> zwei Gewichtsmarker ind=SP_Data.moveind+[-1,0]; end if SP_Data.handles(4)==0 SP_Data.handles(4)=... plot(SP_Data.PW(ind,1),SP_Data.PW(ind,2),... 'om','markersize',20); else set(SP_Data.handles(4),'Xdata',SP_Data.PW(ind,1)); set(SP_Data.handles(4),'Ydata',SP_Data.PW(ind,2)); end end if SP_Data.movetype==4 if SP_Data.handles(4)==0 SP_Data.handles(4)=... plot(SP_Data.PW(SP_Data.moveind,1),SP_Data.PW(SP_Data.moveind,2),... 'om','markersize',20); else set(SP_Data.handles(4),'Xdata',SP_Data.PW(SP_Data.moveind,1)); set(SP_Data.handles(4),'Ydata',SP_Data.PW(SP_Data.moveind,2)); end end end function myplottau global SP_Data; % Knotenvektor im zweiten Fenster zeichnen deg=min(SP_Data.degree,size(SP_Data.c,1)-1); SP_Data.tau=[-.1;zeros(deg,1);SP_Data.innertau;ones(deg,1);1.1]; tau=SP_Data.tau; dtau=diff(tau); ind=find(dtau<1000*eps); mult=ones(size(tau)); for k=fliplr(ind') mult(k)=mult(k)+mult(k+1); mult(k+1)=[]; tau(k+1)=[]; end subplot(SP_Data.axis(2)); % Fenster löoeschen und Gerade zeichnen cla; plot([0 1],[0,0],'-k'); axis manual axis([-.01,1.01,-.1,.1]); axis off hold on; n=size(SP_Data.c,1); if n>1 % Ab zwei Punkten zeichnen for k=1:length(tau) % falls vorhanden Zwischenwerte setzen plot(tau(k)*[1 1],[-.08,.08],'-k'); if mult(k)>1 text(tau(k)-.002,.12,sprintf('%d',mult(k))); end end end if SP_Data.movetype==2 % Aktiven Knoten in Rot zeichnen plot(SP_Data.innertau(SP_Data.moveind)*[1 1],[-.08,.08],'-r'); end