function obj = modelModifyRegionAnalysis(obj,varargin)
	% Remove PCAs in a particular region or exclude from preprocessing, etc.
	% Biafra Ahanonu
	% started: 2015.09.14 14:46:02
	% inputs
		%
	% outputs
		%

	% changelog
		% 2017.01.14 [20:06:04] - support switched from [nSignals x y] to [x y nSignals]
		% 2021.06.18 [21:41:07] - added modelVarsFromFilesCheck() to check and load signals if user hasn't already.
		% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
		% 2022.02.28 [13:01:20] - Fix modelVarsFromFilesCheck and modelVarsFromFiles recursion edge case.
		% 2022.08.26 [15:00:28] - Misc code standard updates.
	% TODO
		%

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	%========================
	options.defaultOption = 'loadPreviousSelections';

	% get options
	options = getOptions(options,varargin);
	%========================

	if obj.guiEnabled==1
		scnsize = get(0,'ScreenSize');
		[fileIdxArray, idNumIdxArray, nFilesToAnalyze, nFiles] = obj.getAnalysisSubsetsToAnalyze();

		% [fileIdxArray, ok] = listdlg('ListString',obj.fileIDNameArray,'ListSize',[scnsize(3)*0.2 scnsize(4)*0.25],'Name','which folders to analyze?');
		scnsize = get(0,'ScreenSize');
		dlgSize = [scnsize(3)*0.7 scnsize(4)*0.8];
		usrIdxChoiceStr = {'selectRegions','modifyExistingRegions','runAlreadySelectedRegions','loadPreviousSelections'};
		[sel, ok] = listdlg('ListString',usrIdxChoiceStr,'ListSize',dlgSize);
		analysisToRun = usrIdxChoiceStr{sel};
	else
		if isempty(obj.foldersToAnalyze)
			fileIdxArray = 1:length(obj.fileIDNameArray);
		else
			fileIdxArray = obj.foldersToAnalyze;
		end
		analysisToRun = options.defaultOption;
	end
	nFolders = length(fileIdxArray);

	framesPerSecond = obj.FRAMES_PER_SECOND;
	for thisFileNumIdx = 1:length(fileIdxArray)
		try
			fileNum = fileIdxArray(thisFileNumIdx);
			obj.fileNum = fileNum;
			display(repmat('=',1,21))
			display([num2str(fileNum) '/' num2str(nFolders) ': ' obj.fileIDNameArray{obj.fileNum}]);

			% obj.modelVarsFromFilesCheck(fileNum);

			if strcmp(analysisToRun,'loadPreviousSelections')
				regionFile = getFileList(obj.inputFolders{obj.fileNum},obj.regionModSaveStr);
				% if no file, skip
				if isempty(regionFile)
					disp('No region analysis to load!')
					continue;
				end
				regionFile = regionFile{1};
				fprintf('loading: %s',regionFile)
				loadFile = load(regionFile,'roipolyRegion','roipolyCoords');
				obj.analysisROIArray{obj.fileNum} = loadFile.roipolyRegion;
				obj.validRegionModPoly{obj.fileNum} = loadFile.roipolyCoords;
				% NOT DONE!!!!!!!!
			end
			if strcmp(analysisToRun,'modifyExistingRegions')
				regionFile = getFileList(obj.inputFolders{obj.fileNum},obj.regionModSaveStr);
				% if no file, skip
				if isempty(regionFile)
					disp('No region analysis to load!')
					continue;
				end
				loadFile = load(regionFile,'roipolyRegion','roipolyCoords');
				obj.analysisROIArray{obj.fileNum} = loadFile.roipolyRegion;
				obj.validRegionModPoly{obj.fileNum} = loadFile.roipolyCoords;
			end
			[inputSignals, inputImages, signalPeaks, signalPeaksArray] = modelGetSignalsImages(obj,'returnType','raw');
			% inputImages = thresholdImages(inputImages,'binary',0)/3;
			% [inputSignals inputImages signalPeaks signalPeaksArray] = modelGetSignalsImages(obj);

			% display cellmap and ask user to select a region
			[~, ~] = openFigure(obj.figNoAll, '');
			clf
			colormap gray
			subX = 3; subY = 3;
			if sum(strcmp(analysisToRun,{'selectRegions','modifyExistingRegions','runAlreadySelectedRegions','loadPreviousSelections'}))
				subplot(subX,subY,3);
					numPeakEvents = sum(signalPeaks,2);
					numPeakEvents = numPeakEvents/size(signalPeaks,2)*framesPerSecond;
					size(numPeakEvents)
					size(signalPeaks)
					[groupedImagesRates] = groupImagesByColor(inputImages,numPeakEvents);
					thisCellmap = createObjMap(groupedImagesRates);
					imagesc(thisCellmap);
					title('firing rate cell map')
					% colormap(obj.colormap);
				subplot(subX,subY,6);
					movieList = getFileList(obj.inputFolders{obj.fileNum}, 'concat');
					if ~isempty(movieList)
						movieFrame = loadMovieList(movieList{1},'convertToDouble',0,'frameList',1:2);
						movieFrame = squeeze(movieFrame(:,:,1));
						% movieFrameMean = mean(movieFrame(:));
						% imagesc(imadjust(movieFrame+cast(thisCellmap*10000,class(movieFrame))));
						E = normalizeVector(double(movieFrame),'normRange','zeroToOne')/2;
						% Comb = E;
						I = cast(normalizeVector(thisCellmap,'normRange','zeroToOne')*mean(movieFrame(:)),class(movieFrame));
						I = normalizeVector(double(I),'normRange','zeroToOne')/3;
						Comb(:,:,1) = E; % red
						Comb(:,:,2) = E+I; % green
						Comb(:,:,3) = E; % blue
						imagesc(Comb)
						% inputImagesThresholded = thresholdImages(inputImages,'binary',0)/3;
						% zeroMap = zeros(size(movieFrame));
						% oneMap = ones(size(movieFrame));
						% warning off
						% colorOverlay = imshow(cat(3, zeroMap, oneMap, zeroMap));
						% warning on
						% thisCellmap = createObjMap(inputImagesThresholded);
						% set(colorOverlay, 'AlphaData', thisCellmap);
					end
					title('movie frame + cell map')
				subplot(subX,subY,[1 2 4 5 7 8]);
					try obj.valid{obj.fileNum}.(obj.signalExtractionMethod).manual;check.manual=1; catch check.manual=0; end
					% if isfield(obj.valid,obj.signalExtractionMethod)&isfield(obj.valid.(obj.signalExtractionMethod),'manual')
					if check.manual==1
						[groupedImagesRates] = groupImagesByColor(inputImages,obj.valid{obj.fileNum}.(obj.signalExtractionMethod).manual);
					else
						[groupedImagesRates] = groupImagesByColor(inputImages,obj.validAuto{obj.fileNum});
					end
					thisCellmap = createObjMap(groupedImagesRates);
					% obj.rawImagesFiltered{obj.fileNum} = thisCellmap;
					imagesc(thisCellmap+0.1);
					% imagesc(obj.rawImagesFiltered{obj.fileNum}+0.1);
					title('draw selection on this image')
					% colormap(obj.colormap)
					ciapkg.overloaded.suptitle(strrep(obj.folderBaseSaveStr{obj.fileNum},'_',' | '))
					if ~sum(strcmp(analysisToRun,{'runAlreadySelectedRegions','loadPreviousSelections'}))
						% [polyCoords] = obj.validRegionModPoly{obj.fileNum};
						if strcmp(analysisToRun,'modifyExistingRegions')
							[polyCoords] = obj.validRegionModPoly{obj.fileNum};
							h = impoly(gca,polyCoords);
							h.wait
							polyCoords = h.getPosition;
							[obj.analysisROIArray{obj.fileNum}, xpoly, ypoly] = roipoly(thisCellmap+0.1,polyCoords(:,1),polyCoords(:,2));
						else
							[obj.analysisROIArray{obj.fileNum}, xpoly, ypoly] = roipoly;
						end
						obj.validRegionModPoly{obj.fileNum} = [xpoly ypoly];
					end
					% imagesc(obj.rawImagesFiltered{obj.fileNum}.*obj.analysisROIArray{obj.fileNum})
					imagesc(thisCellmap.*obj.analysisROIArray{obj.fileNum})
					drawnow;
			end
			if strcmp(analysisToRun,'runAlreadySelectedRegions')
				disp('Using previous ROI')
			end
			inputImagesROI = obj.analysisROIArray{obj.fileNum};
			inputImagesThres = thresholdImages(inputImages,'waitbarOn',1,'binary',1);
			% signalInROI = squeeze(nansum(nansum(bsxfun(@times,inputImages,permute(inputImages,[2 3 1])),1),2));
			disp('finding ROIs inside region...')
			signalInROI = squeeze(sum(sum(bsxfun(@times,inputImagesThres,inputImagesROI),1,'omitnan'),2,'omitnan'));

			% signalInROI = applyImagesToMovie(inputImages,permute(inputImages,[2 3 1]), 'alreadyThreshold',1);
			signalsToKeep = signalInROI~=0;

			if isempty(obj.validManual{obj.fileNum})
				validRegionModHere = obj.valid{obj.fileNum}.(obj.signalExtractionMethod).auto(:)&signalsToKeep(:);
			else
				validRegionModHere = obj.valid{obj.fileNum}.(obj.signalExtractionMethod).manual(:)&signalsToKeep(:);
				% if ~isempty(obj.validManual{obj.fileNum})
				% 	obj.validRegionMod{obj.fileNum} = obj.validAuto{obj.fileNum}(:)&signalsToKeep(:);
				% end
			end
			obj.validRegionMod{obj.fileNum} = validRegionModHere;
			obj.valid{obj.fileNum}.(obj.signalExtractionMethod).regionMod = validRegionModHere;

			[~, ~] = openFigure(obj.figNoAll, '');
				subplot(subX,subY,9);
				[filterImageGroups] = createObjMap(groupImagesByColor(inputImages,obj.validRegionMod{obj.fileNum}+1));
				% size(filterImageGroups)
				imagesc(filterImageGroups)
				title(sprintf('new cell map | %d cells',sum(validRegionModHere)))
				% colormap(obj.colormap)

			roipolyRegion = obj.analysisROIArray{obj.fileNum};
			roipolyCoords = obj.validRegionModPoly{obj.fileNum};

			thisDirSaveStr = [obj.inputFolders{obj.fileNum} filesep obj.folderBaseSaveStr{obj.fileNum}];
			savestring = [thisDirSaveStr obj.regionModSaveStr];
			display(['saving: ' savestring])
			save(savestring,'roipolyRegion','-v7.3','roipolyCoords');

			if sum(strcmp(analysisToRun,{'runAlreadySelectedRegions','loadPreviousSelections'}))
			else
				uiwait(ciapkg.overloaded.msgbox('press OK to move onto next folder','Success','modal'));
			end

		catch err
			display(repmat('@',1,7))
			disp(getReport(err,'extended','hyperlinks','on'));
			display(repmat('@',1,7))
		end
	end
end


function [ cvx_ver2, cvx_bld ] = cvx_version
cvx_ver = 1.21;
cvx_bld = '795';
cvx_bdate = '2010-05-31 08:26:19';
cvx_dbld = '795';
cvx_ddate = '2010-05-31 08:26:19';
if nargout == 0,
   fprintf( '\n' );
   fprintf( 'CVX version %g\n', cvx_ver );
   fprintf( '    Code: build %s, %s\n', cvx_bld, cvx_bdate );
   fprintf( '    Documentation: build %s, %s\n', cvx_dbld, cvx_ddate );
   if exist( 'OCTAVE_VERSION', 'var' ),
       fprintf( 'GNU Octave %s on %s\n', version, computer );
       fprintf( 'NOTE: Sorry, Octave support is not yet functional.\n' );
   else
       verd = ver('MATLAB');
       fprintf( 'MATLAB version %s %s on %s\n', verd.Version, verd.Release, computer );
   end
   fprintf( '\n' );
else
    cvx_ver2 = cvx_ver;
