function TCM_tangent_plane
% TCM_TANGENT_PLANE Interactive illustration of tangent plane and partial
% derivatives
%
%   TCM_TANGENT_PLANE illustrates that the tangent plane is the best
%   approximating plane to the graph of a function f(x,y) at a point p.
%   The slope in each coordinate direction equals the partial derivative
%   of f at point p with respect to this coordinate.
%
%   There are four different plots to visualize this: First the graph of f
%   and the tangent plane are shown. Then there are two plots showing cuts
%   along the coordinate directions. The last plot shows again the full
%   graph, but higlights the cuts of the previous two plots.
%   
%   EXAMPLES
%
%   Elliptic paraboloid: In this basic example, the tangent plane is always 
%   located above the graph of f.
%
%   Hyperbolic paraboloid: For the function f(x,y) = x.^2-y.^2 the tangent 
%   plane coincides with the graph of f along the directions x+y and x-y.
%   
%   Peaks: A more complex example with two maxima and two minima.
%    
%   To investigate other cases, insert a function and its derivatives
%   in the corresponding fields. If the <a href="http://www.mathworks.com/matlabcentral/fileexchange/61849-autodiff-r2016b">AutoDiff</a> toolbox is installed, 
%   the derivatives can be computed automatically by leaving the fields  
%   blank.
%
%   TCM_TANGENT_PLANE is part of the <a href="http://www2.imng.uni-stuttgart.de/LstNumGeoMod/TCM/">TCM</a> project.
%   Related course materials can be found at <a href="http://www.mathematics-online.org/inhalt/aussage/aussage542">Mathematics Online</a>.

% Authors: Florian Martin, Lars von Wolff
% Last updated: 30.09.2016

% Initialize data
TCM_Data.fun = @(x,y) 0;  
TCM_Data.funstring = '@(x,y) 0';
TCM_Data.xder = @(x,y) 0;  
TCM_Data.xderstring = '@(x,y) 0';
TCM_Data.yder = @(x,y) 0;  
TCM_Data.yderstring = '@(x,y) 0';
TCM_Data.point = [];
TCM_Data.view = 0;

% Do not delete these Variables
TCM_Data.plot_window = [-2 2 -2 2];
TCM_Data.LmbMenuString = '';
TCM_Data.examples = {'Elliptic paraboloid','Hyperbolic paraboloid','Peaks'};

% Layout:
% ---------------------------------------
% |               Title                 |
% | ------------ --------- ------------ |
% | | Settings | |Buttons| | Feedback | |
% | ------------ --------- ------------ |
% | ----------------------------------- |
% | |           Axis (Plot)           | |
% | ----------------------------------- |
% | Info-Button         Hint            |
% ---------------------------------------
% Initialize Layout:
TCM_UI.TitleHeightMin = 10;        % minimal height of the title
TCM_UI.TitleHeightMax = 32;        % maximal height of the title
TCM_UI.HintHeightMin = 8;          % minimal height of the hint
TCM_UI.HintHeightMax = 15;         % maximal height of the hint
TCM_UI.SettingsHeightMin = 40;     % minimal height of the feedback, buttons and 
                                   % setting panel
TCM_UI.SettingsHeightMax = 70;     % maximal height of the feedback, buttons and 
                                   % setting panel
TCM_UI.SettingsRatio = 4.5;          % aspect ratio of the settings  
                                   % (increase if more settings needed)
TCM_UI.ButtonsRatio = 1.8;         % aspect ratio of the Buttons panel
TCM_UI.FeedbackRatio = 3.0;        % aspect ratio of the feedback
TCM_UI.AxisHeightTarget = 0.73;    % desired fraction of figure 
                                   % used for the plot
TCM_UI.InfoPortion = 1/8;          % Portion of the Info Button in the last  
                                   % line 
TCM_UI.Title = 'Tangent Plane';
TCM_UI.mono = get(0,'FixedWidthFontName');
% Strings for generating the information window
TCM_UI.Author = 'F. Martin and L. von Wolff';
TCM_UI.Date = '30.09.2016';
TCM_UI.Web = 'http://www.mathematics-online.org/inhalt/aussage/aussage542';
      
