Code covered by the BSD License  

Highlights from
ODEvu

image thumbnail
from ODEvu by Vladimir Bondarenko
Ordinary differential equations viewer

odevu.m
function varargout = odevu(varargin)
% ODEVU MATLAB code for odevu.fig
%      Ordinary differential equations viewer (ODEvu). 
%
%      ODEvu is an educational tool for studying first order ODEs. 
%      It aims at getting an early insight into a solution of an ODE by visualising
%      its direction field and isoclines. 
%      Moreover, ODEvu contains a collection of annotated ODE illustrating
%      the wide application of differential equations and allows a user to save his
%      models to the examples database.
%      
% See also: 

% Copyright (c) 2009, Dr. Vladimir Bondarenko <http://sites.google.com/site/bondsite>

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @odevu_OpeningFcn, ...
                   'gui_OutputFcn',  @odevu_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before odevu is made visible.
function odevu_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to odevu (see VARARGIN)

% Choose default command line output for odevu
handles.output = hObject;
% Name the figure window:
set(handles.figure1,'Name','ODEvu: ordinary differential equations viewer');
% set axes parameters:
handles.xlims = [-1 1]*1;
handles.ylims = [-1 1]*1;
set(handles.edXlims,'String',[ '[' num2str(handles.xlims) ']*1']);
set(handles.edYlims,'String',[ '[' num2str(handles.ylims) ']*1']);

% set the default function:
handles.f = @(t,y) t.^2+y.^2;
handles.fstr = 't^2+y^2';

% set plot parameters
handles.plotType = 'dirfield';
set(handles.cbLabels,'Value',1);
set(handles.cbDirlines,'Value',1);
handles.dirLines = '';
handles.contour = '';
handles.xlabel = 't';
handles.ylabel = 'y';

% Info axes & description:
handles.funexpr = text('Interpreter','latex',...
 'String','$$\frac{dy}{dt}=f(t,y)$$',...
 'Position',[1 1],...
 'FontSize',14);
% Default descriptions:
handles.desc0 = {'Ordinary differential equations viewer (ODEvu 1.0)',...
                 '',...
                 '- specify a 1st order ODE in its explicit form or select an ODE model from the examples database;',...
                 '',...
                 '- inspect the model: choose between the direction field and isoclines views, specify axes limits;',...
                 '',...
                 '- add / modify a description for the model and save it to the database.';
                };
handles.desc1 = 'Some notes on the model.';
            
% Setup example functions:
handles.ode = load('odevuDB.mat');
handles.classID = 0;
handles.modelID = 0;

% Update handles structure:
guidata(hObject, handles);

% Setup the functions menu:
% set(handles.classMenu,'String',['Custom function', {handles.fdb.class}]);
set(handles.classMenu,'String',['Custom function'; handles.ode.odeClass(:,2)]);
set(handles.lbModels,'String',' ');

% Update description
% updateDescription(handles);
updateViews(handles);
set(handles.fxy, 'String', handles.fstr);

% plot
updatePlot(handles);

% UIWAIT makes odevu wait for user response (see UIRESUME)
% uiwait(handles.figure1);

function fdb = fdbSetup()
% define the functions database fdb

% set the classes
class = {'Linear homogeneous','Linear nonhomogeneous','Bernoulli','Riccati'};
expr  = {'f(t) y','f(t) y + g(t)','f(t) y + g(t) y^c','f(t) y^2 + g(t) y + h(t)'};
fdb = struct('class',class,'expr',expr);

% set the instances
% 1. Linear ODE: homogeneous
fdb(1).name = 'Linear homogeneous ODE';
fdb(1).desc = ['Linear ODE with constant coefficients\n\n',...
               'is the simplest nontrivial form of ODE.\n',...
               'It has a simple meaning: the growth rate of a certain quantity is ',...
               'proportional to the quantity itself.\n\nWhether it is a growth or a decay ',...
               'depends on the sign of f(y). The value of y, for which the derivative ',...
               'f(y)=0, corresponds to a stationary solution. Depending on the sign of the ',...
               'coefficient alpha, this solution can be stable or unstable.\n\n',...
               'Despite its simplicity, this differential equation turns to be an adequate model ',... 
               'for many phenomena observed in physics, biology, economics, and sociology.'...
              ];
% 1.1 Continuous interest compounding
f(1) = struct('name',     '  Bank account',...
                'symexpr',  'r y',...
                'numexpr',  '0.03*y',...
                'tlims',    '[0 10]',...
                'ylims',    '[0 5e3]',...
                'tlabel',   'Time [years]',...
                'ylabel',   'Money [$]',...
                'desc',     ['Continuous interest compounding.\n\n',... 
                             'In a bank, money does make money.',...
                             'Each second, or millisecond, or ten thousandth of a second ',...
                             'your bank credits your saving account with an interest at a rate, ',...
                             'proportional to the acount''s current value. ',...
                             'The coefficient of proportionality, r, is called the interest rate.\n\n'...
                             'The theory predicts an exponential increase of your wealth:\n\n',...
                             'y(t) ~ exp(rt)\n\n',... 
                             'In praxis, however, the apparently fast growth is just '... 
                             'a marketing trick. '... 
                             'The trick is that due to a small interest rate, the exponent ',...
                             'function over a reasonable time horizon can be ',...
                             'safely replaced by it''s first order approximation, which is the ',...
                             'linear function: exp(rt) = 1 + rt. Thus, the amount of money ',...
                             'on your account grows only linearly. Sorry.'
                            ]);
% 1.2 Radioactive decay
f(2) = struct('name',     '  Radioactive decay',...
              'symexpr',  '-\omega y',...
              'numexpr',  '-1/6.45e9*y',...
              'tlims',    '[0 5e9]',...
              'ylims',    '[0 1]',...
              'tlabel',   'Time [years]',...
              'ylabel',   'Mass [kg]',...
              'desc',     ['Radioactive decay is a real gem of mathematics found in physics.\n\n',...
                           'Compared to the bank account model, the only difference is the ',...
                           'minus sign: the quantity of a radioactive matter decreases at a rate, ',...
                           'proportinal to its current quantity.\n\n',...
                           'Yet this experimental observation rises ',...
                           'a nontrivial question: how does an atom know when to decay? '...
                           'Indeed, there is know central instituion like a bank overseeing the '...
                           'current state of "radioactive account". There is no simple answer to '...
                           'this simple question. Quantum mechanics is necessary.\n\n'...
                           'The coefficient omega is called "the probability of decay" and '...
                           'it''s reciprocal value equals to atoms'' average lifetime. ',...
                           'The lifetime varies vastly among different radioactive atoms. '...
                           'E.g., for uranium 235, it is 6.45e9 years, for radium 2400 years and ',...
                           'for strontium 28 days.\n\n'...
                           'The Earth is about 5e9 years old. The graph suggests that out of 1 kg '...
                           'of U235 that has appeared at the moment of creation about half a kilo '...
                           'is still here!'
                          ]);

