function TCM_steepest_descent
% TCM_STEEPEST_DESCENT Interactive illustration of minimization via steepest descent
%
% TCM_STEEPEST_DESCENT illustrates the minimization of a bivariate
% function f(x,y) with the steepest descent method by visualizing the
% search directions and the minimizing sequence in conjunction with the
% contours and the graph of f.
%
% EXAMPLES
%
% Cubic polynomial: convergence to the local minimum for appropriately
% chosen starting values.
%
% Quadratic function: f(x,y) = [x,y]*A*[x;y]-[x,y]*b with a positive
% definite matrix A and a 2x1 vector b; global convergence to the unique
% solution of the linear system A*[x;y] = b.
%
% Oscillation: the local appearence of the function can lead to typical
% zigzag behavior of the iteration for the majority of starting points.
%
% To investigate other cases, insert a function and its derivatives
% in the corresponding fields. If the AutoDiff toolbox is installed,
% the derivatives can be computed automatically by leaving the fields
% blank.
%
% TCM_STEEPEST_DESCENT is part of the TCM project.
% Related course materials can be found at Mathematics Online.
% Authors: Florian Martin, Lars von Wolff
% Last updated: 27.06.2016
% Initialize data
TCM_Data.fun = @(x,y) x.*exp(-x.^2-y.^2);
TCM_Data.funstring = '@(x,y) x.*exp(-x.^2-y.^2)';
TCM_Data.xder = @(x,y) exp(-x.^2-y.^2) -2*x.^2.*exp(-x.^2-y.^2);
TCM_Data.xderstring = '@(x,y) exp(-x.^2-y.^2) -2*x.^2.*exp(-x.^2-y.^2)';
TCM_Data.yder = @(x,y) -2*y.*x.*exp(-x.^2-y.^2);
TCM_Data.yderstring = '@(x,y) -2*y.*x.*exp(-x.^2-y.^2)';
TCM_Data.iter = [];
% Do not delete these Variables
TCM_Data.plot_window = [-2 2 -2 2];
TCM_Data.LmbMenuString = '';
TCM_Data.examples = {'Polynomial','Quadratic function','Oscillation'};
% 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.4; % aspect ratio of the Buttons panel
TCM_UI.FeedbackRatio = 3.4; % 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 = 'Steepest descent';
TCM_UI.mono = get(0,'FixedWidthFontName');
% Strings for generating the information window
TCM_UI.Author = 'F. Martin and L. von Wolff';
TCM_UI.Date = '27.06.2016';
TCM_UI.Web = 'http://www.mathematics-online.org/inhalt/aussage/aussage1499';
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');
% Next button
TCM_UI.next=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
'BackgroundColor',get(gcf,'Color'), ...
'String','Next Step', ...
'FontUnits','normalized','FontSize',0.7,...
'Position',[.01,0,.98,.30], ...
'Callback',@examples,...
'Style','pushbutton','Callback',@steepest_descent);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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',[.1,.7,.30,.3],...
'String',sprintf('Initial x:'),...
'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',[.37,.7,.5,.3],...
'String',' ',...
'Style','edit',...
'Callback',@initial_update);
% Text for current x value
TCM_UI.xval=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel, ...
'BackgroundColor',get(gcf,'Color'), ...
'HorizontalAlignment','left', ...
'FontUnits','normalized','FontSize',0.7,...
'Position',[.1,.35,.8,.3],...
'String',sprintf('Current x: %5.3e',NaN),...
'Style','text');
% 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',[.1,.01,.9,.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;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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');
if (isempty(TCM_UI.xderinput.String) || strcmp(TCM_UI.xderinput.String,'automatic')...
|| (~funchange && derchange)) && exist('ainit','file')
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);
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);
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')
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);
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);
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.iter=[str2double(input(strfind(input,'[')+1:strfind(input,',')-1));...
str2double(input(strfind(input,',')+1:strfind(input,']')-1))];
if ~inpolygon(TCM_Data.iter(1,1),TCM_Data.iter(2,1),TCM_Data.plot_window([1 2 2 1]),...
TCM_Data.plot_window([3 3 4 4]))
TCM_Data.plot_window = [TCM_Data.iter(1,1)-2,TCM_Data.iter(1,1)+2,TCM_Data.iter(2,1)-2,TCM_Data.iter(2,1)+2];
end
TCM_UI.next.Enable = 'on';
steepest_descent;
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.iter = [];
TCM_UI.next.Enable = 'off';
myplot;
end
% mouse button pressed
function myDownfct(varargin)
p = getClickPosition(TCM_UI.ax);
if isnan(p)
return
end
if strcmp(get(gcf,'SelectionType'),'normal')
TCM_Data.iter = [p(1);p(2)];
steepest_descent;
if ~isnan(TCM_Data.iter(1,end))
TCM_UI.next.Enable = 'on';
myplot;
end
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);
x = linspace(TCM_Data.plot_window(1)-hx,TCM_Data.plot_window(2)+hx,100);
hy = TCM_Data.plot_window(4) - TCM_Data.plot_window(3);
y = linspace(TCM_Data.plot_window(3)-hy,TCM_Data.plot_window(4)+hy,100);
[X,Y] = meshgrid(x(x>=TCM_Data.plot_window(1) & x<=TCM_Data.plot_window(2)),...
y(y>=TCM_Data.plot_window(3) & y<=TCM_Data.plot_window(4)));
f = TCM_Data.fun(X,Y);
[~,h1]=contour(X,Y,f);
Level1 = h1.LevelList;
[az,el]=view(TCM_UI.ax2);
cla(TCM_UI.ax2,'reset');
s = surf(TCM_UI.ax2,X,Y,f,'EdgeColor','none','LineStyle','none','FaceLighting','phong');
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)]);
[X,Y] = meshgrid(x,y);
f = TCM_Data.fun(X,Y);
[~,h2]=contour(X,Y,f);
Level2 = h2.LevelList;
contour(X,Y,f,union(Level1,Level2));
hold on
xlim([TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim([TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);
for i=1:size(TCM_Data.iter,2)
plot(TCM_Data.iter(1,i),TCM_Data.iter(2,i),'or');
if (i==1 || i==size(TCM_Data.iter,2))
text(TCM_Data.iter(1,i),TCM_Data.iter(2,i),horzcat('x_{',num2str(i-1),'}'),'VerticalAlignment','Bottom','Clipping','on');
text(TCM_Data.iter(1,i),TCM_Data.iter(2,i),TCM_Data.fun(TCM_Data.iter(1,i),...
TCM_Data.iter(2,i)),horzcat('x_{',num2str(i-1),'}'),'VerticalAlignment','Bottom','Parent',TCM_UI.ax2,'Clipping','on');
end
plot3(TCM_UI.ax2,TCM_Data.iter(1,i),TCM_Data.iter(2,i),TCM_Data.fun(TCM_Data.iter(1,i),...
TCM_Data.iter(2,i)),'or');
if i >= 2
t = linspace(0,1);
x = [TCM_Data.iter(1,i);TCM_Data.iter(2,i)]*t + [TCM_Data.iter(1,i-1);TCM_Data.iter(2,i-1)]*(1-t);
plot3(TCM_UI.ax2,x(1,:),x(2,:),TCM_Data.fun(x(1,:),x(2,:)),'r');
end
end
if (i >= 1)
TCM_UI.initinput.String = horzcat('[',sprintf('%.3f',TCM_Data.iter(1,1)),...
',',sprintf('%.3f',TCM_Data.iter(2,1)),']');
p1 = TCM_Data.iter(:,end-1); p2 = TCM_Data.iter(:,end);
quiver(p1(1),p1(2),2*(p2(1)-p1(1)),2*(p2(2)-p1(2)),'MaxHeadSize',.4,'Color','red');
TCM_UI.ax2.View=[az,el];
TCM_UI.xval.String=sprintf(horzcat('Current x: [',sprintf('%.3f',TCM_Data.iter(1,end)),...
',',sprintf('%.3f',TCM_Data.iter(2,end)),']'));
TCM_UI.gradval.String=sprintf(horzcat('Gradient: [',sprintf('%.2f',TCM_Data.xder(p1(1),p1(2))),...
',',sprintf('%.2f',TCM_Data.yder(p1(1),p1(2))),']'));
else
TCM_UI.initinput.String = '[NaN, NaN]';
TCM_UI.xval.String='Current x: [NaN, NaN]';
TCM_UI.gradval.String='Gradient: [NaN, NaN]';
end
set(TCM_UI.xval,'ForegroundColor','black');
set(TCM_UI.gradval,'ForegroundColor','black');
SetWindowLimits;
end
% perform a step of the steepest descent algorithm
function steepest_descent(varargin)
tol=1e-2;
if ~isempty(TCM_Data.iter)
x = TCM_Data.iter(:,end);
d = [TCM_Data.xder(x(1),x(2));TCM_Data.yder(x(1),x(2))];
if norm(d,inf) < tol
set(TCM_UI.next,'Enable','off');
TCM_Data.iter(:,end+1) = TCM_Data.iter(:,end);
myplot;
return;
end;
try
minfun = @(t) TCM_Data.fun(x(1)-t*d(1),x(2)-t*d(2));
t0 = fminsearch(minfun,0);
TCM_Data.iter(:,end+1) = x - t0*d;
catch
TCM_UI.xval.String='no minimum found';
set(TCM_UI.xval,'ForegroundColor','red');
TCM_UI.gradval.String='during line search.';
set(TCM_UI.gradval,'ForegroundColor','red');
TCM_Data.iter(:,end) = [NaN; NaN];
return;
end
end
myplot;
end
% Update the Hinttext
function UpdateHinttext
LmbString = 'Left Mouse = Choose start point (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.^3-3*x+y.^2');
set(TCM_UI.xderinput,'String','@(x,y) 3*x.^2-3');
set(TCM_UI.yderinput,'String','@(x,y) 2*y');
function_update;
case 2
set(TCM_UI.funinput,'String','@(x,y) x.^2-x.*y+y.^2-3*x');
set(TCM_UI.xderinput,'String','@(x,y) 2*x-y-3');
set(TCM_UI.yderinput,'String','@(x,y) -x+2*y');
function_update;
case 3
set(TCM_UI.funinput,'String','@(x,y) sin(x+y)+cos(x.*y)');
set(TCM_UI.xderinput,'String','@(x,y) cos(x+y)-y.*sin(x.*y)');
set(TCM_UI.yderinput,'String','@(x,y) cos(x+y)-x.*sin(x.*y)');
function_update;
set(TCM_UI.initinput,'String','[2.294,-0.957]');
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