end


function install_eq_toolbox(arg)
%INSTALL_EQ_TOOLBOX Install using Toolbox Installer, with sensible defaults
%
%Syntax
% install_eq_toolbox(arg);
%
%Description
% INSTALL_EQ_TOOLBOX uses Toolbox Installer to install this toolbox. It must
% be run from the top level directory of this toolbox, which must contain the
% file info.ins.
%
% If INSTALL_EQ_TOOLBOX is called with no argument, it asks the user for
% confirmation before taking any action.
%
% INSTALL_EQ_TOOLBOX first checks if the file pathname.m exists on the current
% Matlab path and can be appended to.
% If so, it asks the user for confirmation and then simply calls the INSTALL
% function of Toolbox Installer to update the file pathname.m.
% If not, it tries to choose a sensible directory in which to create a new
% pathdef.m file, and suggests this to the user before proceeding.
%
% INSTALL_EQ_TOOLBOX DIRNAME, that is INSTALL_EQ_TOOLBOX('DIRNAME'),
% calls INSTALL('DIRNAME'), using DIRNAME as the directory for pathdef.m.
%
% INSTALL_EQ_TOOLBOX -, that is INSTALL_EQ_TOOLBOX('-'), calls INSTALL('-'),
% which displays a dialog box so the user can select the directory to be used
% for pathdef.m.
%
%Notes
% If it is necessary to create a new pathdef.m file, the directory which
% INSTALL_EQ_TOOLBOX suggests to the user for this file defaults to
% the directory which contains startup.m, if this exists.
%
% Otherwise INSTALL_EQ_TOOLBOX suggests the following directory to the user:
% For Unix systems including Linux and Mac: $HOME/matlab
% For Windows systems: [matlabroot '\toolbox\local'].
% If this directory does not exist, INSTALL_EQ_TOOLBOX tries to create it.
%
% For Toolbox Installer 2.2 by B. Rasmus Anthin, see
% Matlab Central File Exchange
% http://www.mathworks.com/matlabcentral/fileexchange/
% Home page of B. Rasmus Anthin
% http://www.etek.chalmers.se/~e8rasmus/
%
%Examples
% > install_eq_toolbox
% Info: Installer will create the file /c/leopardi/matlab/pathdef.m
% Proceed (y/n)? y
% Info: Installer has created the directory /c/leopardi/matlab
% Info: Installed.
%
% > install_eq_toolbox
% Info: Installer will update the file /c/leopardi/matlab/pathdef.m
% Proceed (y/n)? y
% Info: Installed.
%
% > install_eq_toolbox
% Info: Installer will update the file /c/leopardi/matlab/pathdef.m
% Info: Proceed (y/n)? n
%
% > cd ~
% > install_eq_toolbox
% ??? Error using ==> install_eq_toolbox
% Please run install_eq_toolbox from toolbox directory, eg. /c/leopardi/[...]
%
%See also
% UNINSTALL_EQ_TOOLBOX, PATHDEF, PATH, Toolbox Installer 2.2

% Copyright 2004-2005 Paul Leopardi for the University of New South Wales.
% $Revision 1.10 $ $Date 2005-06-01 $
% Partial overhaul
% o Restructure tests for existing pathdef.m file and guesses for pathdef_dir
% o Simplify tests after mkdir
% o On Windows, do not use [getenv('USERPROFILE') '\matlab'] directory
% o Clearly distinguish between info and error messages
% Documentation files renamed
% $Revision 1.00 $ $Date 2005-02-12 $
%
% For licensing, see COPYING.
% For references, see AUTHORS.
% For revision history, see CHANGELOG.

if (nargin == 1) && strcmp(arg,'-')
    install(arg);
    return;
end

command = mfilename;
[command_dir name ext ver] = fileparts(mfilename('fullpath'));
wd = pwd;
if ~strcmp(command_dir,wd)
    error(['Please run ' command ' from toolbox directory, eg. ' command_dir]);
end

if nargin
    %
    % If user has used an argument, just call install with this argument.
    %
    install(arg);
    return;
end

estr = 'Error: ';
wstr = 'Warning: ';
istr = 'Info: ';
was_not_installed_msg = 'Installer cannot continue.';
try_to_create_msg =     'Try to create the directory outside of Matlab';