CreateUI();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Settings %%%%%%%%%%%%%%%%%%%%
% Text for Function
TCM_UI.funtext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.70,.5,.30],...
	'String','Function:',...
	'Style','text');

% Input for Function
TCM_UI.funinput=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.30,.70,.68,.30],...
	'String','',...
	'Style','edit',...
	'Callback',@function_update);

% Text for x-Derivative
TCM_UI.xdertext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.35,.3,.30],...
	'String','x-Derivative:',...
	'Style','text');

% Input for x-Derivative
TCM_UI.xderinput=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.30,.35,.68,.30],...
	'String',sprintf('%s',TCM_Data.xderstring),...
	'Style','edit','Callback',@function_update);

% Text for y-Derivative
TCM_UI.ydertext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.0,.3,.30],...
	'String','y-Derivative:',...
	'Style','text');

% Input for y-Derivative
TCM_UI.yderinput=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.30,0,.68,.30],...
	'String',sprintf('%s',TCM_Data.yderstring),...
	'Style','edit','Callback',@function_update);

%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Buttons Panel %%%%%%%%%%%%%%%%%%%%
% Reset-Button
TCM_UI.reset=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Reset', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.70,.98,.30], ...
	'Callback',@reset,...
	'Style','pushbutton');

% Example button
TCM_UI.examples=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Examples', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.35,.98,.30], 'Callback',@OpenExampleMenu,...
	'Style','pushbutton');

% Change View button
TCM_UI.next=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Change Plot', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,0,.98,.30], ...
	'Style','pushbutton','Callback',@change_view);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Feedback %%%%%%%%%%%%%%%%%%%%

% Text for initial x-value 
TCM_UI.inittext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.06,.7,.30,.3],...
	'String',sprintf('Point:'),...
	'Style','text');

% Input for initial x-value
TCM_UI.initinput=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.35,.7,.5,.3],...
	'String',' ',...
	'Style','edit',...
    'Callback',@initial_update);

% Text for current gradient value
TCM_UI.gradval=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.06,.35,.8,.3],...
	'String',sprintf('Gradient: %5.3e',NaN),...
	'Style','text');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Axis %%%%%%%%%%%%%%%%%%%%%%%%
% Axis for Function display
TCM_UI.ax=axes('Position',[.05,.1,.45,.85],'Parent',TCM_UI.axispanel);
axis(TCM_UI.ax,'equal');
TCM_UI.modification = uicontextmenu('Callback',@reset_zoom_limits);  
TCM_UI.mpan = uimenu('Parent',TCM_UI.modification,'Label','Enable pan',...
        'Callback',@axis_context);
TCM_UI.mzoomi = uimenu('Parent',TCM_UI.modification,'Label','Enable zoom in',...
    'Callback',@axis_context); 
TCM_UI.mzoomo = uimenu('Parent',TCM_UI.modification,'Label','Enable zoom out',...
        'Callback',@axis_context); 
TCM_UI.ax.UIContextMenu = TCM_UI.modification;

% functions for smoothless zooming and panning
panfun = pan(TCM_UI.fig);
panfun.UIContextMenu = TCM_UI.modification;
set(panfun,'ActionPostCallback',@pan_modify);
zoomfun = zoom(TCM_UI.fig);
zoomfun.UIContextMenu = TCM_UI.modification; 
set(zoomfun,'ActionPostCallback',@zoom_modify);
set(zoomfun,'ButtonDownFilter',@set_zoom_limits);
set(zoomfun,'RightClickAction','PostContextMenu');
set(findall(TCM_UI.fig,'tag','Exploration.Pan'),'enable','off');
set(findall(TCM_UI.fig,'tag','Exploration.ZoomOut'),'enable','off');
set(findall(TCM_UI.fig,'tag','Exploration.ZoomIn'),'enable','off');

% Second axis for a 3D plot with its own Contextmenu
TCM_UI.ax2=axes('Position',[.55,.1,.44,.85],'Parent',TCM_UI.axispanel);
linkaxes([TCM_UI.ax, TCM_UI.ax2], 'xy');
axis equal
TCM_UI.rotatemenu = uicontextmenu;
TCM_UI.rotate = uimenu('Parent',TCM_UI.rotatemenu,'Label','Enable rotate',...
        'Callback',@axis2_context);