% 1.3 Capacitor discharge:
f(3) = struct('name',     '  Capacitor discharge in an RC-chain',...
              'symexpr',  '-\frac{1}{RC}y',...
              'numexpr',  '-1/200*y',...
              'tlims',    '[0 400]',...
              'ylims',    '[0 5]',...
              'tlabel',   'Time [seconds]',...
              'ylabel',   'Voltage [V]',...
              'desc',     ['Discharge of a capacitor through a resistor.\n \n',...
                           'RC-chain is a simple electrical circuit consisting '...
                           'of two elements: a charged capacitor, C, '...
                           'and a resistor, R.\n\n'...
                           '                 R        C\n'...
                           '           -----xxxx-----| |-------\n'...
                           '          |                           |\n'...
                           '          |                           |\n'...
                           '          |_______/  _______|\n'...
                           '                       P\n\n'...
                           'It turns out that the voltage in this circuit after closing '...
                           'the switch P is described by the same differential equation '...
                           'as the radioactive decay. The product RC is called time constant '...
                           'of the RC-chain. In this example, R = 10 MOm and C = 20 uF, '...
                           'so RC = 200 seconds.'
                          ]);

% 1.4 Bacterial growth
f(4) = struct('name',     '  Bacterial growth',...
              'symexpr',  '\rho y',...
              'numexpr',  '.05*y - 1000',...
              'tlims',    '[0 120]',...
              'ylims',    '[0 100000]',...
              'tlabel',   'Time [minutes]',...
              'ylabel',   'Bacteria population',...
              'desc',    ['Bacterial growth.\n\n'...
                          'Suppose I''ve got an infection of some bacteria that '...
                          'reproduces itself at a rate 5% per minute of its existing '...
                          'population. Suppose also that my immune system is strong '...
                          'enough to kill up to 1000 bacteria per minute. Will I '...
                          'become thick?\n\n'... 
                          'The answer, as the direction field plot suggests, depends on how '...
                          'many bacteria did I caught initially. If they are more than 2000, '...
                          'I''m in trouble: in couple of hours there will be millions of them. '...
                          'If they are fewer, I have nothing to worry about: my body will get '...
                          'rid of the invaders within half an hour or so.\n\n'...
                          'If there happens to be exactly 2000 bacteria in the beginning, '...
                          'their number would not change with the time. However, a tiny deviation '...
                          'of the population size from 2000 would lead to one of the above cases.\n\n'...
                          'This is an example of a system with unstable equilibrium.'
                      ]);

% Store the first class of functions to the database:
fdb(1).f = f;
clear f;
                  
% 2. Linear ODE: nonhomogeneous
fdb(2).name = 'Linear nonhomogeneous ODE';
fdb(2).desc = ['Nonhomogeneous linear ODE \n\n',...
               'is the ...\n',...
              ];
% 2.1 Heat conduction/diffusion
f(1) = struct('name',     '  Heat conduction: stable system',...
              'symexpr',  'k(y_{out}-y)',...
              'numexpr',  '0.5*(296-y)',...
              'tlims',    '[0 10]',...
              'ylims',    '[260 350]',...
              'tlabel',   'Time [hours]',...
              'ylabel',   'Temperature [K]',...
              'desc',    ['Heat conduction model.\n\n'...
                          'The physicist Joseph Fourier demonstrated that propagation of heat obeys '...
                          'a simple law: the rate of heat transfer from one area to another '...
                          'is proportional to the difference of areas'' temperatures.\n\n'...
                          'In its simplest form, the heat conduction law can be applied to '...
                          'model the heating/cooling of an object in an environment at a'...
                          'constant temperature, say y_out = 296 K (23C). '...
                          'Depending on its initial value, the temperature of the object '...
                          'will rise or fall at a rate proportional to the current '...
                          'difference to the outside temperature. The coefficient k is called '...
                          'the thermal conductivity of the object.\n\n'...
                          'Independent of the initial temperature, after some time '...
                          'the object will inevitably reach the temperature of the environment. '...
                          'This is a simple example of a system with stable equilibrium.'
                        ]);
         
% Store the first class of functions to the database:
fdb(2).f = f;
clear f;

% 3. Quadratic ODE
fdb(3).name = 'Bernoulli ODE';
fdb(3).desc = ['Quadratic ODE with constant coefficients\n\n',...
               'is the simplest nontrivial form of ODE.\n',...
              ];
% 3.1 Logistic growth: bacterial growth 2
f(1) = struct('name',     '  Logistic growth: bacterial growth 2',...
              'symexpr',  'k y(Y-y)',...
              'numexpr',  '5e-8*y*(1e6-y)',...
              'tlims',    '[0 120]',...
              'ylims',    '[0 1e6]',...
              'tlabel',   'Time [minutes]',...
              'ylabel',   'Bacteria population',...
              'desc',    ['Logistic growth of bacteria.\n\n'...
                          'The linear ODE model of bacterial growth implies that '...
                          'under auspicious conditions the population of bacteria would '...
                          'grow without bounds. This is clearly an unrealistic real life scenario: '...
                          'an infinite growth of anything is highly improbable '...
                          'due to the limited resources in the first place.\n\n'...
                          'A more adequate model, called logistic growth, assumes that '...
                          'there is a maximum population size, Y, that an environment can '...
                          'bear and suggests that the growth rate of a population is '...
                          'proportional to both its current size and the difference between '...
                          'the current and the maximum size.\n\n'...
                          'The resulting population size follows a characteristic '...
                          'S-shaped curve saturating at y = Y (here one million), '...
                          'which is a state of stable equilibrium.'
                        ]);