current_pathdef_name = which('pathdef');
pathdef_exists = (exist('pathdef') == 2);
if pathdef_exists
    pathdef_name = current_pathdef_name;
    %
    % We have been told that pathdef.m exists.
    % Try to open it for updating
    %
    pathdefid = fopen(pathdef_name,'a');
    if pathdefid > 0
        %
        % File open for pathdef.m succeeded.
        % Ask whether to proceed with update.
        %
        fclose(pathdefid);
        reply = input([istr 'Installer will update the file ' strrep(pathdef_name,'\','\\') ...
            '\nProceed (y/n)? '],'s');
        if strcmpi(reply(1),'y')
            install;
        end
        return;
    elseif (exist('startup') == 2)
        %
        % File open for pathdef.m failed.
        % We have been told that startup.m exists.
        % Guess that we should use pathdef.m in the same directory.
        %
        startup_name = which('startup');
        [pathdef_dir name ext ver] = fileparts(startup_name);
        pathdef_name = fullfile(pathdef_dir,'pathdef.m');
    else
        %
        % File open for pathdef.m failed.
        % We have been told that no startup.m exists.
        %
        if isunix
            %
            % We are using Unix.
            % Guess that we should use $HOME/matlab/pathdef.m
            %
            [status,dir_str] = system('echo $HOME');
            home_dir = deblank(dir_str);
            pathdef_name = fullfile(home_dir,'matlab','pathdef.m');
        else
            %
            % We are using Windows.
            % Guess that we should use $matlabroot\toolbox\local\pathdef.m
            %
            pathdef_name = fullfile(matlabroot,'toolbox','local','pathdef.m');
        end
    end
    [pathdef_dir name ext ver] = fileparts(pathdef_name);
end

if strcmp(pathdef_name, current_pathdef_name)
    %
    % If we have not changed pathdef_name,
    % then this is the same file that we have been told we cannot write to,
    % and we have not been able to guess another possiblity for pathdef_name.
    %
    disp([wstr 'Cannot write to the pathdef file ' ...
        pathdef_name ' which was found on the Matlab path.']);
    reply = input([istr 'Do you want to select an existing pathdef.m file to update (y/n)? '], 's');
    if strcmpi(reply(1),'y')
        %
        % The user has chosen to update an existing pathdef.m file.
        % Use install -
        %
        install('-');
        return;
    end
    reply = input([istr 'Do you want to select a directory and create a new pathdef.m file (y/n)? '], 's');
    if ~strcmpi(reply(1),'y')
        %
        % The user has chosen not to create a new pathdef.m file.
        % There are no other possibilities, so return.
        %
        return;
    end
    %
    % The user has chosen to create a new pathdef.m file.
    % Ask where it should go. The directory must exist.
    %
    pname=uigetdir('','Please choose an existing directory for the new ''pathdef.m'' file.');
    if isequal(pname,0)
        %
        % The user has cancelled the directory selection.
        % There are no other possibilities, so return.
        %
        return;
    end
    pathdef_name = fullfile(pname,'pathdef.m');
    [pathdef_dir name ext ver] = fileparts(pathdef_name);
end
%
% Determine if Matlab thinks that pathdef_name already exists as a file.
%
if exist(pathdef_name) == 2
    willstr = 'update';
else
    willstr = 'create';
end
%
% Ask before attempting to perform the appropriate action.
%
reply = input([istr 'Installer will ' willstr ' the file ' strrep(pathdef_name,'\','\\') ...
    '\nProceed (y/n)? '],'s');
if ~strcmpi(reply(1),'y')
    return;
end
%
% Determine if Matlab thinks that pathdef_dir already exists as a directory.
%
pathdef_dir_exists = (exist(pathdef_dir) == 7);
%
% Even if Matlab says that pathdef_dir exists, it might not exist as a directory.
% Try to create it anyway. This should not hurt if it really does exist.
%
[parent dirname ext ver] = fileparts(pathdef_dir);
[status message] = mkdir(parent,dirname);
if status < 0
    created_pathdef_dir = false;
    if ~pathdef_dir_exists
        %
        % Matlab said pathdef_dir does not exist and that we cannot not create it.
        %
        disp([estr was_not_installed_msg]);
        disp([estr 'Cannot create ' pathdef_dir ': ' message]);
        error([try_to_create_msg '\n and then try "' command pathdef_dir '"']);
    end
else
    created_pathdef_dir = true;
    if ~pathdef_dir_exists
        %
        % Matlab has said that pathdef_dir did not exist and that we have now created it.
        %
        disp([istr command ' has created the directory ' pathdef_dir]);
    end
end
%
% At this point, pathdef_dir should exist as a directory.
% Check again.
%
pathdef_dir_exists = (exist(pathdef_dir) == 7);
if ~pathdef_dir_exists
    %
    % Matlab said pathdef_dir does not exist.
    %
    disp([estr was_not_installed_msg]);
    disp([estr 'Cannot create ' pathdef_dir ': ' message]);
    error([try_to_create_msg '\n and then try "' command pathdef_dir '"']);
end
%
% Finally, we can ask install to use pathdef_dir.
%
install(pathdef_dir);
%
% If we created pathdef_dir, we should add it to the path,
% so that the current Matlab session can see the new pathdef.m
%
if created_pathdef_dir
    addpath(pathdef_dir);
end
rehash pathreset;
%
%end function


% Mediterranian diet defintion. For details, please see https://www.vmh.life/#nutrition
% Ines Thiele 2016-2019

Diet = {%'Reaction'	'Flux Value'
'EX_etoh(e)'	'355.9923939'
'EX_h2o(e)'	'220593.0188'
'EX_caro(e)'	'0.005979072'
'EX_retinol(e)'	'0.558558584'
'EX_thm(e)'	'8.328478195'
'EX_adpcbl(e)'	'1.22881E-05'
'EX_ribflv(e)'	'0.006111107'
'EX_nac(e)'	'0.445244325'
'EX_ncam(e)'	'0.211750949'
'EX_pnto_R(e)'	'0.085186526'
'EX_pydam(e)'	'0.004669003'
'EX_pydxn(e)'	'0.004669643'
'EX_pydx(e)'	'0.004725954'
'EX_btn(e)'	'0.00036173'
'EX_10fthf(e)'	'0.000264396'
'EX_5mthf(e)'	'0.000271879'
'EX_thf(e)'	'0.000281098'
'EX_ascb_L(e)'	'0.782052146'
'EX_vitd3(e)'	'3.18352E-05'
'EX_avite1(e)'	'0.054573176'
'EX_phyQ(e)'	'0.000301578'
'EX_ca2(e)'	'44.23174809'
'EX_cl(e)'	'543.2352692'
'EX_k(e)'	'101.3033815'
'EX_mg2(e)'	'25.58362477'
'EX_na1(e)'	'150.9506232'
'EX_pi(e)'	'23.46662249'
'EX_cu2(e)'	'0.04957039'
'EX_fe2(e)'	'0.200734175'
'EX_fe3(e)'	'0.200734175'
'EX_mn2(e)'	'0.136335394'
'EX_zn2(e)'	'0.261394922'
'EX_mnl(e)'	'0.779483982'
'EX_xylt(e)'	'0'
'EX_lcts(e)'	'35.94427638'
'EX_malt(e)'	'4.206879124'
'EX_sucr(e)'	'110.5182203'
'EX_fru(e)'	'125.4517893'
'EX_gal(e)'	'10.8426091'
'EX_cellul(e)'	'0.14529578'
'EX_ala_L(e)'	'61.65072082'
'EX_arg_L(e)'	'42.32821506'
'EX_asp_L(e)'	'80.87258611'
'EX_cys_L(e)'	'13.29163028'
'EX_glu_L(e)'	'168.7925032'
'EX_gly(e)'	'67.07363861'
'EX_urate(e)'	'5.780252608'
'EX_his_L(e)'	'18.55149638'
'EX_ile_L(e)'	'45.97634115'
'EX_leu_L(e)'	'73.5657289'
'EX_lys_L(e)'	'49.72502556'
'EX_met_L(e)'	'15.16359686'
'EX_phe_L(e)'	'34.89104305'
'EX_pro_L(e)'	'78.89334277'
'EX_ser_L(e)'	'59.78051737'
'EX_thr_L(e)'	'39.41362937'
'EX_trp_L(e)'	'6.728111908'
'EX_tyr_L(e)'	'23.68527804'
'EX_val_L(e)'	'54.75068355'
'EX_dca(e)'	'3.170503694'
'EX_ddca(e)'	'3.503490546'
'EX_ttdca(e)'	'12.65654482'
'EX_ttdcea(e)'	'1.377519391'
'EX_ptdca(e)'	'1.183522405'
'EX_hdca(e)'	'56.41911375'
'EX_hpdca(e)'	'0.886830484'
'EX_ocdca(e)'	'13.64408774'
'EX_ocdcea(e)'	'128.1607541'
'EX_lnlc(e)'	'73.59427421'
'EX_lnlnca(e)'	'7.87609621'
'EX_strdnc(e)'	'0.614547697'
'EX_arach(e)'	'1.876943078'
'EX_CE2510(e)'	'5.347026525'
'EX_CE4843(e)'	'0.130800704'
'EX_arachd(e)'	'0.268570142'
'EX_docosac(e)'	'2.557574808'
'EX_doco13ac(e)'	'4.673543672'
'EX_adrn(e)'	'0.025338442'
'EX_clpnd(e)'	'0.108954216'
'EX_crvnc(e)'	'3.860995608'
'EX_but(e)'	'8.165474895'
'EX_octa(e)'	'1.800374991'
'EX_chsterol(e)'	'0.687954461'
'EX_sbt_D(e)'	'8.441207695'
'EX_glc_D(e)'	'96.02127935'
'EX_hdcea(e)'	'5.649717068'
'EX_lgnc(e)'	'0.909993994'
'EX_fol(e)'	'0.000283028'
'EX_strch1(e)'	'41.02641436'
'EX_i(e)'	'0.003914204'
'EX_starch1200(e)'	'0.379838969'

    };
Diet = regexprep(Diet,'EX_','Diet_EX_');
Diet = regexprep(Diet,'\(e\)','\[d\]');


classdef FuncHelpClass < matlab.mixin.Copyable

    properties
        funcname;
        helpstr;
        sections;
        userfuncdir;
        config;
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %  Initialization methods
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % -------------------------------------------------------------
        function obj = FuncHelpClass(funcname)
            % Syntax:
            %       fhelp = FuncHelpClass(funcname);
            %
            % Examples:
            %       fhelp = FuncHelpClass('hmrR_DeconvHRF_DriftSS');
            %
            obj.userfuncdir = {};
            obj.config = [];

            if nargin==0
                return;
            end
            obj.funcname = funcname;
            if isdeployed()
                obj.userfuncdir = obj.UserFuncDirs(obj);
            end
            obj.Help();
            obj.ParseSections();

        end

        % ---------------------------------------------------------------
        function AddSection(obj, name)
            fieldname = name;
            fieldname(fieldname==' ')='';
            fieldname = lower(fieldname);
            newsection = struct(...
                'name',name, ...
                'lines',[0,0], ...
                'str','', ...
                'subsections', [] ...
                );
            eval( sprintf('obj.sections.%s = newsection;', fieldname) );
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %  Methods for parsing sections
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % -------------------------------------------------------------
        function ParseSections(obj)
            % This method dynamically parses the help section of a matlab function
            % that is in the generic format described below. The format
            % consists of a series of sections and sub-sections like this:
            %
            % ------------------------------------------------------------
            % <SECTION NAME i>:
            % <help text for <SECTION NAME i> spanning one or more lines>
            %
            %       -- OR --
            %
            % <SECTION NAME i>:
            % <sub-section1_i>: <help text for sub-section1_i spanning one or more lines>
            % <sub-section2_i>: <help text for sub-section2_i spanning one or more lines>
            %  . . . . . .
            % <sub-sectionN_i>: <help text for sub-sectionN_i spanning one or more lines>
            %
            % ------------------------------------------------------------
            %
            % For <SECTION NAME i> to be considered a section (as opposed to sub-section)
            % it must be in all uppercase, must be followed by a ':', and must not have
            % any other text on that line. A sub-section is any string without white
            % spaces preceding the first occurrence of ':', ' - ' or '--' on a line line.
            %
            % In addition to the generic parsing of sections, this class also provides
            % methods for accessing specific 'designated' sections should they exist in
            % the help comments. A section is defined as designated in this class if it
            % provides public Get* methods to access it. Current designated sections are
            %
            %       Designated Sections    Associated methods
            %       --------------------------------------
            %       'DESCRIPTION:'          % GetDescr()
            %       'INPUT(S):'             % GetParamDescr()
            %       'USAGE OPTIONS:'        % GetUsageOptions()
            %       'PARAMETERS:'           % GetParamUsage()
            %
            % New methods can be added for any new designated sections added to this
            % class in the future
            %
            % Example from Homer3: formal description of user functions
            % help section syntax:
            % --------------------------------------------------------------
            % SYNTAX:
            % [r1,...,rN] = <funcname>(a1,...,aM,p1,...,pL)
            %
            % UI NAME:
            % <User Interface Function Name>
            %
            % DESCRIPTION:
            % <General function description>
            %
            % INPUT:
            % a1: <Description of a1>
            %    . . . . . . . . . .
            % aM: <Description of am>
            % p1: <Description of am>
            %    . . . . . . . . . .
            % pL: <Description of am>
            %
            % OUPUT:
            % r1: <Description of r1>
            %    . . . . . . . . . .
            % rN: <Description of rN>
            %
            % USAGE OPTIONS:
            % <User-friendly name for option 1>: [r11,...,r1N] = <funcname>(a11,...,a1M,p1,...,pL)
            %    . . . . . . . . . .
            % <User-friendly name for option K>: [rK1,...,rKN] = <funcname>(aK1,...,aKM,p1,...,pL)
            %
            % PARAMETERS:
            % p1: [v11,...,v1J]
            %    . . . . . . . . . .
            % pL: [vL1,...,vLJ]
            %
            if isempty(obj.helpstr)
                return;
            end

            sect = obj.FindSections();
            for ii=1:length(sect)
                obj.AddSection(sect{ii});
            end

            % Find the lines in the help that belong to each help section
            obj.FindSectionLines();

            % Remove leading and trailing blank lines from each section
            obj.RemoveBlankLines();

            % Now that we have the lines associated with each help section,
            % assign the text from these lines to corresponding help sections.
            obj.AssignSectionText();

            % We have parsed all sections: now parse sub-sections for each
            % section
            sect = propnames(obj.sections);
            for ii=1:length(sect)
                obj.ParseSubSections(sect{ii});
            end

        end

        % -------------------------------------------------------------
        function sect = FindSections(obj)
            kk=1;
            sect = cell(length(obj.helpstr),1);
            for ii=1:length(obj.helpstr)
                if obj.IsSectionName(obj.helpstr{ii})
                    k = find(obj.helpstr{ii}==':');
                    sect{kk,:} = strtrim(obj.helpstr{ii}(1:k-1));
                    kk=kk+1;
                end
            end
            sect(kk:end) = [];
        end

        % -------------------------------------------------------------
        function FindSectionLines(obj)
            fields = propnames(obj.sections);
            for iLine=1:length(obj.helpstr)
                for jj=1:length(fields)
                    secname = eval( sprintf('obj.sections.%s.name', fields{jj}) );
                    if includes(obj.helpstr{iLine}, secname)
                        EndPrevSection(obj, iLine);
                        eval( sprintf('obj.sections.%s.lines(1) = iLine+1;', fields{jj}) );
                    end
                end
            end
            EndPrevSection(obj, iLine+1);
        end

        % -------------------------------------------------------------
        function AssignSectionText(obj)
            fields = propnames(obj.sections);
            for jj=1:length(fields)
                lines = eval( sprintf('obj.sections.%s.lines(1):obj.sections.%s.lines(2)', fields{jj}, fields{jj}) );
                for iLine = lines
                    if iLine < 1 || isempty(obj.helpstr{iLine})
                        continue;
                    end
                    eval( sprintf('obj.sections.%s.str = sprintf(''%%s%%s\\n'', obj.sections.%s.str, obj.helpstr{iLine});', ...
                        fields{jj}, fields{jj}) );
                end
            end
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Public methods for accessing designated sections
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % -------------------------------------------------------------
        function str = GetUiname(obj)
            str = '';
            if isproperty(obj.sections,'uiname')
                str = strtrim(obj.sections.uiname.str);
            end
        end

        % -------------------------------------------------------------
        function str = GetDescr(obj)
            str = '';
            if isproperty(obj.sections,'description')
                str = obj.sections.description.str;
            end
        end

        % -------------------------------------------------------------
        function str = GetParamDescr(obj, param)
            str = '';
            subsections = [];
            if isproperty(obj.sections,'input')
                subsections = obj.sections.input.subsections;
            end
            if isproperty(obj.sections,'inputs')
                subsections = obj.sections.inputs.subsections;
            end
            if isempty(subsections)
                return;
            end

            % if the input is a number index into the inputs arrays, then we
            % don't need to seach for the param name
            if iswholenum(param)
                if param <= length(subsections)
                    str = subsections(param).str;
                end
                return;
            end

            % Search for the parameter with name <param>
            for ii=1:length(subsections)
                if strcmp(param, subsections(ii).name)
                    str = subsections(ii).str;
                end
            end
        end

        % -------------------------------------------------------------
        function [usage, friendlyname] = GetUsageOptions(obj)
            usage = {};
            friendlyname = {};
            subsections = [];
            if isproperty(obj.sections,'usageoptions')
                subsections = obj.sections.usageoptions.subsections;
            end
            if isempty(subsections)
                return;
            end
            usage        = cell(length(subsections),1);
            friendlyname = cell(length(subsections),1);
            for ii=1:length(subsections)
                friendlyname{ii}  = strtrim(subsections(ii).name);
                usage{ii}         = strtrim(subsections(ii).str);
            end
        end

        % -------------------------------------------------------------
        function [paramname, valformat] = GetParamUsage(obj)
            paramname = {};
            valformat = {};
            subsections = [];
            if isproperty(obj.sections,'parameters')
                subsections = obj.sections.parameters.subsections;
            end
            if isempty(subsections)
                return;
            end
            paramname = cell(length(subsections),1);
            valformat = cell(length(subsections),1);
            for ii=1:length(subsections)
                paramname{ii} = strtrim(subsections(ii).name);
                valformat{ii} = strtrim(subsections(ii).str);
            end
        end

        % -------------------------------------------------------------
        function helpstr = GetStr(obj)
            helpstr = '';
            fields = propnames(obj.sections);
            for ii=1:length(fields)
                name = eval( sprintf('obj.sections.%s.name', fields{ii}) );
                str = eval( sprintf('obj.sections.%s.str', fields{ii}) );
                helpstr = sprintf('%s%s:\n', helpstr, name);
                helpstr = sprintf('%s%s\n', helpstr, str);
            end
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %  Methods for parsing sub-sections
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % -------------------------------------------------------------
        function ParseSubSections(obj, section)
            obj.FindSubSectionLines(section);
            obj.AssignSubSectionText(section);
        end

        % -------------------------------------------------------------
        function FindSubSectionLines(obj, section)
            strs = str2cell_fast(eval(sprintf('obj.sections.%s.str', section)), [], 'keepblanks');
            kk=0;
            subsect = [];
            for iLine=1:length(strs)
                name = obj.GetSubSectionName(section, iLine);
                if ~isempty(name)
                    kk=kk+1;

                    % Create new sub-section
                    subsect(kk).name = strtrim(name);
                    subsect(kk).lines = [0,0];
                    subsect(kk).str = '';

                    % Assign starting line to current subsection
                    subsect(kk).lines(1) = iLine;

                    % See if there's sub-section preceding current one. If
                    % so, end it
                    if kk>1 && subsect(kk-1).lines(2)==0
                        subsect(kk-1).lines(2) = iLine-1;
                    end
                end
            end
            if kk>0
                if subsect(kk).lines(2)==0
                    subsect(kk).lines(2) = iLine;
                end
            end
            eval( sprintf('obj.sections.%s.subsections = subsect;', section) );
        end

        % -------------------------------------------------------------
        function [name, k] = GetSubSectionName(obj, section, iLine)
            name = '';
            k = [];
            strs = str2cell_fast(eval(sprintf('obj.sections.%s.str', section)), [], 'keepblanks');

            % Rule 1: Valid section is must end with a ':',' - ', or '--'
            k1 = find(strs{iLine}==':');
            k2 = strfind(strs{iLine}, ' - ');
            k3 = strfind(strs{iLine}, '--');
            d=1;
            if ~isempty(k1)
                k = k1(1);
            elseif ~isempty(k2)
                k = k2(1)+2; d = 2;
            elseif ~isempty(k3)
                k = k3(1)+1;
            end
            if isempty(k)
                return;
            end
            temp = strtrim(strs{iLine}(1:(k-d)));

            %%%% Check to make sure string preceding ':' is a valid section name.

            % Rule 2: If string preceding ':' (ie, potential section name) has spaces then it's
            % not a section name.
            if ~isempty(find(temp==' '))
                return;
            end

            % Rule 3: If string preceding ':' is indented greater than 2 spaces then it's not
            % a section name.
            indent_size = strfind(strs{iLine}(1:(k-d)), temp)-1;
            if isempty(indent_size) || indent_size > 2
                return;
            end

            name = strtrim(strs{iLine}(1:(k-d)));
            k = k(1);
        end

        % -------------------------------------------------------------
        function AssignSubSectionText(obj, section)
            strs = str2cell_fast(eval(sprintf('obj.sections.%s.str', section)), [], 'keepblanks');
            subsect = eval(sprintf('obj.sections.%s.subsections', section));
            for ii=1:length(subsect)
                lines = subsect(ii).lines(1):subsect(ii).lines(2);
                for iLine = lines
                    % The help part corresponding to the subsection name (eg, SD: or trange:)
                    % starts after the colon (:) or double dash (--)
                    [~, k] = obj.GetSubSectionName(section, iLine);
                    if ~isempty(k)
                        strs{iLine} = strtrim(strs{iLine}(k+1:end));
                    end
                    subsect(ii).str = sprintf('%s%s\n', subsect(ii).str , strs{iLine});
                end
            end
            eval( sprintf('obj.sections.%s.subsections = subsect;', section) );
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods (Access = 'private')

        % -----------------------------------------------------------------
        function InitLineIdxs(obj)
            fields = propnames(obj.sections);
            for ii=1:length(fields)
                eval( sprintf('obj.sections.%s.lines = [0 0];', fields{ii}) );
            end
        end

        % -----------------------------------------------------------------
        function RemoveBlankLines(obj)
            % Identify lines in each section where actual text begins and
            % ends and remove the leading and trailing blank lines
            fields = propnames(obj.sections);
            for jj=1:length(fields)
                lines = eval( sprintf('obj.sections.%s.lines(1):obj.sections.%s.lines(2)', fields{jj}, fields{jj}) );

                % If there a zeroes in lines then section is invalid or
                % empty
                if ismember(0, lines)
                    continue;
                end

                % Find first non-blank line of text
                for iLine = lines
                    if ~isblankline(obj.helpstr{iLine})
                        eval( sprintf('obj.sections.%s.lines(1) = iLine;', fields{jj}) );
                        break;
                    end
                end

                % Find last non-blank line of text
                for iLine = fliplr(lines)
                    % Find first non-blank line of text
                    if ~isblankline(obj.helpstr{iLine})
                        eval( sprintf('obj.sections.%s.lines(2) = iLine;', fields{jj}) );
                        break;
                    end
                end
            end
        end

        % -----------------------------------------------------------------
        function EndPrevSection(obj, iLine) %#ok<INUSD>
            fields = propnames(obj.sections);
            for ii=1:length(fields)
                if eval( sprintf('obj.sections.%s.lines(1)>0 && obj.sections.%s.lines(2)==0', fields{ii}, fields{ii}) )
                    eval( sprintf('obj.sections.%s.lines(2) = iLine-1;', fields{ii}) );
                    break;
                end
            end
        end

        % -----------------------------------------------------------------
        function b = IsSectionName(obj, str)
            b = false;
            if isblankline(str)
                return;
            end
            str = strtrim(str);
            if ~isuppercase(str)
                return;
            end
            if str(end)~=':'
                return;
            end
            b = true;
        end

        % ----------------------------------------------------------------------------------
        function Copy(obj, obj2)
            obj.funcname = obj2.funcname;
            obj.helpstr = obj2.helpstr;
            obj.sections = obj2.sections;
        end

        % ----------------------------------------------------------------------------------
        function b = IsEmpty(obj)
            b = false;
            if isempty(obj.funcname)
                b=true;
            end
            if isempty(obj.helpstr)
                b=true;
            end
            if isempty(obj.sections)
                b=true;
            end
        end

        % ---------------------------------------------------------------------------------
        function Help(obj)
            funcname = '';
            if ~isdeployed()
                funcname = [obj.funcname, '.m'];
            else
                for ii = 1:length(obj.userfuncdir)
                    if exist([obj.userfuncdir{ii}, obj.funcname, '.m'], 'file')
                        funcname = [obj.userfuncdir{ii}, obj.funcname, '.m'];
                    end
                end
            end
            if isempty(funcname)
                return
            end
            obj.helpstr = str2cell_fast(help_local(funcname), [], 'keepblanks');
        end

    end

    methods (Static)

        % ----------------------------------------------------------------------------------
        function out = UserFuncDirs(arg)
            persistent userfuncdir;
            if isempty(userfuncdir)
                userfuncdir = FindUserFuncDir(arg);
            end
            out = userfuncdir;
        end

    end

end


function measure_types = nst_measure_types()
% NST_MEASURE_TYPES return an enumeration of measure types (eg wavelength, Hb)
%
% MEASURE_TYPES = NST_MEASURE_TYPES()
%    MEASURE_TYPES: struct with numerical fields listing all available
%                   channel types:
%                   - MEASURE_TYPES.WAVELENGTH
%                   - MEASURE_TYPES.Hb

measure_types.WAVELENGTH = 1;
measure_types.HB = 2;
end


function varargout = process_segment_fastsurfer( varargin )
% PROCESS_SEGMENT_FASTSURFER: Run the segmentation of a T1 MRI with FastSurfer.
%
% USAGE:     OutputFiles = process_segment_fastsurfer('Run',     sProcess, sInputs)
%         [isOk, errMsg] = process_segment_fastsurfer('Compute', iSubject, iAnatomy=[default], nVertices, isInteractive)
%                          process_segment_fastsurfer('ComputeInteractive', iSubject, iAnatomy)

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2021-2023

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Segment MRI with FastSurfer';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = {'Import', 'Import anatomy'};
    sProcess.Index       = 33;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/SegFastSurfer';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'import'};
    sProcess.OutputTypes = {'import'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 0;
    % Title
    sProcess.options.install.Comment = [
        'FastSurfer must be installed on the computer, the command
' ...
        'run_fastsurfer.sh must be available in the system path.
' ...
        'Click "Online tutorial" for installation instructions.'];
    sProcess.options.install.Type    = 'label';
    % Doc
    sProcess.options.optdoc.Comment = [
        '
' ...
        'Network specific arguments:
' ...
        '--clean_seg : Flag to clean up FastSurferCNN segmentation
' ...
        '--no_cuda   : Flag to disable CUDA usage in FastSurferCNN
' ...
        '--batch     : Batch size for inference. Default: 16.
' ...
        '              Lower this to reduce memory requirement.
' ...
        '--order     : Interp. for mri_convert T1 before segmentation
' ...
        '              (0=nearest, 1=linear(default), 2=quadratic, 3=cubic)

' ...
        'Surface pipeline arguments:
' ...
        '--fstess    : Use mri_tesselate instead of marching cube (default)
' ...
        '--fsqsphere : Use FreeSurfer default instead of novel
' ...
        '              spectral spherical projection for qsphere
' ...
        '--fsaparc   : Use FS aparc segmentations + DL prediction
' ...
        '--surfreg   : sphere.reg registration with FreeSurfer
' ...
        '--parallel  : Run both hemispheres in parallel
' ...
        '--threads   : Set openMP and ITK threads to ...

' ...
        'Other:
' ...
        '--py        : Python version to use. Default: python3.6
' ...
        '--seg_only  : Only run FastSurferCNN
' ...
        '--surf_only : Only run the surface pipeline recon_surf

'];
    sProcess.options.optdoc.Type    = 'label';
    % Option: Extra command-line parameters
    sProcess.options.param.Comment = 'Command-line options:';
    sProcess.options.param.Type    = 'text';
    sProcess.options.param.Value   = '--batch 4 --surfreg --parallel --threads 4';
    % Option: Subject name
    sProcess.options.subjectname.Comment = 'Subject name:';
    sProcess.options.subjectname.Type    = 'subjectname';
    sProcess.options.subjectname.Value   = '';
    % File selection options
    SelectOptions = {...
        '', ...                            % Filename
        '', ...                            % FileFormat
        'open', ...                        % Dialog type: {open,save}
        'Select FastSurfer output folder...', ...     % Window title
        'ExportAnat', ...                  % LastUsedDir: {ImportData,ImportChannel,ImportAnat,ExportChannel,ExportData,ExportAnat,ExportProtocol,ExportImage,ExportScript}
        'single', ...                      % Selection mode: {single,multiple}
        'dirs', ...                        % Selection mode: {files,dirs,files_and_dirs}
        {{'.folder'}, 'FastSurfer database folder', 'FastSurfer'}, ... % Available file formats
        []};                               % DefaultFormats: {ChannelIn,DataIn,DipolesIn,EventsIn,AnatIn,MriIn,NoiseCovIn,ResultsIn,SspIn,SurfaceIn,TimefreqIn}
    % Option: FastSurfer output folder
    sProcess.options.outdir.Comment = 'Output folder:';
    sProcess.options.outdir.Type    = 'filename';
    sProcess.options.outdir.Value   = SelectOptions;
    % Default outdir
    sProcess.options.defdir.Comment = 'Default output folder: $HOME/.brainstorm/tmp/fastsurfer';
    sProcess.options.defdir.Type    = 'label';
    % Option: Number of vertices
    sProcess.options.nvertices.Comment = 'Number of vertices (cortex): ';
    sProcess.options.nvertices.Type    = 'value';
    sProcess.options.nvertices.Value   = {15000, '', 0};
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    OutputFiles = {};
    % Number of vertices
    nVertices = sProcess.options.nvertices.Value{1};
    if isempty(nVertices) || (nVertices < 50)
        bst_report('Error', sProcess, [], 'Invalid number of vertices.');
        return
    end
    % Get subject name
    SubjectName = file_standardize(sProcess.options.subjectname.Value);
    if isempty(SubjectName)
        bst_report('Error', sProcess, [], 'Subject name is empty.');
        return;
    end
    % Get subject
    [sSubject, iSubject] = bst_get('Subject', SubjectName);
    if isempty(iSubject)
        bst_report('Error', sProcess, [], ['Subject "' SubjectName '" does not exist.']);
        return
    end
    % Get extra parameters
    param = sProcess.options.param.Value;
    outdir = sProcess.options.outdir.Value{1};
    % Call processing function
    [isOk, errMsg] = Compute(iSubject, [], nVertices, 0, param, outdir);
    % Handling errors
    if ~isOk
        bst_report('Error', sProcess, [], errMsg);
    elseif ~isempty(errMsg)
        bst_report('Warning', sProcess, [], errMsg);
    end
    % Return an empty structure
    OutputFiles = {'import'};
end

%% ===== COMPUTE SEGMENTATION =====
function [isOk, errMsg] = Compute(iSubject, iAnatomy, nVertices, isInteractive, param, outdir)
    errMsg = '';
    isOk = 0;

    % ===== INSTALL FASTSURFER =====
    bst_progress('text', 'Testing FastSurfer installation...');
    % Get FreeSurfer path from environment variable
    FreeSurferDir = getenv('FREESURFER_HOME');
    SubjectsDir = getenv('SUBJECTS_DIR');
    if isempty(FreeSurferDir) || ~file_exist(FreeSurferDir) || isempty(SubjectsDir) || ~file_exist(SubjectsDir)
        errMsg = ['FreeSurfer is not installed on your computer.' 10 ...
                  'The environment variables FREESURFER_HOME and SUBJECTS_DIR must be set.' 10 10 ...
                  'See the online Brainstorm tutorial about FastSurfer for help:' 10 ...
                  'https://neuroimage.usc.edu/brainstorm/Tutorials/SegFastSurfer'];
        return
    end
    % Get FastSurfer path with system call
    [res,FastSurferDir] = system('which run_fastsurfer.sh');
    if (res ~= 0) || isempty(FastSurferDir)
        errMsg = ['FastSurfer is not installed on your computer.' 10 ...
                  'The executable run_fastsurfer.sh must be available in the system path.' 10 ...
                  'Click on "Help" in the process options for installation instructions.'];
        return
    end
    FastSurferDir = bst_fileparts(strtrim(FastSurferDir));

    % ===== GET SUBJECT =====
    % Get subject
    [sSubject, iSubject] = bst_get('Subject', iSubject);
    if isempty(sSubject)
        errMsg = 'Subject does not exist.';
        return
    end
    % Check if a MRI is available for the subject
    if isempty(sSubject.Anatomy)
        errMsg = ['No MRI available for subject "' SubjectName '".'];
        return
    end
    % Get default MRI if not specified
    if isempty(iAnatomy)
        iAnatomy = sSubject.iAnatomy;
    end

    % ===== CHECK OUTPUT FOLDER =====
    % Folder for FastSurfer output
    if ~isempty(outdir)
        TmpDir = [];
        procDir = outdir;
    else
        TmpDir = bst_get('BrainstormTmpDir', 0, 'fastsurfer');
        procDir = TmpDir;
    end
    if ~file_exist(procDir)
        mkdir(procDir);
    end
    % Check if subject dir already exists
    subjid = strrep(sSubject.Name, '@', '');
    subjDir = bst_fullfile(procDir, subjid);
    if file_exist(subjDir)
        if isInteractive
            isDelete = java_dialog('confirm', ...
                    ['Subject folder already exists:
' subjDir '

' ...
                    'Delete existing folder?

'], 'FastSurfer segmentation');
            if isDelete
                file_delete(subjDir, 1, 3);
            end
        else
            bst_report('Warning', 'process_segment_fastsurfer', [], ['Subject folder already exists: ' subjDir]);
        end
    end

    % ===== DELETE EXISTING SURFACES =====
    % Confirm with user that the existing surfaces will be removed
    if isInteractive && ~isempty(sSubject.Surface)
        isDel = java_dialog('confirm', ['Warning: There are already surfaces in this subject.' 10 ...
            'Running FastSurfer will remove all the existing surfaces.' 10 10 ...
            'Delete the existing files?' 10 10], 'FastSurfer segmentation');
        if ~isDel
            errMsg = 'Process aborted by user.';
            % Delete temporary files
            if ~isempty(TmpDir)
                file_delete(TmpDir, 1, 1);
            end
            return;
        end
    end

    % ===== VERIFY FIDUCIALS IN MRI =====
    bst_progress('text', 'Saving temporary files...');
    % Load MRI file
    T1FileBst = sSubject.Anatomy(iAnatomy).FileName;
    sMri = in_mri_bst(T1FileBst);
    % If the SCS transformation is not defined: compute MNI transformation to get a default one
    if isempty(sMri) || ~isfield(sMri, 'SCS') || ~isfield(sMri.SCS, 'NAS') || ~isfield(sMri.SCS, 'LPA') || ~isfield(sMri.SCS, 'RPA') || (length(sMri.SCS.NAS)~=3) || (length(sMri.SCS.LPA)~=3) || (length(sMri.SCS.RPA)~=3) || ~isfield(sMri.SCS, 'R') || isempty(sMri.SCS.R) || ~isfield(sMri.SCS, 'T') || isempty(sMri.SCS.T)
        % Issue warning
        errMsg = 'Missing NAS/LPA/RPA: Computing the MNI normalization to get default positions.';
        % Compute MNI normalization
        [sMri, errNorm] = bst_normalize_mni(T1FileBst);
        % Handle errors
        if ~isempty(errNorm)
            errMsg = [errMsg 10 'Error trying to compute the MNI normalization: ' 10 errNorm 10 ...
                'Missing fiducials: the surfaces cannot be aligned with the MRI.'];
        end
    end

    % ===== SAVE MRI AS NII =====
    % Save MRI in .nii format
    NiiFile = bst_fullfile(procDir, [subjid, '.nii']);
    out_mri_nii(sMri, NiiFile);
    % If a "world transformation" was not available in the MRI in the database, it was set to a default when saving to .nii
    % Let's reload this file to get the transformation matrix, it will be used when importing the results
    if ~isfield(sMri, 'InitTransf') || isempty(sMri.InitTransf) || isempty(find(strcmpi(sMri.InitTransf(:,1), 'vox2ras')))
        % Load again the file, with the default vox2ras transformation
        [tmp, vox2ras] = in_mri_nii(NiiFile, 0, 0, 0);
        % Prepare the history of transformations
        if ~isfield(sMri, 'InitTransf') || isempty(sMri.InitTransf)
            sMri.InitTransf = cell(0,2);
        end
        % Add this transformation in the MRI
        sMri.InitTransf(end+1,[1 2]) = {'vox2ras', vox2ras};
        % Save modification on hard drive
        bst_save(file_fullpath(T1FileBst), sMri, 'v7');
    end

    % ===== RUN FASTSURFER =====
    bst_progress('text', 'Running FastSurfer...    (see command window)');
    strCall = ['cd ' FastSurferDir '; run_fastsurfer.sh --t1 "' NiiFile '" --sid "' subjid '" --sd "' procDir '" ' param];
    disp(['BST> System call: ' strCall]);
    status = system(strCall)
    % Error handling
    if (status ~= 0)
        errMsg = ['FastSurfer failed.', 10, 'Check the Matlab command window for more information.'];
        return
    end

    % ===== IMPORT OUTPUT FOLDER =====
    % Import FreeSurfer anatomy folder
    isExtraMaps = 0;
    isKeepMri = 1;
    isVolumeAtlas = 1;
    FsDir = bst_fullfile(procDir, subjid);
    errMsg = import_anatomy_fs(iSubject, FsDir, nVertices, 0, [], isExtraMaps, isVolumeAtlas, isKeepMri);
    if ~isempty(errMsg)
        return;
    end
    % Delete temporary files
    if ~isempty(TmpDir)
        file_delete(TmpDir, 1, 1);
    end
    % Return success
    isOk = 1;
end

%% ===== COMPUTE/INTERACTIVE =====
function ComputeInteractive(iSubject, iAnatomy) %#ok<DEFNU>
    % Get inputs
    if (nargin < 2) || isempty(iAnatomy)
        iAnatomy = [];
    end
    % Get default parameters
    sProcess = GetDescription();
    optdoc = sProcess.options.optdoc.Comment;
    defParam = sProcess.options.param.Value;
    defDir = bst_fullfile(bst_get('BrainstormTmpDir'), 'fastsurfer');
    % Ask for extra processing parameters
    [param, isCancel] = java_dialog('input', optdoc, 'FastSurfer command-line options', [], defParam);
    if isCancel
        return;
    end
    % Ask for output folder
    [outdir, isCancel] = java_dialog('input', 'FastSurfer output folder:', 'FastSurfer MRI segmentation', [], defDir);
    if isCancel
        return;
    end
    % Ask for number of vertices
    [nVertices, isCancel] = java_dialog('input', 'Number of vertices on the cortex surface:', 'FastSurfer segmentation', [], '15000');
    if isempty(nVertices) || isCancel
        return
    end
    nVertices = str2double(nVertices);
    % Open progress bar
    bst_progress('start', 'FastSurfer', 'FastSurfer MRI segmentation...');
    % Run FastSurfer
    isInteractive = 1;
    [isOk, errMsg] = Compute(iSubject, iAnatomy, nVertices, isInteractive, param, outdir);
    % Error handling
    if ~isOk
        bst_error(errMsg, 'FastSurfer MRI segmentation', 0);
    elseif ~isempty(errMsg)
        java_dialog('msgbox', ['Warning: ' errMsg]);
    end
    % Close progress bar
    bst_progress('stop');
end


function bool = VMCPresent
%"VMCPresent"
%   Returns true only if the .exe files necessary to run VMC are present
%   and in the proper location for the current copy of CERR.
%
%   JRA 10/15/04
%
%Usage:
%   function bool = VMCPresent
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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 3 of the License, or
%     (at your option) any later version.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

bool = 0;
IMRTPdir = fileparts(which('IMRTP'));
if exist(fullfile(IMRTPdir, 'vmc++', 'bin', 'vmc_Linux.exe'), 'file') == 2 && ...
   exist(fullfile(IMRTPdir, 'vmc++', 'bin', 'vmc_Windows.exe'), 'file') == 2
   bool = 1;
end


function varargout = size(cS,varargin)
% overloads size

varargout{1} = length(cS);


function checkopt(inopt, dfopt)

% checkopt -- Check options structure for unrecognized fields
%
% Usage:
%       checkopt(inopt, dfopt)
%
% Input:
%       inopt         Input options structure
%       dfopt         Default options structure
%
%
% Author: Brendt Wohlberg <brendt@lanl.gov>  Modified: 2015-06-17
%
% This file is part of the SPORCO library. Details of the copyright
% and user license can be found in the 'License' file distributed with
% the library.

if ~isempty(inopt) && (~isfield(inopt,'NoOptionCheck') || ~inopt.NoOptionCheck),
  ifnc = fieldnames(inopt);
  for k = 1:length(ifnc),
    ifn = ifnc{k};
    if ~strcmp(ifn, 'NoOptionCheck') && ~isfield(dfopt, ifn),
      warning('SPORCO:UnknownOption', 'Unknown option field %s', ifn);
    end
  end
end

return


function [signalMatrix, signalSpikes] = viewSpikeSummary(signalMatrix,signalSpikes,varargin)
	% gives several summary plots for a set of signals
	% biafra ahanonu
	% started: 2013.10.28
	% inputs
		% signalMatrix - 2D matrix of [signals frames] that contains the raw analog signal
		% signalSpikes - 2D matrix of [signals frames] that contains 0/1 indicating whether a peak occurred at that point
	% outputs
		% signalMatrix - 2D matrix of [signals frames] that contains the raw analog signal
		% signalSpikes - 2D matrix of [signals frames] that contains 0/1 indicating whether a peak occurred at that point

	[signalMatrix, signalSpikes] = ciapkg.view.viewSpikeSummary(signalMatrix,signalSpikes,'passArgs', varargin);
end


function transformParams = buildAntsTransform(antsWarpProducts,inverseFlag)

if ~exist('inverseFlag','var')
    inverseFlag = 0;
end

% generate transform command
transformParams ='';

if ~inverseFlag
    if ~isempty(antsWarpProducts.Warp)
        if exist(antsWarpProducts.Warp,'file')
            transformParams = [' -t ' antsWarpProducts.Warp ' '];
        else
            error(['Unable to complete transform, warp product missing: ' antsWarpProducts.Warp]);
        end
    end
    if ~isempty(antsWarpProducts.Affine)
        if exist(antsWarpProducts.Affine, 'file')
            transformParams = [transformParams ' -t ' antsWarpProducts.Affine ' '];
        else
            error(['Unable to complete transform, affine product missing: ' antsWarpProducts.Affine]);
        end
    end
    if isempty(transformParams)
        error('ANTs: No transformation products specified');
    end
else
    if ~isempty(antsWarpProducts.InverseWarp)
        if exist(antsWarpProducts.InverseWarp,'file')
            transformParams = [' -t ' antsWarpProducts.InverseWarp ' '];
        else
            error(['Unable to complete transform, warp product missing: ' antsWarpProducts.Warp]);
        end
    end
    if ~isempty(deformS.algorithmParamsS.antsWarpProducts.Affine)
        if exist(deformS.algorithmParamsS.antsWarpProducts.Affine, 'file')
            transformParams = [transformParams ' -t  [ ' antsWarpProducts.Affine ',1] '];
        else
            error(['Unable to complete transform, affine product missing: ' antsWarpProducts.Affine]);
        end
    end
    if isempty(transformParams)
        error('ANTs: No transformation products specified');
    end
end


% This make.m is for MATLAB and OCTAVE under Windows, Mac, and Unix

try
	Type = ver;
	% This part is for OCTAVE
	if(strcmp(Type(1).Name, 'Octave') == 1)
		mex libsvmread.c
		mex libsvmwrite.c
		mex train.c linear_model_matlab.c ../linear.cpp ../tron.cpp ../blas/*.c
		mex predict.c linear_model_matlab.c ../linear.cpp ../tron.cpp ../blas/*.c
	% This part is for MATLAB
	% Add -largeArrayDims on 64-bit machines of MATLAB
	else
		mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims libsvmread.c
		mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims libsvmwrite.c
		mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims train.c linear_model_matlab.c ../linear.cpp ../tron.cpp "../blas/*.c"
		mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims predict.c linear_model_matlab.c ../linear.cpp ../tron.cpp "../blas/*.c"
	end
catch
	fprintf('If make.m fails, please check README about detailed instructions.\n');
end


%% chkarg
% Throw an exception if the argument is |false|.

%%% Syntax
%  chkarg(truth, errmsg, varargin)

%%% Description
% |chkarg(truth, errmsg, varargin)| throws an exception of |truth| is |false|.
% |errmsg| is an error message embedded in the exception.  The error message
% can be generated by formatting additional arguments |varargin|; in this case,
% |errmsg| should be a format string.

%%% Example
%   chkarg(isreal(3+4i), '3+4i is not real.');

function chkarg(truth, errmsg, varargin)

errmsg = sprintf(errmsg, varargin{:});

if ~truth
	exception = MException('Maxwell:argChk', 'Wrong argument: %s', errmsg);
	throwAsCaller(exception);
end


function result = emap(func, x)
%An alias for cellfun, which applies function func to all elements
%of cell array x, but with UniformOutput set to false
if nargin<2
  error('Not enough arguments to cellfunc2\n');
end
result = cellfun(func, x, 'UniformOutput', false);


function iter = newIterator(max_its,varargin)
%function iter = newIterator(max_its,varargin)
%
% create new iterator.  assumes the value is always increasing
    iter.max_its = max_its;
    [iter.thresh iter.min_its iter.debug] = process_options(varargin,'thresh',0,'min-its',0,'debug',false);
    iter.prev = -inf;
    iter.done = false;
    iter.its = 0;
end


%NONNEWTONIAN Non-Newtonian flow physics mode.

% Copyright 2013-2022 Precise Simulation, Ltd.


function K = k11(x, xp, hyp, i)

K = k11.ku1u1(x, xp, hyp, i);

end


function [rx,re] = addToGraph(x,e,rx,re)

global Map

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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 3 of the License, or
%   (at your option) any later version.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


clear

pocr_dir = 'D:\Dropbox\Dropbox\AAM\3rd party models\PO-CR\300VW_mtcnn/';
cfss_dir = 'D:\Dropbox\Dropbox\AAM\3rd party models\CVPR15-CFSS-master\CVPR15-CFSS-master\300VW_mtcnn/';
iccr_dir = 'D:\demo_iccr\iCCR\iCCR\out_300VW/';
chehra_dir = 'D:\Datasets\300VW_Dataset_2015_12_14\chehra_out_mtcnn/';
sdm_dir = 'D:\Datasets\300VW_Dataset_2015_12_14\sdm_out_mtcnn/';
cfan_dir = 'D:\Datasets\300VW_Dataset_2015_12_14\CFAN_mtcnn/';

%% Gather predictions and ground truth
cat_1 = [ 114, 124, 125, 126, 150, 158, 401, 402, 505, 506, 507, 508, 509, 510, 511, 514, 515, 518, 519, 520, 521, 522, 524, 525, 537, 538, 540, 541, 546, 547, 548];
cat_2 = [203, 208, 211, 212, 213, 214, 218, 224, 403, 404, 405, 406, 407, 408, 409, 412, 550, 551, 553];
cat_3 = [410, 411, 516, 517, 526, 528, 529, 530, 531, 533, 557, 558, 559, 562];

%%

cfss_preds = zeros(68,2,0);
iccr_preds = zeros(66,2,0);
clnf_preds = zeros(68,2,0);
cfan_preds = zeros(68,2,0);
sdm_preds = zeros(49,2,0);
drmf_preds = zeros(49,2,0);
pocr_preds = zeros(49,2,0);

labels = zeros(68,2,0);
% Load results
for i=cat_1

    load(['CLNF_res_general/', num2str(i)]);
    clnf_preds = cat(3, clnf_preds, preds);

    labels = cat(3, labels, gt_landmarks);
    load([cfss_dir,  num2str(i)]);
    cfss_preds = cat(3, cfss_preds, preds);

    load([cfan_dir,  num2str(i)]);
    cfan_preds = cat(3, cfan_preds, preds);

    load([iccr_dir,  num2str(i)]);
    iccr_preds = cat(3, iccr_preds, pred_landmarks);

    load([chehra_dir,  num2str(i)]);
    drmf_preds = cat(3, drmf_preds, preds);

    load([pocr_dir,  num2str(i)]);
    pocr_preds = cat(3, pocr_preds, preds);
end

% As ICCR uses only 66 landmarks adapt all the others to the same format
labels = labels([1:60,62:64,66:end],:,:);
clnf_preds = clnf_preds([1:60,62:64,66:end],:,:);
cfss_preds = cfss_preds([1:60,62:64,66:end],:,:);
cfan_preds = cfan_preds([1:60,62:64,66:end],:,:);

clnf_error_66_cat_1 = compute_error(labels, clnf_preds);
cfss_error_66_cat_1 = compute_error(labels, cfss_preds - 0.5);
cfan_error_66_cat_1 = compute_error(labels, cfan_preds - 1.0);
iccr_error_66_cat_1 = compute_error(labels, iccr_preds);

% Do the 49 point version
labels = labels(18:end,:,:);

clnf_preds = clnf_preds(18:end,:,:);
cfan_preds = cfan_preds(18:end,:,:);
cfss_preds = cfss_preds(18:end,:,:);
iccr_preds = iccr_preds(18:end,:,:);

clnf_error_49_cat_1 = compute_error(labels, clnf_preds);
cfss_error_49_cat_1 = compute_error(labels, cfss_preds - 0.5);
cfan_error_49_cat_1 = compute_error(labels, cfan_preds - 1.0);
iccr_error_49_cat_1 = compute_error(labels, iccr_preds);
drmf_error_49_cat_1 = compute_error(labels, drmf_preds-0.5);
pocr_error_49_cat_1 = compute_error(labels, pocr_preds);

%%

cfss_preds = zeros(68,2,0);
iccr_preds = zeros(66,2,0);
clnf_preds = zeros(68,2,0);
cfan_preds = zeros(68,2,0);
sdm_preds = zeros(49,2,0);
drmf_preds = zeros(49,2,0);
pocr_preds = zeros(49,2,0);

labels = zeros(68,2,0);
% Load results
for i=cat_2

    load(['CLNF_res_general/', num2str(i)]);
    clnf_preds = cat(3, clnf_preds, preds);

    labels = cat(3, labels, gt_landmarks);
    load([cfss_dir,  num2str(i)]);
    cfss_preds = cat(3, cfss_preds, preds);

    load([cfan_dir,  num2str(i)]);
    cfan_preds = cat(3, cfan_preds, preds);

    load([iccr_dir,  num2str(i)]);
    iccr_preds = cat(3, iccr_preds, pred_landmarks);

    load([chehra_dir,  num2str(i)]);
    drmf_preds = cat(3, drmf_preds, preds);

    load([pocr_dir,  num2str(i)]);
    pocr_preds = cat(3, pocr_preds, preds);
end

% As ICCR uses only 66 landmarks adapt all the others to the same format
labels = labels([1:60,62:64,66:end],:,:);
clnf_preds = clnf_preds([1:60,62:64,66:end],:,:);
cfss_preds = cfss_preds([1:60,62:64,66:end],:,:);
cfan_preds = cfan_preds([1:60,62:64,66:end],:,:);

clnf_error_66_cat_2 = compute_error(labels, clnf_preds);
cfss_error_66_cat_2 = compute_error(labels, cfss_preds - 0.5);
cfan_error_66_cat_2 = compute_error(labels, cfan_preds - 1.0);
iccr_error_66_cat_2 = compute_error(labels, iccr_preds);

% Do the 49 point version
labels = labels(18:end,:,:);

clnf_preds = clnf_preds(18:end,:,:);
cfan_preds = cfan_preds(18:end,:,:);
cfss_preds = cfss_preds(18:end,:,:);
iccr_preds = iccr_preds(18:end,:,:);

clnf_error_49_cat_2 = compute_error(labels, clnf_preds);
cfss_error_49_cat_2 = compute_error(labels, cfss_preds - 0.5);
cfan_error_49_cat_2 = compute_error(labels, cfan_preds - 1.0);
iccr_error_49_cat_2 = compute_error(labels, iccr_preds);
drmf_error_49_cat_2 = compute_error(labels, drmf_preds-0.5);
pocr_error_49_cat_2 = compute_error(labels, pocr_preds);

%%
cfss_preds = zeros(68,2,0);
iccr_preds = zeros(66,2,0);
clnf_preds = zeros(68,2,0);
cfan_preds = zeros(68,2,0);
sdm_preds = zeros(49,2,0);
drmf_preds = zeros(49,2,0);
pocr_preds = zeros(49,2,0);

labels = zeros(68,2,0);
% Load results
for i=cat_3

    load(['CLNF_res_general/', num2str(i)]);
    clnf_preds = cat(3, clnf_preds, preds);

    labels = cat(3, labels, gt_landmarks);
    load([cfss_dir,  num2str(i)]);
    cfss_preds = cat(3, cfss_preds, preds);

    load([cfan_dir,  num2str(i)]);
    cfan_preds = cat(3, cfan_preds, preds);

    load([iccr_dir,  num2str(i)]);
    iccr_preds = cat(3, iccr_preds, pred_landmarks);

    load([chehra_dir,  num2str(i)]);
    drmf_preds = cat(3, drmf_preds, preds);

    load([pocr_dir,  num2str(i)]);
    pocr_preds = cat(3, pocr_preds, preds);
end

% As ICCR uses only 66 landmarks adapt all the others to the same format
labels = labels([1:60,62:64,66:end],:,:);
clnf_preds = clnf_preds([1:60,62:64,66:end],:,:);
cfss_preds = cfss_preds([1:60,62:64,66:end],:,:);
cfan_preds = cfan_preds([1:60,62:64,66:end],:,:);

clnf_error_66_cat_3 = compute_error(labels, clnf_preds);
cfss_error_66_cat_3 = compute_error(labels, cfss_preds - 0.5);
cfan_error_66_cat_3 = compute_error(labels, cfan_preds - 1.0);
iccr_error_66_cat_3 = compute_error(labels, iccr_preds);

% Do the 49 point version
labels = labels(18:end,:,:);

clnf_preds = clnf_preds(18:end,:,:);
cfan_preds = cfan_preds(18:end,:,:);
cfss_preds = cfss_preds(18:end,:,:);
iccr_preds = iccr_preds(18:end,:,:);

clnf_error_49_cat_3 = compute_error(labels, clnf_preds);
cfss_error_49_cat_3 = compute_error(labels, cfss_preds - 0.5);
cfan_error_49_cat_3 = compute_error(labels, cfan_preds - 1.0);
iccr_error_49_cat_3 = compute_error(labels, iccr_preds);
drmf_error_49_cat_3 = compute_error(labels, drmf_preds-0.5);
pocr_error_49_cat_3 = compute_error(labels, pocr_preds);

%% Save the results
save('results/clnf_errors', 'clnf_error_66_cat_1', 'clnf_error_66_cat_2', 'clnf_error_66_cat_3',...
    'clnf_error_49_cat_1', 'clnf_error_49_cat_2', 'clnf_error_49_cat_3');
save('results/cfss_errors', 'cfss_error_66_cat_1', 'cfss_error_66_cat_2', 'cfss_error_66_cat_3',...
    'cfss_error_49_cat_1', 'cfss_error_49_cat_2', 'cfss_error_49_cat_3');
save('results/iccr_errors', 'iccr_error_66_cat_1', 'iccr_error_66_cat_2', 'iccr_error_66_cat_3',...
    'iccr_error_49_cat_1', 'iccr_error_49_cat_2', 'iccr_error_49_cat_3');
save('results/cfan_errors', 'cfan_error_66_cat_1', 'cfan_error_66_cat_2', 'cfan_error_66_cat_3',...
    'cfan_error_49_cat_1', 'cfan_error_49_cat_2', 'cfan_error_49_cat_3');

save('results/drmf_errors', 'drmf_error_49_cat_1', 'drmf_error_49_cat_2', 'drmf_error_49_cat_3');
save('results/pocr_errors', 'pocr_error_49_cat_1', 'pocr_error_49_cat_2', 'pocr_error_49_cat_3');


function lvmClassVisualisePath(call)

% LVMCLASSVISUALISEPATH Latent variable model path drawing in latent space.

% MLTOOLS

global visualiseInfo

switch call
 case 'click'
  visualiseInfo.clicked = ~visualiseInfo.clicked;
  if visualiseInfo.clicked
    [x, y]  = localCheckPointPosition(visualiseInfo);
    if ~isempty(x)
      visualiseInfo.latentPos = [x, y];
      visualiseInfo.posTrail = [x, y];
      visualiseInfo.lastTime = clock;
      visualiseInfo.timeTrail = 0;
    end
  end
 case 'move'
  if visualiseInfo.clicked
    [x, y]  = localCheckPointPosition(visualiseInfo);
    if ~isempty(x)
      visualiseInfo.latentPos=[x, y];
      % Draw connecting lines
      iplot(visualiseInfo.plotAxes, x, y);
      visualiseInfo.posTrail = [visualiseInfo.posTrail; [x, y]];
      timeNow = clock;
      visualiseInfo.timeTrail = [visualiseInfo.timeTrail; visualiseInfo.timeTrail(end)+etime(timeNow, ...
                                                        visualiseInfo.lastTime)];
      visualiseInfo.lastTime = timeNow;
    end
  end
end

function point = localGetNormCursorPoint(figHandle)

point = get(figHandle, 'currentPoint');
figPos = get(figHandle, 'Position');
% Normalise the point of the curstor
point(1) = point(1)/figPos(3);
point(2) = point(2)/figPos(4);

function [x, y] = localGetNormAxesPoint(point, axesHandle)

position = get(axesHandle, 'Position');
x = (point(1) - position(1))/position(3);
y = (point(2) - position(2))/position(4);
lim = get(axesHandle, 'XLim');
x = x*(lim(2) - lim(1));
x = x + lim(1);
lim = get(axesHandle, 'YLim');
y = y*(lim(2) - lim(1));
y = y + lim(1);

function [x, y] = localCheckPointPosition(visualiseInfo)

% Get the point of the cursor
point = localGetNormCursorPoint(gcf);

% get the position of the axes
position = get(visualiseInfo.plotAxes, 'Position');

% Check if the pointer is in the axes
if point(1) > position(1) ...
      & point(1) < position(1) + position(3) ...
      & point(2) > position(2) ...
      & point(2) < position(2) + position(4);

  % Rescale the point according to the axes
  [x y] = localGetNormAxesPoint(point, visualiseInfo.plotAxes);

  % Find the nearest point
else
  % Return nothing
  x = [];
  y = [];
end


function updateScatterhistogram(obj, plotIndex)

    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%

    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    [xsource, ysource, xoverlay, yoverlay] = findSourceAxis(obj,axIndex);

    [~, ~, groupName] = getTraceData(plotData);

    %-------------------------------------------------------------------------%

    %-SET MAIN SCATTER PLOT-%

    %-set plotly layout-%
    updateMainScatterAxis(obj, plotIndex);
    updateTitle(obj, plotIndex);
    updateLegend(obj, plotIndex, groupName);
    obj.layout.barmode = 'overlay';
    obj.layout.bargap = 0.05;

    %-set plotly data-%
    updateMainScatter(obj, plotIndex);

    %-------------------------------------------------------------------------%

    %-SET MARGINAL PLOTS-%

    %-set plotly layout-%
    updateXMarginalAxis(obj, plotIndex);
    updateYMarginalAxis(obj, plotIndex);

    %-set plotly data-%
    switch plotData.HistogramDisplayStyle
        case 'stairs'
            updateMarginalHistogram(obj, plotIndex, 'X');
            updateMarginalHistogram(obj, plotIndex, 'Y');
        case 'smooth'
            updateMarginalSmooth(obj, plotIndex, 'X');
            updateMarginalSmooth(obj, plotIndex, 'Y');
    end

    %-------------------------------------------------------------------------%
end

function updateMainScatter(obj, plotIndex)

    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%

    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    [xSource, ySource] = findSourceAxis(obj,axIndex);

    %-get trace data-%
    traceIndex = plotIndex;
    [xData, yData, groupName] = getTraceData(plotData);

    %-------------------------------------------------------------------------%

    %-SET EACH ALL TRACES-%

    for t = 1:length(xData)

        %---------------------------------------------------------------------%
        %-get current trace index-%
        if t > 1
            obj.PlotOptions.nPlots = obj.PlotOptions.nPlots + 1;
            traceIndex = obj.PlotOptions.nPlots;
        end

        %---------------------------------------------------------------------%

        %-set current trace-%
        obj.data{traceIndex}.type = 'scatter';
        obj.data{traceIndex}.mode = 'markers';
        obj.data{traceIndex}.xaxis = sprintf('x%d', xSource);
        obj.data{traceIndex}.yaxis = sprintf('y%d', ySource);
        obj.data{traceIndex}.visible = strcmp(plotData.Visible,'on');

        %---------------------------------------------------------------------%

        %-set current trace data-%
        obj.data{traceIndex}.x = xData{t};
        obj.data{traceIndex}.y = yData{t};

        %---------------------------------------------------------------------%

        %-scatter marker-%
        childmarker = extractScatterhistogramMarker(plotData, t);
        obj.data{traceIndex}.marker = childmarker;

        %-legend-%

        if ~isempty(groupName)
            try
              obj.data{traceIndex}.name = char(groupName(t));
            catch
              obj.data{traceIndex}.name = char(string(groupName(t)));
            end

            obj.data{traceIndex}.legendgroup = obj.data{traceIndex}.name;
            obj.data{traceIndex}.showlegend = true;
        end

        %---------------------------------------------------------------------%
    end

    %-------------------------------------------------------------------------%
end

function updateMainScatterAxis(obj, plotIndex)

    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%

    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    [xSource, ySource] = findSourceAxis(obj,axIndex);

    %-------------------------------------------------------------------------%

    %-set x axis-%
    xaxis = getMainScatterAxis(plotData, 'X');
    xaxis.anchor = sprintf('y%d', xSource);
    obj.layout = setfield(obj.layout, sprintf('xaxis%d',xSource), xaxis);

    %-set y axis-%
    yaxis = getMainScatterAxis(plotData, 'Y');
    yaxis.anchor = sprintf('x%d', ySource);
    obj.layout = setfield(obj.layout, sprintf('yaxis%d',xSource), yaxis);

    %-------------------------------------------------------------------------%
end

function ax = getMainScatterAxis(plotData, axName)

    axisPos = plotData.Position;
    axisColor = 'rgba(0,0,0, 0.9)';
    axisLim = eval(sprintf('plotData.%sLimits', axName));
    axisPlot = eval(sprintf('plotData.%sData', axName));
    axisLabel = eval(sprintf('plotData.%sLabel', axName));

    switch axName
        case 'X'
            axisDomain = min([axisPos(1) sum(axisPos([1 3]))], 1);
        case 'Y'
            axisDomain = min([axisPos(2) sum(axisPos([2 4]))], 1);
    end

    %-general-%
    ax.domain = axisDomain;
    ax.linecolor = axisColor;
    ax.zeroline = true;
    ax.showgrid = false;
    ax.mirror = 'ticks';

    %-ticks-%
    ax.showticklabels = true;
    ax.ticks = 'inside';
    ax.tickfont.size = 1.2*plotData.FontSize;
    ax.tickcolor = axisColor;
    ax.tickfont.family = matlab2plotlyfont(plotData.FontName);

    %-label-%
    ax.title.text = axisLabel;
    if ~isempty(axisLabel), axisLabel = parseString(axisLabel); end
    ax.title.font.size = 1.2*plotData.FontSize;
    ax.title.font.color = 1.2*axisColor;
    ax.title.font.family = matlab2plotlyfont(plotData.FontName);

    %-range and ticklabels-%
    if ~iscategorical(axisPlot)
        ax.range = axisLim;
        ax.nticks = 10;
    else
        [axCateg, ~, axisPlot] = unique(axisPlot);
        ax.range = [min(axisPlot)-0.5, max(axisPlot)+0.5];
        ax.tickvals = 1:max(axisPlot);
        for n=1:length(axCateg), ax.ticktext{n} = char(axCateg(n)); end
    end
end

function updateMarginalHistogram(obj, plotIndex, axName)

    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%
    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    if strcmp(axName, 'X'), xySource = 1; else, xySource = 2; end
    xySource = obj.State.Figure.NumAxes + xySource;

    %-get trace data-%
    [xData, yData, groupName] = getTraceData(plotData);

    %-------------------------------------------------------------------------%

    %-SET ALL TRACES-%

    for t = 1:length(xData)

        %---------------------------------------------------------------------%

        %-get current trace index-%
        obj.PlotOptions.nPlots = obj.PlotOptions.nPlots + 1;
        traceIndex = obj.PlotOptions.nPlots;

        %---------------------------------------------------------------------%

        %-set current trace-%
        obj.data{traceIndex}.type = 'histogram';
        obj.data{traceIndex}.xaxis = sprintf('x%d', xySource);
        obj.data{traceIndex}.yaxis = sprintf('y%d', xySource);

        %---------------------------------------------------------------------%

        %-set current plot data-%
        obj.data{traceIndex}.x = xData{t};
        obj.data{traceIndex}.y = yData{t};

        %---------------------------------------------------------------------%

        %-set other trace properties-%
        traceColor = getStringColor(plotData.Color(t,:), 0.7);

        obj.data{traceIndex}.marker.color = traceColor;
        obj.data{traceIndex}.histnorm = 'probability';
        obj.data{traceIndex}.histfunc = 'count';
        obj.data{traceIndex}.showlegend = false;

        switch axName
            case 'X'
                obj.data{traceIndex}.orientation = 'v';
                try obj.data{traceIndex}.nbinsx = plotData.NumBins(1,t); end
            case 'Y'
                obj.data{traceIndex}.orientation = 'h';
                try obj.data{traceIndex}.nbinsy = plotData.NumBins(2,t); end
        end

        %---------------------------------------------------------------------%

        %-link legend-%
        if ~isempty(groupName)
            try
                obj.data{traceIndex}.name = char(groupName(t));
            catch
                obj.data{traceIndex}.name = char(string(groupName(t)));
            end

            obj.data{traceIndex}.legendgroup = obj.data{traceIndex}.name;
        end

        %---------------------------------------------------------------------%
    end

    %-------------------------------------------------------------------------%
end

function updateMarginalSmooth(obj, plotIndex, axName)

    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%
    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    if strcmp(axName, 'X'), xySource = 1; else, xySource = 2; end
    xySource = obj.State.Figure.NumAxes + xySource;

    %-get trace data-%
    [xData, yData, groupName] = getTraceData(plotData);
    axisLim = getAxisLim(plotData, axName);
    evalPoints = linspace(axisLim(1), axisLim(2), 500);

    %-------------------------------------------------------------------------%

    %-SET ALL TRACES-%

    for t = 1:length(xData)

        %---------------------------------------------------------------------%

        %-get current trace index-%
        obj.PlotOptions.nPlots = obj.PlotOptions.nPlots + 1;
        traceIndex = obj.PlotOptions.nPlots;

        %---------------------------------------------------------------------%

        %-set current trace-%
        obj.data{traceIndex}.type = 'scatter';
        obj.data{traceIndex}.mode = 'lines';
        obj.data{traceIndex}.xaxis = sprintf('x%d', xySource);
        obj.data{traceIndex}.yaxis = sprintf('y%d', xySource);

        %---------------------------------------------------------------------%

        %-get current trace data-%
        if strcmp(axName, 'X')
            [ySmooth, xSmooth] = ksdensity(xData{t}, evalPoints);
        elseif strcmp(axName, 'Y')
            [xSmooth, ySmooth] = ksdensity(yData{t}, evalPoints);
        end

        %-set current plot data-%
        obj.data{traceIndex}.x = xSmooth;
        obj.data{traceIndex}.y = ySmooth;

        %---------------------------------------------------------------------%

        %-set other trace properties-%
        traceColor = getStringColor(plotData.Color(t,:), 0.7);
        lineStyle = plotData.LineStyle(t);

        obj.data{traceIndex}.line.color = traceColor;
        obj.data{traceIndex}.line.width = 2*plotData.LineWidth(t);
        obj.data{traceIndex}.line.dash = getLineDash(lineStyle);
        obj.data{traceIndex}.showlegend = false;

        %---------------------------------------------------------------------%

        %-link legend-%
        if ~isempty(groupName)
            try
                obj.data{traceIndex}.name = char(groupName(t));
            catch
                obj.data{traceIndex}.name = char(string(groupName(t)));
            end

            obj.data{traceIndex}.legendgroup = obj.data{traceIndex}.name;
        end

        %---------------------------------------------------------------------%
    end

    %-------------------------------------------------------------------------%
end

function updateXMarginalAxis(obj, plotIndex)
    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%

    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    % [xSource, ySource] = findSourceAxis(obj,axIndex);

    xySource = obj.State.Figure.NumAxes + 1;

    %-------------------------------------------------------------------------%

    %-set x axis-%
    xaxis = getXMarginalAxis(plotData, 'X');
    xaxis.anchor = sprintf('y%d', xySource);
    obj.layout = setfield(obj.layout, sprintf('xaxis%d',xySource), xaxis);

    %-set y axis-%
    yaxis = getXMarginalAxis(plotData, 'Y');
    yaxis.anchor = sprintf('x%d', xySource);
    obj.layout = setfield(obj.layout, sprintf('yaxis%d',xySource), yaxis);

    %-------------------------------------------------------------------------%
end

function ax = getXMarginalAxis(plotData, axName)

    switch axName
        case 'X'
            ax.showline = true;
            ax.linecolor = 'black';
            ax.range = getAxisLim(plotData, 'X');
        case 'Y'
            ax.showline = false;
    end

    ax.domain = getXMarginalDomain(plotData, axName);
    ax.showgrid = false;
    ax.showticklabels = false;
    ax.zeroline = false;
end

function axisDomain = getXMarginalDomain(plotData, axName)
    axisPos = plotData.Position;
    plotLocation = plotData.ScatterPlotLocation;
    isTitle = ~isempty(plotData.Title);

    switch axName
        case 'X'
            axisDomain = min([axisPos(1) sum(axisPos([1,3]))], 1);
        case 'Y'
            if ~isempty(strfind(plotLocation, 'South'))
                yo = axisPos(2) + axisPos(4) + 0.01;
                if isTitle, h=0.9-yo; else, h = 0.96 - yo; end
            elseif ~isempty(strfind(plotLocation, 'North'))
                yo = 0.02; h = axisPos(2)*0.7-yo;
            end

            axisDomain = min([yo yo+h], 1);
    end
end

function updateYMarginalAxis(obj, plotIndex)
    %-------------------------------------------------------------------------%

    %-INITIALIZATIONS-%

    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    % [xSource, ySource] = findSourceAxis(obj,axIndex);

    xySource = obj.State.Figure.NumAxes + 2;

    %-------------------------------------------------------------------------%

    %-set x axis-%
    xaxis = getYMarginalAxis(plotData, 'X');
    xaxis.anchor = sprintf('y%d', xySource);
    obj.layout = setfield(obj.layout, sprintf('xaxis%d',xySource), xaxis);

    %-set y axis-%
    yaxis = getYMarginalAxis(plotData, 'Y');
    yaxis.anchor = sprintf('x%d', xySource);
    obj.layout = setfield(obj.layout, sprintf('yaxis%d',xySource), yaxis);

    %-------------------------------------------------------------------------%
end

function ax = getYMarginalAxis(plotData, axName)

    switch axName
        case 'X'
            ax.showline = false;
        case 'Y'
            ax.showline = true;
            ax.linecolor = 'black';
            ax.range = getAxisLim(plotData, 'Y');
    end

    ax.domain = getYMarginalDomain(plotData, axName);
    ax.showgrid = false;
    ax.showticklabels = false;
    ax.zeroline = false;
end

function axisDomain = getYMarginalDomain(plotData, axName)
    axisPos = plotData.Position;
    plotLocation = plotData.ScatterPlotLocation;

    switch axName
        case 'X'
            if ~isempty(strfind(plotLocation, 'West'))
                xo = axisPos(1) + axisPos(3) + 0.01;
                w = 0.96-xo;
            elseif ~isempty(strfind(plotLocation, 'East'))
                xo = 0.02; w = axisPos(1)*0.7-xo;
            end

            axisDomain = min([xo xo+w], 1);

        case 'Y'

            axisDomain = min([axisPos(2) sum(axisPos([2,4]))], 1);

    end
end

function lineDash = getLineDash(lineStyle)
    switch lineStyle
        case '-'
            lineDash = 'solid';
        case '--'
            lineDash = 'dash';
        case ':'
            lineDash = 'dot';
        case '-.'
            lineDash = 'dashdot';
    end
end

function axisLim = getAxisLim(plotData, axName)
    axisLim = eval(sprintf('plotData.%sLimits', axName));
    axisPlot = eval(sprintf('plotData.%sData', axName));

    if iscategorical(axisPlot)
        axisPlot = eval(sprintf('plotData.%sData', axName));
        [~, ~, axisPlot] = unique(axisPlot);
        axisLim = [min(axisPlot)-0.5, max(axisPlot)+0.5];
    end
end

function [xData, yData, groupName] = getTraceData(plotData)
    %-parcing data-%
    xPlot = plotData.XData;
    yPlot = plotData.YData;

    if iscategorical(xPlot), [~, ~, xPlot] = unique(xPlot); end
    if iscategorical(yPlot), [~, ~, yPlot] = unique(yPlot); end

    xData = {}; yData = {};
    groupData = plotData.GroupData;
    isByGroups = ~isempty(groupData);
    groupName = {};

    if isByGroups
        if iscellstr(groupData), groupData = string(groupData); end
        groupName = unique(groupData,'stable');

        for g = 1:length(groupName)
            groudInd = groupData == groupName(g);
            xData{g} = xPlot(groudInd);
            yData{g} = yPlot(groudInd);
        end

        if isnumeric(groupName), groupName=num2str(groupName); end;
    else
        xData{1} = xPlot;
        yData{1} = yPlot;
    end
end

function updateTitle(obj, plotIndex)
    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    [xSource, ~] = findSourceAxis(obj,axIndex);
    isTitle = ~isempty(plotData.Title);

    obj.layout.annotations{1}.text = plotData.Title;
    obj.layout.annotations{1}.showarrow = false;

    if isTitle
        titleText = sprintf('%s', parseString(plotData.Title));
        titleFamily = matlab2plotlyfont(plotData.FontName);
        xaxis = eval(sprintf('obj.layout.xaxis%d', xSource));

        obj.layout.annotations{1}.text = titleText;
        obj.layout.annotations{1}.x = mean(xaxis.domain);
        obj.layout.annotations{1}.y = 0.96;
        obj.layout.annotations{1}.xref = 'paper';
        obj.layout.annotations{1}.yref = 'paper';
        obj.layout.annotations{1}.yanchor = 'top';
        obj.layout.annotations{1}.xanchor = 'middle';

        obj.layout.annotations{1}.font.color = 'black';
        obj.layout.annotations{1}.font.family = titleFamily;
        obj.layout.annotations{1}.font.size = 1.5*plotData.FontSize;
    end
end

function updateLegend(obj, plotIndex, groupName)
    axIndex = obj.getAxisIndex(obj.State.Plot(plotIndex).AssociatedAxis);
    plotData = get(obj.State.Plot(plotIndex).Handle);
    isTitle = isempty(plotData.Title);

    if ~isempty(groupName)
        fontFamily = matlab2plotlyfont(plotData.FontName);
        legTitle = plotData.LegendTitle;
        plotLocation = plotData.ScatterPlotLocation;

        obj.layout.showlegend = true;
        obj.layout.legend.xref = 'paper';
        obj.layout.legend.valign = 'middle';
        obj.layout.legend.borderwidth = 1;
        obj.layout.legend.bordercolor = 'rgba(0,0,0,0.2)';
        obj.layout.legend.font.size = 1.0*plotData.FontSize;
        obj.layout.legend.font.family = fontFamily;

        if ~isempty(legTitle) > 0
            legTitle = sprintf('%s', parseString(legTitle));

            obj.layout.legend.title.text = legTitle;
            obj.layout.legend.title.side = 'top';
            obj.layout.legend.title.font.size = 1.2*plotData.FontSize;
            obj.layout.legend.title.font.color = 'black';
            obj.layout.legend.title.font.family = fontFamily;
        end

        if ~isempty(strfind(plotLocation, 'SouthWest'))
            obj.layout.legend.x = 0.96;
            obj.layout.legend.y = 0.96;
            obj.layout.legend.xanchor = 'right';
            obj.layout.legend.yanchor = 'top';
        elseif ~isempty(strfind(plotLocation, 'NorthEast'))
            obj.layout.legend.x = 0.02;
            obj.layout.legend.y = 0.02;
            obj.layout.legend.xanchor = 'left';
            obj.layout.legend.yanchor = 'bottom';
        end
    end
end


classdef Mesh_Composite < AbstractMesh

    properties (GetAccess = public, SetAccess = public)
        meshes
        nMeshes
        nActiveMeshes
        activeMeshesList

        globalConnectivities
    end

    methods (Access = public)

        function obj = Mesh_Composite()
            obj.createMeshes();
        end

        function aMeshes = getActiveMeshes(obj)
            aMeshes = cell(1,obj.nActiveMeshes);
            for iActive = 1:obj.nActiveMeshes
                iMesh = obj.activeMeshesList(iActive);
                aMeshes{iActive} = obj.meshes{iMesh};
            end
        end
    end

    methods (Access = protected)

        function append(obj,mesh)
           obj.meshes{end+1} = mesh;
        end

    end

    methods (Access = private)

        function createMeshes(obj)

        end

    end

end


classdef MimMouseCursorStatus < handle
    % MimMouseCursorStatus. Data representing the voxel under the mouse cursor
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties
        GlobalCoordX
        GlobalCoordY
        GlobalCoordZ

        ImageExists

        ImageValue
        RescaledValue
        RescaleUnits
        OverlayValue
    end
end