TCM_UI.ax2.UIContextMenu = TCM_UI.rotatemenu;
rotatefun = rotate3d(TCM_UI.ax2);
rotatefun.UIContextMenu = TCM_UI.rotatemenu;
set(rotatefun,'ButtonDownFilter',@myDownfct);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Start the program %%%%%%%%%%%%%%%%%%%%%%%%%%
changeexample(1);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%% Callback functions %%%%%%%%%%%%%%%%%%%%%%%%%%%

% new function input
function function_update(varargin)
funchange = strcmp(TCM_Data.funstring,get(TCM_UI.funinput,'String'));
derchange = strcmp(TCM_Data.xderstring,get(TCM_UI.xderinput,'String'));
TCM_Data.funstring = get(TCM_UI.funinput,'String');
var1 = TCM_Data.funstring(find(TCM_Data.funstring=='(',1)+1:...
        find(TCM_Data.funstring==',',1)-1);
var2 = TCM_Data.funstring(find(TCM_Data.funstring==',',1)+1:...
        find(TCM_Data.funstring==')',1)-1);
if (isempty(TCM_UI.xderinput.String) || strcmp(TCM_UI.xderinput.String,'automatic')...
        || (~funchange && derchange)) && exist('ainit','file')
    TCM_Data.fun = eval([TCM_Data.funstring '+0*' var1 '+0*' var2]);
    TCM_Data.xder = adiff(TCM_Data.fun,[1 0]);
    set(TCM_UI.xderinput,'String','automatic');
else
    TCM_Data.fun = eval([TCM_Data.funstring '+0*' var1 '+0*' var2]);
    TCM_Data.xderstring = get(TCM_UI.xderinput,'String');
    TCM_Data.xder = eval(TCM_Data.xderstring);
end
if (isempty(TCM_UI.yderinput.String) || strcmp(TCM_UI.yderinput.String,'automatic')...
        || (~funchange && derchange)) && exist('ainit','file')
    TCM_Data.fun = eval([TCM_Data.funstring '+0*' var1 '+0*' var2]);
    TCM_Data.yder = adiff(TCM_Data.fun,[0 1]);
    set(TCM_UI.yderinput,'String','automatic');
else
    TCM_Data.fun = eval([TCM_Data.funstring '+0*' var1 '+0*' var2]);
    TCM_Data.yderstring = get(TCM_UI.yderinput,'String');
    TCM_Data.yder = eval(TCM_Data.yderstring);
end
minfun = @(x) TCM_Data.fun(x(1),x(2));
[x,~,flag] = fminsearch(minfun,[0 0],optimset('Display','off'));
if flag == 1
    TCM_Data.plot_window=[x(1)-2 x(1)+2 x(2)-2 x(2)+2];
else
    TCM_Data.plot_window=[-2 2 -2 2];
end
reset;
end

% new initial x value input
function initial_update(varargin)
input=get(TCM_UI.initinput,'String');
TCM_Data.point=[str2double(input(strfind(input,'[')+1:strfind(input,',')-1));...
    str2double(input(strfind(input,',')+1:strfind(input,']')-1))];
if ~inpolygon(TCM_Data.point(1,1),TCM_Data.point(2,1),TCM_Data.plot_window([1 2 2 1]),...
        TCM_Data.plot_window([3 3 4 4]))
    TCM_Data.plot_window = [TCM_Data.point(1,1)-2,TCM_Data.point(1,1)+2,TCM_Data.point(2,1)-2,TCM_Data.point(2,1)+2];
end
TCM_UI.next.Enable = 'on';
myplot;
end

% refresh all data from UI
function reset(varargin)
window_disable.Label = 'Disable pan';
axis_context(window_disable);
axis2_context(window_disable);
TCM_Data.point = [];
TCM_Data.view=0;
TCM_UI.next.Enable = 'off';
myplot;
end

% mouse button pressed 
function [res] = myDownfct(varargin)
res = 0;
p = getClickPosition(TCM_UI.ax);
if isnan(p)
    return
end
if strcmp(get(gcf,'SelectionType'),'normal') 
  TCM_Data.point = [p(1);p(2)];
  TCM_UI.next.Enable = 'on';
  myplot;