% 3.2 Logistic growth with stable and unstable equilibrium
f(2) = struct('name',     '  Logistic growth 2: stable and unstable equilibrium',...
              'symexpr',  'k y(Y-y)-p',...
              'numexpr',  '5e-8*y*(1e6-y)-4.5e3',...
              'tlims',    '[0 120]',...
              'ylims',    '[0 1e6]',...
              'tlabel',   'Time [minutes]',...
              'ylabel',   'Bacteria population',...
              'desc',    ['Logistic growth of bacteria.\n\n'...
                          'stable equilibrium.'
                        ]);
% 3.3 Logistic growth: rumours propagation
f(3) = struct('name',     '  Logistic growth 3: rumours propagation',...
              'symexpr',  'k y(Y-y)-p',...
              'numexpr',  '5e-8*y*(1e6-y)-4.5e3',...
              'tlims',    '[0 120]',...
              'ylims',    '[0 1e6]',...
              'tlabel',   'Time [minutes]',...
              'ylabel',   'Bacteria population',...
              'desc',    ['Logistic growth of bacteria.\n\n'...
                          'stable equilibrium.'
                        ]);
                    
% Store the second class of functions to the database:                    
fdb(3).f = f;
clear f;
                    
% --- Outputs from this function are returned to the command line.
function varargout = odevu_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;



