function fsLabelName = fs_getROILabelNameFromLUT(labelIndex)
%Initialize a new vista function
%
%  fName(arguments)
%
%
% INPUTS
%        index for an ROI returned by the freesurfer LUT
%        e.g., left calcarine is 1021
%
% RETURNS
%        the name of the freesurfer roi
%        'ctx-lh-pericalcarine'
%
% Web Resources
%     mrvBrowseSVN('fs_getROILabelNameFromLUT')
%
% Example:
%   calcarineROIname = fs_getROILabelNameFromLUT(1021);
%
% Copyright Stanford team, mrVista, 2011
%
% MP, FP 2011.08.20

load fslabel.mat; % load the freesurfer lut

index = find(ismember(fslabel.num, num2str(labelIndex))==1);
if isempty(index)
    fsLabelName = '';
    warning('This index does not exit in the fslabel LUT');
else
    fsLabelName = (fslabel.name{index});
end

return


function [varargout] = iscolumn(varargin)

% ISCOLUMNN is a drop-in replacement for the same function that was
% introduced in MATLAB R2010b.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% try to automatically remove incorrect compat folders from the path, see https://github.com/fieldtrip/fieldtrip/issues/899

if isempty(strfind(mfilename('fullpath'), matlabroot))
  % automatic cleanup does not work when FieldTrip is installed inside the MATLAB path, see https://github.com/fieldtrip/fieldtrip/issues/1527

  alternatives = which(mfilename, '-all');
  if ~iscell(alternatives)
    % this is needed for octave, see https://github.com/fieldtrip/fieldtrip/pull/1171
    alternatives = {alternatives};
  end

  keep = true(size(alternatives));
  for i=1:numel(alternatives)
    keep(i) = keep(i) && ~any(alternatives{i}=='@');  % exclude methods from classes
    keep(i) = keep(i) && alternatives{i}(end)~='p';   % exclude precompiled files
  end
  alternatives = alternatives(keep);

  if exist(mfilename, 'builtin') || any(strncmp(alternatives, matlabroot, length(matlabroot)) & cellfun(@isempty, strfind(alternatives, fullfile('private', mfilename))))
    % remove this directory from the path
    p = fileparts(mfilename('fullpath'));
    warning('removing "%s" from your path, see http://bit.ly/2SPPjUS', p);
    rmpath(p);
    % call the original MATLAB function
    if exist(mfilename, 'builtin')
      [varargout{1:nargout}] = builtin(mfilename, varargin{:});
    else
      [varargout{1:nargout}] = feval(mfilename, varargin{:});
    end
    return
  end

end % automatic cleanup of compat directories

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% this is where the actual replacement code starts
% function tf = iscolumn(x)

% deal with the input arguments
if nargin==1
  [x] = deal(varargin{1:1});
else
  error('incorrect number of input arguments')
end

tf = length(size(x))==2 && size(x,2)==1;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% deal with the output arguments

varargout = {tf};


function varargout = process_mri_deface( varargin )
% PROCESS_MRI_DEFACE: Remove the facial features from an MRI.

