 function BinPCA = BinPCA(xaxis,X,analyzedata)
%% BinPCA; An interactive graphical user interface to visualization of the Bin applied in the spectra loaded in GNAT
%
%   function Bin = BinPCA(xaxis,X,size_bucket,slackness)
%   --------------------------------INPUT-------------------------------------------------
%   xaxis       =   Vector with chemical shifts in ppm;
%   X           =   Matrix with row-vectors relatives to each NMR spectrum of each sample;
%   analyzedata =   See PrepareAnalyzeData in GNAT to more information.
%
%   --------------------------------OUTPUT------------------------------------------------
%   BinPCA = A model containing the size of each bin, number of variables
%   (columns of the new matrix), and a non-normalized-buckets matrix,
%
%   --------------------------------REFERENCE---------------------------------------------
%   Sousa, S. A. A., Magalhães, A., & Ferreira, M. M. C. (2013). Optimized bucketing for NMR 
%   spectra: Three case studies. Chemometrics and Intelligent Laboratory Systems, 122, 93–102. 
%   doi:10.1016/j.chemolab.2013.01.00
%
%   This is a part of the GNAT
%   Copyright  2024  <Mathias Nilsson>%
%   This program is free software; you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation; either version 2 of the License, or
%   (at your option) any later version.
%
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License along
%   with this program; if not, write to the Free Software Foundation, Inc.,
%   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%
%   Dr. Mathias Nilsson
%   School of Chemistry, University of Manchester,
%   Oxford Road, Manchester M13 9PL, UK
%   Telephone: +44 (0) 161 306 4465
%   Fax: +44 (0)161 275 4598
%
%   Hugo da Silva Rocha, Undergrad Studant
%   Institute of Chemistry, Univeristy of Brasilia,
%   POBOX 70910-900, Asa Norte - Brasília (DF), Brazil
%   rocha.hugo@aluno.unb.br

%% Setup the GUI CONTROLS
BinPCA.version='Bin application using NMR data for PCA v1.0';
hBinFigure = figure(...
    'Units','normalized',...
    'MenuBar','none',...
    'Name',BinPCA.version,... 
    'NumberTitle','Off',...
    'Toolbar','Figure',...
    'OuterPosition',[0.0 0.0 0.8 0.85],...
    'WindowButtonUpFcn',@mouseUp,...
    'WindowButtonDownFcn',@mouseDown,...
    'WindowButtonMotionFcn',@mouseMove,...
    'Tag','Bin',...
    'Color','w',...
    'Visible','off');
hAxes = axes('Parent',hBinFigure,...
    'Units','normalized',...
    'Tag','Axes',...
    'Position',[0.065 0.08 0.70 0.89],...
    'XLim',[0 10],...
    'Box','on');
BinPCA.non_normalized_buckets = [];
hLineToDrag = [];
BinPCA.ForceCloseFlag = 1;
BinPCA.hBinFigure = hBinFigure;
movegui(hBinFigure,'center')
mpath= mfilename('fullpath');
mname=mfilename;
mlength=numel(mname);
mpath((end-mlength+1):end)=[];
axpath=[mpath 'axes_small.png'];
if exist(axpath,'file')
    axim=imread(axpath);
    image(hAxes,axim);
end
%Clean up the Toolbar, removing things we don't need
    tmp = findall(hBinFigure,'ToolTipString','New Figure');
    set(tmp,'Visible','Off')
    tmp = findall(hBinFigure,'ToolTipString','Open File');
    set(tmp,'Visible','Off')
    tmp = findall(hBinFigure,'ToolTipString','Link Plot');
    set(tmp,'Visible','Off')
    tmp = findall(hBinFigure,'ToolTipString','Insert Legend');
    set(tmp,'Visible','Off')
    tmp = findall(hBinFigure,'ToolTipString','Rotate 3D');
    set(tmp,'Visible','Off')
    tmp = findall(hBinFigure,'ToolTipString','Brush/Select Data');
    set(tmp,'Visible','Off')
    tmp = findall(hBinFigure,'ToolTipString','Insert Colorbar');
    set(tmp,'Visible','Off')
guidata(hBinFigure,BinPCA);
%% Setup the BIN Painel
hPCAProcessPanel=uipanel(...
    'Parent',hBinFigure,...
    'Title','Process',...
    'TitlePosition','centertop',...
    'Units','Normalized',...
    'FontWeight','bold',...
    'Position',[0.795 0.01 0.2 0.97]);
%% Data Selection
hData_selection=uipanel(...
    'Parent',hPCAProcessPanel,...
    'Title','Data Selection',...
    'TitlePosition','centertop',...
    'Units','Normalized',...
    'FontWeight','bold',...
    'Position',[0.01 0.79 0.98 0.2]);
hPlotButton = uicontrol(...
    'Parent',hData_selection,...
    'Style','PushButton',...
    'String','Plot',...
    'Units','normalized',...
    'TooltipString','Plot current spectra',...
    'Position',[0.3 0.60 0.4 0.3],...
    'Callback', {@PlotButton_Callback});
%hTextStart=
uicontrol(...
    'Parent',hData_selection,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.1 0.16 0.45 0.3 ],...
    'horizontalalignment','left',...
    'String','Vertical Offset:' );
hEditStart=uicontrol(...
    'Parent',hData_selection,...
    'Style','edit',...
    'BackgroundColor','w',...
    'TooltipString','Space between spectra',...
    'String',10,...
    'Units','Normalized',...
    'Position',[0.1 0.12 0.3 0.2 ]); 

%hTextStart=
uicontrol(...
    'Parent',hData_selection,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.52 0.16 0.45 0.3 ],...
    'horizontalalignment','left',...
    'String','Color Scheme:' );
hColorSchem = uicontrol(...
    'Parent',hData_selection,...
    'Style','popupmenu',...
    'Units','Normalized',...
    'value',2,...
    'TooltipString','Integral method for each bin',...
    'FontWeight','normal',...
    'FontSize',8.5,...
    'HorizontalAlignment','center',...
    'BackgroundColor','w',...    
    'String','None|Color 1|Color 2|Color 3|Color 4|Color 5|Color 6|Color 7|Color 8|Color 9|Color 10|Color 11|Color 12',...
    'Position',[0.52,0.04,0.43,0.28],...
    'CallBack', {@PlotButton_Callback});
%% Bin Data Selection
hBin_selection=uipanel(...
    'Parent',hPCAProcessPanel,...
    'Title','Bin Limits Calculation',...
    'TitlePosition','centertop',...
    'Units','Normalized',...
    'FontWeight','bold',...
    'Position',[0.01 0.59 0.98 0.2]);
hCalculationButton = uicontrol(...
    'Parent',hBin_selection,...
    'Style','PushButton',...
    'String','Calculation',...
    'Units','normalized',...
    'TooltipString','Bin limits calculation using current parameters',...
    'Position',[0.1 0.60 0.4 0.3],...
    'Callback', {@CalculationBin_Callback});
hClearButton = uicontrol(...
    'Parent',hBin_selection,...
    'Style','PushButton',...
    'String','Clear',...
    'Units','normalized',...
    'TooltipString','Restore the initial data matrix',...
    'Position',[0.5 0.60 0.4 0.3],...
    'Callback', {@ClearButtonBin_Callback});

%hTextStep=
uicontrol(...
    'Parent',hBin_selection,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.06 0.33 0.3 0.19],...
    'horizontalalignment','left',...
    'String','Bucket:' );
hBucket=uicontrol(...
    'Parent',hBin_selection,...
    'Style','edit',...
    'BackgroundColor','w',...
    'TooltipString','Width range of bins',...
    'String',0.05,...
    'Units','Normalized',...
    'Position',[0.23 0.35 0.3 0.21]);
%hTextStart=
uicontrol(...
    'Parent',hBin_selection,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.55 0.35 0.3 0.19],...
    'horizontalalignment','left',...
    'String','ppm' );
%hTextStart=
uicontrol(...
    'Parent',hBin_selection,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.02 0.09 0.3 0.19],...
    'horizontalalignment','left',...
    'String','Slacknes:' );
hSlackness=uicontrol(...
    'Parent',hBin_selection,...
    'Style','edit',...
    'BackgroundColor','w',...
    'TooltipString','Limit of how far the boundary can move searching for the local minimum',...
    'String',50,...
    'Units','Normalized',...
    'Position',[0.23 0.09 0.3 0.21]);