function fxy_Callback(hObject, eventdata, handles)
% hObject    handle to fxy (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of fxy as text
%        str2double(get(hObject,'String')) returns contents of fxy as a double
handles.fstr = get(hObject,'String');

if handles.classID == 0 % custom function
    % set the model description to the default one:
    set(handles.edDesc,'string',handles.desc1);
end

set(handles.pbSave,'enable','on');


guidata(hObject, handles);
updatePlot(handles);


% --- Executes during object creation, after setting all properties.
function fxy_CreateFcn(hObject, eventdata, handles)
% hObject    handle to fxy (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function updatePlot(handles)
% redraw the main axes.

% get the function
% fstr = get(handles.fxy,'string');
fstr = handles.fstr;
if isempty(fstr), return; end
char2rep = '*/^';   % characters to replace
% replace '*' by '.*', etc.
for ii=1:3
    char1 = char2rep(ii);
    char2 = ['.' char1];
    fstr = strrep(fstr, char1, char2);
end
try
    f = eval(['@(t,y) ' fstr]);
catch
    msgbox('Ther is an error in the input function.');
end
handles.f = f;
guidata(handles.output, handles);


switch handles.plotType
    case 'dirfield'
        xmin = handles.xlims(1);
        xmax = handles.xlims(2);
        ymin = handles.ylims(1);
        ymax = handles.ylims(2);

        n = 11;     % n-by-n mesh
        % plot direction field
        %  get axes size
        dum = get(handles.axes,'Position');
        axW = dum(3);
        axH = dum(4);
        %  compute scale coeficients
        mx = axW/diff(handles.xlims);
        my = axH/diff(handles.ylims);
        %  the length of the direction lines w.r.t. the axes width
        r = axW*.05; 
        
        [X0,Y0] = meshgrid(linspace(xmin,xmax,n), linspace(ymin,ymax,n)); % line origins
        A = atan( my/mx*f(X0,Y0) );             % line angle

        % compute the end points
        X1 = X0 + cos(A)*r/mx;
        Y1 = Y0 + sin(A)*r/my;
        X2 = X0 - cos(A)*r/mx;
        Y2 = Y0 - sin(A)*r/my;
        % plot the line segments
        cla(handles.axes); axes(handles.axes);
        handles.dirLines = plot([X1(:) X2(:)]',[Y1(:) Y2(:)]', 'b-');

        axis(handles.axes,'square','tight');
        xlabel(handles.axes,handles.xlabel); 
        ylabel(handles.axes,handles.ylabel);
                
    case 'isoclines'
        drawIsoclines1(handles);
%         drawIsoclines2(handles);
end


function drawIsoclines1(handles)
% draw isoclines using contour plot

f = handles.f;
xmin = handles.xlims(1);
xmax = handles.xlims(2);
ymin = handles.ylims(1);
ymax = handles.ylims(2);

n = 101;
nlevels = 10;
[X,Y] = meshgrid(linspace(xmin,xmax,n), linspace(ymin,ymax,n));
D = f(X,Y); % compute the derivative.
levels = quantile(D(:),10); % set levels to evenly spaced quantiles.
% maxL = min(max(D(:)), 20);
% minL = max(min(D(:)),-20);
% levelsLin = linspace(minL,maxL,nlevels);     % linearly spaced levels
% % 
% if max(abs(maxL),abs(minL))==20
%     ftrans = @(x) 20*(x/20).^3;
%     levels = round(10*ftrans(levelsLin))/10;
% else
%     levels = round(10*levelsLin)/10;
% end
% get in the level 0;
% if (maxL*minL < 0) && isempty(find(levels==0,1))
    levels(end+1) = 0;
%     nlevels = nlevels+1;
% end

% 1. Plot the level lines:
[C,ch] = contour(X,Y,D,levels);
handles.contour = ch;   % store the handle to the contour plot

% 2. Overlay the directions
%  get axes size
dum = get(handles.axes,'Position');
axW = dum(3);
axH = dum(4);
%  compute scale coeficients
mx = axW/diff(xlim() );
my = axH/diff(ylim() );
%  the length of the direction lines w.r.t. the axes width
r = axW*.05; 

% 2.1 Find the coordinates for the centers of the direction lines
ixs = [];       % to store the indices of the first points of each level line.
ixlong = [];    % store the indices of long lines (longer than 100 points)
ix0 = [];
ixCur = 1;
ixLast = size(C,2);
while ixCur < ixLast
    lineLength = C(2,ixCur);    % number of points in the current level line
    if lineLength>50 && lineLength<=100
        ixs = [ixs ixCur];         % indices of the beginnings of the level lines
    elseif lineLength>100 
        ixlong = [ixlong ixCur]; 
    end
    if C(1,ixCur)==0 
        ix0 = [ ix0 [ixCur+1; ixCur+lineLength] ];
    end
    ixCur = ixCur + lineLength + 1;
end
X0 = []; Y0 = [];   % coordinates of the centers of the direction lines
if ~isempty(ixs)    
    dix1 = floor(C(2,ixs)/2);     % 1/2 of the level line
    X0 = [X0 C(1,ixs + dix1)];
    Y0 = [Y0 C(2,ixs + dix1)];
end
% add more direction lines for the level lines longer than 100 points.
if ~isempty(ixlong)
    dix2 = floor(C(2,ixlong)/4);     % 1/4 of the level line
    dix3 = floor(C(2,ixlong)*.75);   % 3/4 of the level line
    X0 = [X0 C(1,ixlong + dix2) C(1,ixlong+dix3)];
    Y0 = [Y0 C(2,ixlong + dix2) C(2,ixlong+dix3)];
end

A = atan( my/mx*f(X0,Y0) );             % line angle

% 2.2 Plot the direction lines
% compute the end points
X1 = X0 + cos(A)*r/mx;
Y1 = Y0 + sin(A)*r/my;
X2 = X0 - cos(A)*r/mx;
Y2 = Y0 - sin(A)*r/my;
% plot the line segments
hold on;
handles.dirLines = plot([X1(:) X2(:)]',[Y1(:) Y2(:)]', 'k-','LineWidth',2);
hold off;        
if get(handles.cbDirlines,'value')==0
    set(handles.dirLines,'visible','off');
end

% 3. bold the zero level line:
if ~isempty(ix0)
   hold on;
   for ii=1:size(ix0,2)
       plot(C(1,ix0(1,ii):ix0(2,ii)),C(2,ix0(1,ii):ix0(2,ii)),'g:','LineWidth',3);
   end
    hold off;
end

% 4. Add labels:
if get(handles.cbLabels,'Value')
    clabel(C,ch);
end
axis(handles.axes,'square','tight');
xlabel(handles.axes,handles.xlabel); ylabel(handles.axes,handles.ylabel);

% update the handle structure:
guidata(handles.axes, handles);




function drawIsoclines2(handles)
% draw isoclines using ezplot
f = handles.f;
xmin = handles.xlims(1);
xmax = handles.xlims(2);
ymin = handles.ylims(1);
ymax = handles.ylims(2);

% automatically select levels
n = 101;
nlevels = 10;
[X,Y] = meshgrid(linspace(xmin,xmax,n), linspace(ymin,ymax,n));
D = f(X,Y); % the derivative
maxL = min(max(D(:)), 20);
minL = max(min(D(:)),-20);
levels = round(10*linspace(minL,maxL,nlevels))/10;
% get in the level 0;
if (maxL*minL < 0) && isempty(find(levels==0,1))
    levels(end+1) = 0;
    nlevels = nlevels+1;
end
    
% construct the function for the level lines computations
f1 = @(t,y,c) f(t,y)-c;

% plot the level lines
cla(handles.axes);
hezp = zeros(nlevels,1); % handles of the level lines
hold on;
for ii=1:nlevels
    c = levels(ii);
    f2 = @(t,y) f1(t,y,c);
    hezp(ii)=ezplot(f2,[xmin xmax ymin ymax]);
    set(hezp(ii),'LineColor','b','TextList',c,'ShowText','on');
end

% Add labels

hold off;
title('Isoclines');


% --- Executes when selected object is changed in uipanel1.
function uipanel1_SelectionChangeFcn(hObject, eventdata, handles)
% hObject    handle to the selected object in uipanel1 
% eventdata  structure with the following fields (see UIBUTTONGROUP)
%	EventName: string 'SelectionChanged' (read only)
%	OldValue: handle of the previously selected object or empty if none was selected
%	NewValue: handle of the currently selected object
% handles    structure with handles and user data (see GUIDATA)

switch get(eventdata.NewValue, 'Tag')
    case 'rbDirfield'
        handles.plotType = 'dirfield';
        set(handles.cbLabels,'Enable','off');
        set(handles.cbDirlines,'Enable','off');        
    case 'rbIsoclines'
        handles.plotType = 'isoclines';
        set(handles.cbLabels,'Enable','on');
        set(handles.cbDirlines,'Enable','on');
end
guidata(hObject,handles);
updatePlot(handles);


% --- Executes on button press in cbLabels.
function cbLabels_Callback(hObject, eventdata, handles)
% hObject    handle to cbLabels (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of cbLabels
switch get(hObject,'Value')
    case 0
        set(handles.contour,'ShowText','off');
    case 1
        set(handles.contour,'ShowText','on');
end

% --- Executes on button press in cbDirlines.
function cbDirlines_Callback(hObject, eventdata, handles)
% hObject    handle to cbDirlines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of cbDirlines
switch get(hObject,'Value')
    case 0
        set(handles.dirLines,'Visible','off');
    case 1
        set(handles.dirLines,'Visible','on');
end


function edXlims_Callback(hObject, eventdata, handles)
% hObject    handle to edXlims (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edXlims as text
%        str2double(get(hObject,'String')) returns contents of edXlims as a double
try
    handles.xlims = eval(get(hObject,'String'));
catch
    msgbox({'Wrong specification of limits.', 'Use the format [min max].'});
end

set(handles.pbSave,'enable','on');

guidata(handles.axes, handles);
updatePlot(handles);


% --- Executes during object creation, after setting all properties.
function edXlims_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edXlims (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edYlims_Callback(hObject, eventdata, handles)
% hObject    handle to edYlims (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edYlims as text
%        str2double(get(hObject,'String')) returns contents of edYlims as a double
try
    handles.ylims = eval(get(hObject,'String'));
catch
    msgbox({'Wrong specification of limits.', 'Use the format [min max].'});
end

set(handles.pbSave,'enable','on');

guidata(handles.axes, handles);
updatePlot(handles);

% --- Executes during object creation, after setting all properties.
function edYlims_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edYlims (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in lbModels.
function lbModels_Callback(hObject, eventdata, handles)
% hObject    handle to lbModels (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns lbModels contents as cell array
%        contents{get(hObject,'Value')} returns selected item from lbModels

mNum = get(hObject,'Value')-1;  % model number.

switch mNum
    case 0   % no model selected.
        handles.modelID = 0;
        handles.fstr = '';
        % disable the function edit box:
        set(handles.fxy,'enable','off');
        % activate the Up and Down buttons:
        N = length(handles.ode.odeClass(:,1)); % number of classes.
        n = get(handles.classMenu,'value')-1;
        switch n
            case 1
                set(handles.pbUp,'enable','off');
                set(handles.pbDown,'enable','on');
            case N
                set(handles.pbUp,'enable','on');
                set(handles.pbDown,'enable','off');
            otherwise
                set(handles.pbUp,'enable','on');
                set(handles.pbDown,'enable','on');
        end        
    otherwise
        % IDs of the current class' models:
        mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==handles.classID,2);
        % model indices:
        [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
        mixs = sort(mixs);
        % index of the selected model:
        mix = mixs(mNum);
        mID = handles.ode.odeModel{mix,1};        % id of the current model
        handles.modelID = mID;
        % update the function string:
        handles.fstr = handles.ode.odeModel{mix,4};
        % update axes parameters:
        handles.xlims = eval(handles.ode.odeModel{mix,6});
        handles.ylims = eval(handles.ode.odeModel{mix,7});
        handles.xlabel = handles.ode.odeModel{mix,8};
        handles.ylabel = handles.ode.odeModel{mix,9};
        % enable the function edit box:
        set(handles.fxy,'enable','on');
        % activate the Up and Down buttons:
        N = length(mixs); % number of models of the current class.
        if N==1
            set(handles.pbUp,  'enable','off');
            set(handles.pbDown,'enable','off');
        else            
            switch mNum
                case 1
                    set(handles.pbUp,  'enable','off');
                    set(handles.pbDown,'enable','on');
                case N
                    set(handles.pbUp,  'enable','on');
                    set(handles.pbDown,'enable','off');
                otherwise
                    set(handles.pbUp,  'enable','on');
                    set(handles.pbDown,'enable','on');
            end        
        end
end
guidata(hObject,handles);
% updateDescription(handles);
updatePlot(handles);
updateViews(handles);
% disable the save button:
set(handles.pbSave,'enable','off');

% --- Executes during object creation, after setting all properties.
function lbModels_CreateFcn(hObject, eventdata, handles)
% hObject    handle to lbModels (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in classMenu.
function classMenu_Callback(hObject, eventdata, handles)
% hObject    handle to classMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns classMenu contents as cell array
%        contents{get(hObject,'Value')} returns selected item from classMenu
n = get(hObject,'Value')-1;
switch n
    case 0 % custom function
        set(handles.lbModels,'String','','Value',1,'Enable','off');
        set(handles.pbEdit,'Enable','off');
        set(handles.pbDelete,'Enable','off');
        set(handles.pbUp,'Enable','off');
        set(handles.pbDown,'Enable','off');
        set(handles.fxy,'Enable','on');
        handles.classID = 0;
    otherwise
        handles.classID  = handles.ode.odeClass{n,1};                   % class ID
        % update the models listbox:
        % corresponding model IDs:        
        mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==handles.classID,2);
        % model indices:
        [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
        mixs = sort(mixs);
        set(handles.lbModels,'String',...
           [handles.ode.odeClass{n,2}; strcat({'-- '},handles.ode.odeModel(mixs,2))]);
        set(handles.lbModels,'Value',1,'Enable','on');
        % enable the edit button:
        set(handles.pbEdit,'enable','on');
        set(handles.pbDelete,'Enable','on');
        % disable the function edit box:
        set(handles.fxy,'enable','off');
        % activate the Up and Down buttons:
        N = length(handles.ode.odeClass(:,1)); % number of classes.
        switch n
            case 1
                set(handles.pbUp,'enable','off');
                set(handles.pbDown,'enable','on');
            case N
                set(handles.pbUp,'enable','on');
                set(handles.pbDown,'enable','off');
            otherwise
                set(handles.pbUp,'enable','on');
                set(handles.pbDown,'enable','on');
        end
end
handles.modelID = 0;
handles.xlabel = 't';
handles.ylabel = 'y';
xlabel(handles.axes,handles.xlabel); 
ylabel(handles.axes,handles.ylabel);

guidata(hObject, handles);
updateViews(handles);
% disable the save button:
set(handles.pbSave,'enable','off');


% --- Executes during object creation, after setting all properties.
function classMenu_CreateFcn(hObject, eventdata, handles)
% hObject    handle to classMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function updateViews(handles)
% update the gui views:
cID = handles.classID;
mID = handles.modelID;

cix = find([handles.ode.odeClass{:,1}]==cID);

if cID == 0          % custom function
    fexpr = '$$\frac{dy}{dt}=f(t,y)$$';
    desc = handles.desc0;
    fstr = '';
    handles.xlims = [-1 1];
    handles.ylims = [-1 1];    
    cla(handles.axes);
    axis(handles.axes, [-1 1 -1 1]);
    set(handles.edXlims,'String',[ '[' num2str(handles.xlims) ']*1']);
    set(handles.edYlims,'String',[ '[' num2str(handles.ylims) ']*1']);    
else
    switch mID
        case 0
            fstr = '';
            fexpr = ['$$\frac{dy}{dt}=' handles.ode.odeClass{cix,3} '$$'];
            desc  = handles.ode.odeClass{cix,4};
            handles.xlims = [-1 1];
            handles.ylims = [-1 1];
            cla(handles.axes);
            axis(handles.axes,[-1 1 -1 1]);
            set(handles.edXlims,'String',[ '[' num2str(handles.xlims) ']*1']);
            set(handles.edYlims,'String',[ '[' num2str(handles.ylims) ']*1']);
        otherwise
            % get current model index:
            mix = find([handles.ode.odeModel{:,1}] == mID);
            % update functions display, description and axes labels:
            fstr = handles.ode.odeModel{mix,4};
            fexpr = ['$$\frac{dy}{dt}=' handles.ode.odeModel{mix,3} '$$'];
            desc  = handles.ode.odeModel{mix,5};
            set(handles.edXlims,'String',handles.ode.odeModel{mix,6});
            set(handles.edYlims,'String',handles.ode.odeModel{mix,7});
    end
end
set(handles.funexpr,'String',fexpr);
set(handles.edDesc,'String',desc);
set(handles.fxy,'String',fstr);
handles.fstr = fstr;
guidata(handles.output,handles);

function updateDescription(handles)
% update the function expressions and the description text.
cNum = handles.classNum;
fNum = handles.fNum;

if cNum==0          % custom function
    fexpr = '$$\frac{dy}{dt}=f(t,y)$$';
    desc  = handles.desc0;
    fstr = '';
    cla(handles.axes);
else
    switch fNum
        case 0
            fstr = '';
            fexpr = ['$$\frac{dy}{dt}=' handles.fdb(cNum).expr '$$'];
            desc  = regexp(handles.fdb(cNum).desc,'\\n','split');
            cla(handles.axes);
        otherwise
            fstr = handles.fdb(cNum).f(fNum).numexpr;
            fexpr = ['$$\frac{dy}{dt}=' handles.fdb(cNum).f(fNum).symexpr '$$'];
            desc  = regexp(handles.fdb(cNum).f(fNum).desc,'\\n','split');
    end
end
set(handles.funexpr,'String',fexpr);
set(handles.edDesc,'String',desc);
set(handles.fxy,'String',fstr);
handles.fstr = fstr;
guidata(handles.output,handles);


% --- Executes on button press in pbSave.
function pbSave_Callback(hObject, eventdata, handles)
% hObject    handle to pbSave (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% fdb = handles.fdb;
% kk = 1;
% for ii=1:length(fdb)
%     odeClass{ii,1} = ii;
%     odeClass{ii,2} = fdb(ii).class;
%     odeClass{ii,3} = fdb(ii).expr;
%     odeClass{ii,4} = fdb(ii).desc;
%     for jj=1:length(fdb(ii).f)
%        odeModel{kk,1} = kk; 
%        odeModel{kk,2} = fdb(ii).f(jj).name;
%        odeModel{kk,3} = fdb(ii).f(jj).symexpr;
%        odeModel{kk,4} = fdb(ii).f(jj).numexpr;
%        odeModel{kk,5} = fdb(ii).f(jj).desc;
%        odeModel{kk,6} = fdb(ii).f(jj).tlims;
%        odeModel{kk,7} = fdb(ii).f(jj).ylims;
%        odeModel{kk,8} = fdb(ii).f(jj).tlabel;
%        odeModel{kk,9} = fdb(ii).f(jj).ylabel;
%        
%        odeClassModel(kk,1) = ii;
%        odeClassModel(kk,2) = kk;
%        kk = kk+1;
%     end
% end
% 
% save odevuDB.mat odeClass odeModel odeClassModel;

cID = handles.classID;
mID = handles.modelID;

if cID==0 % save new custom function
    % create a new record in the odeModel table:
    newID = max([handles.ode.odeModel{:,1}])+1; % auto increment ID.
    mix = length(handles.ode.odeModel(:,1))+1;  % index of the record in the table.
    desc = {'Some notes about the model.'};
    numExpr = get(handles.fxy,'string');
    xlims = get(handles.edXlims,'string');
    ylims = get(handles.edYlims,'string');
    % add new record to the table:
    handles.ode.odeModel(mix,:) = { newID, 'Enter name', 'f(t,y)', numExpr,...
                                    desc, xlims, ylims, 't', 'y' };
    handles.modelID = newID;
%     guide(hObject,handles);
    [val, new_cID] = pbEdit_Callback(handles.pbEdit,[],handles);
    if val % model was saved to the odevuDB.mat file.
        handles.ode = load('odevuDB.mat'); % reload the ode tables.
        handles.classID = new_cID;
    else % model was not saved
        % remove the new record:
        handles.ode.odeModel(mix,:) = [];
        handles.modelID = 0;
    end
else
    if mID==0  % update odeClass table
        % find the raw of the current class in the odeClass table:
        ix = find([handles.ode.odeClass{:,1}]==cID); 
        % update the description for the current class:
        desc = cellstr(get(handles.edDesc,'string'));
        desc{end+1} = ''; % add an empty row.
        handles.ode.odeClass{ix,4} = desc;
        % save the updated table to the database:
        odeClass = handles.ode.odeClass;
        save('odevuDB.mat','odeClass','-append');
    else       % update odeModel table 
        % find the raw of the current model in the odeModel table:
        ix = find([handles.ode.odeModel{:,1}]==mID); 
        % update all the records :
        desc = cellstr(get(handles.edDesc,'string')); % description;
        desc{end+1} = ''; % add an empty row.
        numExpr = get(handles.fxy,'string');
        xlims = get(handles.edXlims,'string');
        ylims = get(handles.edYlims,'string');
        handles.ode.odeModel{ix,4} = numExpr;
        handles.ode.odeModel{ix,5} = desc;
        handles.ode.odeModel{ix,6} = xlims;
        handles.ode.odeModel{ix,7} = ylims;
        % save the updated table to the database:
        odeModel = handles.ode.odeModel;
        save('odevuDB.mat','odeModel','-append');        
    end
end
% disable the save button:
set(hObject,'enable','off');
guidata(hObject,handles);

% --- Executes on button press in pbEdit.
function [val, cID] = pbEdit_Callback(hObject, eventdata, handles)
% hObject    handle to pbEdit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
%
% returns 1 if OK is pressed, and 0 if Cancel.

val = 0; % default output

cID = handles.classID;
mID = handles.modelID;

cix = find([handles.ode.odeClass{:,1}]==cID);
    
if mID == 0 % edit class name and expression
    dlg_title = 'ODE class description';
    promt = {'Name:','Symbolic expression:'};
    num_lines = 1;
    defs  = handles.ode.odeClass(cix,[2,3]);
    % enter class description:
    cInfo = inputdlg(promt,dlg_title,num_lines,defs);
    if ~isempty(cInfo)
        % save class description:
        handles.ode.odeClass(cix,[2,3]) = cInfo;
        odeClass = handles.ode.odeClass;
        save('odevuDB.mat','odeClass','-append');
        % update the class and models menu: 
        set(handles.classMenu,'String',['Custom function'; handles.ode.odeClass(:,2)]);
        % update the models menu:
        % get model ids:
        mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==cID,2);
        %  get models indices:
        [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
        mixs = sort(mixs);
        set(handles.lbModels,'String',...
           [handles.ode.odeClass{cix,2}; strcat({'-- '},handles.ode.odeModel(mixs,2))]);
        % update symbolic expresion:
        fexpr = ['$$\frac{dy}{dt}=' handles.ode.odeClass{cix,3} '$$'];
        set(handles.funexpr,'String',fexpr);
        val = 1;
    end
else   % edit model
    % get model ix:
    mix = find([handles.ode.odeModel{:,1}]==mID); 
    % get IDs of the classes the model belongs to:
    cIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,2)==mID, 1);
    % get the indices of those classes:
    [~,cixs,~] = intersect([handles.ode.odeClass{:,1}],cIDs);
    % enter model description:
    mInfo = odeModelInfo(handles.ode.odeModel(mix,[2,3,8,9]),... 
                         handles.ode.odeClass(:,2),cixs);
    if ~isempty(mInfo)
        % update model parameters:
        handles.ode.odeModel(mix,[2,3,8,9]) = mInfo{1};
        % check for a new class:
        numClass = length(handles.ode.odeClass(:,1));
        numNewClass = length(mInfo{2}) - numClass;
        if numNewClass % new class/classes were added
            % insert new class ID and name into the odeClass table:
            odeClass = handles.ode.odeClass;
            maxID = max([odeClass{:,1}]);
            newIx = numClass+(1:numNewClass);
            odeClass(newIx,1) = num2cell((maxID+1:maxID+numNewClass)');
            odeClass(newIx,2) = mInfo{2}(newIx);
            odeClass(newIx,3:4) = {'f(t) y'; {} };
            handles.ode.odeClass = odeClass;
            set(handles.classMenu,'string',odeClass(:,2));
            % save the updated odeClass table:
            save('odevuDB.mat','odeClass','-append');
        end
        % update the ClassModel references:
        cixs = mInfo{3};
        cIDs = [ handles.ode.odeClass{cixs,1} ]';
        handles.ode.odeClassModel(handles.ode.odeClassModel(:,2)==mID,:) = [];
        handles.ode.odeClassModel(end+1:end+length(cIDs),:) = ...
                                              [cIDs, ones(length(cIDs),1)*mID];
        odeModel = handles.ode.odeModel;
        odeClassModel = handles.ode.odeClassModel;
        save('odevuDB.mat','odeModel','odeClassModel','-append');
        val = 1;
        
        % update the class and models menus:
        cix = cixs(1);
        set(handles.classMenu,'string',['Custom function'; handles.ode.odeClass(:,2)],...
            'value',cix+1);
        cID = cIDs(1);
        handles.classID = cID;        
        %  get models ids:
        mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==cID,2);
        %  get models indices:
        [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
        mixs = sort(mixs);
        %  get the position of the current model in the list:
        mPos = find([handles.ode.odeModel{mixs,1}]==mID)+1;
        set(handles.lbModels,'String',...
           [handles.ode.odeClass{cix,2}; strcat({'-- '}, handles.ode.odeModel(mixs,2))],...
           'Value', mPos,'enable','on');
        
        % update symbolic expresion:
        fexpr = ['$$\frac{dy}{dt}=' handles.ode.odeModel{mix,3} '$$'];
        set(handles.funexpr,'String',fexpr);
        % update labels:
        xlabel(handles.axes,handles.ode.odeModel{mix,8});
        ylabel(handles.axes,handles.ode.odeModel{mix,9});
    end
end
guidata(hObject,handles);
       
% --- Executes on button press in pbDelete.
function pbDelete_Callback(hObject, eventdata, handles)
% hObject    handle to pbDelete (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
cID = handles.classID;
mID = handles.modelID;

if mID ==0  % delete class    
    if cID == 5 % the 'Misc' class cannot be deleted.
        errordlg({'You cannot delete this class, since there must be at least one class.'},...
                  'Error');
    else
    % show confirmation dialog
        answ = questdlg({'You are about to delete a class.',...
           'The class'' models will not be deleted, but reassigned to the class ''Misc''.'},...
                    'Delete class?',...
                    'Proceed','Cancel','Proceed');
        if strcmpi(answ,'cancel'), return; end
        
        % locate class indices in the odeClass and odeClassModel tables:
        cix  = find([handles.ode.odeClass{:,1}]==cID);
        cixs = find(handles.ode.odeClassModel(:,1)==cID);
        % delete the class record from the odeClass table:
        handles.ode.odeClass(cix,:) = [];
        % reassign the class models to the 'Misc' class:
        handles.ode.odeClassModel(cixs,1) = 5;
        % save the updated tables to the database:
        odeClass = handles.ode.odeClass;
        odeClassModel = handles.ode.odeClassModel;
        save('odevuDB.mat','odeClass','odeClassModel','-append');
        
        % update the class and models menus:
        cID = 0; 
        handles.classID = cID;
        set(handles.classMenu,'string',['Custom function'; handles.ode.odeClass(:,2)],...
                              'value', 1);
        set(handles.lbModels,'string','');
        set(handles.edDesc,'string',handles.desc0);
        set(handles.fxy,'enable','on');
    end
else        % delete model
    answ = questdlg('You are about to permanently delete a model.',...
                    'Delete model?',...
                    'Proceed','Cancel','Proceed');
    if strcmpi(answ,'cancel'), return; end
    
    % locate model indices in the odeModel and odeClassModel tables:
    mix  = find([handles.ode.odeModel{:,1}]==mID);
    mixs = find(handles.ode.odeClassModel(:,2)==mID);
    % delete model record from the tables:
    handles.ode.odeModel(mix,:) = [];
    handles.ode.odeClassModel(mixs,:) = [];
    % save the updated tables to the database:
    odeModel = handles.ode.odeModel;
    odeClassModel = handles.ode.odeClassModel;
    save('odevuDB.mat','odeModel','odeClassModel','-append');
    
    % update the model list:
    mID = 0;
    handles.modelID = mID;
    %  get models ids of the current class:
    mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==cID,2);
    %  get models indices:
    [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
    mixs = sort(mixs);
    % get the index of the current class:
    cix = find([handles.ode.odeClass{:,1}]==cID);
    % set the position of the model list to 1:
    mPos = 1;
    set(handles.lbModels,'String',...
       [handles.ode.odeClass{cix,2}; strcat({'-- '}, handles.ode.odeModel(mixs,2))],...
       'Value', mPos);
    % set description to the description of the class:
    set(handles.edDesc,'string',handles.ode.odeClass{cix,4});
    % disable the numeric expression:
    set(handles.fxy,'string','','enable','off');
end
guidata(hObject,handles);

% --- Executes on button press in rbDirfield.
function rbDirfield_Callback(hObject, eventdata, handles)
% hObject    handle to rbDirfield (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of rbDirfield
set(hObject,'Value',1);
set(handles.rbIsoclines,'Value',0);

handles.plotType = 'dirfield';
set(handles.cbLabels,'Enable','off');
set(handles.cbDirlines,'Enable','off');        

guidata(hObject,handles);
updatePlot(handles);
% updateViews(handles);


% --- Executes on button press in rbIsoclines.
function rbIsoclines_Callback(hObject, eventdata, handles)
% hObject    handle to rbIsoclines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of rbIsoclines
set(hObject,'Value',1);
set(handles.rbDirfield,'Value',0);

handles.plotType = 'isoclines';
set(handles.cbLabels,'Enable','on');
set(handles.cbDirlines,'Enable','on');        

guidata(hObject,handles);
updatePlot(handles);
% updateViews(handles);

% --- Executes on button press in pbClose.
function pbClose_Callback(hObject, eventdata, handles)
% hObject    handle to pbClose (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
close(handles.figure1);


% --- Executes on key press with focus on edDesc and none of its controls.
function edDesc_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to edDesc (see GCBO)
% eventdata  structure with the following fields (see UICONTROL)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)

keyascii = double(eventdata.Character);

if isempty(keyascii), return; end;

if (keyascii >=32 && keyascii<=126)||...
   strcmpi(eventdata.Key,'return')||...
   strcmpi(eventdata.Key,'backspace')
    set(handles.pbSave,'enable','on');
end


% --- Executes on button press in pbUp.
function pbUp_Callback(hObject, eventdata, handles)
% hObject    handle to pbUp (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

cID = handles.classID;
mID = handles.modelID;

if mID == 0     % modify class' order
    cIDs = [handles.ode.odeClass{:,1}]; % ids of the classes.
    cix = find(cID == cIDs); % index of the current class.
    if cix==1, return; end; 
    % make swap:
    dum = handles.ode.odeClass(cix-1,:);
    handles.ode.odeClass(cix-1,:) = handles.ode.odeClass(cix,:);
    handles.ode.odeClass(cix,:) = dum;
    % save the updated table to the database file:
    odeClass = handles.ode.odeClass;
    save('odevuDB.mat','odeClass','-append');
    % update the class menu:
    set(handles.classMenu,'String',['Custom function'; handles.ode.odeClass(:,2)],...
                          'Value',cix);
    % enable the Down button:
    set(handles.pbDown,'enable','on');
    % if hit the top, disable the Up button:
    if cix==2, set(handles.pbUp,'enable','off'); end;
else % modify models order
    mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==cID,2);
    % models indices:
    [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
    mixs = sort(mixs);
    % indices of the current and upper model:
    n = get(handles.lbModels,'value')-1;
    mix = mixs(n);
    mix_up = mixs(n-1);
    % swap the models:
    dum = handles.ode.odeModel(mix_up,:);
    handles.ode.odeModel(mix_up,:) = handles.ode.odeModel(mix,:);
    handles.ode.odeModel(mix,:) = dum;
    % save the updated table to the database file:
    odeModel = handles.ode.odeModel;
    save('odevuDB.mat','odeModel','-append');        
    % update the models menu:
    cix = find([handles.ode.odeClass{:,1}]==cID);
    set(handles.lbModels,'String',...
       [handles.ode.odeClass{cix,2}; strcat({'-- '},handles.ode.odeModel(mixs,2))],...
       'value',n);
    % enable the Down button:
    set(handles.pbDown,'enable','on');
    % if hit the top, disable the Up button:
    if n==2, set(handles.pbUp,'enable','off'); end;   
end
guidata(hObject,handles);

% --- Executes on button press in pbDown.
function pbDown_Callback(hObject, eventdata, handles)
% hObject    handle to pbDown (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
cID = handles.classID;
mID = handles.modelID;

if mID == 0     % modify class' order
    cIDs = [handles.ode.odeClass{:,1}]; % ids of the classes.
    cix = find(cID == cIDs); % index of the current class.
    if cix==length(cIDs), return; end; 
    % make swap:
    dum = handles.ode.odeClass(cix+1,:);
    handles.ode.odeClass(cix+1,:) = handles.ode.odeClass(cix,:);
    handles.ode.odeClass(cix,:) = dum;
    % save the updated table to the database file:
    odeClass = handles.ode.odeClass;
    save('odevuDB.mat','odeClass','-append');
    % update the class menu:
    set(handles.classMenu,'String',['Custom function'; handles.ode.odeClass(:,2)],...
                          'Value',cix+2);
    % enable the Up button:
    set(handles.pbUp,'enable','on');
    % if hit the bottom, disable the Down button:
    if cix+1==length(cIDs), set(handles.pbDown,'enable','off'); end;
else            % modify models order
    mIDs = handles.ode.odeClassModel(handles.ode.odeClassModel(:,1)==cID,2);
    % models indices:
    [~,mixs,~] = intersect([handles.ode.odeModel{:,1}],mIDs);
    mixs = sort(mixs);
    % indices of the current and upper model:
    n = get(handles.lbModels,'value')-1;
    mix = mixs(n);
    mix_down = mixs(n+1);
    % swap the models:
    dum = handles.ode.odeModel(mix_down,:);
    handles.ode.odeModel(mix_down,:) = handles.ode.odeModel(mix,:);
    handles.ode.odeModel(mix,:) = dum;
    % save the updated table to the database file:
    odeModel = handles.ode.odeModel;
    save('odevuDB.mat','odeModel','-append');    
    % update the models menu:
    cix = find([handles.ode.odeClass{:,1}]==cID);
    set(handles.lbModels,'String',...
       [handles.ode.odeClass{cix,2}; strcat({'-- '},handles.ode.odeModel(mixs,2))],...
       'value',n+2);
    % enable the Up button:
    set(handles.pbUp,'enable','on');
    % if hit the bottom, disable the Down button:
    if n+1==length(mixs), set(handles.pbDown,'enable','off'); end;       
end
guidata(hObject,handles);

Contact us