end
end

function p = getClickPosition(myaxes)
x = get(myaxes,'xlim');
y = get(myaxes,'ylim');
newpoint=get(myaxes,'CurrentPoint');
if (newpoint(1) < x(1) || newpoint(1) > x(2) || ...
    newpoint(3) < y(1) || newpoint(3) > y(2))
    p = NaN;
else
    p = newpoint([1 3]);
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kernel functions %%%%%%%%%%%%%%%%%%%%%%%%%%%
% plot function
function myplot
axes(TCM_UI.ax)
cla reset; hold off; axis equal

% evaluating and plotting of the input function on a square containing the
% current window for smoothless panning and zooming
hx = TCM_Data.plot_window(2) - TCM_Data.plot_window(1);
xfull = linspace(TCM_Data.plot_window(1)-hx,TCM_Data.plot_window(2)+hx,150);
x     = linspace(TCM_Data.plot_window(1),TCM_Data.plot_window(2),33);
hy = TCM_Data.plot_window(4) - TCM_Data.plot_window(3);
yfull = linspace(TCM_Data.plot_window(3)-hy,TCM_Data.plot_window(4)+hy,150);
y     = linspace(TCM_Data.plot_window(3),TCM_Data.plot_window(4),33);
[Xfull,Yfull] = meshgrid(xfull,yfull);
ffull = TCM_Data.fun(Xfull,Yfull);
[X,Y] = meshgrid(x,y);
f = TCM_Data.fun(X,Y);

if(max(f(:))-min(f(:)) > 1e-8)
    [~,h1]=contour(X,Y,f,10);
    Level1 = h1.LevelList;
    [~,h2]=contour(Xfull,Yfull,ffull,10);
    Level2 = h2.LevelList;
    contour(Xfull,Yfull,ffull,union(Level1,Level2(Level2>max(Level1)|Level2<min(Level1))));
    caxis(TCM_UI.ax,[min(f(:)),max(f(:))]);
