% Software for the computation of multi-component factorizations and the set of feasible solutions 
% using analytical approach and inner boundary curve.
%
% Copyright (c) 2021 Tomass Andersons, University of Rostock, Department of Mathematics.
% 
% The software can be used for academic, research and other similar noncommercial uses. The user
% acknowledges that this software is still in the development stage and that it is provided by the
% copyright holders and contributors "as is" and any express or implied warranties, fitness for a
% particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable
% for any direct, indirect, incidental, special, exemplary, or consequential damages.
% 
% The copyright holders provide no reassurances that the source code provided does not infringe any
% patent, copyright, or any other intellectual property rights of third parties. The copyright
% holders disclaim any liability to any recipient for claims brought against recipient by any third
% party for infringement of that parties intellectual property rights.

%% WARNING!
% The algorithm in its current form is not intended for noisy data or
% cases when INNPOL is very close to FIRPOL. The results might be 
% incorrect. Use polygon inflation algorithm in these cases.

%% 
% This experimental GUI accompanies:
% T. Andersons, M. Sawall, K. Neymeyr,
% Analytical enclosure of the set of solutions of the three-species multivariate curve resolution problem
% J. Math. Chem. 60, 1750-1780 (2022).
% DOI: 10.1002/cem.3381

%%
% This is the GUI of the inner boundary curve algorithm.
% Press run to start the GUI. 
% The calculations are done in the object IBCA.
% The file IBCA.m (from subfcnsIBC) should be located on Matlab path.

% Loaded .mat files should contain:
% Matrix D that contains the nonnegative matrix of the NMF problem
% Vectors T and X (optional) that contain time steps and wavelengths

% Saved results contain:
% 'D' original matrix
% 'T' original time steps
% 'X' original wavelengths
% 'U','S','V' calculated truncated singlular value decomposition
% 'FIRPOL_1','INNPOL_1' primal inner and outer boundaries
% 'FIRPOL_2','INNPOL_2' dual inner and outer boundaries
% 'Contact_Change_Val_Param' parameters for each contact change value:
%         Contact_Change_Val_Param(:,1)  is  alpha
%         Contact_Change_Val_Param(:,2)  is  v_1
%         Contact_Change_Val_Param(:,3)  is  v_2
%         Contact_Change_Val_Param(:,4)  is  u_l1
%         Contact_Change_Val_Param(:,5)  is  u_l2
%         Contact_Change_Val_Param(:,6)  is  u_r1
%         Contact_Change_Val_Param(:,7)  is  u_r2
%         Contact_Change_Val_Param(:,8)  is  o_l1
%         Contact_Change_Val_Param(:,9)  is  o_12
%         Contact_Change_Val_Param(:,10) is  o_r1
%         Contact_Change_Val_Param(:,11) is  o_r2
%         Contact_Change_Val_Param(:,12) is  type of contact change value     
% 'Inn_Bound_Cur_Fun_Coeffs' coefficients for each IBC subfunction:
%         res(:,1)  is alpha   
%         res(:,2)  is c_x0
%         res(:,3)  is c_x1
%         res(:,4)  is c_x2
%         res(:,5)  is c_y0
%         res(:,6)  is c_y1
%         res(:,7)  is c_y2
%         res(:,8)  is c_d0
%         res(:,9)  is c_d1
%         res(:,10) is c_d2
% 'Inn_Bound_Cur_Approx' approximation of the inner boundary curve
% 'Intersect_FIRPOL_Inn_Bound_Cur'(optional) intersection of IBC and FIRPOL
% 'Set_of_Feasib_Sol' set of feasible solutions


