function slinkything
%  SLINKYTHING  GUI for plotting squiggly parametrized curves
%
%  Usage: >> slinkything
%
%  Change the parameters to make pretty spirograph-type plots.  The
%  "quality" slider controls the number of points used to plot.
%
%  Requires a screen at least 630 tall x 1050 wide.
%  
%  Theory: 
%
%  Created by: Matt Tearle
%  Last updated: August 1, 2011
%  Legal stuff: I wrote this.  For fun.  You can use it.  For fun.  You can
%  edit it and pass it on to your friends.  As long as it's all for fun.
%  If it ceases to be fun, cease using it.  Use at your own risk.  I
%  apologize if it crashes your machine, installs CIA mind-control viruses
%  into your brain, or insults yo momma.  But you can't sue me.

%  Initialization
%  Get screen dimensions
scsz = get(0,'ScreenSize');
%  Find any current incarnations and close them
h = findobj('-regexp','tag','slinky');
if (~isempty(h))
    close(h);
end
%  Open a new window
h_fig = figure;

%  Set some defaults
bgc = [0.4 0.6 0.8];  % Background color
ww = 1050;  % Window width
wh = 630;   % Window height
lp = 20;    % Left align for controls
lp2 = 450;  % Left align for axes
%  Bail out if screen is too small
if (ww>scsz(3) || wh>scsz(4))
    error('You, like, totally need a bigger screen, dude!')
end

%  Set window properties
set(h_fig,'Tag','slinkyplot','name','"Slinky Thing" Plotter',...
    'position',[(scsz(3)-ww)/2,(scsz(4)-wh)/2,ww,wh],...
    'menubar','none','resize','off','color',bgc,...
    'visible','on','numbertitle','off');
set(h_fig,'DefaultUicontrolHorizontalAlignment','left');
set(h_fig,'DefaultUicontrolFontWeight','bold');
set(h_fig,'DefaultTextFontWeight','bold');

%  GUI controls
%  sliders for parameters
h_pin = zeros(5,1);
for k = 5:-1:1
    vp = 500-60*(k-1);
h_pin(k) = uicontrol(h_fig,'style','slider','position',[lp+25,vp,350,20],...
    'min',0,'max',80,'value',1,'sliderstep',[0.1 1]/80,'callback',@update);
uicontrol(h_fig,'style','text','position',[lp,vp-2,25,25],'string',[char(100+k),':'],...
    'backgroundcolor',bgc,'fontsize',12);
h_pout(k) = uicontrol(h_fig,'style','text','position',...
    [lp+380,vp-2,40,25],'string',' 1.0','backgroundcolor',bgc,...
    'horizontalalignment','r','fontsize',12);
end
params = ones(5,1);

%  Pushbutton for picking a random setting
uicontrol(h_fig,'style','pushbutton','position',[160,175,120,35],...
    'string','Randomize!','callback',@rndsetting)

%  Slider for quality (number of plot points)
vp = 75;
uicontrol(h_fig,'style','slider','position',[lp+85,vp,230,20],...
    'min',1,'max',11,'value',8,'sliderstep',[1 2]/10,'callback',@setqual);
uicontrol(h_fig,'style','text','position',[lp,vp-2,80,25],'string','Quality:',...
    'backgroundcolor',bgc,'fontsize',12);
h_qout = uicontrol(h_fig,'style','text','position',...
    [lp+320,vp-2,40,25],'string',' 8','backgroundcolor',bgc,...
    'horizontalalignment','r','fontsize',12);
%  Set default quality value
npoints = 16000;

%  GUI Output = axes to plot into
%  Using a hack here: two nested axes.  The plot fills the inner axes,
%  which leave a nice uniform border to the outer axes.  This was easier
%  than calculating the axis limits to leave a nice border each time a plot
%  was created.  Yes I'm lazy.
axes('parent',h_fig,'units','pixels','position',[lp2+15,15,570,600],...
    'xtick',[],'ytick',[],'box','on','color',[0.99,0.99,1]);
h_ax = axes('parent',h_fig,'units','pixels','position',[lp2+30,30,540,570],...
    'visible','off');

%  Make plot with default values
makeplot

%  GUI Callbacks

%  A parameter is changed
    function update(hObject,~)
        %  Get the new value from the slider control
        p = get(hObject,'Value');
        %  Round to the nearest 0.1
        p = round(10*p)/10;
        %  Set the slider back to the rounded value
        set(hObject,'Value',p)
        %  Update all the values shown on the screen
        for k = 1:5
            params(k) = get(h_pin(k),'Value');
            set(h_pout(k),'string',num2str(params(k),'%4.1f'))
        end
        %  Update the plot
        makeplot
    end

%  The "randomize" button is pushed
    function rndsetting(~,~)
        %  Get random values (from 1 to 80 in steps of 0.1)
        for k = 1:5
            params(k) = randi(800)/10;
            set(h_pin(k),'Value',params(k))
            set(h_pout(k),'string',num2str(params(k),'%4.1f'))
        end
        %  Update the plot with the new settings
        makeplot
    end

%  The quality slider is changed
    function setqual(hObject,~)
        %  Get the new value from the slider control & round to nearest
        %  integer
        n = round(get(hObject,'Value'));
        %  Set the slider back to the rounded value
        set(hObject,'Value',n)
        %  Update the value shown on the screen
        set(h_qout,'string',num2str(n,'%2d'))
        %  Set number of points
        npoints = 2000*n;
        %  Update the plot
        makeplot
    end

%  Make the plot with current settings
    function makeplot
        %  Line color
        pc = [0.2,0,0.75];
        %  Create the points to plot
        t = linspace(0,8,npoints);
        x = cos(params(1)*t) - cos(params(2)*t).*sin(params(3)*t);
        y = 2*sin(params(4)*t) - sin(params(5)*t);
        %  Determine the axis limits (max value in all directions)
        axlim = max(max(abs(x)),max(abs(y)));
        %  Make the plot with all the chosen settings
        plot(h_ax,x,y,'linewidth',1.25,'color',pc)
        %  Set the axis limits and hide the inner axes
        set(h_ax,'visible','off','xlim',[-axlim,axlim],...
            'ylim',[-axlim,axlim])
    end

end