end
hold on
xlim([TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim([TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);

if(~isempty(TCM_Data.point))
    xd = TCM_Data.xder(TCM_Data.point(1),TCM_Data.point(2));
    yd = TCM_Data.yder(TCM_Data.point(1),TCM_Data.point(2));
end

[az,el]=view(TCM_UI.ax2);
if(TCM_Data.view == 1 && ~isempty(TCM_Data.point))
    if((az<270&&az>90)||(az>-270&&az<-90))
        ysurf = [y(y<TCM_Data.point(2)),TCM_Data.point(2)];
        ygridv = linspace(TCM_Data.point(2)-hy/4,TCM_Data.point(2),9);
    else
        ysurf = [TCM_Data.point(2), y(y>TCM_Data.point(2))];
        ygridv = linspace(TCM_Data.point(2),TCM_Data.point(2)+hy/4,9);
    end
else
    ysurf = y;
    if(~isempty(TCM_Data.point))
        ygridv = linspace(TCM_Data.point(2)-hy/4,TCM_Data.point(2)+hy/4,17);
    end
end
if(TCM_Data.view == 2 && ~isempty(TCM_Data.point))
    if((az<180&&az>0)||(az<-180))
        xsurf=[x(x<TCM_Data.point(1)),TCM_Data.point(1)];
        xgridv = linspace(TCM_Data.point(1)-hx/4,TCM_Data.point(1),9);
    else
        xsurf=[TCM_Data.point(1),x(x>TCM_Data.point(1))];
        xgridv = linspace(TCM_Data.point(1),TCM_Data.point(1)+hx/4,9);
    end
else
    xsurf = x;
    if(~isempty(TCM_Data.point))
        xgridv = linspace(TCM_Data.point(1)-hx/4,TCM_Data.point(1)+hx/4,17);
    end
end
[Xsurf,Ysurf] = meshgrid(xsurf,ysurf);

cla(TCM_UI.ax2,'reset');
s = mesh(TCM_UI.ax2,Xsurf,Ysurf,TCM_Data.fun(Xsurf,Ysurf),'FaceAlpha',.65);
caxis(TCM_UI.ax2,[min(f(:))-1e-8,max(f(:))]);
s.UIContextMenu = TCM_UI.rotatemenu;
shading(TCM_UI.ax2,'interp');
hold(TCM_UI.ax2,'on');
xlim(TCM_UI.ax2,[TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim(TCM_UI.ax2,[TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);
if(~isempty(TCM_Data.point))
    %s.FaceAlpha = 0.85;
    plot3(TCM_UI.ax2,TCM_Data.point(1),TCM_Data.point(2),TCM_Data.fun(TCM_Data.point(1),TCM_Data.point(2)),'or','MarkerFaceColor',[1,0,0]);
    [Xg,Yg] = meshgrid(xgridv,ygridv);
    Zg = TCM_Data.fun(TCM_Data.point(1),TCM_Data.point(2)) + xd*(Xg-TCM_Data.point(1)) + yd*(Yg-TCM_Data.point(2));
    m=mesh(TCM_UI.ax2,Xg,Yg,Zg,'EdgeColor',[1 .3 .3],'FaceAlpha',.65);
    m.UIContextMenu = TCM_UI.rotatemenu;
    TCM_UI.ax2.View=[az,el];
end
zl = zlim(TCM_UI.ax2);
if((TCM_Data.view == 1||TCM_Data.view == 3) && ~isempty(TCM_Data.point))
    %s.FaceAlpha = 0.5;
    %m.FaceAlpha = 0.8;
    plot3(TCM_UI.ax2,x,0*x+TCM_Data.point(2),TCM_Data.fun(x,TCM_Data.point(2)),'-','linewidth',2,'color',[0,0,.6]);
    plot(TCM_UI.ax,xfull,0*xfull+TCM_Data.point(2),'-','linewidth',2,'color',[0,0,.6]);
    plot3(TCM_UI.ax2,[x(1) x(end)], [TCM_Data.point(2) TCM_Data.point(2)], ...
        xd*([x(1) x(end)]-TCM_Data.point(1))+TCM_Data.fun(TCM_Data.point(1),TCM_Data.point(2)),'-','linewidth',2,'color',[1,.3,.3]);
end
if((TCM_Data.view == 2||TCM_Data.view == 3) && ~isempty(TCM_Data.point))
    %s.FaceAlpha = 0.5;
    %m.FaceAlpha = 0.8;
    plot3(TCM_UI.ax2,0*y+TCM_Data.point(1),y,TCM_Data.fun(TCM_Data.point(1),y),'-','linewidth',2,'color',[0,0,.6]);
    plot(TCM_UI.ax,0*yfull+TCM_Data.point(1),yfull,'-','linewidth',2,'color',[0,0,.6]);
    plot3(TCM_UI.ax2, [TCM_Data.point(1) TCM_Data.point(1)], [y(1) y(end)], ...
        yd*([y(1) y(end)]-TCM_Data.point(2))+TCM_Data.fun(TCM_Data.point(1),TCM_Data.point(2)),'-','linewidth',2,'color',[1,.3,.3]);
end
zlim(TCM_UI.ax2, zl);

if(~isempty(TCM_Data.point))
    plot(TCM_Data.point(1),TCM_Data.point(2),'or','MarkerFaceColor',[1,0,0]);
    TCM_UI.initinput.String = sprintf('[%.3f,%.3f]',TCM_Data.point(1),TCM_Data.point(2)); 
    TCM_UI.gradval.String=sprintf('Gradient: [%.2f,%.2f]',xd,yd);
else
    TCM_UI.initinput.String = '[NaN, NaN]';
    TCM_UI.gradval.String='Gradient: [NaN, NaN]';
end
set(TCM_UI.gradval,'ForegroundColor','black');
SetWindowLimits;
end

% Change View
function change_view(varargin)
window_disable.Label = 'Disable pan';
axis_context(window_disable);
axis2_context(window_disable);
switch TCM_Data.view
    case 0;  TCM_Data.view=1;
    case 1;  TCM_Data.view=2;
    case 2;  TCM_Data.view=3;
    case 3;  TCM_Data.view=0;
end
myplot;
end

% Update the Hinttext
function UpdateHinttext
LmbString = 'Left Mouse = Choose point of tangency (left window). ';
if(numel(TCM_Data.LmbMenuString) == 0)
    set(TCM_UI.hinttext,'String',[LmbString 'Right Mouse = Access window menus.']);
else
    set(TCM_UI.hinttext,'String',[TCM_Data.LmbMenuString 'Right Mouse = Access window menus.']);
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% example functions %%%%%%%%%%%%%%%%%%%%%%%%%%%

function CreateExampleMenu
TCM_UI.UICexamples = uicontextmenu;
for i=1:numel(TCM_Data.examples)
    uimenu('Parent',TCM_UI.UICexamples,'Label',TCM_Data.examples{i},'Callback',{@changeexample,i});
end
end

function OpenExampleMenu(varargin)
TCM_UI.examples.Units = 'pixels';
TCM_UI.buttonspanel.Units = 'pixels';
TCM_UI.UICexamples.Position = TCM_UI.examples.Position(1:2) + TCM_UI.buttonspanel.Position(1:2);
TCM_UI.examples.Units = 'normalized';
TCM_UI.buttonspanel.Units = 'points';
TCM_UI.UICexamples.Visible = 'on';
end
 
              
function changeexample(varargin) 
switch varargin{numel(varargin)}      
    case 1
        set(TCM_UI.funinput,'String','@(x,y) -x.^2-y.^2');
        set(TCM_UI.xderinput,'String','@(x,y) -2*x');
        set(TCM_UI.yderinput,'String','@(x,y) -2*y');
        function_update;
    case 2
        set(TCM_UI.funinput,'String','@(x,y) x.^2-y.^2');
        set(TCM_UI.xderinput,'String','@(x,y) 2*x');
        set(TCM_UI.yderinput,'String','@(x,y) -2*y');
        function_update;
    case 3
        set(TCM_UI.funinput,'String',   '@(x,y) -x.*y.*exp(-x.^2-y.^2)');
        set(TCM_UI.xderinput,'String','@(x,y) y.*(2*x.^2-1).*exp(-x.^2-y.^2)');
        set(TCM_UI.yderinput,'String','@(x,y) x.*(2*y.^2-1).*exp(-x.^2-y.^2)');
        function_update;
        TCM_Data.plot_window = [-2 2 -2 2];
        set(TCM_UI.initinput,'String', '[-0.5,-0.6]');
        initial_update;
        
end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% zoom functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% function for updating the internal plot window
function zoom_modify(varargin)
if max(abs(TCM_Data.plot_window-axis(TCM_UI.ax)))< 10^-10
    ZoomOut;
end
pan_modify;
end

function pan_modify(varargin)
TCM_Data.plot_window = axis(TCM_UI.ax);
myplot;
end

% functions for proper zooming in (prevent double-click zoom out)
function [flag] = set_zoom_limits(varargin)
if(strcmp(TCM_UI.fig.SelectionType,'open'))
    flag = true;
else
    flag = false;
end
end

function reset_zoom_limits(varargin)
SetWindowLimits;
end

% handling the axis context menu
function axis_context(varargin)
zoom(TCM_UI.fig, 'off'); pan(TCM_UI.fig, 'off');
mode_strings = {'pan','zoom in','zoom out'};
status_strings = {'Enable ','Disable '};
switch varargin{1}.Label(1:7)
    case 'Enable '
        mode = strcmp(varargin{1}.Label(8:end),mode_strings);
        TCM_Data.LmbMenuString = ['Left Mouse = ' mode_strings{mode} ' the window. '];
        set(TCM_UI.mpan,'Label',horzcat(status_strings{mode(1)+1},mode_strings{1}));
        set(TCM_UI.mzoomi,'Label',horzcat(status_strings{mode(2)+1},mode_strings{2}));
        set(TCM_UI.mzoomo,'Label',horzcat(status_strings{mode(3)+1},mode_strings{3}));
        split_mode = strsplit(mode_strings{mode},' ');
        if numel(split_mode) == 1
            pan(TCM_UI.fig, 'on');
        else
            zoomh = zoom(TCM_UI.fig);
            zoomh.Enable = 'on';
            zoomh.Direction = split_mode{2};
        end
    case 'Disable'
        TCM_Data.LmbMenuString = '';
        set(TCM_UI.mpan,'Label','Enable pan');
        set(TCM_UI.mzoomi,'Label','Enable zoom in');
        set(TCM_UI.mzoomo,'Label','Enable zoom out');     
end
UpdateHinttext;
end

function axis2_context(varargin)
if strcmp(varargin{1}.Label,'Enable rotate')
    set(TCM_UI.rotate,'Label','Disable rotate');
    TCM_Data.LmbMenuString = 'Left Mouse = Rotate the Box. ';
    rotate3d(TCM_UI.ax2,'on');
else
    rotate3d(TCM_UI.ax2,'off');
    set(TCM_UI.rotate,'Label','Enable rotate');
    TCM_Data.LmbMenuString = '';
end
UpdateHinttext;
end

function SetWindowLimits
ZoomOut;
xlim(TCM_UI.ax,[TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim(TCM_UI.ax,[TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);
end

function ZoomOut
hx = TCM_Data.plot_window(2) - TCM_Data.plot_window(1);
hy = TCM_Data.plot_window(4) - TCM_Data.plot_window(3);
xlim(TCM_UI.ax,[TCM_Data.plot_window(1)-hx/2 TCM_Data.plot_window(2)+hx/2]);
ylim(TCM_UI.ax,[TCM_Data.plot_window(3)-hy/2 TCM_Data.plot_window(4)+hy/2]);
zoom(TCM_UI.fig, 'reset');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% layout functions %%%%%%%%%%%%%%%%%%%%%%%%%%%

% functions for creating the different UI-Parts and managing the resize of
% the window
function CreateUI
TCM_UI.fig = figure('Visible','off', 'Units','points','Toolbar','none',...
    'SizeChangedFcn',@calculate_layout, 'WindowButtonDownFcn',@myDownfct,...
    'numbertitle','off','name',TCM_UI.Title);
TCM_UI.titletext = uicontrol('Units','points','BackgroundColor',TCM_UI.fig.Color, ...
	'HorizontalAlignment','center',  'FontWeight','bold', ...
    'FontUnits','normalized',        'FontSize',0.7,...
	'String',TCM_UI.Title,        'Style','text');
TCM_UI.hinttext = uicontrol('Units','points','BackgroundColor',TCM_UI.fig.Color, ...
    'HorizontalAlignment','right',...
    'FontUnits','normalized',        'FontSize',0.635,...
	'String','','Style','text');
TCM_UI.infobutton=uicontrol('Units','points', ...
	'BackgroundColor',TCM_UI.fig.Color, ...
	'String','Info', 'HorizontalAlignment','left',...
    'FontUnits','normalized','FontSize',0.7,...
	'Callback',@information,'Style','pushbutton');
TCM_UI.feedbackpanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
TCM_UI.buttonspanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
TCM_UI.settingspanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
TCM_UI.axispanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
CreateExampleMenu
TCM_UI.fig.Visible= 'on';
end


function calculate_layout(varargin) 
figpos = TCM_UI.fig.Position;
WMax = TCM_UI.SettingsHeightMax*(TCM_UI.SettingsRatio + TCM_UI.FeedbackRatio + TCM_UI.ButtonsRatio);
WMin = TCM_UI.SettingsHeightMin*(TCM_UI.SettingsRatio + TCM_UI.FeedbackRatio + TCM_UI.ButtonsRatio);
HMax = TCM_UI.TitleHeightMax + TCM_UI.HintHeightMax;
HMin = TCM_UI.TitleHeightMin + TCM_UI.HintHeightMin;

wratio = max(0,min(1,(min(figpos(3),1.3*figpos(4))-WMin)/(WMax-WMin)));
XCoords = [1,0,0,0,0,0,0,figpos(3)];
SWidth =      wratio*TCM_UI.SettingsHeightMax*TCM_UI.SettingsRatio...
        + (1-wratio)*TCM_UI.SettingsHeightMin*TCM_UI.SettingsRatio;
BWidth =      wratio*TCM_UI.SettingsHeightMax*TCM_UI.ButtonsRatio...
        + (1-wratio)*TCM_UI.SettingsHeightMin*TCM_UI.ButtonsRatio; 
FWidth =      wratio*TCM_UI.SettingsHeightMax*TCM_UI.FeedbackRatio...
        + (1-wratio)*TCM_UI.SettingsHeightMin*TCM_UI.FeedbackRatio; 
RemWidth = figpos(3)-3-SWidth-FWidth-BWidth;
XCoords(2) = max(1,0.3*RemWidth);
XCoords(3) = XCoords(2) + 1 + SWidth;
XCoords(4) = XCoords(3) + max(0,0.2*RemWidth);
XCoords(5) = XCoords(4) + 1 + BWidth;
XCoords(6) = XCoords(5) + max(0,0.2*RemWidth);
XCoords(7) = XCoords(6) + 1 + FWidth;
SHeight = wratio*TCM_UI.SettingsHeightMax + (1-wratio)*TCM_UI.SettingsHeightMin;
if(HMin > min(figpos(4),0.9*figpos(3))*(1-TCM_UI.AxisHeightTarget)-5-SHeight)
    hratio = 0;
elseif(HMax <  min(figpos(4),0.9*figpos(3))*(1-TCM_UI.AxisHeightTarget)-5-SHeight)
    hratio = 1;
else
    hratio = (( min(figpos(4),0.9*figpos(3))*(1-TCM_UI.AxisHeightTarget)-5-SHeight)  -HMin)/(HMax-HMin);
end
YCoords = [1,0,0,0,figpos(4)-1];
YCoords(2) = YCoords(1) + 1 + hratio*TCM_UI.HintHeightMax     + (1-hratio)*TCM_UI.HintHeightMin;
YCoords(4) = YCoords(5) - 1 - hratio*TCM_UI.TitleHeightMax    - (1-hratio)*TCM_UI.TitleHeightMin;
YCoords(3) = YCoords(4) - 1 - SHeight;
TCM_UI.titletext.Position =     [XCoords(1)+1,  YCoords(4)+1,  XCoords(8)-XCoords(1)-1,  YCoords(5)-YCoords(4)-1];
TCM_UI.settingspanel.Position = [XCoords(2)+1,  YCoords(3)+1,  XCoords(3)-XCoords(2)-1,  YCoords(4)-YCoords(3)-1];
TCM_UI.buttonspanel.Position =  [XCoords(4)+1,  YCoords(3)+1,  XCoords(5)-XCoords(4)-1,  YCoords(4)-YCoords(3)-1];
TCM_UI.feedbackpanel.Position = [XCoords(6)+1,  YCoords(3)+1,  XCoords(7)-XCoords(6)-1,  YCoords(4)-YCoords(3)-1];
TCM_UI.axispanel.Position =     [XCoords(1)+1,  YCoords(2)+1,  XCoords(8)-XCoords(1)-1,  max(0,YCoords(3)-YCoords(2)-1)];
TmpXCoord = TCM_UI.InfoPortion*(XCoords(8)-XCoords(1)-1);
TCM_UI.infobutton.Position =    [XCoords(1)+1,  YCoords(1)+1,  TmpXCoord-XCoords(1)-1,   YCoords(2)-YCoords(1)-1];
TCM_UI.hinttext.Position =      [TmpXCoord+1,   YCoords(1)+1,  XCoords(8)-TmpXCoord-1,   YCoords(2)-YCoords(1)-1];
end

% function for opening the information webpage
function information(varargin)
pos = TCM_UI.fig.OuterPosition;
infodialog = dialog('Name','Information','Units','points',...
    'OuterPosition',[pos(1)+pos(3)/4 pos(2)+pos(4)/4 pos(3)/2 pos(4)/2]);
uicontrol('Units','normalized', 'Parent',infodialog, ...
        'FontUnits','normalized', 'FontSize',0.67,...
        'HorizontalAlignment','center', 'Position',[.01,.6,.98,.15],...
	'String','Mathematics Online Link', 'Style','text',...
    'ForegroundColor','blue','ButtonDownFcn',@linkfun,...
    'Enable','inactive');
uicontrol('Units','normalized', 'Parent',infodialog, ...
        'FontUnits','normalized', 'FontSize',0.4,...
        'HorizontalAlignment','center', 'Position',[.01,.4,.98,.15],...
	'String',{horzcat('Created by ',TCM_UI.Author,'.');horzcat('Version: ',TCM_UI.Date)} , 'Style','text');
end

function linkfun(varargin)
web(TCM_UI.Web)
end
end