%%
classdef subGUI4IBC < handle
    %% Properties
    properties
        basicfsz = 10
        sty = 'Helvetica'
        colorMat
        transposed
        
        calc            IBCA
        data
                
        hRootFigure     matlab.ui.Figure

        hTabs
        
        hLeftPanel      
        hLeftAxes       
        hLeftCurves
        
        hRightPanel
        hRightAxes
        hRightCurves
        
        pIBC
    end
    
    %% Ordinary methods
    methods
        %% GUI4IBCA: Constructor
        function obj = subGUI4IBC(version,figNR)

            if nargin == 0 % Started as standalone (add subfcnsIBC to path)
                % Create the root figure
                obj.hRootFigure = figure('Visible','off','outerposition',[50 50 1440 1100],...
                    'NumberTitle','off','MenuBar','none','Toolbar','none','color','white',...
                    'Name','Inner boundary curve algorithm');
            else % Started in Facpack
                % Create the root figure
                obj.hRootFigure = figure(figNR);
                set(obj.hRootFigure,'Visible','off','outerposition',[50 50 1440 1100],...
                    'NumberTitle','off','MenuBar','none','Toolbar','none','color','white',...
                    'Name',['FACPACK (Inner boundary curve algorithm, version ',num2str(version,'%2.2f'), '']);
            end
            createComp(obj)
            startupFcn(obj)
        end
        %% createComp: Create GUI components
        function createComp(obj)
            % Top panel
            obj.hTabs.panel = uipanel('Title','Controls',...
                'BackgroundColor','w','FontUnits','pixel','Units','pixel','FontSize',obj.basicfsz*1.2,...
                'FontName',obj.sty,'Position',[10 910 1420 180],'Visible','on','Units','normalized');

            obj.hTabs.aa = uicontrol('Style','pushbutton','string','aa','Parent',obj.hTabs.panel,...
                'Position',[200 630 120 40],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gLoad(hObj,event,1)});

            obj.hTabs.hButton1221 = uicontrol('Style','pushbutton','string','Transpose D','Parent',obj.hTabs.panel,...
                'Position',[10 10 120 40],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gTranspose(hObj,event)});
            obj.hTabs.hButton112 = uicontrol('Style','pushbutton','string','Save','Parent',obj.hTabs.panel,...
                'Position',[10 60 120 40],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gSave(hObj,event)});
            obj.hTabs.hButton111 = uicontrol('Style','pushbutton','string','Open','Parent',obj.hTabs.panel,...
                'Position',[10 110 120 40],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gLoad(hObj,event)});

            obj.hTabs.hButton1222 = uicontrol('Style','pushbutton','string','Analytical SFS','Parent',obj.hTabs.panel,...
                'Position',[140 10 120 40],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gAnalyticalSFS(hObj,event)});
            obj.hTabs.hButton121 = uicontrol('Style','pushbutton','string','Run','Parent',obj.hTabs.panel,...
                'Position',[140 60 120 90],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gRun(hObj,event)});

            obj.hTabs.hButton1313 = uicontrol('Style','checkbox','string','Set of feasible solutions','Parent',obj.hTabs.panel,...
                'Position',[270 10 180 40],'Units','normalized','Value',1,...
                'Callback',{@(hObj,event) obj.gShow3(hObj,event)});
            obj.hTabs.hButton1312 = uicontrol('Style','checkbox','string','Inner boundary curve','Parent',obj.hTabs.panel,...
                'Position',[270 60 180 40],'Units','normalized','Value',1,...
                'Callback',{@(hObj,event) obj.gShow2(hObj,event)});
            obj.hTabs.hButton1311 = uicontrol('Style','checkbox','string','INNPOL and FIRPOL','Parent',obj.hTabs.panel,...
                'Position',[270 110 180 40],'Units','normalized','Value',1,...
                'Callback',{@(hObj,event) obj.gShow1(hObj,event)});

            obj.hTabs.hLabel1413 = uicontrol('Style','text','string','Number of subfunctions: ','Parent',obj.hTabs.panel,...
                'Position',[460 10 400 40],'Units','normalized');
            obj.hTabs.hLabel1412 = uicontrol('Style','text','string','CPU Time:','Parent',obj.hTabs.panel,...
                'Position',[460 60 400 40],'Units','normalized');
            obj.hTabs.hLabel1411 = uicontrol('Style','text','string',' ','Parent',obj.hTabs.panel,...
                'Position',[460 110 400 40],'Units','normalized');

            obj.hTabs.hButton2122 = uicontrol('Style','text','string','0 deg','Parent',obj.hTabs.panel,...
                'Position',[870 10 180 40],'Units','normalized','Visible','off');
                %'Callback',{@(hObj,event) obj.gSpin(hObj,event)}
            obj.hTabs.hButton2132 = uicontrol('Style','slider','Parent',obj.hTabs.panel,...
                'Min',-180,'Max',180,'Value',0,'SliderStep',[1/360 1/36],...
                'Position',[870 60 180 40],'Units','normalized','Visible','off',...
                'Callback',{@(hObj,event) obj.gSlide(hObj,event)},...
                'Tooltip','You can use arrow keys to display the rotation');
            obj.hTabs.hButton2111 = uicontrol('Style','checkbox','string','Boundary triangle','Parent',obj.hTabs.panel,...
                'Position',[870 110 180 40],'Units','normalized',...
                'Callback',{@(hObj,event) obj.gShow4(hObj,event)});

            obj.hTabs.hLabel2223 = uicontrol('Style','text','string',' ','Parent',obj.hTabs.panel,...
                'Position',[1060 10 350 40],'Units','normalized','Visible','off');
            obj.hTabs.hLabel2222 = uicontrol('Style','text','string',' ','Parent',obj.hTabs.panel,...
                'Position',[1060 60 350 40],'Units','normalized','Visible','off');
            obj.hTabs.hLabel22229 = uicontrol('Style','text','string','Current boundary curve function:','Parent',obj.hTabs.panel,...
                'Position',[1060 110 350 40],'Units','normalized','Visible','off');

            % Left side
            obj.hLeftPanel = uipanel('Title','Factor S',...
                'BackgroundColor','w','FontUnits','pixel','Units','pixel','FontSize',obj.basicfsz*1.2,...
                'FontName',obj.sty,'Position',[10 10 700 880],'Visible','on','Units','normalized');
            obj.hLeftAxes = axes('Parent',obj.hLeftPanel,'FontSize',obj.basicfsz,...
                'FontName',obj.sty,'units','pixel','Position',[70 280 560 560],'Units','normalized');
            plot(obj.hLeftAxes,0,1,'w');
            hold(obj.hLeftAxes,'on');
            obj.hLeftCurves=axes('Parent',obj.hLeftPanel,'FontSize',obj.basicfsz,...
                'FontName',obj.sty,'units','pixel','Position',[70 40 560 180],'Units','normalized');
            plot(obj.hLeftCurves,0,1,'w');
            hold(obj.hLeftCurves,'on');
            
            % Right side
            obj.hRightPanel = uipanel('Title','Factor C',...
                'BackgroundColor','w','FontUnits','pixel','Units','pixel','FontSize',obj.basicfsz*1.2,...
                'FontName',obj.sty,'Position',[730 10 700 880],'Visible','on','Units','normalized');
            obj.hRightAxes = axes('Parent',obj.hRightPanel,'FontSize',obj.basicfsz,...
                'FontName',obj.sty,'units','pixel','Position',[70 280 560 560],'Units','normalized');
            plot(obj.hRightAxes,0,1,'w');
            hold(obj.hRightAxes,'on');
            obj.hRightCurves=axes('Parent',obj.hRightPanel,'FontSize',obj.basicfsz,...
                'FontName',obj.sty,'units','pixel','Position',[70 40 560 180],'Units','normalized');
            plot(obj.hRightCurves,0,1,'w');
            hold(obj.hRightCurves,'on');
            
            % Plot callback
            obj.hLeftAxes.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,1)};
            obj.hRightAxes.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,2)};
            obj.hLeftCurves.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,3)};
            obj.hRightCurves.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,4)};
            
            % Empty plot handles
            obj.pIBC.F1 = plot([],[]);
            obj.pIBC.I1 = plot([],[]);
            obj.pIBC.F2 = plot([],[]);
            obj.pIBC.I2 = plot([],[]);
            obj.pIBC.nIBC = plot([],[]);
            obj.pIBC.SFS{1} = plot([],[]);
            obj.pIBC.TrLeft = plot([],[]);
            obj.pIBC.TrRight = plot([],[]);
            obj.pIBC.TrpLeft = plot([],[]);
            obj.pIBC.TrpRight = plot([],[]);
            obj.pIBC.TnLeft = plot([],[]);
            obj.pIBC.TnRight = plot([],[]);
            obj.pIBC.CurLeft = plot([],[]);
            obj.pIBC.CurRight = plot([],[]);
            obj.colorMat = [0 0.7 1; 1 0.5 0; 0 0.7 0];
            
            % Dock the figure into the ToolGroup and set all to visible
            % obj.hToolGroup.open(); % Display the ToolGroup window
            % obj.hToolGroup.addFigure(obj.hRootFigure);
            drawnow
            set(obj.hRootFigure,'Visible','on');
        end
        %% startupFcn: Startup Function
        function startupFcn(obj)
            addpath('subfcnsIBC')
            obj.calc = IBCA();
            obj.transposed = 0;
        end
        %% gLoad: Load data matrix
        function gLoad(obj, hObj, event)
            % Reset everything
            obj.resetFct
            
            % Load file
            obj.data.succ_loaded=0;
            [fname, fpath] = uigetfile({'*.mat'},...
                'Select *.mat file with a matrix D and optionally vectors T and X');
            if max(fname)>0 && max(fpath)>0
                S=load([fpath fname]);
                names=fieldnames(S);
                if not(isempty(names))
                    obj.data.lastLoadPath=fpath;
                    if ismember('D',names)
                        temp_D = getfield(S,'D');
                        if ismember('T',names)
                            temp_T = getfield(S,'T');
                        else
                            temp_T = 1:size(temp_D,1);
                        end
                        if ismember('X',names)
                            temp_X = getfield(S,'X');
                        else
                            temp_X = 1:size(temp_D,2);
                        end
                        % Dimension check
                        if ~ismatrix(temp_D) && ~isvector(temp_D)
                            disp('ERROR: Variable for D is no matrix')
                            obj.hTabs.hLabel1411.String = 'ERROR: Variable for D is no matrix';
                        elseif ~isvector(temp_T)
                            disp('ERROR: Variable for T is no vector')
                            obj.hTabs.hLabel1411.String = 'ERROR: Variable for T is no vector';
                        elseif ~isvector(temp_X)
                            disp('ERROR: Variable for X is no vector')
                            obj.hTabs.hLabel1411.String = 'ERROR: Variable for X is no vector';
                        else
                            if min(size(temp_D)==[length(temp_T) length(temp_X)])==1
                                obj.hTabs.hLabel1411.String = 'Variables loaded';
                                obj.calc.D = temp_D;
                                obj.calc.X = temp_X;
                                obj.calc.T = temp_T;
                                obj.data.succ_loaded=1;
                            elseif min(size(temp_D)==[length(temp_X) length(temp_T)])==1
                                disp('WARNING: Variables loaded - matrix D had to be transposed')
                                obj.hTabs.hLabel1411.String = 'WARNING: Variables loaded - matrix D had to be transposed';
                                obj.calc.D = temp_D';
                                obj.calc.X = temp_X;
                                obj.calc.T = temp_T;
                                obj.data.succ_loaded=1;
                            else
                                disp('ERROR: Dimension mismatch for D, T and X - Choose correct variables')
                                obj.hTabs.hLabel1411.String = 'ERROR: Dimension mismatch for D, T and X - Choose correct variables';
                            end
                        end
                    else
                        disp('ERROR: Matrix D missing in mat file')
                        obj.hTabs.hLabel1411.String = 'ERROR: Matrix D missing in mat file';
                    end
                else
                    disp('ERROR: empty mat file')
                    obj.hTabs.hLabel1411.String = 'ERROR: empty mat file';
                end
            end
        end
        %% gSave: Save results
        function gSave(obj, hObj, event)
            % Input
            D = obj.calc.D;
            T = obj.calc.T;
            X = obj.calc.X;
            % Singular value decomposition
            U = obj.calc.U;
            S = obj.calc.S;
            V = obj.calc.V;
            % Inner and outer boundaries
            FIRPOL_1 = obj.calc.F1(obj.calc.kF1,:);
            INNPOL_1 = obj.calc.I1(obj.calc.kI1,:);
            FIRPOL_2 = obj.calc.F2(obj.calc.kF2,:);
            INNPOL_2 = obj.calc.I2(obj.calc.kI2,:);
            % Results
            Contact_Change_Val_Param = obj.calc.TV;
            Inn_Bound_Cur_Fun_Coeffs = obj.calc.IBCF;
            Inn_Bound_Cur_Approx = obj.calc.nIBC;
            Intersect_FIRPOL_Inn_Bound_Cur = obj.calc.isec;
            Set_of_Feasib_Sol = obj.calc.SFS;
            % Save
            uisave({'D','T','X','U','S','V','FIRPOL_1','INNPOL_1','FIRPOL_2','INNPOL_2',...
                'Contact_Change_Val_Param','Inn_Bound_Cur_Fun_Coeffs','Inn_Bound_Cur_Approx',...
                'Intersect_FIRPOL_Inn_Bound_Cur','Set_of_Feasib_Sol'},'results.mat')
        end
        %% gRun: Run calculation
        function gRun(obj, hObj, event)
            obj.hTabs.hLabel1411.String = 'Calculating... ';
            obj.pClear([1 1 1 1]);
            drawnow
            if ~isempty(obj.calc.D)
                tStart = cputime;
                obj.calc.calcSVD;
                obj.calc.calcTSVD;
                obj.calc.calcBoundaries;
                obj.calc.calcIBCF;
                obj.calc.calcnIBC;
                tEnd = cputime - tStart;
                obj.hTabs.hLabel1411.String = 'Done (right click a plot to open it in an new figure)';
                obj.hTabs.hLabel1412.String = ['CPU Time: ',num2str(tEnd), ''];
                obj.hTabs.hLabel1413.String = ['Number of subfunctions: ',num2str(size(obj.calc.IBCF,1)), ''];
                obj.pRedraw(obj.hLeftAxes,1,0,[1 1 1 1])
                obj.pRedraw(obj.hRightAxes,2,0,[1 0 0 1])
                obj.pRedraw(obj.hLeftCurves,3,0,[0 0 0 1])
                obj.pRedraw(obj.hRightCurves,4,0,[0 0 0 1])
            else
                obj.hTabs.hLabel1411.String = 'ERROR: Load matrix D first';
            end
        end
        %% gTranspose: Transpose matrix D
        function gTranspose(obj, hObj, event)
            if ~isempty(obj.calc.D)
                if obj.transposed == 0
                    obj.transposed = 1;
                    obj.hLeftPanel.Title = 'Factor C';
                    obj.hRightPanel.Title = 'Factor S';
                    Dnew = obj.calc.D';
                    Tnew = obj.calc.X;
                    Xnew = obj.calc.T;
                    obj.resetFct
                    obj.calc.D = Dnew;
                    obj.calc.T = Tnew;
                    obj.calc.X = Xnew;
                    
                    
                    obj.hTabs.hLabel1411.String = 'Matrix D is transposed';
                else
                    obj.transposed = 0;
                    obj.hLeftPanel.Title = 'Factor S';
                    obj.hRightPanel.Title = 'Factor C';
                    Dnew = obj.calc.D';
                    Tnew = obj.calc.X;
                    Xnew = obj.calc.T;
                    obj.resetFct
                    obj.calc.D = Dnew;
                    obj.calc.T = Tnew;
                    obj.calc.X = Xnew;
                    obj.hTabs.hLabel1411.String = 'Matrix D is transposed';
                end
            else
                obj.hTabs.hLabel1411.String = 'ERROR: Load matrix D first';
            end
        end
        %% gAnalyticalSFS: Calculate analytical SFS
        function gAnalyticalSFS(obj, hObj, event)
            obj.hTabs.hLabel1411.String = 'Calculating... ';
            drawnow
            obj.pClear([0 0 1 0]);
            if ~isempty(obj.calc.nIBC)
                tStart = cputime;
                obj.calc.calcIntersection;
                obj.calc.calcSFS;
                tEnd = cputime - tStart;
                obj.hTabs.hLabel1412.String = ['CPU Time: ',num2str(tEnd), ''];
                obj.hTabs.hLabel1411.String = 'Done (right click a plot to open it in an new figure)';
                obj.pRedraw(obj.hLeftAxes,1,0,[0 0 1 0])
            else
                obj.hTabs.hLabel1411.String = 'ERROR: Calculate the inner boundary first';
            end
        end

        %% gShow1: Show/hide INNPOL and FIRPOL
        function gShow1(obj, hObj, event)
            if obj.hTabs.hButton1311.Value==1
                set(obj.pIBC.F1,'Visible','on')
                set(obj.pIBC.I1,'Visible','on')
                set(obj.pIBC.F2,'Visible','on')
                set(obj.pIBC.I2,'Visible','on')
            else
                set(obj.pIBC.F1,'Visible','off')
                set(obj.pIBC.I1,'Visible','off')
                set(obj.pIBC.F2,'Visible','off')
                set(obj.pIBC.I2,'Visible','off')
            end
        end
        %% gShow2: Show/hide IBC
        function gShow2(obj, hObj, event)
            if obj.hTabs.hButton1312.Value==1
                set(obj.pIBC.nIBC,'Visible','on')
            else
                set(obj.pIBC.nIBC,'Visible','off')
            end
        end
        %% gShow3: Show/hide SFS
        function gShow3(obj, hObj, event)
            if obj.hTabs.hButton1313.Value==1
                for i = 1:size(obj.pIBC.SFS,2)
                    set(obj.pIBC.SFS{i},'Visible','on');
                end
            else
                for i = 1:size(obj.pIBC.SFS,2)
                    set(obj.pIBC.SFS{i},'Visible','off');
                end
            end
        end
        %% gShow4: Show/hide Triangle
        function gShow4(obj, hObj, event)
            if hObj.Value==1
                set(obj.hTabs.hButton2122,'Visible','on')
                set(obj.hTabs.hButton2132,'Visible','on')
                set(obj.hTabs.hLabel2223,'Visible','on')
                set(obj.hTabs.hLabel2222,'Visible','on')
                set(obj.hTabs.hLabel22229,'Visible','on')
                obj.calc.alpha = obj.hTabs.hButton2122.Value;
                obj.updateRotation;
                set(obj.pIBC.TrLeft,'Visible','on');
                set(obj.pIBC.TrRight,'Visible','on');
                set(obj.pIBC.TrpLeft,'Visible','on');
                set(obj.pIBC.TrpRight,'Visible','on');
                set(obj.pIBC.TnLeft,'Visible','on');
                set(obj.pIBC.TnRight,'Visible','on');
                set(obj.pIBC.CurLeft,'Visible','on');
                set(obj.pIBC.CurRight,'Visible','on');
            else
                set(obj.hTabs.hButton2122,'Visible','off')
                set(obj.hTabs.hButton2132,'Visible','off')
                set(obj.hTabs.hLabel2223,'Visible','off')
                set(obj.hTabs.hLabel2222,'Visible','off')
                set(obj.hTabs.hLabel22229,'Visible','off')
                set(obj.pIBC.TrLeft,'Visible','off');
                set(obj.pIBC.TrRight,'Visible','off');
                set(obj.pIBC.TrpLeft,'Visible','off');
                set(obj.pIBC.TrpRight,'Visible','off');
                set(obj.pIBC.TnLeft,'Visible','off');
                set(obj.pIBC.TnRight,'Visible','off');
                set(obj.pIBC.CurLeft,'Visible','off');
                set(obj.pIBC.CurRight,'Visible','off');
            end
        end
        %% gSlide: Rotaton slider
        function gSlide(obj,hObj,event)
            obj.calc.alpha = hObj.Value/180*pi;
            obj.hTabs.hButton2122.String = [num2str(hObj.Value),' deg'];
            obj.updateRotation;
        end
        %% updateRotation: Update rotation triangle calculation and plot
        function updateRotation(obj)
            if ~isempty(obj.calc.nIBC) && obj.hTabs.hButton2111.Value==1
                obj.calc.calcTriangle;
                
                obj.hTabs.hLabel2222.String = ['p_x(a) = (' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,2),'%8.4f') '+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,3),'%8.4f') 'a+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,4),'%8.4f') 'a^2) / (' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,8),'%8.4f') '+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,9),'%8.4f') 'a+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,10),'%8.4f') 'a^2)'];
                obj.hTabs.hLabel2223.String = ['p_y(a) = (' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,5),'%8.4f') '+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,6),'%8.4f') 'a+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,7),'%8.4f') 'a^2) / (' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,8),'%8.4f') '+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,9),'%8.4f') 'a+' ...
                    num2str(obj.calc.IBCF(obj.calc.iTr,10),'%8.4f') 'a^2)'];
                
                % Redraw the triangles
                obj.pIBC.TrLeft.XData = [obj.calc.pTriangle(:,1); obj.calc.pTriangle(1,1)];
                obj.pIBC.TrLeft.YData =  [obj.calc.pTriangle(:,2); obj.calc.pTriangle(1,2)];
                obj.pIBC.TrpLeft.XData = obj.calc.pTriangle(:,1);
                obj.pIBC.TrpLeft.YData =  obj.calc.pTriangle(:,2);
                obj.pIBC.TnLeft.XData = obj.calc.pTangents(:,1);
                obj.pIBC.TnLeft.YData =  obj.calc.pTangents(:,2);
                obj.pIBC.TrRight.XData = [obj.calc.dTriangle(:,1); obj.calc.dTriangle(1,1)];
                obj.pIBC.TrRight.YData = [obj.calc.dTriangle(:,2); obj.calc.dTriangle(1,2)];
                obj.pIBC.TrpRight.XData = obj.calc.dTriangle(:,1);
                obj.pIBC.TrpRight.YData = obj.calc.dTriangle(:,2);
                obj.pIBC.TnRight.XData = obj.calc.dTangents(:,1);
                obj.pIBC.TnRight.YData = obj.calc.dTangents(:,2);
                
                % Recalculate curves
                Tmat = [[1;1;1] obj.calc.pTriangle];
                cL = (Tmat*obj.calc.V')'; % Spectre
                cR = obj.calc.U*obj.calc.S*inv(Tmat); % Concentration
                % Redraw the curves
                obj.pIBC.CurLeft(1).XData = obj.calc.X;
                obj.pIBC.CurLeft(1).YData = cL(:,1);
                obj.pIBC.CurLeft(2).XData = obj.calc.X;
                obj.pIBC.CurLeft(2).YData = cL(:,2);
                obj.pIBC.CurLeft(3).XData = obj.calc.X;
                obj.pIBC.CurLeft(3).YData = cL(:,3);
                obj.pIBC.CurRight(1).XData = obj.calc.T;
                obj.pIBC.CurRight(1).YData = cR(:,1);
                obj.pIBC.CurRight(2).XData = obj.calc.T;
                obj.pIBC.CurRight(2).YData = cR(:,2);
                obj.pIBC.CurRight(3).XData = obj.calc.T;
                obj.pIBC.CurRight(3).YData = cR(:,3);
            end
        end
        %% gPlotNew: Open the axis in a new figure
        function gPlotNew(obj, hObj, event, axisNr)
            if event.Button == 3
                figure();
                hAxis = axes();
                hold(hAxis,'on');
                obj.pRedraw(hAxis,axisNr,1,[1 1 1 1])
            end
        end
        %% pRedraw: Update axis
        function pRedraw(obj,hAxis,axisNr,ifNewFig,ifUpdate)
            % Check what is has to be visible
            viewSett = [obj.hTabs.hButton1311.Value,...
                obj.hTabs.hButton1312.Value,...
                obj.hTabs.hButton1313.Value,...
                obj.hTabs.hButton2111.Value];
            
            % Update plots
            if ifUpdate(1)==1 && (axisNr==1 || axisNr==2) % Update boundaries
                [pF,pI] = obj.calc.plotBoundary(axisNr,hAxis);
                pF.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                pI.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                if viewSett(1)==1
                    set(pF,'Visible','on')
                    set(pI,'Visible','on')
                end
                % View
                if axisNr==1
                    F = obj.calc.F1;
                elseif axisNr==2
                    F = obj.calc.F2;
                end
                xMax = max(F(:,1));
                xMin = min(F(:,1));
                yMax = max(F(:,2));
                yMin = min(F(:,2));
                multi = 0.7;
                xlim(hAxis,[xMin-(xMax-xMin)*multi xMax+(xMax-xMin)*multi]);
                ylim(hAxis,[yMin-(yMax-yMin)*multi yMax+(yMax-yMin)*multi]);
                if axisNr==1 && ifNewFig==0
                    obj.pIBC.F1 = pF;
                    obj.pIBC.I1 = pI;
                elseif axisNr==2 && ifNewFig==0
                    obj.pIBC.F2 = pF;
                    obj.pIBC.I2 = pI;
                end
            end
            if ifUpdate(2)==1 && axisNr==1 % Update IBC
                pnIBC = obj.calc.plotIBC(hAxis);
                pnIBC.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,1)};
                if viewSett(2)==1
                    set(pnIBC,'Visible','on')
                end
                if ifNewFig==0
                    obj.pIBC.nIBC = pnIBC;
                end
            end
            if ifUpdate(3)==1 && axisNr==1 % Update SFS
                pSFS = obj.calc.plotSFS(hAxis);
                if viewSett(3)==1
                    for i = 1:size(pSFS,2)
                        if ~isempty(pSFS{i})
                            pSFS{i}.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,1)};
                        end
                        set(pSFS{i},'Visible','on');
                    end
                end
                if ifNewFig==0
                    obj.pIBC.SFS = pSFS;
                end
            end
            if ifUpdate(4)==1 && viewSett(4)==1 % Boundary triangle
                if ifNewFig==1 % Calculate again and plot
                    obj.calc.calcTriangle;
                    if axisNr==1
                        plot(hAxis,[obj.calc.pTriangle(:,1); obj.calc.pTriangle(1,1)],...
                            [obj.calc.pTriangle(:,2); obj.calc.pTriangle(1,2)],'k-');
                        scatter(hAxis,obj.calc.pTangents(:,1),obj.calc.pTangents(:,2),[],'r','filled');
                        scatter(hAxis,obj.calc.pTriangle(:,1),obj.calc.pTriangle(:,2),[],obj.colorMat,'filled');
                    elseif axisNr==2
                        plot(hAxis,[obj.calc.dTriangle(:,1); obj.calc.dTriangle(1,1)],...
                            [obj.calc.dTriangle(:,2); obj.calc.dTriangle(1,2)],'k-');
                        scatter(hAxis,obj.calc.dTangents(:,1),obj.calc.dTangents(:,2),[],'b','filled');
                        scatter(hAxis,obj.calc.dTriangle(:,1),obj.calc.dTriangle(:,2),[],obj.colorMat,'filled');
                    elseif axisNr==3
                        Tmat = [[1;1;1] obj.calc.pTriangle];
                        cL = (Tmat*obj.calc.V')'; % Spectre
                        set(hAxis, 'ColorOrder', obj.colorMat, 'NextPlot', 'replacechildren');
                        plot(hAxis,[obj.calc.X' obj.calc.X' obj.calc.X'],cL);
                    elseif axisNr==4
                        Tmat = [[1;1;1] obj.calc.pTriangle];
                        cR = obj.calc.U*obj.calc.S*inv(Tmat); % Concentration
                        set(hAxis, 'ColorOrder', obj.colorMat, 'NextPlot', 'replacechildren');
                        plot(hAxis,[obj.calc.T' obj.calc.T' obj.calc.T'],cR);
                    end
                elseif ifNewFig==0 % Plot empty invisible triangle
                    if axisNr==1 % Do only once
                        obj.calc.alpha = obj.hTabs.hButton2122.Value;
                        obj.updateRotation;
                        set(obj.pIBC.TrLeft,'Visible','on');
                        set(obj.pIBC.TrRight,'Visible','on');
                        set(obj.pIBC.TrpLeft,'Visible','on');
                        set(obj.pIBC.TrpRight,'Visible','on');
                        set(obj.pIBC.TnLeft,'Visible','on');
                        set(obj.pIBC.TnRight,'Visible','on');
                        set(obj.pIBC.CurLeft,'Visible','on');
                        set(obj.pIBC.CurRight,'Visible','on');
                    end
                end
            end
        end
        %% pClear: Clear plots
        function pClear(obj,ifClear)
            if ifClear(1)==1 % Delete boundary plots
                delete(obj.pIBC.F1)
                delete(obj.pIBC.I1)
                delete(obj.pIBC.F2)
                delete(obj.pIBC.I2)
            end
            if ifClear(2)==1 % Delete IBC plot
                delete(obj.pIBC.nIBC)
            end
            if ifClear(3)==1 % Delete SFS plots
                for i = 1:size(obj.pIBC.SFS,2)
                    delete(obj.pIBC.SFS{i})
                end
            end
            if ifClear(4)==1 % Delete boundary triangle plots and create new empty plots
                delete(obj.pIBC.TrLeft)
                delete(obj.pIBC.TrRight)
                delete(obj.pIBC.TrpLeft)
                delete(obj.pIBC.TrpRight)
                delete(obj.pIBC.TnLeft)
                delete(obj.pIBC.TnRight)
                delete(obj.pIBC.CurLeft)
                delete(obj.pIBC.CurRight)
                
                obj.pIBC.TrLeft = plot(obj.hLeftAxes,[0 0 0 0],[0 0 0 0],'k-','Visible','off');
                obj.pIBC.TnLeft = scatter(obj.hLeftAxes,[0 0 0],[0 0 0],[],'r','filled','Visible','off');
                obj.pIBC.TrpLeft = scatter(obj.hLeftAxes,[0 0 0],[0 0 0],[],...
                    obj.colorMat,'filled','Visible','off');
                obj.pIBC.TrLeft.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.TnLeft.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.TrpLeft.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                
                obj.pIBC.TrRight = plot(obj.hRightAxes,[0 0 0 0],[0 0 0 0],'k-','Visible','off');
                obj.pIBC.TnRight = scatter(obj.hRightAxes,[0 0 0],[0 0 0],[],'b','filled','Visible','off');
                obj.pIBC.TrpRight = scatter(obj.hRightAxes,[0 0 0],[0 0 0],[],...
                    obj.colorMat,'filled','Visible','off');
                obj.pIBC.TrRight.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.TnRight.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.TrpRight.ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                
                set(obj.hLeftCurves, 'ColorOrder', obj.colorMat, 'NextPlot', 'replacechildren');
                obj.pIBC.CurLeft = plot(obj.hLeftCurves,[0 0 0; 0 0 0],[0 0 0; 0 0 0]);
                obj.pIBC.CurLeft(1).ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.CurLeft(2).ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.CurLeft(3).ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                
                set(obj.hRightCurves, 'ColorOrder', obj.colorMat, 'NextPlot', 'replacechildren');
                obj.pIBC.CurRight = plot(obj.hRightCurves,[0 0 0; 0 0 0],[0 0 0; 0 0 0]);
                obj.pIBC.CurRight(1).ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.CurRight(2).ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
                obj.pIBC.CurRight(3).ButtonDownFcn = {@(hObj,event) obj.gPlotNew(hObj,event,axisNr)};
            end
        end
        %% resetFct: Reset everything
        function resetFct(obj)
            % Reset everything
            obj.hTabs.hLabel1411.String = ' ';
            obj.hTabs.hLabel1412.String = 'CPU Time: ';
            obj.hTabs.hLabel1413.String = 'Number of subfunctions: ';
            obj.hTabs.hLabel2212.String = 'Left:';
            obj.hTabs.hLabel2213.String = 'Right:';
            obj.hTabs.hLabel2222.String = ' ';
            obj.hTabs.hLabel2223.String = ' ';
            obj.pClear([1 1 1 1]);
            obj.calc = IBCA();
        end
    end
    
end
    