% @=============================================================================
% 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, 2019-2023
%          Inspired from SPM12 function spm_deface

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Deface MRI volumes';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = {'Import', 'Import anatomy'};
    sProcess.Index       = 41;
    sProcess.Description = 'https://surfer.nmr.mgh.harvard.edu/fswiki/mri_deface';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'import'};
    sProcess.OutputTypes = {'import'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 0;
    % Option: Subject name
    sProcess.options.subjectname.Comment = 'Subject name:';
    sProcess.options.subjectname.Type    = 'subjectname';
    sProcess.options.subjectname.Value   = 'NewSubject';
    % Option: Method
    sProcess.options.method_title.Comment = '
Defacing method:';
    sProcess.options.method_title.Type    = 'label';
    sProcess.options.method.Comment = {'FreeSurfer: mri_deface', 'BrainSuite: Remove the face with a pre-defined mask', 'SPM: Cut below a plane in MNI coordinates (a.x+b.y+c.z+d=0)'; ...
                                       'freesurfer', 'brainsuite', 'spm'};
    sProcess.options.method.Type    = 'radio_label';
    sProcess.options.method.Value   = 'spm';
    % SPM: MNI plane coordinates
    sProcess.options.mniplane.Comment = '      [a, b, c, d]: ';
    sProcess.options.mniplane.Type    = 'value';
    sProcess.options.mniplane.Value   = {[0, -11, 9.6, 1], 'list', 3};
    sProcess.options.mniplanedef.Comment = '      Cut face: a=0, b=-11, c=9.6, d=1
      Cut neck: a=0, b=0, c=1, d=0.115 (Z<-115)';
    sProcess.options.mniplanedef.Type    = 'label';
    % Option: Recompute head surface
    sProcess.options.defacehead.Comment = 'Recompute head surface';
    sProcess.options.defacehead.Type    = 'checkbox';
    sProcess.options.defacehead.Value   = 1;
    sProcess.options.defacehead.Group   = 'output';
    % Option: Overwrite
    sProcess.options.overwrite.Comment = 'Overwrite existing files';
    sProcess.options.overwrite.Type    = 'checkbox';
    sProcess.options.overwrite.Value   = 0;
    sProcess.options.overwrite.Group   = 'output';
end

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

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    OutputFiles = {};

    % ===== GET OPTIONS =====
    OPTIONS.isOverwrite = sProcess.options.overwrite.Value;
    OPTIONS.Method = sProcess.options.method.Value;
    OPTIONS.isInteractive = 0;
    OPTIONS.isDefaceHead = sProcess.options.defacehead.Value;
    if (length(sProcess.options.mniplane.Value{1}) == 4)
        OPTIONS.MNIplane = sProcess.options.mniplane.Value{1};
    else
        bst_report('Error', sProcess, [], 'Invalid MNI plane equation (must be 4 values: a b c d).');
        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 =====
    % Get subject
    [sSubject, iSubject] = bst_get('Subject', SubjectName);
    if isempty(iSubject)
        bst_report('Error', sProcess, [], ['Subject "' SubjectName '" does not exist.']);
        return
    end
    % Check if a MRI is available for the subject
    if isempty(sSubject.Anatomy)
        bst_report('Error', sProcess, [], ['No MRI available for subject "' SubjectName '".']);
        return
    end

    % ===== DEFACE ALL VOLUMES =====
    % Deface volumes
    [DefacedMri, errMsg] = Compute(iSubject, OPTIONS);
    % Error handling
    if isempty(DefacedMri)
        bst_report('Error', sProcess, [], errMsg);
        return;
    elseif ~isempty(errMsg)
        bst_report('Warning', sProcess, [], errMsg);
    end

    OutputFiles = {'import'};
end

%% ===== EXTERNAL CALL =====
% USAGE:  [DefacedFiles, errMsg] = Compute(MriFiles, OPTIONS=[])
%         [DefacedFiles, errMsg] = Compute(iSubject, OPTIONS=[])
function [DefacedFiles, errMsg] = Compute(MriFiles, OPTIONS)
    % Parse inputs
    if ischar(MriFiles)
        MriFiles = {MriFiles};
    elseif isnumeric(MriFiles)
        iSubject = MriFiles;
        % Get volumes to deface
        sSubject = bst_get('Subject', iSubject);
        iNoAtlas = find(cellfun(@(c)isempty(strfind(c, '_volatlas')), {sSubject.Anatomy.FileName}));
        MriFiles = {sSubject.Anatomy(iNoAtlas).FileName};
        % Put the default volume first
        if ~isempty(sSubject.iAnatomy) && ismember(sSubject.iAnatomy, iNoAtlas)
            iOrder = [sSubject.iAnatomy, setdiff(1:length(iNoAtlas), sSubject.iAnatomy)];
            MriFiles = MriFiles(iOrder);
        end
    end
    if (nargin < 2) || isempty(OPTIONS)
        OPTIONS = struct();
    end
    % Ask method to user in interactive mode
    if (~isfield(OPTIONS, 'Method') || isempty(OPTIONS.Method)) && (~isfield(OPTIONS, 'isInteractive') || OPTIONS.isInteractive)
        % Windows: no options anyway (FreeSurfer's mri_deface not available)
        if ismember(bst_get('OsType',0), {'win32', 'win64'})
            OPTIONS.Method = 'spm';
        % Other OS: Ask for choice
        else
            res = java_dialog('combo', 'Select the defacing method:
', 'Deface volume', [], {'spm', 'freesurfer'}, 'freesurfer');
            if isempty(res)
                errMsg = 'Aborted by user.';
                return;
            end
            OPTIONS.Method = res;
        end
    end
    % Default options
    Def_OPTIONS = struct(...
        'Method',        'spm', ...
        'MNIplane',      [0, -11, 9.6, 1], ...
        'isOverwrite',   0, ...
        'isInteractive', 1, ...
        'isDefaceHead',  1);
    OPTIONS = struct_copy_fields(OPTIONS, Def_OPTIONS, 0);
    % Initialize returned variables
    DefacedFiles = {};
    errMsg = [];
    fileTag = ' | deface';

    % Progress bar
    isProgress = bst_progress('isVisible');
    if ~isProgress
        bst_progress('start', 'Deface MRI', 'Initialization...');
    end
    % Process each input file separately
    MriHead = [];
    for iFile = 1:length(MriFiles)
        bst_progress('text', 'Loading input MRI...');
        % Get subject index
        [sSubject, iSubject, iAnatomy] = bst_get('MriFile', MriFiles{iFile});
        % If MRI was already defaced: skip
        if (length(MriFiles) > 1) && ~isempty(strfind(sSubject.Anatomy(iAnatomy).Comment, fileTag))
            continue;
        end
        % Check if it is loaded in memory
        sMri = bst_memory('GetMri', MriFiles{iFile});
        % If not: load it from the file
        if isempty(sMri)
            sMri = in_mri_bst(MriFiles{iFile});
        end

        % Switch depending on the method
        switch (OPTIONS.Method)
            case 'spm'
                % Compute cut mask on first volume only
                if (iFile == 1)
                    [sMri, maskCut, errMsg] = CutMriPlane(sMri, OPTIONS.MNIplane);
                    % Error management (if MNI normalization failed)
                    if ~isempty(errMsg)
                        if ~isProgress
                            bst_progress('stop');
                        end
                        return;
                    end
                % Following volumes: just a apply the same cut
                else
                    sMri.Cube(maskCut) = 0;
                end

            case 'freesurfer'
                % Get path to mri_deface (download if necessary)
                [exePath, talFile, faceFile, errMsg] = InstallMriDeface(OPTIONS.isInteractive);
                if ~isempty(errMsg)
                    if ~isProgress
                        bst_progress('stop');
                    end
                    return;
                end
                disp(['BST> Deface: Using ' exePath]);
                % Save temporary MRI file
                bst_progress('text', 'Saving temporary nii file...');
                TmpDir = bst_get('BrainstormTmpDir', 0, 'deface');
                fileNii = bst_fullfile(TmpDir, 'orig.nii');
                out_mri_nii(sMri, fileNii, 'int16');
                % Change current folder to .brainstorm/tmp, because mri_deface saves a useless log in the current folder
                curdir = pwd;
                cd(TmpDir);
                % Call mri_deface
                bst_progress('text', 'Running mri_deface...');
                fileNiiDefaced = bst_fullfile(TmpDir, 'orig_defaced.nii');
                cmdDeface = [exePath ' ' fileNii ' ' talFile ' ' faceFile ' ' fileNiiDefaced];
                status = system(cmdDeface);
                if (status ~= 0)
                    errMsg = 'Error calling mri_deface (see console for details).';
                    if ~isProgress
                        bst_progress('stop');
                    end
                    return;
                end
                % Restore initial folder
                cd(curdir);
                % Read defaced file
                bst_progress('text', 'Reading defaced file...');
                sMriDefaced = in_mri_nii(fileNiiDefaced, 0, 0, 0);
                % Saves defaced volume
                sMri.Cube = sMriDefaced.Cube;
                % Delete the temporary files
                file_delete(TmpDir, 1, 1);

            case 'brainsuite'
                % Get ICBM152 defacing template
                sTemplate = bst_get('AnatomyDefaults', 'ICBM152');
                MaskFile = bst_fullfile(sTemplate.FilePath, 'facemask_300z.nii.gz');
                if isempty(MaskFile)
                    errMsg = ['Could not find face mask: ' MaskFile];
                    if ~isProgress
                        bst_progress('stop');
                    end
                    return;
                end
                % Load mask file
                sMask = in_mri(MaskFile, 'Nifti1');
                sMask.NCS.R = eye(3);
                sMask.NCS.T = [-99; -135; -184];
                % Reslice to the space of the MRI to deface
                sMaskReslice = mri_reslice(sMask, sMri, 'ncs', 'ncs');
                % Apply mask
                sMri.Cube(sMaskReslice.Cube == 0) = 0;

            otherwise
                errMsg = ['Invalid defacing method: ' OPTIONS.Method];
                if ~isProgress
                    bst_progress('stop');
                end
                return;
        end
        % Add comment tag
        sMri.Comment = [sMri.Comment, fileTag];

        % Remove initial file header
        sMri.Header = [];
        sMri.Histogram = [];
        % Remove file history (may contain information in the original file names)
        sMri.History = [];
        sMri = bst_history('add', sMri, 'process', ['process_mri_deface: ', OPTIONS.Method]);
        if strcmpi(OPTIONS.Method, 'spm')
            sMri = bst_history('add', sMri, 'process', sprintf('Cut below MNI plane: %1.3fx + %1.3fy + %1.3fz + %1.3f = 0', OPTIONS.MNIplane));
        end

        % Save defaced MRI
        bst_progress('text', 'Saving results to database...');
        if OPTIONS.isOverwrite
            % Update file structure
            bst_save(file_fullpath(MriFile), sMri, 'v6');
            DefacedFiles{end+1} = MriFile;
            % Unload from memory to force reloading
            bst_memory('UnloadMri', MriFile);
        % Add new file
        else
            DefacedFiles{end+1} = db_add(iSubject, sMri, 0);
            iAnatomy = length(sSubject.Anatomy) + 1;
        end
        % Update database registration
        sSubject.Anatomy(iAnatomy).FileName = DefacedFiles{end};
        sSubject.Anatomy(iAnatomy).Comment = sMri.Comment;
        bst_set('Subject', iSubject, sSubject);
        % Refresh tree
        panel_protocols('UpdateNode', 'Subject', iSubject);
        panel_protocols('SelectNode', [], 'subject', iSubject, -1);
        % If we recompute the head, it is on the defaced version of the first MRI in the list
        if (iFile == 1)
            MriHead = DefacedFiles{end};
        end
    end

    % ===== RECOMPUTE HEAD SURFACES =====
    if OPTIONS.isDefaceHead && ~isempty(MriHead)
        % Get subject
        [sSubject, iSubject] = bst_get('MriFile', MriHead);
        % Get all head surfaces
        [sSurfaces, iSurfaces] = bst_get('SurfaceFileByType', iSubject, 'Scalp');
        % Remove the BEM surfaces (too smooth to be problematic)
        if ~isempty(sSurfaces)
            iBem = find(~cellfun(@(c)isempty(strfind(c, '_bem_')), {sSurfaces.FileName}));
            if ~isempty(iBem)
                sSurfaces(iBem) = [];
                iSurfaces(iBem) = [];
            end
        end
        % If there are surfaces to delete
        if ~isempty(sSurfaces)
            % Delete head surfaces
            file_delete(file_fullpath({sSurfaces.FileName}), 1);
            % Update database structure
            HeadComment = [sSurfaces(1).Comment, fileTag];
            sSubject.Surface(iSurfaces) = [];
            bst_set('Subject', iSubject, sSubject);
            % Compute new head surface
            sSubject.Anatomy(sSubject.iAnatomy).FileName;
            tess_isohead(MriHead, 10000, 0, 2, HeadComment);
        end
    end

    % Close progress bar
    if ~isProgress
        bst_progress('stop');
    end
end

%% ===== CUT MRI PLANE =====
function [sMri, maskCut, errMsg] = CutMriPlane(sMri, MNIplane)
    % If the MNI normalization are not available: compute it now
    if (~isfield(sMri, 'NCS') || isempty(sMri.NCS) || ~isfield(sMri.NCS, 'R') || ~isfield(sMri.NCS, 'T') || isempty(sMri.NCS.R) || isempty(sMri.NCS.T))
        [sMri, errMsg] = bst_normalize_mni(sMri);
        if ~isempty(errMsg)
            maskCut = [];
            return;
        end
    else
        errMsg = '';
    end
    % Get MNI transformation
    vox2mni = cs_convert(sMri, 'voxel', 'mni');
    % Get cut plane in MRI coordinates
    cutPlane = MNIplane * vox2mni;
    % Get voxel indices under the MNI plane defined in input
    mriSize = size(sMri.Cube);
    [i,j,k] = ndgrid(1:mriSize(1), 1:mriSize(2), 1:mriSize(3));
    maskCut = (cutPlane(1)*i + cutPlane(2)*j + cutPlane(3)*k + cutPlane(4) < 0);
    % Checking for errors
    strPlane = sprintf('%1.3fx + %1.3fy + %1.3fz + %1.3f = 0', MNIplane);
    if (nnz(maskCut) == 0)
        errMsg = ['No voxels are located below the plane: ', strPlane];
        return;
    elseif (nnz(maskCut) == numel(maskCut))
        errMsg = ['All the voxels are located below the plane: ', strPlane];
        return;
    end
    % Replicate over multiple volumes
    if (length(mriSize) == 4) && (mriSize(4) > 1)
        maskCut = repmat(maskCut, [1 1 1 mriSize(4)]);
    end
    % Set to zero the voxels below the plane
    sMri.Cube(maskCut) = 0;
end

%% ===== INSTALL MRI_DEFACE =====
function [exePath, talFile, faceFile, errMsg] = InstallMriDeface(isInteractive)
    exePath = [];
    errMsg = [];
    talFile = [];
    faceFile = [];
    curdir = pwd;
    % Test if FreeSurfer is installed
    fsHome = getenv('FREESURFER_HOME');
    if ~isempty(fsHome)
        exePath = bst_fullfile(fsHome, 'bin', 'mri_deface');
        talFile = bst_fullfile(fsHome, 'average', 'talairach_mixed_with_skull.gca');
        faceFile = bst_fullfile(fsHome, 'average', 'face.gca');
        if file_exist(exePath) && file_exist(talFile) && file_exist(faceFile)
            disp(['BST> mri_deface found in FreeSurfer folder: ' fsHome]);
            return;
        end
        exePath = [];
    end
    % Executable mri_deface not found: Trying to install locally
    disp('BST> Deface: Variable FREESURFER_HOME not set. Trying to install mri_deface locally...');
    % Get download url
    osType = bst_get('OsType', 0);
    switch(osType)
        case 'linux32',  url = 'https://surfer.nmr.mgh.harvard.edu/pub/dist/mri_deface/mri_deface-v1.22-Linux.gz';
        case 'linux64',  url = 'https://surfer.nmr.mgh.harvard.edu/pub/dist/mri_deface/mri_deface-v1.22-Linux64.gz';
        case 'mac32',    url = 'https://surfer.nmr.mgh.harvard.edu/pub/dist/mri_deface/mri_deface-v1.22-MacOS-Leopard-intel.gz';
        case 'mac64',    url = 'https://surfer.nmr.mgh.harvard.edu/pub/dist/mri_deface/mri_deface-v1.22-MacOS-Leopard-intel.gz';
        otherwise,       errMsg = 'The program mri_deface is not available for your operating system.'; return;
    end
    talUrl = 'https://surfer.nmr.mgh.harvard.edu/pub/dist/mri_deface/talairach_mixed_with_skull.gca.gz';
    faceUrl = 'https://surfer.nmr.mgh.harvard.edu/pub/dist/mri_deface/face.gca.gz';

    % Local folder where to install mri_deface
    mriDefaceDir = bst_fullfile(bst_get('BrainstormUserDir'), 'mri_deface', osType);
    % If dir doesn't exist in user folder, try to look for it in the Brainstorm folder
    if ~isdir(mriDefaceDir)
        mriDefaceDirMaster = bst_fullfile(bst_get('BrainstormHomeDir'), 'mri_deface', osType);
        if isdir(mriDefaceDirMaster)
            mriDefaceDir = mriDefaceDirMaster;
        end
    end
    % URL file defines the current version
    urlFile = bst_fullfile(mriDefaceDir, 'url');

    % Read the previous download url information
    if isdir(mriDefaceDir) && file_exist(urlFile)
        fid = fopen(urlFile, 'r');
        prevUrl = fread(fid, [1 Inf], '*char');
        fclose(fid);
    else
        prevUrl = '';
    end

    % Local installation
    exePath = bst_fullfile(mriDefaceDir, 'mri_deface');
    talFile = bst_fullfile(mriDefaceDir, 'talairach_mixed_with_skull.gca');
    faceFile = bst_fullfile(mriDefaceDir, 'face.gca');
    % If binary file doesnt exist: download
    if ~isdir(mriDefaceDir) || ~file_exist(exePath) || ~strcmpi(prevUrl, url)
        % If folder exists: delete
        if isdir(mriDefaceDir)
            file_delete(mriDefaceDir, 1, 3);
        end
        % Create folder
        res = mkdir(mriDefaceDir);
        if ~res
            errMsg = ['Error: Cannot create folder' 10 mriDefaceDir];
            return
        end
        % Message
        if isInteractive
            isOk = java_dialog('confirm', ...
                ['FreeSurfer or mri_deface are not installed on your computer (or out-of-date).' 10 10 ...
                 'Download and the latest version of mri_deface?'], 'mri_deface');
            if ~isOk
                errMsg = 'Download aborted by user';
                return;
            end
        end
        % Download file
        errMsg1 = gui_brainstorm('DownloadFile', url, [exePath '.gz'], 'Download mri_deface');
        errMsg2 = gui_brainstorm('DownloadFile', talUrl, [talFile '.gz'], 'mri_deface template (1)');
        errMsg3 = gui_brainstorm('DownloadFile', faceUrl, [faceFile '.gz'], 'mri_deface template (2)');
        % If file was not downloaded correctly
        if ~isempty(errMsg1) || ~isempty(errMsg2) || ~isempty(errMsg3)
            errMsg = ['Impossible to download mri_deface:' 10 errMsg1];
            return;
        end
        % Display again progress bar
        bst_progress('text', 'Installing mri_deface...');
        % Unzip file
        cd(mriDefaceDir);
        system(['gunzip ' exePath '.gz']);
        system(['gunzip ' talFile '.gz']);
        system(['gunzip ' faceFile '.gz']);
        system(['chmod a+x ' exePath]);
        cd(curdir);
        % Save download URL in folder
        fid = fopen(urlFile, 'w');
        fwrite(fid, url);
        fclose(fid);
    end
    % If the executable is still not accessible
    if ~file_exist(exePath)
        errMsg = ['mri_convert could not be installed in: ' mriDefaceDir];
    end
end


function nvar = p00_nvar ( problem, option )

%*****************************************************************************80
%
%% P00_NVAR sets the number of variables for any problem.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    14 October 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer PROBLEM, the problem index.
%
%    Input, integer OPTION, the option index.
%
%    Output, integer NVAR, the number of variables.
%
  if (  problem == 1 )
    nvar = p01_nvar ( option );
  elseif ( problem == 2 )
    nvar = p02_nvar ( option );
  elseif ( problem == 3 )
    nvar = p03_nvar ( option );
  elseif ( problem == 4 )
    nvar = p04_nvar ( option );
  elseif ( problem == 5 )
    nvar = p05_nvar ( option );
  elseif ( problem == 6 )
    nvar = p06_nvar ( option );
  elseif ( problem == 7 )
    nvar = p07_nvar ( option );
  elseif ( problem == 8 )
    nvar = p08_nvar ( option );
  elseif ( problem == 9 )
    nvar = p09_nvar ( option );
  elseif ( problem == 10 )
    nvar = p10_nvar ( option );
  elseif ( problem == 11 )
    nvar = p11_nvar ( option );
  elseif ( problem == 12 )
    nvar = p12_nvar ( option );
  elseif ( problem == 13 )
    nvar = p13_nvar ( option );
  elseif ( problem == 14 )
    nvar = p14_nvar ( option );
  elseif ( problem == 15 )
    nvar = p15_nvar ( option );
  elseif ( problem == 16 )
    nvar = p16_nvar ( option );
  elseif ( problem == 17 )
    nvar = p17_nvar ( option );
  elseif ( problem == 18 )
    nvar = p18_nvar ( option );
  elseif ( problem == 19 )
    nvar = p19_nvar ( option );
  elseif ( problem == 20 )
    nvar = p20_nvar ( option );
  else
    printf ( 1, '\n' );
    printf ( 1, 'P00_NVAR - Fatal error!\n' );
    printf ( 1, '  Unrecognized problem number = %d\n', problem );
    error ( 'P00_NVAR - Fatal error!' );
  end

  return
end


% eeg_mergelocs() - merge channel structure while preserving channel
%                   order
%
%      >> mergedlocs = eeg_mergelocs(loc1, loc2, loc3, ...);
%
% Inputs:
%     loc1     - EEGLAB channel location structure
%     loc2     - second EEGLAB channel location structure
%
% Output:
%     mergedlocs - merged channel location structure
%     warning    - [0|1] dissimilar structures found (0=false, 1=true)
%
% Author: Arnaud Delorme, August 2006

% Copyright (C) Arnaud Delorme, CERCO, 2006, arno@salk.edu
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [alllocs warn] = eeg_mergelocs(varargin)

persistent warning_shown;
warn = 0;

try
    % sort by length
    % --------------
    len = cellfun(@length, varargin);
    [tmp so] = sort(len, 2, 'descend');
    varargin = varargin(so);

    alllocs = varargin{1};
    for index = 2:length(varargin)

        % fuse while preserving order (assumes the same channel order)
        % ------------------------------------------------------------
        tmplocs = varargin{index};
        newlocs = myunion(alllocs, tmplocs);

        if length(newlocs) > length(union({ alllocs.labels }, { tmplocs.labels }))

            warn = 1;
            if isempty(warning_shown)
                disp('Warning: different channel montage or electrode order for the different datasets');
                warning_shown = 1;
            end;

            % trying to preserve order of the longest array
            %----------------------------------------------
            if length(alllocs) < length(tmplocs)
                tmp     = alllocs;
                alllocs = tmplocs;
                tmplocs = tmp;
            end;
            allchans = { alllocs.labels tmplocs.labels };
            [uniquechan ord1 ord2 ]  = unique_bc( allchans );

            [tmp rminds] = intersect_bc( uniquechan, { alllocs.labels });
            ord1(rminds) = [];
            tmplocsind = ord1-length(alllocs);

            newlocs = [ alllocs tmplocs(tmplocsind) ];

        end;
        alllocs = newlocs;
    end;
catch,
    % temporary fix for dissimilar structures
    % should check channel structure consistency instead
    % using checkchan function
    disp('Channel merging warning: dissimilar fields in the two structures');
    [alllocs warn ] = eeg_mergelocs_diffstruct(varargin{:});
end;

% Checking consistency of chanlocs
alllocs = eeg_checkchanlocs(alllocs);

% union of two channel location structure
% without loosing the order information
% ---------------------------------------
function alllocs = myunion(locs1, locs2)

   labs1 = { locs1.labels };
   labs2 = { locs2.labels };

   count1 = 1;
   count2 = 1;
   count3 = 1;
   alllocs = locs1; alllocs(:) = [];
   while count1 <= length(locs1) || count2 <= length(locs2)

       if count1 > length(locs1)
           alllocs(count3) = locs2(count2);
           count2 = count2 + 1;
           count3 = count3 + 1;
       elseif count2 > length(locs2)
           alllocs(count3) = locs1(count1);
           count1 = count1 + 1;
           count3 = count3 + 1;
       elseif strcmpi(labs1{count1}, labs2{count2})
           alllocs(count3) = locs1(count1);
           count1 = count1 + 1;
           count2 = count2 + 1;
           count3 = count3 + 1;
       elseif isempty(strmatch(labs1{count1}, labs2, 'exact'))
           alllocs(count3) = locs1(count1);
           count1 = count1 + 1;
           count3 = count3 + 1;
       else
           alllocs(count3) = locs2(count2);
           count2 = count2 + 1;
           count3 = count3 + 1;
       end;

   end;


function timers = cvx_toc

%CVX_TOC Returns the current timing information.
%
%   CVX collects the following timing information:
%   --- Elapsed time since CVX_TIC was last called. If CVX_TIC was never
%       called, this measures the time since CVX was first initialized.
%   --- Elapsed time spent between top-level CVX_BEGIN and CVX_END comamnds
%   --- Elapsed time spent within the top-level CVX_END itself. This
%       includes final problem extraction, presolving, and solving.
%   --- Elapsed time spent calling the numerical solver.
%   To retrieve the current totals for these numbers, type CVX_TOC.
%   CVX_TIC resets these numbers to zero.

global cvx___
if isempty( cvx___ ),
    error( 'CVX has not yet been used (or the global workspace has been cleared).' );
end
tstart = tic;
timers = double(cvx___.timers);
dbltim = isa( timers, 'double' );
if dbltim,
    tstart = double(tstart);
end
timers(1) = tstart - timers(1);
if isempty(cvx___.increment),
    switch computer,
        case { 'MACI', 'MACI64' },
            cvx___.increment = 1e9;
        otherwise,
            t1 = tic; pause(0.25); t2 = tic; t3 = toc(t1); t4=toc(t2);
            if dbltim,
                cvx___.increment = (double(t2)-double(t1))/(double(t3)-0.5*double(t4));
            else
                two = uint64(2);
                cvx___.increment = double( two * ( t2 - t1 ) ) / ( two * t3 - t4 );
            end
    end
end
timers = [ timers(1), timers(1:3) - timers(2:4), timers(4) ];
timers = timers / cvx___.increment;
if nargout == 0,
    fprintf( 'Total time:      %g sec\n', timers(1) );
    fprintf( 'Outside of CVX:  %g sec\n', timers(2) );
    fprintf( 'Model building:  %g sec\n', timers(3) );
    fprintf( 'Presolving:      %g sec\n', timers(4) );
    fprintf( 'Solving:         %g sec\n', timers(5) );
    clear timers
end

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function [T]=txtfile2cell(fileName)
% function [T]=txtfile2cell(fileName)
% ------------------------------------------------------------------------
% This function read the text file specified by fileName (path and name)
% whereby each line is read into a seperate entry in the output cell array
% T.
%
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 2015/102/10
%------------------------------------------------------------------------

fid=fopen(fileName);
T=textscan(fid,'%s','delimiter', '\n','Whitespace','');
T=T{1,1};
fclose(fid);

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function test_bug3369

% WALLTIME 00:20:00
% MEM 2gb
% DEPENDENCY read_neuromag_maxfilterlog

cd(dccnpath('/home/common/matlab/fieldtrip/data/test/bug3369'));

file0 = 'tactile_stim_raw.fif_tsss_mc.log';
file1 = 'tactile_stim_raw-1.fif_tsss_mc.log';
file2 = 'tactile_stim_raw-2.fif_tsss_mc.log';

%%

cfg = [];
cfg.dataset = file0;
cfg.viewmode = 'vertical';
ft_databrowser(cfg)

%%

cfg = [];
cfg.dataset = file0;
data0 = ft_preprocessing(cfg);
cfg.dataset = file1;
data1 = ft_preprocessing(cfg);
cfg.dataset = file2;
data2 = ft_preprocessing(cfg);

cfg = [];
cfg.keepsampleinfo = 'no'; % the sampleinfo of the three does not match
data = ft_appenddata(cfg, data0, data1, data2);

%%

cfg = [];
cfg.viewmode = 'vertical';
cfg.preproc.demean = 'yes';
ft_databrowser(cfg, data);


function varargout = QuadAnim4(varargin)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %{
 This GUI provides an "animation-like" view of simulation output.

    Additional quadcopter modeling and simulation materials available at:
    github.com/dch33/Quad-Sim

Copyright (C) 2014 D. Hartman, K. Landis, S. Moreno, J. Kim, M. Mehrer

License:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
%}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%{
Quadcopter "Animation" GUI.
(c) David Hartman, 2014

If you're looking in this code for "answers" or a quick fix...good luck.
Though I've tried to add some comments throughout, altering this code is
likely to be an unpleasant exercise.
If it's any consolation, it wasn't very fun to make this mess either :P
I hope to release a more clearly written and faster version of this
code sometime in the future. Or feel free to do it for me and email it to
me!! Please!?!
%}

%.

% QuadAnim4 MATLAB code for QuadAnim4.fig
%      QuadAnim4, by itself, creates a new QuadAnim4 or raises the existing
%      singleton*.
%
%      H = QuadAnim4 returns the handle to a new QuadAnim4 or the handle to
%      the existing singleton*.
%
%      QuadAnim4('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in QuadAnim4.M with the given input arguments.
%
%      QuadAnim4('Property','Value',...) creates a new QuadAnim4 or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before QuadAnim4_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to QuadAnim4_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help QuadAnim4

% Last Modified by GUIDE v2.5 30-Apr-2014 10:38:36

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

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

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

% Choose default command line output for QuadAnim4
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);
handles.AZval = get(handles.AZslider,'Value')*360; % Get azimuth view in degrees
handles.ELval = get(handles.ELslider,'Value')*90;
handles.j     = 1; % Initialize animation frame counting variable
handles.skipFlag = 0;
% Move GUI to center of screen
movegui('center')
% This sets up the initial plot - only do when we are invisible
% so window can get raised using QuadAnim4.
A = evalin('base', 'yout');
% Generate the geometry used to draw the quadcopter
r = .5; d = 1.25; h = .25; %inches: rotor dia., quad motor distance from
% center of mass, and rotor height above arms (entirely cosmetic)
a = 1; b = 1; c = 0.2; % Top , bottom, and sides lengths of hub (?)

% Construct rotor representations
N = [d  0 h].';% m1 rotor center [X Y Z]
E = [0 -d h].';% m4 rotor center
W = [0  d h].';% m2 rotor center
S = [-d 0 h].';% m3 rotor center
Nr = circlePoints(N, r, 10); Nr = [Nr Nr(:,1)]; % Rotor blade circles
Er = circlePoints(E, r, 10); Er = [Er Er(:,1)];
Wr = circlePoints(W, r, 10); Wr = [Wr Wr(:,1)];
Sr = circlePoints(S, r, 10); Sr = [Sr Sr(:,1)];
% Motors connecting to center of blade circles
mN = [d,d;
      0,0;
      h,0];
mE = [0,0;
     -d,-d;
      h,0];
mW = [0,0;
      d,d;
      h,0];
mS = [-d,-d;
       0,0;
       h,0];
% Construct body plot points
bNS = [ d, -d;
        0,  0;
        0,  0]; %For drawing the body "X" shape
bEW = [ 0,  0;
        d, -d;
        0,  0];
% Body (HUB) Squares
Top = [ a/2,   0,-a/2,   0;
          0, b/2,   0,-b/2;
        c/2, c/2, c/2, c/2];
Bot = vertcat(Top(1:2,:),-Top(3,:)); % Bot is same as top just below the center of mass
NEB = [ a/2, a/2,   0,   0;
          0,   0, b/2, b/2;
        c/2,-c/2,-c/2, c/2]; % By the way... north east is actually north west from above since x is north and y is east :P
NWB = [ a/2, a/2,   0,   0;
          0,   0,-b/2,-b/2;
        c/2,-c/2,-c/2, c/2];
SEB = -NWB;
SWB = -NEB;

phi = A(1,4);
the = A(1,5);
psi = A(1,6);

% ROTATION MATRIX --- ZYX ROTATION (R = Rib)
R = [cos(psi)*cos(the) cos(psi)*sin(the)*sin(phi)-sin(psi)*cos(phi) cos(psi)*sin(the)*cos(phi)+sin(psi)*sin(phi);
       sin(psi)*cos(the) sin(psi)*sin(the)*sin(phi)+cos(psi)*cos(phi) sin(psi)*sin(the)*cos(phi)-cos(psi)*sin(phi);
       -sin(the)         cos(the)*sin(phi)                            cos(the)*cos(phi)];

% Rotate body frame velocity vector
U = A(:,7);
V = A(:,8);
W = A(:,9);
Vi = zeros(length(A),3);
MvMax= max(sqrt(U.^2+V.^2+W.^2));
Vb = 3/MvMax*[U(1), V(1), W(1)]'; % Scale velocity
Vi(1,:) = R*Vb; % Rotate velocity vector to inertial frame for plotting

% Support for X-configuration nifty trick (Requires that quadModel
% structure is in base workspace)
if (~evalin('base','quadModel.plusConfig'))
    Rz = [ sqrt(2)/2, sqrt(2)/2, 0;
          -sqrt(2)/2,sqrt(2)/2, 0;
                   0,          0, 1];
    Nr = Rz*Nr;
    Er = Rz*Er;
    Wr = Rz*Wr;
    Sr = Rz*Sr;
    mN = Rz*mN;
    mE = Rz*mE;
    mW = Rz*mW;
    mS = Rz*mS;
    bNS = Rz*bNS;
    bEW = Rz*bEW;
    Top = Rz*Top;
    Bot = Rz*Bot;
    NEB = Rz*NEB;
    NWB = Rz*NWB;
    SWB = Rz*SWB;
    SEB = Rz*SEB;
end

% Rotate body parts Via Initialized R
NrR = R*Nr;
ErR = R*Er;
WrR = R*Wr;
SrR = R*Sr;
mNr = R*mN;
mEr = R*mE;
mWr = R*mW;
mSr = R*mS;
bNSR = R*bNS;
bEWR = R*bEW;
TopR = R*Top;
BotR = R*Bot;
NEBR = R*NEB;
NWBR = R*NWB;
SWBR = R*SWB;
SEBR = R*SEB;

% Plot the box rotation and ang. velocity and inertial frame velocity
% vector
axes(handles.axes1)
plot3(bNSR(1,:),bNSR(2,:),bNSR(3,:),'b','LineWidth',3) % Body Arm
hold on
plot3(bEWR(1,:),bEWR(2,:),bEWR(3,:),'b','LineWidth',3) % Body Arm
plot3(mNr(1,:),mNr(2,:),mNr(3,:),'k','LineWidth',4) % Motor
plot3(mEr(1,:),mEr(2,:),mEr(3,:),'k','LineWidth',4) % Motor
plot3(mWr(1,:),mWr(2,:),mWr(3,:),'k','LineWidth',4) % Motor
plot3(mSr(1,:),mSr(2,:),mSr(3,:),'k','LineWidth',4) % Motor
plot3(NrR(1,:),NrR(2,:),NrR(3,:),'g') % Motor 1 blades
plot3(ErR(1,:),ErR(2,:),ErR(3,:),'k') % Motor 4 blades
plot3(WrR(1,:),WrR(2,:),WrR(3,:),'k') % Motor 2 blades
plot3(SrR(1,:),SrR(2,:),SrR(3,:),'g') % Motor 3 blades
grey = [0.5 0.5 0.5];
top = fill3(TopR(1,:),TopR(2,:),TopR(3,:),'r'); alpha(top,0.8); % Top Surface
bot = fill3(BotR(1,:),BotR(2,:),BotR(3,:),'g'); alpha(bot,0.8); % Bottom Surface
ne  = fill3(NEBR(1,:),NEBR(2,:),NEBR(3,:),'c'); alpha(ne,0.8); % North East surface
nw  = fill3(NWBR(1,:),NWBR(2,:),NWBR(3,:),grey); alpha(nw,0.8); % North West surface
sw  = fill3(SWBR(1,:),SWBR(2,:),SWBR(3,:),grey); alpha(sw,0.8); % South West surface
se  = fill3(SEBR(1,:),SEBR(2,:),SEBR(3,:),grey); alpha(se,0.8); % South East surface
axis square
xlabel('X')
ylabel('Y')
zlabel('Z')
xlim([-3 3])
ylim([-3 3])
zlim([-3 3])
view(handles.AZval,handles.ELval)
grid on

omi = zeros(length(A),3); % Initialize omega inertiaF points array (L(A)x3)
P = A(:,1); Q = A(:,2); Rw = A(:,3);
MombMax = max(sqrt(P.^2+Q.^2+Rw.^2)); % Calculate max magnitude of omb
omb = 3/MombMax*[P,Q,Rw].'; % Store and scale current omega bodyF
omi(1,:) = R*omb(:,1); % Rotate omegab to inertiaF store in omegai array
qp1 = quiver3(0,0,0,omi(1,1),omi(1,2),omi(1,3),'ro');
qp2 = quiver3(0,0,0,Vi(1,1),Vi(1,2),Vi(1,3),'k');
hold off
% legend([qp1 qp2],'Angular Velocity','I-Frame Velocity','Location',...
%      'SouthOutside')
%  pp1 = plot3(omi(1,1),omi(1,2),omi(1,3),'b.');
%  pp2 = plot3(Vi(1,1),Vi(1,2),Vi(1,3),'k.');
%  legend([qp1 qp2 pp1 pp2],'Angular Velocity','I-Frame Velocity',...
%      'Ang. Vel. Track','I-Frame Vel. Track','Location','SouthOutside')
% hold off

minX = min(A(:,10))*3.28;
minY = min(A(:,11))*3.28;
% minZ = min(A(:,12))*3.28;
maxX = max(A(:,10))*3.28;
maxY = max(A(:,11))*3.28;
maxZ = max(A(:,12))*3.28;

% Plot the three dimensional trajectory of the box
axes(handles.axes2)
X = A(1:1,10)*3.28; Y = A(1:1,11)*3.28; Z = A(1:1,12)*3.28;
scatter3(X,Y,Z,36,'blue')
hold on
fill3([minX-1 maxX+1 maxX+1 minX-1],...
    [minY-1 minY-1 maxY+1 maxY+1],...
    [0 0 0 0],'g');
%alpha(0.7);
xlabel('X (ft)')
ylabel('Y (ft)')
zlabel('Z (ft)')
xlim([minX-1 maxX+1])
ylim([minY-1 maxY+1])
zlim([-0.1 maxZ+1])
view(handles.AZval,handles.ELval)
axis square
grid on
hold off

% phi_A = A(:,4);
% the_A = A(:,5);
% psi_A = A(:,6);

% Choose default command line output for animationGUI2
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

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

% --- Outputs from this function are returned to the command line.
function varargout = QuadAnim4_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

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

% --- Executes on button press in startStop.
function startStop_Callback(hObject, eventdata, handles)
% hObject    handle to startStop (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
plotAnim(hObject, handles) % Pass handle to start/stop button to plot function

% --- Executes on slider movement.
function ELslider_Callback(hObject, eventdata, handles)
% hObject    handle to ELslider (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider
handles.ELval = get(hObject,'Value')*90; % Set elevation view in degrees
handles.AZval = get(handles.AZslider,'Value')*360; % Set aximuth view in degrees
view(handles.axes1,handles.AZval,handles.ELval);
view(handles.axes2,handles.AZval,handles.ELval);
drawnow
guidata(hObject,handles)

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

% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

% --- Executes on slider movement.
function AZslider_Callback(hObject, eventdata, handles)
% hObject    handle to AZslider (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider
handles.AZval = get(hObject,'Value')*360; % Set azimuth view in degrees
handles.ELval = get(handles.ELslider,'Value')*90; % Set elevation view in degrees
view(handles.axes1,handles.AZval,handles.ELval);
view(handles.axes2,handles.AZval,handles.ELval);
drawnow
guidata(hObject,handles);

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

% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

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

% Hint: place code in OpeningFcn to populate axes1

function points = circlePoints(center, radius, numberOfPoints)
% Helper function for plotting points
% Inspired by "circle()" from Peter Corke's MATLAB Robotics Toolbox
c = center.'; % [x;y] location of center
r = radius;
n = numberOfPoints;
% compute points around the circumference
th = (0:n-1)'/n*2*pi; % angles coresponding to each point
x = r*cos(th) + c(1); % x part
y = r*sin(th) + c(2); % y part
points = [x,y].';
    if length(c) > 2
        z = ones(size(x))*c(3); % z part
        points = [x, y, z].';
    end

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

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

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

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

function plotAnim(button, handles)
hObject = button;
mode = get(hObject,'String');
handles = guidata(gcbo);
j = handles.j;
if strcmp(mode,'Start')
    set(hObject,'String','Stop')
    guidata(hObject,handles)
    A = evalin('base', 'yout');
    tout = evalin('base', 'tout');
    frameSkipVal = str2double(get(handles.frameSkips,'String'))+1; % Size of steps to take for plotting animation (1 plots
    % every frame, 2 about every other, etc.

    r = .5; d = 1.25; h = .25; %inches: rotor dia., quad motor distance from
    % cm, and rotor height above arms (entirely cosmetic)
    a = 1; b = 1; c = 0.2;

    % Construct rotor representations
    N = [d  0 h].';% m1 rotor center
    E = [0 -d h].';% m4 rotor center
    W = [0  d h].';% m2 rotor center
    S = [-d 0 h].';% m3 rotor center
    Nr = circlePoints(N, r, 20); Nr = [Nr Nr(:,1)]; % Rotor blade circles
    Er = circlePoints(E, r, 20); Er = [Er Er(:,1)];
    Wr = circlePoints(W, r, 20); Wr = [Wr Wr(:,1)];
    Sr = circlePoints(S, r, 20); Sr = [Sr Sr(:,1)];
    % Motors connecting to center of blade circles
    mN = [d,d;
          0,0;
          h,0];
    mE = [0,0;
         -d,-d;
          h,0];
    mW = [0,0;
          d,d;
          h,0];
    mS = [-d,-d;
           0,0;
           h,0];
    % Construct body plot points
    bNS = [ d, -d;
            0,  0;
            0,  0]; %For drawing the body "X" shape
    bEW = [ 0,  0;
            d, -d;
            0,  0];
    % Body
    Top = [ a/2,   0,-a/2,   0;
              0, b/2,   0,-b/2;
            c/2, c/2, c/2, c/2];
    Bot = vertcat(Top(1:2,:),-Top(3,:));
    NEB = [ a/2, a/2,   0,   0;
          0,   0, b/2, b/2;
        c/2,-c/2,-c/2, c/2];
    NWB = [ a/2, a/2,   0,   0;
              0,   0,-b/2,-b/2;
            c/2,-c/2,-c/2, c/2];
    SEB = -NWB;
    SWB = -NEB;

    phi = A(1,4);
    the = A(1,5);
    psi = A(1,6);
    % Rib
    R = [cos(psi)*cos(the) cos(psi)*sin(the)*sin(phi)-sin(psi)*cos(phi) cos(psi)*sin(the)*cos(phi)+sin(psi)*sin(phi);
           sin(psi)*cos(the) sin(psi)*sin(the)*sin(phi)+cos(psi)*cos(phi) sin(psi)*sin(the)*cos(phi)-cos(psi)*sin(phi);
           -sin(the)         cos(the)*sin(phi)                            cos(the)*cos(phi)];

    % Support for X-configuration
    if (~evalin('base','quadModel.plusConfig'))
        Rz = [ sqrt(2)/2, sqrt(2)/2, 0;
               -sqrt(2)/2,sqrt(2)/2, 0;
                       0,          0, 1];
        Nr=Rz*Nr;
        Er=Rz*Er;
        Wr=Rz*Wr;
        Sr=Rz*Sr;
        mN = Rz*mN;
        mE = Rz*mE;
        mW = Rz*mW;
        mS = Rz*mS;
        bNS = Rz*bNS;
        bEW = Rz*bEW;
        Top = Rz*Top;
        Bot = Rz*Bot;
        NEB = Rz*NEB;
        NWB = Rz*NWB;
        SWB = Rz*SWB;
        SEB = Rz*SEB;
    end

    % Bring in and rotate body frame velocity vector
    U = A(:,7);
    V = A(:,8);
    W = A(:,9);
    Vi = zeros(length(A),3);
    MvMax= max(sqrt(U.^2+V.^2+W.^2));
    Vb = 3/MvMax*[U, V, W].'; % Scale velocity vector points for plotting
    Vi(1,:) = R*Vb(:,1); % Rotate velocity vector to inertial frame for plotting
    % Angular Velocity Vector
    P = A(:,1);
    Q = A(:,2);
    Rw = A(:,3);
    omi = zeros(length(A),3); % Initialize omega inertiaF points array (L(A)x3)
    MombMax = max(sqrt(P.^2+Q.^2+Rw.^2)); % Calculate max magnitude of omb
    omb = 3/MombMax*[P,Q,Rw].'; % Store and scale current omega bodyF
    omi(1,:) = R*omb(:,1); % Rotate omegab to inertiaF store in omegai array

    phi = A(:,4);
    the= A(:,5);
    psi = A(:,6);

    % Next we run through the points in the vector A as an
    % animation until we get a command to stop
    minX = min(A(:,10))*3.28; % meters to feet
    minY = min(A(:,11))*3.28;
    %minZ = min(A(:,12))*3.28;
    maxX = max(A(:,10))*3.28;
    maxY = max(A(:,11))*3.28;
    maxZ = max(A(:,12))*3.28;
    colors = jet(length(A(:,10))); % color the path for time info

    % Precompute R
    % ROTATION MATRIX BELOW --- ZYX ROTATION
    R = cell(length(A),1); % create empty cell array for R matrices
    for i = 1:length(A)
    R{i,1} = [cos(psi(i))*cos(the(i)) cos(psi(i))*sin(the(i))*sin(phi(i))-sin(psi(i))*cos(phi(i)) cos(psi(i))*sin(the(i))*cos(phi(i))+sin(psi(i))*sin(phi(i));
              sin(psi(i))*cos(the(i)) sin(psi(i))*sin(the(i))*sin(phi(i))+cos(psi(i))*cos(phi(i)) sin(psi(i))*sin(the(i))*cos(phi(i))-cos(psi(i))*sin(phi(i));
              -sin(the(i))         cos(the(i))*sin(phi(i))                            cos(the(i))*cos(phi(i))];
    end
    % set up a j vector to straighten out skipping issues
%     if (skipFlag == 1)
%         jSkip = 1:frameSkipVal:j;
%     end
    while ( strcmp(get(hObject,'String'),'Stop'))
        handles = guidata(gcbo);
        j = handles.j;

        guidata(hObject,handles); % Update handles data
        Vi(j,:) = R{j,1}*Vb(:,j);

        NrR = R{j,1}*Nr;
        ErR = R{j,1}*Er;
        WrR = R{j,1}*Wr;
        SrR = R{j,1}*Sr;
        bNSR = R{j,1}*bNS;
        bEWR = R{j,1}*bEW;
        TopR = R{j,1}*Top;
        BotR = R{j,1}*Bot;
        % Rotate body parts Via Initialized R
        mNr = R{j,1}*mN;
        mEr = R{j,1}*mE;
        mWr = R{j,1}*mW;
        mSr = R{j,1}*mS;
        NEBR = R{j,1}*NEB;
        NWBR = R{j,1}*NWB;
        SWBR = R{j,1}*SWB;
        SEBR = R{j,1}*SEB;

        % Plot the quad rotation and ang. velocity and inertial frame velocity vector
        axes(handles.axes1)
        plot3(bNSR(1,:),bNSR(2,:),bNSR(3,:),'b','LineWidth',3)
        hold on
        plot3(bEWR(1,:),bEWR(2,:),bEWR(3,:),'b','LineWidth',3)
        plot3(NrR(1,:),NrR(2,:),NrR(3,:),'g')
        plot3(ErR(1,:),ErR(2,:),ErR(3,:),'k')
        plot3(WrR(1,:),WrR(2,:),WrR(3,:),'k')
        plot3(SrR(1,:),SrR(2,:),SrR(3,:),'g')
        plot3(mNr(1,:),mNr(2,:),mNr(3,:),'k','LineWidth',4) % Motor
        plot3(mEr(1,:),mEr(2,:),mEr(3,:),'k','LineWidth',4) % Motor
        plot3(mWr(1,:),mWr(2,:),mWr(3,:),'k','LineWidth',4) % Motor
        plot3(mSr(1,:),mSr(2,:),mSr(3,:),'k','LineWidth',4) % Motor
        fill3(TopR(1,:),TopR(2,:),TopR(3,:),'r'); alpha(0.8);
        fill3(BotR(1,:),BotR(2,:),BotR(3,:),'g'); alpha(0.8);
        grey = [0.5 0.5 0.5];
        ne  = fill3(NEBR(1,:),NEBR(2,:),NEBR(3,:),'c'); alpha(ne,0.8); % North East surface
        nw  = fill3(NWBR(1,:),NWBR(2,:),NWBR(3,:),grey); alpha(nw,0.8); % North West surface
        sw  = fill3(SWBR(1,:),SWBR(2,:),SWBR(3,:),grey); alpha(sw,0.8); % South West surface
        se  = fill3(SEBR(1,:),SEBR(2,:),SEBR(3,:),grey); alpha(se,0.8); % South East surface
        xlabel('X')
        ylabel('Y')
        zlabel('Z')
        xlim([-3 3])
        ylim([-3 3])
        zlim([-3 3])
        % set(gca,'Color',[215/255 244/255 247/255]) % This is a technique
        % that can be used for setting the plot background color
        handles.AZval = get(handles.AZslider,'Value')*360;
        handles.ELval = get(handles.ELslider,'Value')*90;
        view(handles.axes1,handles.AZval,handles.ELval)
%         P = A(j,1); Q = A(j,2); Rw = A(j,3);
%         M = sqrt(P^2+Q^2+Rw^2);
%         omb = 3/M*[P,Q,Rw].';%/M; % Scaling of angular velocity vector
        omi(j,:) = R{j,1}*omb(:,j);
        qp1 = quiver3(0,0,0,omi(j,1),omi(j,2),omi(j,3),'r');
        qp2 = quiver3(0,0,0,Vi(j,1),Vi(j,2),Vi(j,3),'k');
        axis square
        grid on
        hold off
        drawnow

        % Plot the three dimensional trajectory of the box
        if (j==1)
            cla(handles.axes2)
        else if (handles.skipFlag==1)
                cla(handles.axes2)
                X = A(1:frameSkipVal:j,10)*3.28;
                Y = A(1:frameSkipVal:j,11)*3.28;
                Z = A(1:frameSkipVal:j,12)*3.28;
                axes(handles.axes2)
                hold on
                scatter3(X,Y,Z,36,colors(1:frameSkipVal:j,:));
            end
        end
        % Ordinary plotting sequence (one frame at a time using hold on to
        % create persistance
        X = A(j,10)*3.28; Y = A(j,11)*3.28; Z = A(j,12)*3.28;
        axes(handles.axes2)
        hold on
        scatter3(X,Y,Z,36,colors(j,:));
        if (j == 1 || handles.skipFlag==1)
            fill3([minX-1 maxX+1 maxX+1 minX-1],...
                  [minY-1 minY-1 maxY+1 maxY+1],...
                  [0 0 0 0],'g'); % make a plane to represent the ground (Z = 0)
            alpha(0.7); % Makes the ground "see-through"
            handles.skipFlag = 0;
            guidata(hObject,handles)
        end
        xlabel('X (ft)')
        ylabel('Y (ft)')
        zlabel('Z (ft)')
        xlim([minX-1 maxX+1])
        ylim([minY-1 maxY+1])
        zlim([-0.1 maxZ+1]) % Keep ground within view
        % These calls are necessarry to avoid stutter in the frames if adjusting slider while animation is running
        handles.AZval = get(handles.AZslider,'Value')*360;
        handles.ELval = get(handles.ELslider,'Value')*90;
        view(handles.axes2,handles.AZval,handles.ELval) % Set the current vie
        axis square
%         axis equal
%         xlimz = xlim;
%         ylimz = ylim;
%                     fill3([xlimz(1) xlimz(2) xlimz(2) xlimz(1)],...
%                   [ylimz(1) ylimz(1) ylimz(2) ylimz(2)],...
%                   [0 0 0 0],'k'); % make a plane to represent the ground (Z = 0)
%             alpha(0.9); % Makes the ground "see-through"
        grid on
        drawnow
        hold off
%         if (firstRunFlag==1)
%             firstRunFlag = 0;
%         end
        if (j == size(A,1)) % If we've reached the last group of data
            handles.j = 1; % Reset j to 1
            set(hObject,'String','Start'); % Reset button state
            guidata(hObject,handles);
            return % exit the function

        else if (j+frameSkipVal<size(A,1)) % If the next index won't exceed total frames...
        j = j+frameSkipVal; % increase the index by frame skip value
            else
            j = size(A,1); % We're almost out of data so to the last value for the final plot
            end
        end
        set(handles.simTime,'String',num2str(tout(j))); % update time display
        handles = guidata(gcbo); % update handles structure again...
        if (handles.skipFlag == 1)
            guidata(hObject,handles) % this nightmare is used to catch skip events which otherwise tend to go unheeded.
            return
        else
        handles.j = j; % store j as a handles value for persistance
        guidata(hObject,handles);
        end
    end
    else if strcmp(get(hObject,'String'),'Stop')
        set(hObject,'String','Start');
        end
end

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

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

% Hint: place code in OpeningFcn to populate axes2

% --- Executes on button press in XYview.
function XYview_Callback(hObject, eventdata, handles)
% hObject    handle to XYview (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.ELslider,'Value',1);
set(handles.AZslider,'Value',0);
view(handles.axes1,0,90);
view(handles.axes2,0,90);
drawnow
guidata(hObject,handles);

% --- Executes on button press in XZview.
function XZview_Callback(hObject, eventdata, handles)
% hObject    handle to XZview (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.ELslider,'Value',0);
set(handles.AZslider,'Value',0);
view(handles.axes1,0,0);
view(handles.axes2,0,0);
drawnow
guidata(hObject,handles);

% --- Executes on button press in YZview.
function YZview_Callback(hObject, eventdata, handles)
% hObject    handle to YZview (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.ELslider,'Value',0);
set(handles.AZslider,'Value',90/360);
view(handles.axes1,90,0);
view(handles.axes2,90,0);
drawnow
guidata(hObject,handles);

% --- Executes on button press in defaultView.
function defaultView_Callback(hObject, eventdata, handles)
% hObject    handle to defaultView (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.ELslider,'Value',25/90);
set(handles.AZslider,'Value',35/360)
view(handles.axes1,35,25);
view(handles.axes2,35,25);
drawnow
guidata(hObject,handles);

% --- Executes on button press in timeSkipButton.
function timeSkipButton_Callback(hObject, eventdata, handles)
% hObject    handle to timeSkipButton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.startStop,'String','Start');
tout = evalin('base', 'tout');
TimeSkip = str2double(get(handles.timeSkipEditBox,'String'));
handles.j = find(tout<=TimeSkip, 1,'last');
handles.skipFlag = 1;
% if handles.doubleRunFlag == 0
%     timeSkipButton_Callback(hObject, eventdata, handles) % run it again
% end
% handles.doubleRunFlag = 1;
guidata(hObject,handles);
% yes i'm seriously resorting to this...

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

% Hints: get(hObject,'String') returns contents of timeSkipEditBox as text
%        str2double(get(hObject,'String')) returns contents of timeSkipEditBox as a double
handles.TimeSkip = str2double(get(hObject,'String'));
handles.skipFlag = 0;
guidata(hObject,handles);

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

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

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

github-repos/MATLAB/dch33-Quad-Sim/Quad-Sim-961bc69d4939c8d0661eeb9820de668994262f65/Quadcopter Dynamic Modeling and Simulation/Graphical User Interfaces/QuadAnim4.m


function dat = read_spike6mat_data(filename, varargin)

% read_spike6mat_data() - read Matlab files exported from Spike 6
%
% Usage:
%   >> header = read_spike6mat_data(filename, varargin);
%
% Inputs:
%   filename - [string] file name
%
% Optional inputs:
%   'begsample'      first sample to read
%   'endsample'      last sample to read
%   'chanindx'  -    list with channel indices to read
%   'header'    - FILEIO structure header
%
% Outputs:
%   dat    - data over the specified range
% _______________________________________________________________________
% Copyright (C) 2008 Institute of Neurology, UCL
% Vladimir Litvak

if nargin < 1
    help read_spike6mat_data;
    return;
end;

header    = ft_getopt(varargin, 'header');
begsample = ft_getopt(varargin, 'begsample');
endsample = ft_getopt(varargin, 'endsample');
chanindx  = ft_getopt(varargin, 'chanindx');

if isempty(header)
    header = read_spike6mat_header(filename);
end

if isempty(begsample), begsample = 1; end;
if isempty(endsample), endsample = header.nSamples; end;

try
    vars = struct2cell(load(filename));
catch
    ft_error('File not found or wrong format.');
end

if isempty(chanindx)
    chanindx = 1:numel(vars);
end

dat = zeros(length(chanindx), endsample-begsample+1);

for i = 1:length(chanindx)
    dat(i, :) = vars{chanindx(i)}.values(begsample:endsample);
end


% eeglab2fieldtrip() - do this ...
%
% Usage:    >> data = eeglab2fieldtrip( EEG, fieldbox, transform );
%
% Inputs:
%   EEG       - [struct] EEGLAB structure
%   fieldbox  - ['preprocessing'|'freqanalysis'|'timelockanalysis'|'companalysis']
%   transform - ['none'|'dipfit'] transform channel locations for DIPFIT
%               using the transformation matrix in the field
%               'coord_transform' of the dipfit substructure of the EEG
%               structure.
% Outputs:
%   data    - FIELDTRIP structure
%
% Author: Robert Oostenveld, F.C. Donders Centre, May, 2004.
%         Arnaud Delorme, SCCN, INC, UCSD
%
% See also:

% Copyright (C) 2004 Robert Oostenveld, F.C. Donders Centre, roberto@smi.auc.dk
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function data = eeglab2fieldtrip(EEG, fieldbox, transform)

if nargin < 2
  help eeglab2fieldtrip
  return;
end;

% start with an empty data object
data = [];

% add the objects that are common to all fieldboxes
tmpchanlocs  = EEG.chanlocs;
data.label   = { tmpchanlocs(EEG.icachansind).labels };
data.fsample = EEG.srate;

% get the electrode positions from the EEG structure: in principle, the number of
% channels can be more or less than the number of channel locations, i.e. not
% every channel has a position, or the potential was not measured on every
% position. This is not supported by EEGLAB, but it is supported by FIELDTRIP.

if strcmpi(fieldbox, 'chanloc_withfid')
    % insert "no data channels" in channel structure
    % ----------------------------------------------
    if isfield(EEG.chaninfo, 'nodatchans') && ~isempty( EEG.chaninfo.nodatchans )
        chanlen = length(EEG.chanlocs);
        fields = fieldnames( EEG.chaninfo.nodatchans );
        for index = 1:length(EEG.chaninfo.nodatchans)
            ind = chanlen+index;
            for f = 1:length( fields )
                EEG.chanlocs = setfield(EEG.chanlocs, { ind }, fields{f}, ...
                                        getfield( EEG.chaninfo.nodatchans, { index },  fields{f}));
            end;
        end;
    end;
end;

data.elec.pnt   = zeros(length( EEG.chanlocs ), 3);
for ind = 1:length( EEG.chanlocs )
    data.elec.label{ind} = EEG.chanlocs(ind).labels;
    if ~isempty(EEG.chanlocs(ind).X)
        data.elec.pnt(ind,1) = EEG.chanlocs(ind).X;
        data.elec.pnt(ind,2) = EEG.chanlocs(ind).Y;
        data.elec.pnt(ind,3) = EEG.chanlocs(ind).Z;
    else
        data.elec.pnt(ind,:) = [0 0 0];
    end;
end;

if nargin > 2
    if strcmpi(transform, 'dipfit')
        if ~isempty(EEG.dipfit.coord_transform)
            disp('Transforming electrode coordinates to match head model');
            transfmat = traditionaldipfit(EEG.dipfit.coord_transform);
            data.elec.pnt = transfmat * [ data.elec.pnt ones(size(data.elec.pnt,1),1) ]';
            data.elec.pnt = data.elec.pnt(1:3,:)';
        else
            disp('Warning: no transformation of electrode coordinates to match head model');
        end;
    end;
end;

switch fieldbox
  case 'preprocessing'
    for index = 1:EEG.trials
      data.trial{index}  = EEG.data(:,:,index);
      data.offset(index) = EEG.xmin*EEG.srate+1;                   % should be checked in FIELDTRIP
      data.time{index}   = linspace(EEG.xmin, EEG.xmax, EEG.pnts); % should be checked in FIELDTRIP
    end;

  case 'timelockanalysis'
    data.avg  = mean(EEG.data, 3);
    data.var  = std(EEG.data, [], 3).^2;
    data.time = linspace(EEG.xmin, EEG.xmax, EEG.pnts); % should be checked in FIELDTRIP

  case 'componentanalysis'
    for index = 1:EEG.trials
      % the trials correspond to the raw data trials, except that they
      % contain the component activations
      try,
          data.trial{index}  = EEG.icaact(:,:,index);
      catch
      end;
      data.offset(index) = EEG.xmin*EEG.srate+1;                   % should be checked in FIELDTRIP
      data.time{index}   = linspace(EEG.xmin, EEG.xmax, EEG.pnts); % should be checked in FIELDTRIP
    end;
    for comp = 1:size(EEG.icawinv,2)
      % the labels correspond to the component activations that are stored in data.trial
      data.label{comp} = sprintf('ica_%03d', comp);
    end
    % get the spatial distribution and electrode positions
    tmpchanlocs    = EEG.chanlocs;
    data.topolabel = { tmpchanlocs(EEG.icachansind).labels };
    data.topo      = EEG.icawinv;

  case { 'chanloc' 'chanloc_withfid' }

  case 'freqanalysis'
    error('freqanalysis fieldbox not implemented yet')

  otherwise
    error('unsupported fieldbox')
end

try
  % get the full name of the function
  data.cfg.version.name = mfilename('fullpath');
catch
  % required for compatibility with Matlab versions prior to release 13 (6.5)
  [st, i] = dbstack;
  data.cfg.version.name = st(i);
end

% add the version details of this function call to the configuration
data.cfg.version.id   = '$Id: eeglab2fieldtrip.m,v 1.6 2009-07-02 23:39:29 arno Exp $';

return


function NMPC_Solve_CodeGen(target,targetLang,NMPCSolveOptions)
% target: 'mex' 'lib' 'dll'
% targetLang: 'C' 'C++'

% clear persistent variables
clear NMPC_Solve

target     = lower(target);
targetLang = upper(targetLang);
%% global variable
global ParNMPCGlobalVariable
globalVariable = {'ParNMPCGlobalVariable',coder.Constant(ParNMPCGlobalVariable)};
%% args
dim   = ParNMPCGlobalVariable.dim;
N     = ParNMPCGlobalVariable.N;
DoP   = NMPCSolveOptions.DoP;
args_x0               = zeros(dim.x,1);
args_p                = zeros(dim.p,N);
args_NMPCSolveOptions = coder.Constant(NMPCSolveOptions);
args = {args_x0,args_p,args_NMPCSolveOptions};
%% config
cfg = coder.config(target);
cfg.FilePartitionMethod = 'SingleFile';
if DoP == 1
    isInParallel = false;
else
    isInParallel = true;
end

if ismac
    disp('Note: install libomp for mac: brew install libomp');
    % Code to run on Mac plaform
    if verLessThan('matlab','9.6')
        % -- earlier than R2019a --
        isInParallel = false;
        warning('Mac is only supported for parallel code generation from R2019a!');
    end
    if DoP == 1
        isInParallel = true;
    end
end
cfg.EnableOpenMP            = isInParallel;
cfg.TargetLang              = targetLang;
stackUsageMax               = (dim.x + dim.u)*N/360*10000;
cfg.StackUsageMax           = stackUsageMax;
cfg.GenerateReport          = true;
cfg.DynamicMemoryAllocation = 'off';
if ~strcmp(target,'mex')
    cfg.BuildConfiguration  = 'Faster Runs';
    cfg.SupportNonFinite    = false;
    cfg.GenerateExampleMain = 'DoNotGenerate';
end

myCCompiler = mex.getCompilerConfigurations(targetLang,'Selected');
if ~strcmp(myCCompiler.Manufacturer,'Microsoft')
    if ismac
        % flag to call openmp in mac
        cfg.PostCodeGenCommand = 'buildInfo.addLinkFlags(''-Xpreprocessor -fopenmp'')';
    else
        cfg.PostCodeGenCommand = 'buildInfo.addLinkFlags(''-fopenmp'')';
    end
end
%% generate
switch target
    case 'mex'
        genMSg = ['Generating ', target, ' file for NMPC_Solve...' ];
    case 'lib'
        genMSg = ['Generating ', targetLang, ' source files for NMPC_Solve...' ];
		cfg.GenerateExampleMain = 'GenerateCodeOnly';
        cfg.GenCodeOnly = true;
    case 'dll'
        if ismac
           genMSg = ['Generating NMPC_Solve.dylib...' ];
        elseif isunix
           genMSg = ['Generating NMPC_Solve.so...' ];
        elseif ispc
           genMSg = ['Generating NMPC_Solve.dll...' ];
        else
           disp('Platform not supported')
        end
    otherwise
end
disp(genMSg);
codegen  -config cfg ...
          NMPC_Solve ...
         -globals globalVariable...
         -args args
%% copy dll/so/dylib to the working folder
if strcmp(target,'dll')
    clear mex
    if ismac
        %% comment omp in NMPC_Solve.h for mac
        fid = fopen('./codegen/dll/NMPC_Solve/NMPC_Solve.h','r');
        strAll = [];
        tline = fgetl(fid);
        while ischar(tline)
            isFound = strfind(tline, 'omp');
            if isempty(isFound)
                strAll = [strAll,tline,'\n'];
            else
                strAll = [strAll,'// ', tline,'\n'];
            end
            % Read next line
            tline = fgetl(fid);
        end
        fclose(fid);
        fid = fopen('./codegen/dll/NMPC_Solve/NMPC_Solve.h','w');
        fprintf(fid,strAll);
        fclose(fid);

        copyfile('./codegen/dll/NMPC_Solve/NMPC_Solve.dylib');
    elseif isunix
       copyfile('./codegen/dll/NMPC_Solve/NMPC_Solve.so');
    elseif ispc
       copyfile('./codegen/dll/NMPC_Solve/NMPC_Solve.dll');
    else
       disp('Platform not supported')
    end
end
%%
disp('Done!');


%
% Demo of how to load and use the dataset
%

% Parameters
nletter_per_alpha = 20; % number of letters of each alphabet to show

% Load the dataset
if ~exist('D','var')
    load('data_background','drawings','images','names','timing');
    D = Dataset(drawings,images,names,timing);
end

nrow = ceil(sqrt(nletter_per_alpha));
ncol = nrow;
nalpha = numel(D.images);

% for each alphabet
for a=1:nalpha
    msg = ['Alphabet ',D.names{a},' ',num2str(a),' of ',num2str(nalpha)];
    fprintf(1,[msg,'\n']);

    figure(1);
    clf
    for r=1:nletter_per_alpha % for each replication
       item = D.get('all',a,1,r); % get image 'r' of first character in alphabet 'a'

       % display the motor plotted on the image
       subplot(nrow,ncol,r);

       % switch between these two lines to show either image vs. motor data
       plot_motor_on_image(item.image,item.drawing,true,1);
%        plot_image_only(item.image);
    end

    % keyboard prompt
    str = input('  press <enter> to continue (''q'' to quit) : ','s');
    if str == 'q'
       break
    end
end


function M = spm_mesh_join(Ms)
% Join a list of surface meshes into a single one
% FORMAT M = spm_mesh_join(Ms)
% Ms       - a patch structure array
%
% M        - a patch structure
%
% See also spm_mesh_split
%__________________________________________________________________________
% Copyright (C) 2016 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_mesh_join.m 6912 2016-10-26 14:50:53Z guillaume $

fn = fieldnames(Ms(1));
M  = cell2struct(cell(numel(fn),1),fn);

for i=1:numel(Ms)
    if i==1
        M.faces   = Ms(i).faces;
    else
        M.faces   = [M.faces; Ms(i).faces+size(M.vertices,1)];
    end
    M.vertices    = [M.vertices; Ms(i).vertices];
    try, M.cdata  = [M.cdata; Ms(i).cdata]; end
    try
        if i==1, M.mat = Ms(i).mat; end
    end
    if isfield(M,'mat')
        if sum(sum(M.mat - Ms(i).mat)) > 10*eps
            error('Meshes have different orientation.');
        end
    end
end


function [cbar] = cbarEdit(cbar);
%
% [cbar] = cbarEdit([cbar]);
%
% Edit a mrVista color bar using a GUI.
%
% These color bars differ from the built-in matlab color bar tools in a few
% basic respects:
%
%   * They draw colorbars based on any colormap the user inputs, rather
%   than the color map assigned to the current figure. This is useful for
%   illustrating color codes on true color images, or images with overlaid
%   responses (like fMRI activation maps)
%
%   * In addition to being able to render vertical and horizontal color bars,
%   these tools may also render 'color wheel' colorbars, showing e.g. polar
%   angle. Other possible renderings (like rings) may be added down the
%   line.
%
% cbar is a struct with the following fields:
%
%   cmap: color map (nColors x 3) for the color bar. (Columns ar [R G B],
%   from 0-255).
%
%   nColors: # of colors to use in the cmap.
%
%   clim: color limits (aka 'clip mode'), which determines primarily
%   the labeling of the color bar. Can be set to 'auto', in which case
%   the labeling will be from 1:nColors. Otherwise, will label according to
%   the clim values (format is [min max]).
%
%   colorWheel: use a color wheel instead of a bar (e.g., to show polar
%   angle for a polar angle map).
%
%   colorWheelStart: degrees clockwise from 12-o-clock which map to beginning of
%   color map.
%
%   colorWheelDirection: direction of the color wheel. Can be:
%   1 or 'clockwise' (equivalent); or, 2 or 'counterclockwise' (equiv.)
%
%   colorWheelExtent: degrees (1 - 360) subtended by the color map, for polar
%   angle maps.
%
% ras, 08/06
if notDefined('cbar')
    cbar = cbarDefault;
end

if ischar(cbar) & isequal(lower(cbar), 'update')
    cbarEditUpdate; return;
end

% Create the GUI, attaching cmap and cbar
hfig = cbarEditGUI(cbar);

% Wait for user to respond  via GUI
uiwait;

% check if user canceled
OK = evalin('base', 'OK');
evalin('base', 'clear OK');
if OK==0
    warning('User Canceled.')
    delete(hfig);
    return
end

cbar = get(hfig, 'UserData');
delete(hfig);

cbar = rmfield(cbar, 'ui');

return
% /------------------------------------------------------------------/ %

% /------------------------------------------------------------------/ %
function cbar = cbarEditUpdate;
% update the color map editor GUI, based on the settings in the current
% figure.
cbar = get(gcf, 'UserData');

% modify the cmap according to any callback objects
if ishandle(gcbo)
    switch get(gcbo, 'Tag')
        case 'Shift'            % shift according to slider
            delta = round( get(gcbo, 'Value') );
            cbar.cmap = circshift(cbar.cmap, delta);
            set(gcbo, 'Value', 0);   % reset slider

        case 'Flip'             % flip cmap order
            cbar.cmap = flipud(cbar.cmap);

        case 'Reset'            % reset to original cbar
            cbar = get(gcbo, 'UserData'); % stashed here

        case 'NumColors'        % set # colors in cmap
            cbar.nColors = str2num(get(gcbo, 'String'));
            while size(cbar.cmap, 1) < cbar.nColors
                cbar.cmap = [cbar.cmap; cbar.cmap];
            end
            cbar.cmap = cbar.cmap(1:cbar.nColors,:);

        case 'Preset'           % use a preset color map
            cbar.cmap = mrvColorMaps(get(gcbo, 'Label'), cbar.nColors);

        case 'ColorWheel'       % set to color wheel
            cbar.colorWheel = get(gcbo, 'Value');
            vals = {'off' 'on'};
            set(cbar.ui.colorWheelPanel, 'Visible', vals{cbar.colorWheel+1});

        case 'ColorWheelDirection' % set color wheel CW / CCW
            options = {'clockwise' 'counterclockwise'};
            cbar.colorWheelDirection = options{ get(gcbo, 'Value') };

        case 'ColorWheelStart'  % set start angle for color wheel
            cbar.colorWheelStart = str2num(get(gcbo, 'String'));

        case 'ColorWheelExtent'  % set angle subtended by cmap
			% ras 02/07: new strategy; will set things so that the wedge
			% always goes a full 360 degrees, but will pad the cmap such
			% that it only occupies [extent] degrees. This should help it
			% to work properly for all extents in [0 360].
            newExtent = str2num(get(gcbo, 'String'));

			% bounds check
			newExtent = max(1, min(360, newExtent));
			set(gcbo, 'String', num2str(newExtent));

			if newExtent ~= cbar.colorWheelExtent
				% undo any previous gray padding
				cbar.cmap = cbar.cmap(1:cbar.nColors,:);
			end

			if cbar.colorWheelExtent < 360
				prop = cbar.colorWheelExtent / 360; % proprtion of cbar filled by cmap
				totalColors = ceil(cbar.nColors / prop);
				grayPad = repmat(.5, [(totalColors - cbar.nColors) 3]);
				cbar.cmap = [cbar.cmap; grayPad];
			end

			cbar.colorWheelExtent = newExtent;

        case 'Expression'       % user inputs expression for color map
			map = cbar.cmap;
			msg = ['Enter a MATLAB expression for the color map. ' ...
				'The color map should be a matrix of 3 x ncolors, ' ...
				'with values from 0 - 255. ' ...
				'You can directly enter the expression, or assign ' ...
				'it to a variable named "map". ' ...
				'E.g.:    [gray(128); hot(128)]; ' ...
				'or:      tmp = hsv(256);  map = tmp(1:100,:); '];
			def = sprintf('gray(%i); ', cbar.nColors);
			resp = inputdlg({msg}, mfilename, 4, {def});
			if strfind(resp{1}, '=') & strfind(resp{1}, 'map')
				% presume that the expression is assigning a value to the
				% 'map' variable:
				eval(resp{1})
			else
				% the map assignment is implied
				map = eval(resp{1});
			end
			cbar.cmap = map;

    end
end

% draw the color bar
cbarDraw(cbar);
title('Edit Color Map');

% update GUI figure
set(gcf, 'UserData', cbar);

return
% /------------------------------------------------------------------/ %

% /------------------------------------------------------------------/ %
function hfig = cbarEditGUI(cbar);
% creates the figure for cbarEdit.

%%%%% create figure, main axes
hfig = figure('Color', 'w', 'Name', 'Edit Color Map', 'MenuBar', 'none');
hax = subplot('Position', [.1 .7 .8 .16]);

%%%%% callback for all uicontrols
cb = 'cbarEdit(''update''); ';

%%%%%main slider
nC = cbar.nColors;
hs = uicontrol('Style', 'slider', 'Min', -nC/2, 'Max', nC/2, 'Value', 0, ...
   'BackgroundColor', 'w', 'ForegroundColor', [.6 .55 .6], 'Tag', 'Shift', ...
    'Units', 'normalized', 'Position', [.1 .55 .6 .08], 'Callback', cb);

% text label
uicontrol('Style', 'text', 'String', 'Amount of Rotation', ...
    'BackgroundColor', [1 1 1], 'FontSize', 12, ...
    'Units', 'normalized', 'Position', [.1 .47 .8 .08]);

% flip button
uicontrol('Style', 'pushbutton', 'String', 'Flip', 'Callback', cb, ...
    'Tag', 'Flip', ...
    'Units', 'normalized', 'Position', [.75 .55 .2 .05]);

% reset button
uicontrol('Style', 'pushbutton', 'String', 'Reset', 'Callback', cb, ...
    'Tag', 'Reset', ...
    'UserData', cbar, 'Units', 'normalized', 'Position', [.75 .5 .2 .05]);

% edit field for setting # of colors
uicontrol('Style', 'text', 'String', '# Colors in Cmap', 'Callback', cb, ...
    'BackgroundColor', 'w', 'FontSize', 10, 'FontWeight', 'bold', ...
    'UserData', cbar, 'Units', 'normalized', 'Position', [.75 .5 .2 .05]);

uicontrol('Style', 'edit', 'String', num2str(cbar.nColors), 'Callback', cb, ...
    'Tag', 'NumColors', 'Units', 'normalized', 'Position', [.75 .45 .15 .05]);

%%%%%% wedge cbar controls
% checkbox to set the colorbar as a color wheel (wedge)
uicontrol('Style', 'checkbox', 'String', 'Color Wheel (Wedge)', ...
          'Units', 'normalized', 'Position', [.1 .3 .4 .08], ...
          'BackgroundColor', 'w', 'FontSize', 10, 'FontWeight', 'bold', ...
          'Value', cbar.colorWheel, ...
          'Tag', 'ColorWheel', 'Callback', cb);

% put the subsidiary controls in a uipanel
hp = uipanel('Units', 'normalized', 'Position', [.4 .1 .5 .3], ...
             'BorderType', 'none', 'BackgroundColor', 'w');
cbar.ui.colorWheelPanel = hp;

% popup to set the colorbar direction
uicontrol('Parent', hp, 'Style', 'text', 'String', 'Direction', 'Callback', cb, ...
         'BackgroundColor', 'w', 'Units', 'normalized', 'Position', [.1 .85 .8 .1]);

uicontrol('Parent', hp, 'Style', 'popup', ...
          'String', {'clockwise' 'counterclockwise'}, ...
          'BackgroundColor', 'w', ...
          'Units', 'normalized', 'Position', [.1 .6 .8 .25], ...
          'Tag', 'ColorWheelDirection', 'Callback', cb);

% edit field to set the color wheel start angle
uicontrol('Parent', hp, 'Style', 'text', 'String', 'Start Angle', 'Callback', cb, ...
          'BackgroundColor', 'w', 'Units', 'normalized', 'Position', [.1 .35 .3 .25]);

uicontrol('Parent', hp, 'Style', 'edit', ...
          'String', num2str(cbar.colorWheelStart), ...
          'BackgroundColor', 'w', ...
          'Units', 'normalized', 'Position', [.6 .35 .3 .25], ...
          'Tag', 'ColorWheelStart', 'Callback', cb);

% edit field to set the color wheel extent
uicontrol('Parent', hp, 'Style', 'text', 'String', 'Extent', 'Callback', cb, ...
          'BackgroundColor', 'w', 'Units', 'normalized', 'Position', [.1 .05 .4 .25]);

uicontrol('Parent', hp, 'Style', 'edit', ...
          'String', num2str(cbar.colorWheelExtent), ...
          'Units', 'normalized', 'Position', [.6 .05 .4 .25], ...
          'BackgroundColor', 'w', ...
          'Tag', 'ColorWheelExtent', 'Callback', cb);

onoff = {'off' 'on'};
set(hp, 'Visible', onoff{cbar.colorWheel+1});

%%%%% also add menus to set preset color bars.
mapNames = mrvColorMaps;
mapNames = mapNames(1:end-1); % remove the 'user' option
hm = uimenu('Label','Preset Colormaps','ForegroundColor','b');
for i = 1:length(mapNames)
    uimenu(hm, 'Label', mapNames{i}, 'Tag', 'Preset', 'Callback', cb);
end

% add a callback to let the user input a MATLAB expression for the cbar
uimenu(hm, 'Label', 'Set color map using MATLAB expression...', ...
           'Tag', 'Expression', 'Callback', cb);

addFigMenuToggle(hm);

%%%%%Accept and Cancel buttons
evalin('base', 'OK=0;');
cb = 'OK = 1; close(gcf); ';
uicontrol('Style', 'pushbutton', 'String', 'Accept', 'Callback', cb, ...
    'BackgroundColor', [.1 .5 .1], 'ForegroundColor', [1 1 1], ...
    'Units', 'normalized', 'Position', [.1 .04 .3 .06]);

cb = 'OK = 0; delete(gcf); uiresume;';
uicontrol('Style', 'pushbutton', 'String', 'Cancel', 'Callback', cb, ...
    'BackgroundColor', [.7 .1 .1], 'ForegroundColor', [1 1 1], ...
    'Units', 'normalized', 'Position', [.5 .04 .3 .06]);

set(hfig, 'CloseRequestFcn', 'uiresume;', 'UserData', cbar);

cbarEditUpdate;

return


% Copyright (C) 2018  Symeon Symeonidis, Stefanos Tsantilas, Stelios Mitilineos
% simos421@gmail.com, steftsantilas@gmail.com, smitil@gmail.com
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.

function CstDefaultUnits(mws)

%define units (Default selection)
Geometry = 'mm';
Frequency = 'GHz';
Time = 'ns';
TemperatureUnit = 'Kelvin';
Voltage = 'V';
Current = 'A';
Resistance = 'Ohm';
Conductance  = 'S';
Capacitance = 'PikoF';
Inductance = 'NanoH';

Units = invoke(mws,'Units');
invoke(Units,'Geometry',Geometry);
invoke(Units,'Frequency',Frequency);
invoke(Units,'Time',Time);
invoke(Units,'TemperatureUnit',TemperatureUnit);
invoke(Units,'Voltage',Voltage);
invoke(Units,'Current',Current);
invoke(Units,'Resistance',Resistance);
invoke(Units,'Conductance',Conductance);
invoke(Units,'Capacitance',Capacitance);
invoke(Units,'Inductance',Inductance);

end


function [keypointsAll, imgidxs_missing]= det2keypointsImgMulticutBBoxMultiPoses(expidx,parts,nImgs)

fprintf('det2keypointsImgMulticutBBoxMultiPoses()\n');

p = rcnn_exp_params(expidx);
load([p.detDir '/annolist_poses'],'annolist_poses');

load(p.testGT,'annolist');

assert(length(annolist) == length(annolist_poses));

nmissing = 0;
bVis = false;
imgidxs_missing = [];

if (isfield(p,'nPeopleMax'))
    nPeopleMax = p.nPeopleMax;
else
    nPeopleMax = inf;
end

if (isfield(p,'nPeopleMin'))
    nPeopleMin = p.nPeopleMin;
else
    nPeopleMin = 0;
end

nTotalBBox = 0;
nTotalBBoxDet = 0;
nTotalImgs = 0;
nTotalImgsNotDet = 0;
idxmap = [8 7 6 12 13 14 -1 -1 2 1 5 4 3 9 10 11];
for imgidx = 1:nImgs

    fprintf('.');
    keypointsAll(imgidx).imgname = annolist_poses(imgidx).image.name;

    ndet = 0; clusidx = 0;
    bNeedNumberOfPeople = false;
    if (length(annolist(imgidx).annorect) <= nPeopleMax && ...
        length(annolist(imgidx).annorect) >= nPeopleMin)
        nTotalImgs = nTotalImgs + 1;
        bNeedNumberOfPeople = true;
        for ridx = 1:length(annolist_poses(imgidx).annorect)
            nTotalBBox = nTotalBBox + 1;
            rect = annolist_poses(imgidx).annorect(ridx);
            if (isfield(rect,'joints') && ~isempty(rect.joints))
                nTotalBBoxDet = nTotalBBoxDet + 1;
                clusidx = clusidx + 1;
                joints = rect.joints;
                %             img = imread(annolist_poses(imgidx).image.name);
                %             figure(100); clf; imagesc(img); axis equal; hold on;
                %             labels = cell(0);
                %             for i = 1:14
                %                 labels{i} = i;
                %             end
                %             joints = double(joints);
                %             plot(joints(:,1),joints(:,2),'r+','MarkerSize',5);
                %             text(joints(:,1)+10,joints(:,2),labels,'FontSize',6,'BackgroundColor','w');
                for i = 1:length(p.pidxs)
                    pidx = p.pidxs(i);
                    assert(parts(pidx+1).pos(1) == parts(pidx+1).pos(2));
                    jidx = parts(pidx+1).pos(1);
                    ndet = ndet + 1;
                    keypointsAll(imgidx).det{clusidx}{jidx+1} = joints(idxmap(jidx+1),:);
                end
            end
        end
        if (ndet == 0)
            nTotalImgsNotDet = nTotalImgsNotDet + 1;
        end
    end
    if (ndet == 0)
        for i = 1:length(p.pidxs)
            pidx = p.pidxs(i);
            jidx = parts(pidx+1).pos(1);
            keypointsAll(imgidx).det{1}{jidx+1} = [];
        end
        if (~bNeedNumberOfPeople)
            imgidxs_missing = [imgidxs_missing; imgidx];
            nmissing = nmissing + 1;
        end
        if (~mod(imgidx, 100))
            fprintf(' %d/%d\n',imgidx,length(annolist_poses));
        end
        continue;
    end

    if (bVis)
        colors = {'r','g','b','c','m','y'};
        markers = {'+','o','s'}; %,'x','.','-','s','d'
        img = imread(annolist_poses(imgidx).image.name);
        figure(100); clf; imagesc(img); axis equal; hold on;
        for j = 1:length(keypointsAll(imgidx).det)
            labels = cell(0);
            n = 0;
            points = zeros(1,2);
            for i = 1:length(p.pidxs)
                pidx = p.pidxs(i);
                jidx = parts(pidx+1).pos(1);
                pp = keypointsAll(imgidx).det{j}{jidx+1};
                if (~isempty(pp))
                    n = n  + 1;
                    labels{n} = num2str(i);
%                     labels{n} = sprintf('%1.2f',pp(3));
                    points(n,:) = pp(1:2);
                end
            end
            lp = j;
            if (lp <= 5)
                m = markers{1};
            elseif (lp > 5 && lp <= 11)
                m = markers{2};
            else
                m = markers{3};
            end
            plot(points(:,1),points(:,2),[colors{mod(lp,6)+1} m],'MarkerSize',5);
            text(points(:,1)+10,points(:,2),labels,'FontSize',6,'BackgroundColor','w');
            axis off;
        end
    end

    if (~mod(imgidx, 100))
        fprintf(' %d/%d\n',imgidx,length(annolist_poses));
    end
end
fprintf(' done\n');
fprintf('nmissing: %d\n',nmissing);
fprintf('nTotal: %d\n',nTotalBBox);
fprintf('nTotalDet: %d\n',nTotalBBoxDet);
fprintf('nTotalImgs: %d\n',nTotalImgs);
fprintf('nTotalImgsNotDet: %d\n',nTotalImgsNotDet);
nTotalImgsNotDet/nTotalImgs
end