%hTextStart=
uicontrol(...
    'Parent',hBin_selection,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.55 0.08 0.3 0.19],...
    'horizontalalignment','left',...
    'String','%' );

hNoiseCheck = uicontrol(...
    'Parent',hBin_selection,...
    'Style','Checkbox',...
    'value',0,...
    'String','Filter noise',...
    'Units','normalized',...
    'Position',[0.69 0.37 0.8 0.2],...
    'Callback',{ @Noise_Callback});
hThreshold=uicontrol(...
    'Parent',hBin_selection,...
    'Style','edit',...
    'BackgroundColor','w',...
    'TooltipString','Decrease this value to increase the sensitivity of the threshold - 0.05 is an standard value for a spectrum of 64k points',...
    'String',0.05,...
    'Units','Normalized',...
    'Enable','off',...
    'Position',[0.69 0.09 0.3 0.21],...
    'Callback',{ @CalculationBin_Callback});
%% Export Data Panel
OptionsExportPanel=uipanel(...
    'Parent',hPCAProcessPanel,...
    'Units','normalized',...            
    'Titleposition','centertop',...
    'Title','Export Data',...
    'FontWeight','bold',...
    'Position',[0.01 0.30 0.98 0.15],...
    'Visible','on');
hExportinfo = uicontrol(...
    'parent',OptionsExportPanel,...
    'style','PushButton',...
    'String','Export Bin Data',...
    'Units','Normalized',...
    'TooltipString','Export info of bin analysis to a .txt file',...
    'Position',[0.04 0.29 0.44 0.48],...
    'Callback',{@ExportBinInfo});    
hExportData = uicontrol(...
    'parent',OptionsExportPanel,...
    'style','PushButton',...
    'String','Export Bin Data',...
    'Units','Normalized',...
    'TooltipString','Export data of bin analysis to a .txt file',...
    'Position',[0.04 0.29 0.44 0.48],...
    'Callback',{@ExportBinData});  
hExportFigures = uicontrol(...
    'parent',OptionsExportPanel,...
    'style','PushButton',...
    'String','Export Figure',...
    'Units','Normalized',...
    'TooltipString','Export curent figures',...
    'Position',[0.52 0.29 0.44 0.48],...
    'Callback',{@ExportFigure});    
%% Normalization method
hMethods_Bin=uipanel(...
    'Parent',hPCAProcessPanel,...
    'Title','Method',...
    'TitlePosition','centertop',...
    'Units','Normalized',...
    'FontWeight','bold',...
    'Position',[0.01,0.45,0.98,0.14]);
%hTextStart=
uicontrol(...
    'Parent',hMethods_Bin,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.11,0.385,0.38,0.44],...
    'horizontalalignment','left',...
    'String','Sum Method:' );
hSumMethod = uicontrol(...
    'Parent',hMethods_Bin,...
    'Style','popupmenu',...
    'Units','Normalized',...
    'value',1,...
    'TooltipString','Integral method for each bin',...
    'FontWeight','normal',...
    'FontSize',8.5,...
    'HorizontalAlignment','center',...
    'BackgroundColor','w',...    
    'String','Sum|Average Sum|Center',...
    'Position',[0.51,0.63,0.43,0.28]);
%hTextStart=
uicontrol(...
    'Parent',hMethods_Bin,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.11,0.07,0.38,0.44],...
    'horizontalalignment','left',...
    'String','Normalization Method:');
hNormMethod=uicontrol(...
    'Parent',hMethods_Bin,...
    'Style','popupmenu',...
    'Units','Normalized',...
    'value',2,...
    'TooltipString','Normalization applied for each sample',...
    'FontWeight','normal',...
    'FontSize',8.5,...
    'HorizontalAlignment','center',...
    'BackgroundColor','w',...    
    'String','None|Sum|Largest Peak|Reference',...
    'Position',[0.51,0.24,0.43,0.28]);

%% Export Data 
hContinue=uipanel(...
    'Parent',hPCAProcessPanel,...
    'Title','Use current parameters',...
    'TitlePosition','centertop',...
    'Units','Normalized',...
    'FontWeight','bold',...
    'Position',[0.01 0.15 0.98 0.14]);
%hVisualizationButton = 
uicontrol(...
    'Parent',hContinue,...
    'Style','PushButton',...
    'String','Apply Binning',...
    'Units','normalized',...
    'TooltipString','Proceed with current parameters to the statistical analysis',...
    'Position',[0.26 0.29 0.50 0.48],...
    'Callback', {@CloseBin_Callback});
%% Tips for user
hTips=uipanel(...
    'Parent',hPCAProcessPanel,...
    'Title','Tips',...
    'TitlePosition','centertop',...
    'Units','Normalized',...
    'FontWeight','bold',...
    'Position',[0.01 0.0002 0.98 0.1451]);
%hTextStart=
uicontrol(...
    'Parent',hTips,...
    'Style','text',...
    'Units','Normalized',...
    'Position',[0.03 0.05 0.95 0.95],...
    'horizontalalignment','left',...
    'String','Before binning it is highly recommended to correct the spectrum phase, its baseline and the chemical shift of the signals.' ); 
%% Setup GUI
PlotButton_Callback()

%% Main Callbacks
    function PlotButton_Callback(source,eventdata)
        BinPCA = guidata(hBinFigure);
       
        % Checking if the user by acident deleted all samples using the prune function
        if isempty(X)
            uiwait(msgbox('Bin PCA: All spectral range has been removed. Return to the cut function to remake the cuts','Error','error','modal'));
            return
        end

        % Determining the vertical offset based on user input
        SPECTRA = X';
        analyzedata.np = size(SPECTRA,1);
        analyzedata.arraydim = size(SPECTRA,2);
        analyzedata.increments = 1:size(SPECTRA,2);
        VertOff=str2double(get(hEditStart,'String'));
        HorizOff=0;
        Horiz=round(HorizOff*analyzedata.np/100);        
        plotspec=nan(analyzedata.np+abs(Horiz)*analyzedata.arraydim,analyzedata.arraydim);            
        if Horiz<0
            for k=1:analyzedata.arraydim
                plotspec((k-1)*abs(Horiz)+1:analyzedata.np+(k-1)*abs(Horiz),k)=real(SPECTRA(:,analyzedata.increments(k)));
            end
        else
            for k=1:analyzedata.arraydim
                plotspec((k-1)*abs(Horiz)+1:analyzedata.np+(k-1)*abs(Horiz),analyzedata.arraydim-k+1)=...
                real(SPECTRA(:,analyzedata.increments(analyzedata.arraydim-k+1)));
            end
        end
        VertIncr=VertOff*max(max(plotspec))/100;
        for k=1:analyzedata.arraydim
            plotspec(:,k)=plotspec(:,k)+(k-1)*VertIncr;
        end
        
        % Determining the ppm scale for the displayed data
        tmp=isnan(plotspec(:,1));        
        start=find(tmp==0,1,'first');
        plotscale=plotspec(:,1);
        plotscale(start:(start+analyzedata.np-1))=xaxis;
        text(analyzedata.arraydim,min(min(real(SPECTRA(:,analyzedata.increments(k))))),num2str(analyzedata.increments(k)))       
        if any(isnan(plotscale(:)))
            plotscale=plotscale(~isnan(plotscale));
        end
        
        % Ploting the result
        plot(hAxes,plotscale,plotspec);
        set(gca,'Xdir','reverse');
        xlabel('Chemical shift','FontSize',10, 'FontWeight','bold');
        axis tight
        text(analyzedata.arraydim,min(min(real(SPECTRA(:,analyzedata.increments(k))))),num2str(analyzedata.increments(k))) 
        
        % Saving variables inside the BinPCA handle
        y = ylim;
        BinPCA.Ylimits=y;
        BinPCA.plotscale = plotscale;
        BinPCA.SPECTRA = plotspec;
        BinPCA.VertIncr = VertIncr;
        BinPCA.Xaxis = xlim;
        BinPCA.Yaxis = ylim;        
        guidata(hBinFigure,BinPCA); 

        % Get the initial set of default plot colors.
        numberOfDataSets = size(SPECTRA,2);
        numberOfDataSets = round(numberOfDataSets*2);
        hColorScheme = get(hColorSchem,'value');

        % Create a new colormap that will define the new default color order property.
        switch hColorScheme
	        case 1
		        newDefaultColors = [0 0 0];
	        case 2
		        newDefaultColors = [0    0.4470    0.7410
                                    0.8500    0.3250    0.0980
                                    0.9290    0.6940    0.1250
                                    0.4940    0.1840    0.5560
                                    0.4660    0.6740    0.1880
                                    0.3010    0.7450    0.9330
                                    0.6350    0.0780    0.1840];
            case 3
		        newDefaultColors = hsv(numberOfDataSets);
	        case 4
		        newDefaultColors = hot(numberOfDataSets);
	        case 5
		        newDefaultColors = cool(numberOfDataSets);
	        case 6
		        newDefaultColors = spring(numberOfDataSets);
	        case 7
		        newDefaultColors = summer(numberOfDataSets);
	        case 8
		        newDefaultColors = autumn(numberOfDataSets);
	        case 9
		        newDefaultColors = [0.83 0.14 0.14
                                   1.00 0.54 0.00
                                   0.47 0.25 0.80
                                   0.25 0.80 0.54];
	        case 10
		        newDefaultColors = [1.0 0.0 0.0
                                   0.0 0.4 0.0
                                   1.0 0.5 0.0];
	        case 11
		        newDefaultColors = pink(numberOfDataSets);
	        case 12
		        newDefaultColors = prism(numberOfDataSets);
	        case 13
		        newDefaultColors = colorcube(numberOfDataSets);
        end
 
        % Apply the new default colors to the current axes.
        BinPCA.newDefaultColors = newDefaultColors;
        set(gca, 'ColorOrder', BinPCA.newDefaultColors);


        
        % create a data tip to user identify the spectrum
        dcm_obj = datacursormode(hBinFigure);
%         datacursormode on
        set(dcm_obj,'UpdateFcn',@myupdatefcn) 

        txtHand = findall(hBinFigure, '-property', 'FontUnits'); 
        set(txtHand, 'FontUnits', 'normalized');
        if isfield(BinPCA,'scaledData')
            BinPCA = rmfield(BinPCA, 'scaledData');
        end
        set(hBinFigure,'Visible','on')
        guidata(hBinFigure,BinPCA); 
    end
    function output_txt = myupdatefcn(obj,event_obj)
        % Display the position of the data cursor
        % obj          Currently not used (empty)
        % event_obj    Handle to event object
        % output_txt   Data cursor text string (string or cell array of strings).
        
        old_data=guidata(obj);
        pos = get(event_obj,'Position');
        [row,col]=find(old_data.SPECTRA==(pos(2)));
        output_txt = {['X: ',num2str(pos(1),4)],['Sample Index: ',num2str(col)]};        
    end
    function CalculationBin_Callback(source,eventdata)
        BinPCA = guidata(hBinFigure);
        BinPCA.Bucket=str2double(get(hBucket,'string'));
        BinPCA.Slackness=str2double(get(hSlackness,'string'));
        BinPCA.threshold=str2double(get(hThreshold,'string'));
        
        check = get(hNoiseCheck,'Value');
        if check == 1
            SPECTRA = mean(X',2);
            baseline = detect_nmr_baseline_plot(BinPCA.plotscale, SPECTRA,BinPCA.threshold);
            filter = isnan(baseline);
            matrix = real(X(:,filter));
            ppm = xaxis(filter);
        else
            matrix = real(X);
            ppm = xaxis;
        end

        %Verify user inputs
        if isnan(BinPCA.Bucket) || isnan(BinPCA.Slackness) || isnan(BinPCA.threshold) 
            set(hBucket,'string',num2str(0.05))
            set(hSlackness,'string',num2str(50))
            set(hThreshold,'string',num2str(0.05))
            warndlg('Bin or Slackness must be a number','Warning');           
            return
        elseif hBucket <= 0
            set(hBucket,'string',num2str(0.05))
            warndlg('Bin size must be greater than 0','Warning');
            return
        end    
        if BinPCA.Slackness>100 || BinPCA.Slackness<0
           uiwait(msgbox('The slackness value must be between 0 and 100%.','Error','error','modal'));
           set(hSlackness,'String',num2str(50));
           return
        end

        % Optimized bucketing calculation
        % Filtering the X matrix based on the noise level

        [~,q]=size(real(matrix));   
        b = ppm(2)-ppm(1);        
        a = BinPCA.Bucket(1)./b;
        a = round(a);        
        l=BinPCA.Slackness(1)*0.01*a;
        l=round(l);
        R=mean(matrix);
        v=[];
        for t=1+a:a:q-a
            [~,I]=min(R(t-l:t+l));
            f=((t-(1+a))./a)+1;
            v(f)=I+a*(f-1)+(a-l);
        end   
        z = unique(v);
        v = [1 z q];
        A=[];
        for k=1:length(v)
            A(k)=ppm(v(k));
        end
        s=length(A);
        I_b=[A(1:(s-1))',A(2:s)'];
        I_b_metanalyst = A(1:(s-1));
        T=[];
        for k=1:(s-1)
            T(k)=A(k)-A(k+1);
            S_b=T';
        end

        v_points=[(1:(s-1))', [v(1:(s-1))]', [v(2:s)]'];
        v_points(end+1,:)=[s, v(1), v(end)];
        
        BinPCA.intervals = I_b;
        BinPCA.MetaBins = I_b_metanalyst;
        BinPCA.size = S_b;  
        BinPCA.xaxis = ppm;  
        BinPCA.index_intervals_ppm = v_points;

        guidata(hBinFigure,BinPCA);
        ClearButtonBin_Callback();
        PlotLimits_Callback()
    end
    function PlotLimits_Callback(source,eventdata)
        BinPCA = guidata(hBinFigure);
        % Setting the limits in the plot
        if isfield(BinPCA,'intervals')
            BinPCA.Axes.intervals = line(hAxes,[BinPCA.intervals(:,1) BinPCA.intervals(:,1)],[BinPCA.Ylimits(1) BinPCA.Ylimits(2)],'color',[0 0 0 0.25],'LineWidth', 0.9,'tag','I_b');
            set(BinPCA.Axes.intervals, 'ButtonDownFcn', {@LineSelected})
        end
        guidata(hBinFigure,BinPCA);
    end
    function ClearButtonBin_Callback(source,eventdata)
        BinPCA = guidata(hBinFigure);
        hLines=findobj(hBinFigure,'tag','I_b');
        delete(hLines);
        BinPCA.non_normalized_buckets = [];
        guidata(hBinFigure,BinPCA);
    end
    function CloseBin_Callback(source,eventdata)
        BinPCA = guidata(hBinFigure);     
        hLines=findobj(hBinFigure,'tag','I_b');
        if ~isfield(BinPCA,'intervals')
            uiwait(msgbox('Could not find Bin calculation for current data.','Error','error','modal'));
        elseif isempty(hLines)
            uiwait(msgbox('Could not find Bin calculation for current data.','Error','error','modal'));
        else
            BinPCA.ForceCloseFlag = 0;
            if isfield(BinPCA,'Axes') 
                if isfield(BinPCA.Axes,'intervals') 
                    Sum_method = get(hSumMethod,'value');
                    Norm_method = get(hNormMethod,'value');
                    vec = [];
                    for k=1:length(BinPCA.Axes.intervals)                
                        xVertLineCoord = get(BinPCA.Axes.intervals(k),'XData'); 
                        ary = abs(BinPCA.xaxis - xVertLineCoord(1));
                        [~,Idx] = min(ary);
                        vec = [vec Idx];
                    end
                    
                    [p,q]=size(real(X)); 
                    % Adding the last point of the ppm scale
                    vec = [vec q]; [v,I] = sort(vec);

                    % Finding the selected limits
                    a = cell2mat(get(BinPCA.Axes.intervals, 'color'));
                    idx = find(a(:,2)); 
                    for i = 1:size(idx,1)
                        selec(1,i) = find(idx(i)==I);
                    end
                    
                    % Integration based on method selected
                    Z = zeros(p,(size(v,2)-1));
                    for j = 1:p
                        for i = 1:(size(v,2)-1)
                            x_prov = X(j,v(i):v(i+1));
                            c = trapzeq(x_prov,Sum_method);
                            Z(j,i) = c;
                        end
                    end

                    % removing the intensity of the point wherre the bin limit was positionated     
                    vv = zeros(p,(length(v)));
                    for j = 1:p
                        vv(j,:) = X(j,v(:));
                    end

                    for tt = 2:(length(v)-1)
                        Z(:,tt) = Z(:,tt) - vv(:,tt);
                    end
                    ZNN=Z;       
                    m=size(Z,1);

                    % normalization based on the signal selected
                    % switch Norm_method
                    %     case 1 % None
                    %         % do nothing
                    %     case 2 % Sum
                    %         for k=1:m
                    %             Z(k,:)=Z(k,:)./(sum(Z(k,:)));
                    %         end
                    %     case 3 % Lagest Peak
                    %         for k=1:m
                    %             Z(k,:)=Z(k,:)./(max(Z(k,:)));
                    %         end
                    %     case 4 % Reference
                    %         if isempty(idx)
                    %             uiwait(msgbox('The limits for the reference signal for normalization were not determined (Use the right button for this)','Error','error','modal'));
                    %             return
                    %         end
                    %         for k=1:m
                    %             Z(k,:)=Z(k,:)./(sum(Z(k,selec(1):selec(end))));
                    %         end                            
                    % end

                    A=[];
                    for k=1:length(v)
                        A(k)=xaxis(v(k));
                    end
                    s=length(A);
                    I_b=[A(1:(s-1))',A(2:s)'];
                    I_b_metanalyst = A(1:(s-1));
                    T=[];
                    for k=1:(s-1)
                        T(k)=A(k)-A(k+1);
                        S_b=T';
                    end

                    v_points=[(1:(s-1))', [v(1:(s-1))]', [v(2:s)]'];
                    v_points(end+1,:)=[s, v(1), v(end)];
                    variables=1:size(ZNN,2);        
                    BinPCA.number_of_variables = variables;
                    BinPCA.intervals = I_b;
                    BinPCA.MetaBins = I_b_metanalyst;
                    BinPCA.non_normalized_buckets = ZNN;
                    BinPCA.normalized_buckets = Z;
                    BinPCA.size = S_b;  
                    BinPCA.limitsIntegration = idx;
                    BinPCA.index_intervals = vec;
                    BinPCA.index_intervals_ppm = v_points;
                    guidata(hBinFigure,BinPCA);
                    uiwait(msgbox('The Bin calculation was carried out successfully','Bin Calculation','help','modal'));
                end
            end
            
            guidata(hBinFigure,BinPCA);
        end

        % Integration method
        function q = trapzeq(y,method)
            switch method
                case 1 % Sum
                    n = length(y);
                    sums = y(1) + 2*sum(y(2:n-1)) + y(n);
                    q = sums/2;
                case 2 % Average Sum
                    n = length(y);
                    sums = y(1) + 2*sum(y(2:n-1)) + y(n);
                    q = sums/n;
                case 3 % Center
                    n = length(y);
                    sumC = round(n/2);
                    q = y(sumC);
            end                           
        end
    end  
    function LineSelected(ObjectH, EventData)
        BinPCA = guidata(hBinFigure);
        currentPoint   = get(hAxes,'CurrentPoint');
        xCurrentPoint  = currentPoint(2,1); 
        xVertLineCoord = get(ObjectH,'XData');
        selected = get(ObjectH, 'color');
        clickType = get(hBinFigure, 'SelectionType');

        % left click
        if strcmp(clickType, 'alt') 
            set(ObjectH, 'LineWidth', 2,'color',[0 1 0 ]);
            return
        elseif strcmp(clickType, 'alt') || selected(2) == 1
            set(ObjectH, 'LineWidth', 1,'color',[0 0 0 0.25]);
            return            
        end        
        % right click
        if strcmp(clickType, 'normal') 
            a = cell2mat(get(BinPCA.Axes.intervals, 'color'));
            idx = find(a(:,2));            
            set(BinPCA.Axes.intervals(BinPCA.Axes.intervals ~= ObjectH), 'LineWidth', 1,'color',[0 0 0 0.25]);            
            set(ObjectH, 'LineWidth', 2,'color','b');
            set(BinPCA.Axes.intervals(idx), 'LineWidth', 2,'color','g');
            set(hBinFigure, 'UserData', ObjectH)
        elseif strcmp(clickType, 'normal') || selected(3) == 1
            set(ObjectH, 'LineWidth', 1,'color',[0 0 0 0.25]);
            return
        end
        guidata(hBinFigure,BinPCA);
    end    
    function mouseDown(hObject,~)
        BinPCA = guidata(hBinFigure);
        % is the mouse down event within the axes?
        if IsCursorInControl(hObject, hAxes)
            if isfield(BinPCA,'Axes')        
                currentPoint   = get(hAxes,'CurrentPoint');
                xCurrentPoint  = currentPoint(2,1);
                for k=1:length(BinPCA.Axes.intervals)                
                    xVertLineCoord = get(BinPCA.Axes.intervals(k),'XData');    
                    if abs(xCurrentPoint - xVertLineCoord(1)) < 0.004
                        hLineToDrag = BinPCA.Axes.intervals(k);
                        break;
                    end
                end
            end
        end
        guidata(hBinFigure,BinPCA);
    end
    function mouseUp(~,~)
        hLineToDrag = [];
    end
    function mouseMove(hObject,~)
        % is the mouse down event within the axes?
        if ~isempty(hLineToDrag) && IsCursorInControl(hObject, hAxes)
            currentPoint = get(hAxes,'CurrentPoint');
            x = currentPoint(2,1);
            set(hLineToDrag, 'XData', [x x]);
        end
    end
    function Noise_Callback(source,eventdata)
        check = get(hNoiseCheck,'Value');
        if check == 1
            set(hThreshold,'Enable','on');
        else
            set(hThreshold,'Enable','off');
        end
    end
    function [status] = IsCursorInControl(hCursor, hControl)
        
        status = false;        
        % get the position of the mouse
        figCurrentPoint = get(hCursor, 'CurrentPoint');
        position      = get(hCursor, 'Position');
        xCursor       = figCurrentPoint(1,1)/position(1,3); % normalize
        yCursor       = figCurrentPoint(1,2)/position(1,4); % normalize

        % get the position of the axes within the GUI
        controlPos = get(hControl,'Position');
        minx    = controlPos(1);
        miny    = controlPos(2);
%         maxx    = minx + controlPos(3);
%         maxy    = miny + controlPos(4);
            
        % is the mouse down event within the axes?
%         if xCursor >= minx && xCursor <= maxx && yCursor >= miny && yCursor <= maxy
        if xCursor >= minx && yCursor >= miny
            status = true;
        end
    end
%% Export Panel Callbacks
    function ExportFigure (eventdata, handles)
        BinPCA = guidata(hBinFigure);
            Fig1 = figure(...
                'Units','normalized',...
                'Name',BinPCA.version,... 
                'NumberTitle','Off',...
                'Toolbar','Figure',...
                'OuterPosition',[0.0 0.0 0.4 0.6],...
                'Color','w',...
                'Visible','off');
            %Clean up the Menu, removing things we don't need
            allhandles = findall(Fig1);
            menuhandles = findobj(allhandles,'type','uimenu');
            edithandle = findobj(menuhandles,'tag','figMenuEdit');
            set(edithandle,'Visible','off');
            edithandle = findobj(menuhandles,'tag','figMenuView');
            set(edithandle,'Visible','off');            
            edithandle = findobj(menuhandles,'tag','figMenuDesktop');
            set(edithandle,'Visible','off');
            edithandle = findobj(menuhandles,'tag','figMenuWindow');
            set(edithandle,'Visible','off');    
            edithandle = findobj(menuhandles,'tag','figMenuHelp');
            set(edithandle,'Visible','off');            
            edithandle = findobj(menuhandles,'tag','figMenuFilePrintPreview');
            set(edithandle,'Visible','off');                    
            edithandle = findobj(menuhandles,'tag','figMenuFileSaveWorkspaceAs');
            set(edithandle,'Visible','off');             
            edithandle = findobj(menuhandles,'tag','figMenuFileImportData');
            set(edithandle,'Visible','off'); 
            edithandle = findobj(menuhandles,'tag','figMenuGenerateCode');
            set(edithandle,'Visible','off');             
            edithandle = findobj(menuhandles,'tag','figMenuFileSaveAs');
            set(edithandle,'Visible','off'); 
            edithandle = findobj(menuhandles,'tag','figMenuFileSave');
            set(edithandle,'Visible','off');             
            edithandle = findobj(menuhandles,'tag','figMenuFileClose');
            set(edithandle,'Visible','off'); 
            edithandle = findobj(menuhandles,'tag','figMenuOpen');
            set(edithandle,'Visible','off');             
            edithandle = findobj(menuhandles,'tag','figMenuUpdateFileNew');
            set(edithandle,'Visible','off');            
            %Clean up the Toolbar, removing things we don't need
            tmp = findall(Fig1,'ToolTipString','Save Figure');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Print Figure');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','New Figure');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Open File');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Link Plot');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Insert Legend');
            set(tmp,'Visible','Off')
            % tmp = findall(Fig1,'ToolTipString','Edit Plot');
            % set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Rotate 3D');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Brush/Select Data');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Hide Plot Tools');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Data Cursor');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Insert Colorbar');
            set(tmp,'Visible','Off')
            tmp = findall(Fig1,'ToolTipString','Show Plot Tools and Dock Figure');
            set(tmp,'Visible','Off')
            movegui(Fig1,'center')
            axch = copyobj(hAxes,Fig1);
            set(axch,'Position',[0.12 0.12 .8 .86]);
            xlabel('Chemical shift','FontSize',10, 'FontWeight','bold');                 
            set(Fig1,'Visible','on');        
        guidata(hBinFigure,BinPCA);
    end
     function ExportBinInfo(eventdata, handles)
        if ~isfield(BinPCA,'intervals')
            msgbox('No bin limits were found','Error','error','modal')
            return
        elseif ~isfield(BinPCA,'normalized_buckets')
            msgbox('Press "Apply Bin" to save limits before exporting','Error','error','modal')
            return
        end

        [filename, pathname] = uiputfile({'*.txt';'*.xlsx'},'Save Bin analysis','Data.txt');
        if isnumeric(filename) || isnumeric(pathname)
            if filename==0 || pathname==0
                return
            end
        end
        x=[pathname filename];
        if isfield(BinPCA,'intervals_edited')
            export2 = [BinPCA.intervals_edited,BinPCA.size]; 
        else
            export2 = [BinPCA.intervals,BinPCA.size]; 
        end
        statfil=fopen(x, 'wt');
        %print out PCA data to file
        fprintf(statfil,'%-s\t\t\t %s \n','Pre-processing:','Binning');
        fprintf(statfil,'%-s\t\t\t %.3f  \n','Bucket: ', BinPCA.Bucket);
        fprintf(statfil,'%-s\t\t\t %.3f  \n','Slackness: ', BinPCA.Slackness);
        fprintf(statfil,'%-s\t\t %.0f  \n','Number of variables: ', size(BinPCA.normalized_buckets,2));        
        fprintf(statfil,'%-s \n','Intervals for each bin');
        fprintf(statfil,'%-s\t \n','=======================================================================');
        fprintf(statfil,'%-s\t\t\t %-s\t \n','Beggining/End (ppm):','Size: ');
        fprintf(statfil,'%-s\t \n','=======================================================================');
        fprintf(statfil,'%.4f\t %.4f\t\t %.4f\t\n',export2.'); 
        fprintf(statfil,'%-s\t \n','=======================================================================');
        fprintf(statfil,'%-s\t \n','Normalized buckets');
        fprintf(statfil,'%-s\t \n','======================================================================='); 
        F = [repmat(' %2.4e',1,size(BinPCA.normalized_buckets,1)),'\n'];
        fprintf(statfil,F,BinPCA.non_normalized_buckets); 
        fclose(statfil);
    end 
     function ExportBinData(eventdata, handles)
        if ~isfield(BinPCA,'intervals')
            msgbox('No bin limits were found','Error','error','modal')
            return
        elseif ~isfield(BinPCA,'normalized_buckets')
            msgbox('Press "Apply Bin" to save limits before exporting','Error','error','modal')
            return
        end

        list = {'Raw bins';'Normalized bins';'Normalized bins - R';'Bin limits - Bruker'};
        [indx,~] = listdlg('PromptString',{'Select a file'},'ListString',list);

        [filename, pathname, idx] = uiputfile({'*.txt';'*.xlsx'},'Save Bin analysis','Data.txt');
        if isnumeric(filename) || isnumeric(pathname)
            if filename==0 || pathname==0
                return
            end
        end

        % Info to be exported
        x=[pathname filename];
        line = '=======================================================================';

        % Constructing the export data matrix
        data = {};

        % Raw bins
        if find(indx==1)>0
            if isempty(data)
                data = {[BinPCA.intervals(:,1) BinPCA.non_normalized_buckets']};
            end            
        end
        % Normalized bins
        if find(indx==2)>0
            if isempty(data)
                data = {[BinPCA.intervals(:,1) BinPCA.non_normalized_buckets']};
            else
                data = [data,[BinPCA.intervals(:,1) BinPCA.normalized_buckets']];
            end  
        end
        % Normalized bins - R
        if find(indx==3)>0
            if isempty(data)
                data = {ExportBinOxford(BinPCA.non_normalized_buckets,idx,BinPCA.intervals)};
            else
                data = [data,{ExportBinOxford(BinPCA.non_normalized_buckets,idx,BinPCA.intervals)}];
            end   
        end
        % Bin limits - Bruker
        if find(indx==4)>0
            if isempty(data)
                data = {ExportBinBruker(BinPCA.intervals,idx)};
            else
                data = [data,{ExportBinBruker(BinPCA.intervals,idx)}];
            end  
        end

        switch  idx
            case 1 
                statfil=fopen(x, 'wt');
                % Special case for bin limits
                list_str = string(list);
                limits = ExportBinBruker(BinPCA.intervals,idx);
                if find(indx==3)>0
                    indx(indx==3) = [];
                    for i=1:size(indx,2)
                        temp = cell2mat(data(indx(i)));
                        fprintf(statfil,'%-s \n',line);
                        fprintf(statfil,'%-s\t \n',list_str(indx(i),:));
                        fprintf(statfil,'%-s \n',line);
                        fprintf(statfil,[repmat('%2.2f\t', 1, size(temp, 2)) '\n'], temp');
                        fprintf(statfil,'%-s \n',line);
                    end   
                    fprintf(statfil,[repmat('%-s\t', 1, size(limits, 2)) '\n'], limits');
                else
                    for i=1:size(indx,2)
                        temp = cell2mat(data(indx(i)));
                        fprintf(statfil,'%-s \n',line);
                        fprintf(statfil,'%-s\t \n',list_str(indx(i),:));
                        fprintf(statfil,'%-s \n',line);
                        fprintf(statfil,[repmat('%2.2f\t', 1, size(temp, 2)) '\n'], temp');
                    end  
                end
                fclose(statfil);
            case 2
                h=waitbar(0,'Writing the data to the Excel file');
                list_str = string(list);
                for i=1:size(indx,2)
                    h=waitbar(i/size(indx,2));
                    % creating the Excel file
                    try
                        temp = table(cell2mat(data(i)));
                        writetable(temp,x,'Sheet',list_str(indx(i),:),"WriteVariableNames",false);
                    catch ME
                        temp = table(data(i));
                        writematrix(temp.Var1{1,1},x,'Sheet',list_str(indx(i),:));
                    end
                end
                close(h)
        end
    end 
 
%% Keyboard shortcuts Callbacks
    set(hBinFigure,'WindowKeyPressFcn',@keyPressLinesCallback);
    set(hBinFigure,'WindowScrollWheelFcn',@ScrollIntensityCallback);
    set(hEditStart, 'KeyPressFcn', @keyPressCallback);
    set(hBucket, 'KeyPressFcn', @keyPressCallback);
    set(hSlackness, 'KeyPressFcn', @keyPressCallback); 
    set(hCalculationButton, 'KeyPressFcn', @keyPressCallback); 
    % Modifier List:
    %  'shift'
    %  'control'
    %  'function'
    %  'alternate'

    function keyPressLinesCallback(source,eventdata)
        BinPCA = guidata(hBinFigure);
        % find the current object (axis, edit box, popmenu...)
        if strcmpi(eventdata.Key,'delete')
            delete(get(hBinFigure, 'UserData'));
            set(hBinFigure, 'UserData', [])
        end
        % for some reason, after deleting, the array of limits is flipped
        % again, here I correct this
        BinPCA.Axes.intervals=findobj(hBinFigure,'tag','I_b');
        BinPCA.Axes.intervals = flip(BinPCA.Axes.intervals);
        BinPCA.intervals_edited = BinPCA.Axes.intervals;
        guidata(hBinFigure,BinPCA);
     end
    function keyPressCallback(source,eventdata) 
          % find the current object (axis, edit box, popmenu...)
          if gco == hEditStart
              % determine the key that was pressed
              keyPressed = eventdata.Key;
              if strcmpi(keyPressed,'return')
                  % the key that was pressed was the return so set focus to 
                  % the plot button
                  uicontrol(hPlotButton);
                  % invoke the callback for the plot button
                  PlotButton_Callback(hPlotButton,[]);
              end
          elseif gco == hBucket || gco == hSlackness
              keyPressed = eventdata.Key;
              if strcmpi(keyPressed,'return')
                  uicontrol(hCalculationButton);
                  CalculationBin_Callback(hCalculationButton,[]);          
              end
          elseif gco == hCalculationButton
              keyPressed = eventdata.Key;
              modifier = eventdata.Modifier;
              if strcmpi(keyPressed,'z') && strcmpi(modifier,'Control')
                  uicontrol(hClearButton);
                  ClearButtonBin_Callback(hClearButton,[]);              
              end                 
          end
    end
    function ScrollIntensityCallback(source,eventdata) 
        BinPCA = guidata(hBinFigure);
        limX = get(hAxes,'XLim');
        limY = get(hAxes,'YLim');
        if eventdata.VerticalScrollCount > 0 
            SPECTRA = real(X');
            tic
            if isfield(BinPCA,'scaledData')
                plotspec = PLUScustomScale(BinPCA.scaledData);
                BinPCA.scaledData = plotspec;
            else
                plotspec = PLUScustomScale(SPECTRA);
                BinPCA.scaledData = plotspec;
            end
            
            for k=1:analyzedata.arraydim
                plotspec(:,k)=plotspec(:,k)+(k-1)*BinPCA.VertIncr;
            end
            
            plotscale = BinPCA.plotscale;
            plot(hAxes,plotscale,plotspec);
            guidata(hBinFigure,BinPCA);

            ClearButtonBin_Callback();
            PlotLimits_Callback();
            
            set(gca, 'ColorOrder', BinPCA.newDefaultColors);
            set(gca,'Xdir','reverse');
            axis tight
            if BinPCA.Xaxis(1) < limX (1) || BinPCA.Xaxis(2) > limX (2) 
                xlim(limX)
            end
            if BinPCA.Yaxis(1) < limY (1) || BinPCA.Yaxis(2) > limY (2) 
                ylim(limY)
            else
                ylim(BinPCA.Yaxis)
            end
            toc
        elseif eventdata.VerticalScrollCount < 0 
            SPECTRA = real(X');
            tic
            if isfield(BinPCA,'scaledData')
                plotspec = MINUScustomScale(BinPCA.scaledData);
                BinPCA.scaledData = plotspec;
            else
                plotspec = MINUScustomScale(SPECTRA);
                BinPCA.scaledData = plotspec;
            end
            for k=1:analyzedata.arraydim
                plotspec(:,k)=plotspec(:,k)+(k-1)*BinPCA.VertIncr;
            end

            plotscale = BinPCA.plotscale;
            plot(plotscale,plotspec);
            guidata(hBinFigure,BinPCA);

            ClearButtonBin_Callback();
            PlotLimits_Callback();

            set(gca, 'ColorOrder', BinPCA.newDefaultColors);
            set(gca,'Xdir','reverse');
            axis tight
            if BinPCA.Xaxis(1) < limX (1) || BinPCA.Xaxis(2) > limX (2) 
                xlim(limX)
            end
            if BinPCA.Yaxis(1) < limY (1) || BinPCA.Yaxis(2) > limY (2) 
                ylim(limY)
            else
                ylim(BinPCA.Yaxis)
            end

            toc
        end
        guidata(hBinFigure,BinPCA);
    end
    function scaledNumbers = PLUScustomScale(inputNumbers)
        % Find the maximum absolute value in the inputNumbers
        maxAbsValue = max(abs(inputNumbers));
        minAbsValue = min(abs(inputNumbers));
    
        % Calculate the scaling factor based on the maximum absolute value
        scalingFactor = (exp(minAbsValue ./ maxAbsValue))/2.2;
    
        % Scale the inputNumbers using the scaling factor
        scaledNumbers = inputNumbers .* scalingFactor;
    end
    function scaledNumbers = MINUScustomScale(inputNumbers)
        % Find the maximum absolute value in the inputNumbers
        maxAbsValue = max(abs(inputNumbers));
        minAbsValue = min(abs(inputNumbers));
    
        % Calculate the scaling factor based on the maximum absolute value
        scalingFactor = exp(minAbsValue ./ maxAbsValue)*2.2;
    
        % Scale the inputNumbers using the scaling factor
        scaledNumbers = inputNumbers .* scalingFactor;
    end

    function zoomOrientation(axh,state)


% ----------------------------------------------------------------------------------------
%
%   The function zoomOrientation reduces current axes width and height and adds
%   two new axes, xOrientation and yOrientation plus xSlider and ySlider.
%
%   You can either use the mouse wheel to zoom in/out or the mouse buttons left-click
%   for zoom in and right-click for zoom out or the keys arrow up/down.
%
%   Supports the following Line Plots: plot, semilogx, semilogy, loglog 
%
% Function Inputs:
%   axh = axes handle
%   state = on/off/update
%
% Examples to use zoomOrientation:
%   figure(1);
%   plot(1:100,randn(1,100))
%   zoomOrientation(gca,'on')
%
%   if zoomOrientation is already on and the plot data of the main axes has changed, 
%   update zoomOrientation with
%   zoomOrientation(gca,'update')
%
%   close function and restore original axes settings
%   zoomOrientation(gca,'off')
%
% Known issues:
%   If axes parent is a figure or uipanel, zooming on xOrientation and yOrientation
%   axes is still possible, although their properties HitTest and PickableParts are
%   set to off/none. Works fine with a uitab parent object!
%
% Written by:
%   Sebastian Roehn - sebastian.roehn(at)gmx.de
%   developed and tested with Matlab R2014b
%   Inspired by Yair Altman's scrollplot function on Mathworks File Exchange
%   http://www.mathworks.com/matlabcentral/fileexchange/14984-scrollplot-scrollable-x-y-axes
%
% Version:
%   1.1 beta
%
% Changelog:
%   v1.1 - disabled initial zoom factor to prevent a warning in uimode.fireActionPostCallback
%        - bugfix if function was called with state off or update but the function 
%          was not started before. In this case function returns with NO message.
%        - use plot data only whose visibility is set to on. If no line handle
%          is visible, the function makes the last added plot data visible
%
% ----------------------------------------------------------------------------------------

    % check argin(1) for valid axes
    if strcmp(get(axh,'Type'),'axes') && ~ishghandle(axh) 
        errordlg('Function input 1 is not a valid axes object','zoomOrientation') 
        return % end function
    end
       
    % state = on/update/off
    switch lower(state)
        case 'on'
            %% --- starts zoomOrientation -------------------------------------
            % current figure
            handles.hfig = gcf;           
            % get actual axes unit
            handles.axhUnit = get(axh,'Units');
            % set axes units to normalized
            set(axh,'Units','normalized')
            % get necassary axes properties
            % axes parent
            handles.axhParent = get(axh,'Parent');
            % axes position
            handles.axhPos = get(axh,'Position');
            % lin or log scaling x axis
            xscale = get(axh,'XScale');
            % lin or log scaling y axis
            yscale = get(axh,'YScale');            
            % current xlim and ylim of main axes
            handles.limX = get(axh,'XLim');
            handles.limY = get(axh,'YLim');
            % save limts to restore when function state is off
            handles.axisBackup = [handles.limX handles.limY];
            % axes BackgroundColor
            BgColor = get(axh,'Color');
            % tickcolors
            xtickColor = get(axh,'XColor');
            ytickColor = get(axh,'YColor');
            % get chart line handle
            hLine = get(axh,'children');
            % get plot data color(s)
            LineColor = get(hLine,'Color');            
            % extract data from plot
            y = get(hLine,'Ydata');
            x = get(hLine,'Xdata');      
            
            % Use only plot data whose visibility is set to on
            if length(hLine) > 1
                visarg = strcmp(get(hLine,'Visible'),'off');
                if sum(visarg) == length(hLine)
                   % no line handle visible, make last added data visible
                   set(hLine(1),'Visible','on')
                   % get new xlim and ylim of main axes
                   handles.limX = get(axh,'XLim');
                   handles.limY = get(axh,'YLim');
                   
                   for i=2:length(hLine)
                        if visarg(i) == 1
                            % exclude data/colors
                            y{i} = [];
                            x{i} = [];
                            LineColor{i} = [];
                        end
                    end
                else
                    for i=1:length(hLine)
                        if visarg(i) == 1
                            % exclude data/colors
                            y{i} = [];
                            x{i} = [];
                            LineColor{i} = [];
                        end
                    end
                end
                
                % delete empty cells
                x = x(~cellfun('isempty',x));
                y = y(~cellfun('isempty',y));
                LineColor = LineColor(~cellfun('isempty',LineColor));
                
            else            
                % in case there is only one trace in the plot 
                % => change XData and YData into cell            
                tmp = x;
                x = cell(1);
                x{1} = tmp;
                tmp = y;
                y = cell(1);
                y{1} = tmp;
                tmp = LineColor;
                LineColor = cell(1);
                LineColor{1} = tmp;
                
                if strcmp(get(hLine,'Visible'),'off')
                    % line handle is not visible
                    set(hLine,'Visible','on')
                    % get new xlim and ylim of main axes
                    handles.limX = get(axh,'XLim');
                    handles.limY = get(axh,'YLim');
                end
            end       
            
            maxXLim = NaN;
            maxYLim = NaN;
            minXLim = NaN;
            minYLim = NaN;
            % get axis limits of plot data and compare to current axes limits
            for i=1:length(y)
                maxXLim = max([maxXLim handles.limX(2) max(x{i})]);
                minXLim = min([minXLim handles.limX(1) min(x{i})]);
                maxYLim = max([maxYLim handles.limY(2) max(y{i})]);
                minYLim = min([minYLim handles.limY(1) min(y{i})]);
            end
            
            % save limits
            handles.dataLim = [minXLim maxXLim minYLim maxYLim];     
            
            zoom(handles.hfig,'reset')
            % get handle
            handles.hZoom = zoom(handles.hfig);            
            % define zoom callback for mouse gestures
            set(handles.hZoom,'Enable','on','RightClickAction','InverseZoom','ActionPostCallback',@MouseAction)

            % reduces main axes width and height
            newAxesPos = handles.axhPos .* [1, 1, 0.85, 0.70] + [0, 0.30*handles.axhPos(4), 0, 0];
            set(axh,'Position',newAxesPos); 
            
            % create xSlider
            handles.xSlider = uicontrol('Style', 'slider',...
                                'Units','normalized',...
                                'Parent',handles.axhParent,...
                                'Min',minXLim,'Max',maxXLim-diff(handles.limX),'Value',handles.limX(1),...
                                'Position', [newAxesPos(1), newAxesPos(2)-0.15, newAxesPos(3), 0.04],...
                                'Tag','xSlider',...
                                'TooltipString','Use slider to move in x direction',...
                                'Callback',@xSlider_Callback);
                            
            % create ySlider
            handles.ySlider = uicontrol('Style', 'slider',...
                                'Units','normalized',...
                                'Parent',handles.axhParent,...
                                'Min',minYLim,'Max',maxYLim-diff(handles.limY),'Value',handles.limY(1),...
                                'Position', [newAxesPos(1)+newAxesPos(3)+0.026, newAxesPos(2), 0.03, newAxesPos(4)],...
                                'Tag','ySlider',...
                                'TooltipString','Use slider to move in y direction',...
                                'Callback',@ySlider_Callback);
                            
            % if axes is in full view, disable sliders
            if minXLim == handles.limX(1) && maxXLim == handles.limX(2) && minYLim == handles.limY(1) && maxYLim == handles.limY(2)
                set(handles.xSlider,'Enable','off')
                set(handles.ySlider,'Enable','off')
            end
            
            % create x orientation axes
            xWinPos = handles.axhPos .* [1, 0.63, 0.85, 0.15];
            handles.xWin = axes('Units','normalized',...
                               'Parent',handles.axhParent,...
                               'Position',xWinPos,...
                               'Color',BgColor,...
                               'XColor',xtickColor,...
                               'YColor','none',...
                               'NextPlot','replacechildren',...
                               'Clipping','on',...
                               'Box','off',...
                               'HitTest','off',...
                               'PickableParts','none',...
                               'XScale',xscale,...
                               'FontSize',7,...
                               'Tag','xWin');

            % create y orientation axes
            yWinPos = [newAxesPos(1)+newAxesPos(3)+0.06, newAxesPos(2), newAxesPos(3)*0.15, newAxesPos(4)];
            handles.yWin = axes('Units','normalized',...
                               'Parent',handles.axhParent,...
                               'Position',yWinPos,...
                               'YAxisLocation','right',...
                               'Color',BgColor,...
                               'XColor','none',...
                               'YColor',ytickColor,...
                               'NextPlot','replacechildren',...
                               'Clipping','on',...
                               'Box','off',...
                               'HitTest','off',...
                               'PickableParts','none',...
                               'yScale',yscale,...
                               'FontSize',7,...
                               'Tag','yWin');               
            
            
            for i=1:length(y)
                handles.hLineX(i) = plot(handles.xWin,x{i},y{i},'Color',LineColor{i,1});
                handles.hLineY(i) = plot(handles.yWin,x{i},y{i},'Color',LineColor{i,1});
                if i == 1 && length(y) > 1
                    % hold axes for multiple plots
                    hold(handles.xWin,'on')
                    hold(handles.yWin,'on')
                end
            end
            
            % adjust axes limits
            axis(handles.xWin,'tight')
            axis(handles.yWin,'tight') 
            
            % get x orientation axes limits
            ylimxWin = get(handles.xWin,'YLim');
            
            % create x zoom window
            set(handles.hfig,'CurrentAxes',handles.xWin)
            handles.hZoomXWin = rectangle('Position',[handles.limX(1),ylimxWin(1),diff(handles.limX),diff(ylimxWin)],...
                                          'LineWidth',2,...
                                          'LineStyle','-',...
                                          'EdgeColor','k',...
                                          'Clipping','on',...
                                          'HitTest','off',...
                                          'PickableParts','none');
                                             
            hold(handles.xWin,'off')
            
            % get y orientation axes limits
            xlimyWin = get(handles.yWin,'XLim');
            
            % create y zoom window
            set(handles.hfig,'CurrentAxes',handles.yWin)
            handles.hZoomYWin = rectangle('Position',[xlimyWin(1),handles.limY(1),diff(xlimyWin),diff(handles.limY)],...
                                          'LineWidth',2,...
                                          'LineStyle','-',...
                                          'EdgeColor','k',...
                                          'Clipping','on',...
                                          'HitTest','off',...
                                          'PickableParts','none');
            
            hold(handles.yWin,'off')          
            
            % back to main axes
            set(handles.hfig,'CurrentAxes',axh)
                        
            % save handles
            setappdata(axh,'zoomOrientation',handles)         
            
            % Wait until axes handles.yWin is deleted
            waitfor(handles.yWin)
            
        case 'update'
            %% --- update plot data -------------------------------------------
            
            % get saved handles
            handles = getappdata(axh,'zoomOrientation');
            if ~isempty(handles)
                % xlim and ylim
                handles.limX = get(axh,'XLim');
                handles.limY = get(axh,'YLim');          
                % save limts to restore when function state is off
                handles.axisBackup = [handles.limX handles.limY];

                % get chart line handle(s)
                hLine = get(axh,'children');
                % get plot data color(s) 
                LineColor = get(hLine,'Color');            
                % extract data from plot
                y = get(hLine,'Ydata');
                x = get(hLine,'Xdata');

                % in case there is only one trace in the plot => change XData and YData into cell
                if iscell(y)==0
                    tmp = x;
                    x = cell(1);
                    x{1} = tmp;
                    tmp = y;
                    y = cell(1);
                    y{1} = tmp;
                    tmp = LineColor;
                    LineColor = cell(1);
                    LineColor{1} = tmp;
                end

                % for SignalViewer only!
                for i=1:length(y)
                    % delete signal dummies
                    if sum(y{i}) == 10 && sum(x{i}) == 55
                        y{i} = [];
                        x{i} = [];
                        LineColor{i} = [];
                    end
                end

                % delete empty cells
                x = x(~cellfun('isempty',x));
                y = y(~cellfun('isempty',y));
                LineColor = LineColor(~cellfun('isempty',LineColor));

                maxXLim = NaN;
                maxYLim = NaN;
                minXLim = NaN;
                minYLim = NaN;
                % get axis limits of plot data and compare to current axis limits
                for i=1:length(y)
                    maxXLim = max([maxXLim handles.limX(2) max(x{i})]);
                    minXLim = min([minXLim handles.limX(1) min(x{i})]);
                    maxYLim = max([maxYLim handles.limY(2) max(y{i})]);
                    minYLim = min([minYLim handles.limY(1) min(y{i})]);
                end

                % save limits in handle
                handles.dataLim = [minXLim maxXLim minYLim maxYLim];

                % zoom reset to set new axis limits for full view
                zoom(handles.hfig,'reset');                

                if length(handles.hLineX) >= length(y)
                    if length(handles.hLineX) > length(y)
                        % delete not needed objects
                        handles.hLineX = handles.hLineX(1:length(y));
                        handles.hLineY = handles.hLineY(1:length(y));
                    end

                    for i=1:length(handles.hLineX)
                        % update plot data in x/yOrientation axes
                        set(handles.hLineX(i),'XData',x{i,1},'YData',y{i,1},'Color',LineColor{i,1})
                        set(handles.hLineY(i),'XData',x{i,1},'YData',y{i,1},'Color',LineColor{i,1})
                    end                

                    % adjust axes limits
                    axis(handles.xWin,'tight')
                    axis(handles.yWin,'tight')

                    % get x orientation y-axes limits
                    ylimxWin = get(handles.xWin,'YLim');

                    % update x zoom window (rectangle)       
                    set(handles.hZoomXWin,'Position',[handles.limX(1),ylimxWin(1),diff(handles.limX),diff(ylimxWin)]); 

                    % get y orientation x-axes limits
                    xlimyWin = get(handles.yWin,'XLim');

                    % update y zoom window (rectangle)        
                    pos = get(handles.hZoomYWin,'Position');    
                    set(handles.hZoomYWin,'Position',[xlimyWin(1),handles.limY(1),diff(xlimyWin),diff(handles.limY)]);

                else
                    % plot new, because number of signals in main axes has changed
                    % init graphic objects
                    handles.hLineX = gobjects(length(y),1);
                    handles.hLineY = gobjects(length(y),1);

                    % keep axes settings but replace axes children
                    set(handles.xWin,'NextPlot','replacechildren')
                    set(handles.yWin,'NextPlot','replacechildren')

                    % plot new
                    for i=1:length(y)
                        handles.hLineX(i) = plot(handles.xWin,x{i},y{i},'Color',LineColor{i,1});
                        handles.hLineY(i) = plot(handles.yWin,x{i},y{i},'Color',LineColor{i,1});
                        % hold on after first plot to delete previous axes children
                        if i == 1 && length(y) > 1
                            % hold axes for multiple plots
                            hold(handles.xWin,'on')
                            hold(handles.yWin,'on')
                        end
                    end

                    % adjust axes limits
                    axis(handles.xWin,'tight')
                    axis(handles.yWin,'tight') 

                    % get x orientation y-axes limits
                    ylimxWin = get(handles.xWin,'YLim');

                    % create x zoom window
                    set(handles.hfig,'CurrentAxes',handles.xWin)
                    handles.hZoomXWin = rectangle('Position',[handles.limX(1),ylimxWin(1),diff(handles.limX),diff(ylimxWin)],...
                                                  'LineWidth',2,...
                                                  'LineStyle','-',...
                                                  'EdgeColor','k',...
                                                  'Clipping','on',...
                                                  'HitTest','off',...
                                                  'PickableParts','none');

                    hold(handles.xWin,'off')

                    % get y orientation x-axes limits
                    xlimyWin = get(handles.yWin,'XLim');

                    % create y zoom window
                    set(handles.hfig,'CurrentAxes',handles.yWin)
                    handles.hZoomYWin = rectangle('Position',[xlimyWin(1),handles.limY(1),diff(xlimyWin),diff(handles.limY)],...
                                                  'LineWidth',2,...
                                                  'LineStyle','-',...
                                                  'EdgeColor','k',...
                                                  'Clipping','on',...
                                                  'HitTest','off',...
                                                  'PickableParts','none');

                    hold(handles.yWin,'off')          

                    % back to main axes
                    set(handles.hfig,'CurrentAxes',axh)
                end          

                % update sliders        
                set(handles.xSlider,'Min',minXLim,...
                                    'Max',handles.dataLim(2) - diff(handles.limX),...
                                    'Value',handles.limX(1),...
                                    'Enable','off')

                set(handles.ySlider,'Min',minYLim,...
                                    'Max',handles.dataLim(4) - diff(handles.limY),...
                                    'Value',handles.limY(1),...
                                    'Enable','off')          

                % save handles
                setappdata(axh,'zoomOrientation',handles)
            else
                % update impossible because function was off
                return
            end
            
        case 'off'
            %% --- close zoomOrientation and restore original axes ------------
            handles = getappdata(axh,'zoomOrientation');
            if ~isempty(handles)
                % remove axes and sliders
                delete(handles.xSlider)
                delete(handles.ySlider)
                delete(handles.xWin)
                delete(handles.yWin)
                % restore zoom settings
                zoom(handles.hfig,'reset');
                set(handles.hZoom,'ActionPostCallback','')
                delete(handles.hZoom)
                % resizes main axes and restore unit property
                set(axh,'Position',handles.axhPos,'Units',handles.axhUnit)
                % restore original limits
                axis(axh,handles.axisBackup)
                % remove appdata
                rmappdata(axh,'zoomOrientation')
            else
                % function is already off
                return
            end
            
        otherwise
            %% error argin(2)
            errordlg('Function input 2 - unknown state','zoomOrientation') 
            return % end function
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %                                                                         %
    %   Callbacks                                                             %
    %                                                                         %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    % x slider moved
    function xSlider_Callback(hObject,~)
        value = get(hObject,'Value');
        % get saved handles
        handles = getappdata(axh,'zoomOrientation');
        pos = get(handles.hZoomXWin,'Position');
        % update x zoom window (rectangle) 
        set(handles.hZoomXWin,'Position',[value pos(2) pos(3) pos(4)]);
        % update main axes x-limits
        set(axh,'XLim',handles.limX - diff([value handles.limX(1)]))
    end    
    % y slider moved
    function ySlider_Callback(hObject,~)
        value = get(hObject,'Value');
        % get saved handles
        handles = getappdata(axh,'zoomOrientation');
        pos = get(handles.hZoomYWin,'Position');
        % update y zoom window (rectangle)  
        set(handles.hZoomYWin,'Position',[pos(1) value pos(3) pos(4)]);
        % update main axes y-limits
        set(axh,'YLim',handles.limY - diff([value handles.limY(1)]))        
    end
    % ActionPostCallback for mouse clicks
    function MouseAction(~,~)
        % get saved handles
        handles = getappdata(axh,'zoomOrientation');
        % get new limits
        handles.limX = get(axh,'XLim');
        handles.limY = get(axh,'YLim');      
        
        % check if main axes is in full view, true = disable slider
        if handles.limX(1) <= get(handles.xSlider,'Min') && handles.limX(1) >= (handles.dataLim(2) - diff(handles.limX))
            set(handles.xSlider,'Enable','off')
            set(handles.ySlider,'Enable','off')
        else
            set(handles.xSlider,'Enable','on')
            set(handles.ySlider,'Enable','on')
            % update sliders        
            set(handles.xSlider,'Max',handles.dataLim(2) - diff(handles.limX),...
                                'Value',handles.limX(1))
                            
            set(handles.ySlider,'Max',handles.dataLim(4) - diff(handles.limY),...
                                'Value',handles.limY(1))            
        end    
        
        % update x zoom window (rectangle)       
        pos = get(handles.hZoomXWin,'Position');
        set(handles.hZoomXWin,'Position',[handles.limX(1),pos(2),diff(handles.limX),pos(4)]);                                             
        % update y zoom window (rectangle)        
        pos = get(handles.hZoomYWin,'Position');    
        set(handles.hZoomYWin,'Position',[pos(1),handles.limY(1),pos(3),diff(handles.limY)]);
        
        % save handles
        setappdata(axh,'zoomOrientation',handles)
    end    
end
end