function [NewTessFile, iSurface] = tess_concatenate( TessFiles, NewComment, fileType, Labels)
% TESS_CONCATENATE: Concatenate various surface files into one new file.
%
% USAGE:  [NewTessFile, iSurface] = tess_concatenate(TessFiles, NewComment='New surface', fileType='Other', Labels)
%          [NewTessMat, iSurface] = tess_concatenate(TessMats,  NewComment='New surface', fileType='Other', Labels)
%
% INPUT:
%    - TessFiles   : Cell-array of paths to surfaces files to concatenate
%    - TessMats    : Array of loaded surface structures
%    - NewComment  : Name of the output surface
%    - fileType    : File type for the new file {'Cortex', 'InnerSkull', 'OuterSkull', 'Scalp', 'Other'}
% OUTPUT:
%    - NewTessFile : Filename of the newly created file
%    - iSurface    : Index of the new surface file

% @=============================================================================
% 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, 2008-2022

% Parse inputs
if (nargin < 4) || isempty(Labels)
    Labels = [];
end
if (nargin < 3) || isempty(fileType)
    fileType = [];
end
if (nargin < 2) || isempty(NewComment)
    NewComment = [];
end
% Save current scouts modifications
panel_scout('SaveModifications');
% Initialize new structure
NewTess = db_template('surfacemat');
isLeft = 0;
isRight = 0;
isWhite = 0;
isCortex = 0;
isVolAtlas = 0;
isSave = 1;

% Progress bar
bst_progress('start', 'Merge surfaces', 'Processing... ');
% Process all the files to concatenate
for iFile = 1:length(TessFiles)
    % Load tesselation
    if iscell(TessFiles)
        [oldTess, TessFiles{iFile}] = in_tess_bst(TessFiles{iFile});
        if isempty(oldTess)
            continue
        end
    % Files already loaded in calling function
    else
        oldTess = TessFiles(iFile);
        isSave = 0;
    end
    % Detect if right/left hemisphere
    if ~isempty(strfind(oldTess.Comment, 'lh.')) || ~isempty(strfind(oldTess.Comment, 'Lhemi')) || ~isempty(strfind(oldTess.Comment, 'Lwhite')) || ~isempty(strfind(oldTess.Comment, 'left'))
        isLeft = 1;
        scoutTag = ' L';
        scoutHemi = 'L';
        scoutComment = 'Cortex';
    elseif ~isempty(strfind(oldTess.Comment, 'rh.')) || ~isempty(strfind(oldTess.Comment, 'Rhemi')) || ~isempty(strfind(oldTess.Comment, 'Rwhite')) || ~isempty(strfind(oldTess.Comment, 'right'))
        isRight = 1;
        scoutTag = ' R';
        scoutHemi = 'R';
        scoutComment = 'Cortex';
    % Detect based on comment (tag ' L' or ' R' already present)
    elseif (length(oldTess.Comment) > 2) && strcmpi(oldTess.Comment(end-1:end), ' L')
        scoutTag = ' L';
        scoutHemi = 'L';
        scoutComment = oldTess.Comment;
    elseif (length(oldTess.Comment) > 2) && strcmpi(oldTess.Comment(end-1:end), ' R')
        scoutTag = ' R';
        scoutHemi = 'R';
        scoutComment = oldTess.Comment;
    % Guess based on the coordinates
    else
        if (nnz(oldTess.Vertices(:,2) > 0) > 5 * nnz(oldTess.Vertices(:,2) < 0))
            scoutTag = ' L';
            scoutHemi = 'L';
        elseif (nnz(oldTess.Vertices(:,2) < 0) > 5 * nnz(oldTess.Vertices(:,2) > 0))
            scoutTag = ' R';
            scoutHemi = 'R';
        else
            scoutTag = '';
            scoutHemi = 'U';
        end
        scoutComment = oldTess.Comment;
    end
    % Detect some specific types of surfaces
    if ~isempty(strfind(oldTess.Comment, 'white'))
        isWhite = 1;
    end
    if ~isempty(strfind(oldTess.Comment, 'cortex_'))
        isCortex = 1;
    end
    if ~isempty(strfind(oldTess.Comment, 'aseg')) || ~isempty(strfind(oldTess.Comment, 'subcortical'))
        isVolAtlas = 1;
    end
    % Concatenate current sub-tess to final tesselation structure
    offsetVertices   = size(NewTess.Vertices,1);
    NewTess.Faces    = [NewTess.Faces; oldTess.Faces + offsetVertices];
    NewTess.Vertices = [NewTess.Vertices; oldTess.Vertices];

    % History: Merged surface #i
    NewTess = bst_history('add', NewTess, 'merge', sprintf('Merge surface #%d: %s', iFile, oldTess.Comment));
    % History: Copy history of surface #i
    if isfield(oldTess, 'History') && ~isempty(oldTess.History)
        NewTess = bst_history('add', NewTess, oldTess.History, '  => ');
    end

    % Add an atlas "Structures" to reference the origins of each structure (if it does not exist)
    iAtlasStruct = find(strcmpi({oldTess.Atlas.Name}, 'Structures'));
    if isempty(iAtlasStruct)
        % Create new atlas
        iAtlasStruct = length(oldTess.Atlas) + 1;
        oldTess.Atlas(iAtlasStruct) = db_template('atlas');
        oldTess.Atlas(iAtlasStruct).Name = 'Structures';
        % Create one scout that describes all the structure
        oldTess.Atlas(iAtlasStruct).Scouts(1).Vertices = 1:length(oldTess.Vertices);
        oldTess.Atlas(iAtlasStruct).Scouts(1).Seed     = 1;
        oldTess.Atlas(iAtlasStruct).Scouts(1).Label    = scoutComment;
        oldTess.Atlas(iAtlasStruct).Scouts(1).Function = 'Mean';
        oldTess.Atlas(iAtlasStruct).Scouts(1).Region   = [scoutHemi 'U'];
        % If there are labels in inputs, try to get a color for the scout
        if ~isempty(Labels) && (size(Labels,2) == 3)
            iLabel = find(strcmpi(Labels(:,2), scoutComment));
            if ~isempty(iLabel)
                oldTess.Atlas(iAtlasStruct).Scouts(1).Color = Labels{iLabel,3} ./ 255;
            end
        end
        % Set scout color
        if isempty(oldTess.Atlas(iAtlasStruct).Scouts(1).Color)
            oldTess.Atlas(iAtlasStruct).Scouts(1) = panel_scout('SetColorAuto', oldTess.Atlas(iAtlasStruct).Scouts(1));
        end
    end

    % Concatenate atlases/scouts
    for iAtlasOld = 1:length(oldTess.Atlas)
        % Look for the same altas in the new surface
        iAtlasNew = find(strcmpi({NewTess.Atlas.Name}, oldTess.Atlas(iAtlasOld).Name));
        if isempty(iAtlasNew)
            iAtlasNew = length(NewTess.Atlas) + 1;
            NewTess.Atlas(iAtlasNew) = db_template('atlas');
            NewTess.Atlas(iAtlasNew).Name = oldTess.Atlas(iAtlasOld).Name;
        end
        % Loop over all scouts in the old surface to fix the indices of the vertices
        for iScout = 1:length(oldTess.Atlas(iAtlasOld).Scouts)
            % Adjust vertex indices
            oldTess.Atlas(iAtlasOld).Scouts(iScout).Vertices = oldTess.Atlas(iAtlasOld).Scouts(iScout).Vertices + offsetVertices;
            oldTess.Atlas(iAtlasOld).Scouts(iScout).Seed     = oldTess.Atlas(iAtlasOld).Scouts(iScout).Seed     + offsetVertices;
            % Add the first letter of the surface comment to the scout name
            if ~ismember(oldTess.Atlas(iAtlasOld).Scouts(iScout).Label(end), {'L', 'R'})
                oldTess.Atlas(iAtlasOld).Scouts(iScout).Label = [oldTess.Atlas(iAtlasOld).Scouts(iScout).Label, scoutTag];
            end
            % Set region name
            oldRegion = oldTess.Atlas(iAtlasOld).Scouts(iScout).Region;
            if isempty(oldRegion) || (length(oldRegion) < 2)
                oldTess.Atlas(iAtlasOld).Scouts(iScout).Region = [scoutHemi, 'U'];
            elseif (oldTess.Atlas(iAtlasOld).Scouts(iScout).Region(1) == 'U')
                oldTess.Atlas(iAtlasOld).Scouts(iScout).Region(1) = scoutHemi;
            end
        end
        % Add to surface
        if isempty(NewTess.Atlas(iAtlasNew).Scouts) && isempty(oldTess.Atlas(iAtlasOld).Scouts)
            NewTess.Atlas(iAtlasNew).Scouts = repmat(db_template('scout'), 0);
        elseif isempty(NewTess.Atlas(iAtlasNew).Scouts)
            NewTess.Atlas(iAtlasNew).Scouts = oldTess.Atlas(iAtlasOld).Scouts;
        else
            NewTess.Atlas(iAtlasNew).Scouts = [...
                struct_fix(db_template('scout'), NewTess.Atlas(iAtlasNew).Scouts), ...
                struct_fix(db_template('scout'), oldTess.Atlas(iAtlasOld).Scouts)];
        end
    end

    % Concatenate FreeSurfer registration spheres
    if isfield(oldTess, 'Reg') && isfield(oldTess.Reg, 'Sphere') && isfield(oldTess.Reg.Sphere, 'Vertices') && ~isempty(oldTess.Reg.Sphere.Vertices)
        if ~isfield(NewTess, 'Reg') || ~isfield(NewTess.Reg, 'Sphere') || ~isfield(NewTess.Reg.Sphere, 'Vertices')
            NewTess.Reg.Sphere.Vertices = oldTess.Reg.Sphere.Vertices;
        else
            NewTess.Reg.Sphere.Vertices = [NewTess.Reg.Sphere.Vertices; oldTess.Reg.Sphere.Vertices];
        end
    % Remove all spheres if they are not available in all the input files (disabled for now because cortex_cereb works well for projections)
    % elseif isfield(NewTess, 'Reg') &&  isfield(NewTess.Reg, 'Sphere') && isfield(NewTess.Reg.Sphere, 'Vertices') && ~isempty(NewTess.Reg.Sphere.Vertices)
    %     NewTess.Reg.Sphere.Vertices = [];
    end

    if isfield(oldTess, 'Reg') && isfield(oldTess.Reg, 'SphereLR') && isfield(oldTess.Reg.SphereLR, 'Vertices') && ~isempty(oldTess.Reg.SphereLR.Vertices)
        if ~isfield(NewTess, 'Reg') || ~isfield(NewTess.Reg, 'SphereLR') || ~isfield(NewTess.Reg.SphereLR, 'Vertices')
            NewTess.Reg.SphereLR.Vertices = oldTess.Reg.SphereLR.Vertices;
        else
            NewTess.Reg.SphereLR.Vertices = [NewTess.Reg.SphereLR.Vertices; oldTess.Reg.SphereLR.Vertices];
        end
    % Remove all spheres if they are not available in all the input files (disabled for now because cortex_cereb works well for projections)
    % elseif isfield(NewTess, 'Reg') &&  isfield(NewTess.Reg, 'SphereLR') && isfield(NewTess.Reg.SphereLR, 'Vertices') && ~isempty(NewTess.Reg.SphereLR.Vertices)
    %         NewTess.Reg.SphereLR.Vertices = [];
    end

    % Concatenate BrainSuite registration squares
    if isfield(oldTess, 'Reg') && isfield(oldTess.Reg, 'Square') && isfield(oldTess.Reg.Square, 'Vertices') && ~isempty(oldTess.Reg.Square.Vertices)
        if ~isfield(NewTess, 'Reg') || ~isfield(NewTess.Reg, 'Square') || ~isfield(NewTess.Reg.Square, 'Vertices')
            NewTess.Reg.Square.Vertices      = oldTess.Reg.Square.Vertices;
            NewTess.Reg.AtlasSquare.Vertices = oldTess.Reg.AtlasSquare.Vertices;
        else
            NewTess.Reg.Square.Vertices      = [NewTess.Reg.Square.Vertices;      oldTess.Reg.Square.Vertices];
            NewTess.Reg.AtlasSquare.Vertices = [NewTess.Reg.AtlasSquare.Vertices; oldTess.Reg.AtlasSquare.Vertices];
        end
    % Remove all spheres if they are not available in all the input files (disabled for now because cortex_cereb works well for projections)
    % elseif isfield(NewTess, 'Reg') && isfield(NewTess.Reg, 'Square') && isfield(NewTess.Reg.Square, 'Vertices') && ~isempty(NewTess.Reg.Square.Vertices)
    %     NewTess.Reg.Square.Vertices = [];
    %     NewTess.Reg.AtlasSquare.Vertices = [];
    end
end

% Sort scouts by name
for iAtlas = 1:length(NewTess.Atlas)
    [tmp, iSort] = sort({NewTess.Atlas(iAtlas).Scouts.Label});
    NewTess.Atlas(iAtlas).Scouts = NewTess.Atlas(iAtlas).Scouts(iSort);
end

% Detect surfaces types
if isLeft && isRight
    % File type: Cortex
    if isempty(fileType)
        fileType = 'Cortex';
    end
    % White matter
    if isWhite
        fileTag = 'cortex_white';
        if isempty(NewComment)
            NewComment = sprintf('white_%dV', length(NewTess.Vertices));
        end
    % Pial/cortex external envelope
    else
        fileTag = 'cortex_pial';
        if isempty(NewComment)
            NewComment = sprintf('cortex_%dV', length(NewTess.Vertices));
        end
    end
elseif isCortex && isVolAtlas
    fileTag = 'cortex_mixed';
    if isempty(fileType)
        fileType = 'Cortex';
    end
    if isempty(NewComment)
        NewComment = sprintf('cortex_mixed_%dV', length(NewTess.Vertices));
    end
else
    fileTag = 'concat';
    if isempty(fileType)
        fileType = 'Other';
    end
    if isempty(NewComment)
        NewComment = 'New surface';
    end
end
% Surface comments
NewTess.Comment = NewComment;
% History: Merge completed
NewTess = bst_history('add', NewTess, 'merge', 'Merge completed');

% ===== SAVE IN DATABASE =====
if isSave
    % Create new filename
    NewTessFile = bst_fullfile(bst_fileparts(TessFiles{1}), ['tess_' fileTag '.mat']);
    NewTessFile = file_unique(NewTessFile);
    % Save file
    bst_save(NewTessFile, NewTess, 'v7');
    % Make output filename relative
    NewTessFile = file_short(NewTessFile);
    % Get subject
    [sSubject, iSubject] = bst_get('SurfaceFile', TessFiles{1});
    % Register this file in Brainstorm database
    iSurface = db_add_surface(iSubject, NewTessFile, NewComment, fileType);
else
    NewTessFile = NewTess;
    iSurface = [];
end
% Close progress bar
bst_progress('stop');


function nword = file_word_count ( file_name )

%*****************************************************************************80
%
%% FILE_WORD_COUNT counts the number of words in a file.
%
%  Discussion:
%
%    The file is assumed to be a simple text file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 February 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string FILE_NAME, the name of the file.
%
%    Output, integer NWORD, the number of words found in the file.
%
  nword = 0;
%
%  Open the file.
%
  fid = fopen ( file_name );

  if ( fid = -1 )
    nword = -1;
    fprintf ( 1, '\n' );
    fprintf ( 1, 'FILE_WORD_COUNT - Fatal error!\n' );
    fprintf ( 1, '  Could not open the file:\n' );
    fprintf ( 1, '"%s".\n', file_name );
    return
  end
%
%  Read the lines.
%
  while ( 1 )

    line = fgets ( fid );

    if ( line == -1 )
      break;
    end

    nplus = s_word_count ( line );

    nword = nword + nplus;

  end

  fclose ( fid )

  return
end


function display(SO3F,varargin)
% standard output

if check_option(varargin,'skipHeader')
  disp('  SO3-function component');
else
  displayClass(SO3F,inputname(1),[],'moreInfo',symChar(SO3F),varargin{:});
end

if length(SO3F) > 1, disp(['  size: ' size2str(SO3F)]); end

if SO3F.antipodal, disp('  antipodal: true'); end

disp(['  weight: ' xnum2str(mean(SO3F,'resolution',5*degree))])

disp(' ')

end


function activate_ap(speed, host, port)
% ACTIVATE_AP(SPEED, HOST, PORT)
%
% Activate Autopilot in Flightgear over socket connection (HOST,
% PORT) and set target speed = SPEED [mph].
% Run Flightgear on HOST (e.g. '127.0.0.1' = localhost) with
% parameter "--props = PORT".

import java.net.Socket
import java.io.*

fg_socket = Socket(host, port);
out = PrintWriter(fg_socket.getOutputStream(), true);

% activate autopilot
out.println(sprintf(...
    'set /autopilot/locks/altitude altitude-hold\r\n'));
out.println(sprintf(...
    'set /autopilot/locks/heading true-heading-hold\r\n'));
out.println(sprintf(...
    'set /autopilot/locks/speed speed-with-throttle\r\n'));

% set target speed (convert mph to knots)
out.println(sprintf(...
    'set /autopilot/settings/target-speed-kt %d\r\n',speed*0.869));

out.close();
fg_socket.close();

end


function p = p25_p_val ( action, p )

%*****************************************************************************80
%
%% P25_P_VAL sets or gets the value of a parameter for problem 25.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 October 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string ACTION.
%    'S', set the interval value to the input value of P.
%    'G', set the output value of P to the internal value.
%
%    Input/output, real P, the value of the parameter that
%    multiplies the Gaussian noise.
%
  persistent p_save

  if ( action(1) == 'S' || action(1) == 's' )
    p_save = p;
  elseif ( action(1) == 'G' || action(1) == 'g' )
    p = p_save;
  end

  return
end


% SEEMOVIE - see an EEG movie produced by EEGMOVIE
%
% Usage: >> seemovie(Movie,ntimes,Colormap)
%
% Inputs:
%         Movie    = Movie matrix returned by EEGMOVIE
%         ntimes   = Number of times to display {0 -> -10}
%                    If ntimes < 0, movie will play forward|backward
%         Colormap = Color map returned by EEGMOVIE {0 -> default}
%
% Author: Scott Makeig & Colin Humphries, CNL / Salk Institute, 6/3/97
%
% See also: EEGMOVIE

% Copyright (C) 6/3/97 Scott Makeig & Colin Humphries, CNL / Salk Institute, La Jolla CA
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

% 10-31-97 changed test for user-defined colormap -ch & sm
% 1-8-98 added '\n' at end, improved help msg -sm
% 01-25-02 reformated help, added link -ad

function seemovie(Movie,ntimes,Colormap)

fps = 10;   % projetion speed (requested frames per second)

if nargin<1
   help seemovie
   return
end
if nargin<3
    Colormap = 0;
end
if nargin<2
	ntimes = -10;    % default to playing forward|backward endlessly
end
if ntimes == 0
	ntimes = -10;
end

clf
axes('Position',[0 0 1 1]);
if size(Colormap,2) == 3 % if colormap user-defined
	colormap(Colormap)
else
	colormap([jet(64);0 0 0]);    % set up the default topoplot color map
end

if ntimes > 0,
 fprintf('Movie will play slowly once, then %d times faster.\n',ntimes);
else
 fprintf('Movie will play slowly once, then %d times faster forwards and backwards.\n',-ntimes);
end
if abs(ntimes) > 7
	fprintf('   Close figure to abort:  ');
end

%%%%%%%%%%%%%%%%%%%%%%%% Show the movie %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

movie(Movie,ntimes,fps);

%%%%%%%%%%%%%%%%%%%%%%%%    The End     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if abs(ntimes) > 7
	fprintf('\n');
end


%DATASETCONV Convert to dataset if needed
%
%		A = DATASETCONV(A)
%
% If A is not a dataset it is converted to a dataset.
%
% SEE ALSO (PRTools Guide)
% DATASETS, DATASET

function a = datasetconv(a)

% This is just programmed like this for speed, as
% a = prdataset(a) will do the same but involves more checking

if ~isdataset(a)
	a = prdataset(a);
end


function [cfg] = ft_clusterplot(cfg, stat)

% FT_CLUSTERPLOT plots a series of topographies with highlighted clusters.
%
% Use as
%   ft_clusterplot(cfg, stat)
% where the input data is obtained from FT_TIMELOCKSTATISTICS or FT_FREQSTATISTICS.
%
% The configuration options can be
%   cfg.alpha                   = number, highest cluster p-value to be plotted max 0.3 (default = 0.05)
%   cfg.highlightseries         = 1x5 cell-array, highlight option series  with 'on', 'labels' or 'numbers' (default {'on', 'on', 'on', 'on', 'on'} for p < [0.01 0.05 0.1 0.2 0.3]
%   cfg.highlightsymbolseries   = 1x5 vector, highlight marker symbol series (default ['*', 'x', '+', 'o', '.'] for p < [0.01 0.05 0.1 0.2 0.3]
%   cfg.highlightsizeseries     = 1x5 vector, highlight marker size series   (default [6 6 6 6 6] for p < [0.01 0.05 0.1 0.2 0.3])
%   cfg.highlightcolorpos       = color of highlight marker for positive clusters (default = [0 0 0])
%   cfg.highlightcolorneg       = color of highlight marker for negative clusters (default = [0 0 0])
%   cfg.subplotsize             = layout of subplots ([h w], default [3 5])
%   cfg.saveaspng               = string, filename of the output figures (default = 'no')
%   cfg.visible                 = string, 'on' or 'off' whether figure will be visible (default = 'on')
%   cfg.position                = location and size of the figure, specified as [left bottom width height] (default is automatic)
%   cfg.renderer                = string, 'opengl', 'zbuffer', 'painters', see RENDERERINFO (default is automatic, try 'painters' when it crashes)
%   cfg.toi                     = vector, or 'all' (default) indicates which time
%                                 points (or frequency bins) are to be plotted. If specified as 'all' only the
%                                 data points with identified clusters are plotted
%
% You can also specify most configuration options that apply to FT_TOPOPLOTER or FT_TOPOPLOTTFR,
% except for cfg.xlim, any of the highlight options, cfg.comment and cfg.commentpos.
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
% If you specify this option the input data will be read from a *.mat
% file on disk. This mat files should contain only a single variable named 'data',
% corresponding to the input structure.
%
% See also FT_TOPOPLOTTFR, FT_TOPOPLOTER, FT_MOVIEPLOTTFR, FT_MOVIEPLOTER

% Copyright (C) 2007-2020, F.C. Donders Centre, Ingrid Nieuwenhuis
% Copyright (C) 2021, Donders Centre for Cognitive Neuroimaging, Ingrid Nieuwenhuis and Jan-Mathijs Schoffelen
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar stat
ft_preamble provenance stat

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

ws = ft_warning('off', 'FieldTrip:getdimord:warning_dimord_could_not_be_determined');

% check if the input data is valid for this function
stat = ft_checkdata(stat, 'datatype', {'timelock', 'freq'}, 'feedback', 'yes');

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed',     {'hlmarkerseries',       'highlightsymbolseries'});
cfg = ft_checkconfig(cfg, 'renamed',     {'hlmarkersizeseries',   'highlightsizeseries'});
cfg = ft_checkconfig(cfg, 'renamed',     {'hlcolorpos',           'highlightcolorpos'});
cfg = ft_checkconfig(cfg, 'renamed',     {'hlcolorneg',           'highlightcolorneg'});
cfg = ft_checkconfig(cfg, 'renamed',     {'zparam', 'parameter'});
cfg = ft_checkconfig(cfg, 'deprecated',  {'hllinewidthseries'});
cfg = ft_checkconfig(cfg, 'deprecated',  {'xparam', 'yparam'});
cfg = ft_checkconfig(cfg, 'renamed',     {'newfigure', 'figure'});

% added several forbidden options
cfg = ft_checkconfig(cfg, 'forbidden',  {'highlight', 'highlightchannel', ...
  'highlightsymbol', 'highlightcolor', 'highlightsize', 'highlightfontsize', ...
  'xlim', 'comment', 'commentpos'});

% set the defaults
cfg.highlightseries         = ft_getopt(cfg, 'highlightseries',         {'on', 'on', 'on', 'on', 'on'});
cfg.highlightsymbolseries   = ft_getopt(cfg, 'highlightsymbolseries',   ['*', 'x', '+', 'o', '.']);
cfg.highlightsizeseries     = ft_getopt(cfg, 'highlightsizeseries',     [6 6 6 6 6]);
cfg.hllinewidthseries       = ft_getopt(cfg, 'hllinewidthseries',       [1 1 1 1 1]);
cfg.highlightfontsizeseries = ft_getopt(cfg, 'highlightfontsizeseries', [8 8 8 8 8]);
cfg.highlightcolorpos       = ft_getopt(cfg, 'highlightcolorpos',       [0 0 0]);
cfg.highlightcolorneg       = ft_getopt(cfg, 'highlightcolorneg',       [0 0 0]);
cfg.marker                  = ft_getopt(cfg, 'marker',                  'off');
cfg.alpha                   = ft_getopt(cfg, 'alpha',                   0.05);
cfg.parameter               = ft_getopt(cfg, 'parameter',               'stat');
cfg.saveaspng               = ft_getopt(cfg, 'saveaspng',               'no');
cfg.subplotsize             = ft_getopt(cfg, 'subplotsize',             [3 5]);
cfg.feedback                = ft_getopt(cfg, 'feedback',                'text');
cfg.visible                 = ft_getopt(cfg, 'visible',                 'on');
cfg.renderer                = ft_getopt(cfg, 'renderer',                []); % let MATLAB decide on the default
cfg.toi                     = ft_getopt(cfg, 'toi',                     'all');

% error if cfg.highlightseries is not a cell, for possible confusion with cfg-options
if ~iscell(cfg.highlightseries)
  ft_error('cfg.highlightseries should be a cell-array of strings')
end

% get the options that are specific for topoplotting
cfgtopo = keepfields(cfg, {'parameter', 'marker', 'markersymbol', 'markercolor', 'markersize', 'markerfontsize', 'style', 'gridscale', 'interplimits', 'interpolation', 'contournum', 'colorbar', 'shading', 'zlim'});

% prepare the layout, this only has to be done once
tmpcfg = keepfields(cfg, {'layout', 'channel', 'rows', 'columns', 'commentpos', 'skipcomnt', 'scalepos', 'skipscale', 'projection', 'viewpoint', 'rotate', 'width', 'height', 'elec', 'grad', 'opto', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'});
cfgtopo.layout = ft_prepare_layout(tmpcfg, stat);
cfgtopo.showcallinfo = 'no';
cfgtopo.feedback = 'no';

% handle with the data, it should be 1D or 2D
dimord = getdimord(stat, cfg.parameter);
dimtok = tokenize(dimord, '_');
dimsiz = getdimsiz(stat, cfg.parameter, numel(dimtok));

switch dimord
  case 'chan'
    is2D = false;
  case {'chan_time' 'chan_freq'}
    is2D = true;
  case 'chan_freq_time'
    % no more than two dimensions are supported, we can ignore singleton dimensions
    is2D = true;
    if dimsiz(2)==1
      tmpcfg = [];
      tmpcfg.avgoverfreq = 'yes';
      tmpcfg.keepfreqdim = 'no';
      tmpcfg.showcallinfo = 'no';
      stat = ft_selectdata(tmpcfg, stat);
    elseif dimsiz(3)==1
      tmpcfg = [];
      tmpcfg.avgovertime = 'yes';
      tmpcfg.keeptimedim = 'no';
      tmpcfg.showcallinfo = 'no';
      stat = ft_selectdata(tmpcfg, stat);
    else
      ft_error('if you input time-frequency data this only works if either frequency or time is a singleton dimension');
    end

  otherwise
    ft_error('unsupported dimord %s', dimord);
end % switch dimord

% these are not valid any more
clear dimord dimsiz

% this determines the labels in the figure
hastime = isfield(stat, 'time');
hasfreq = isfield(stat, 'freq');

% use the vector time, even though the 2nd dimension might be freq
if hastime
  time = stat.time;
elseif hasfreq
  time = stat.freq;
end

if issubfield(stat, 'cfg.correcttail') && ((strcmp(stat.cfg.correcttail, 'alpha') || strcmp(stat.cfg.correcttail, 'prob')) && (stat.cfg.tail == 0));
  if ~(cfg.alpha >= stat.cfg.alpha)
    ft_warning(['the pvalue you plot: cfg.alpha = ' num2str(cfg.alpha) ' is higher than the correcttail option you tested: stat.cfg.alpha = ' num2str(stat.cfg.alpha)]);
  end
end

% find significant clusters
haspos = isfield(stat, 'posclusters');
hasneg = isfield(stat, 'negclusters');

if haspos == 0 && hasneg == 0
  fprintf('%s\n', 'no clusters exceeded the nominal threshold in the data; nothing to plot')
else
  if haspos
    probpos = [stat.posclusters.prob];
    sigpos  = probpos < cfg.alpha;
  else
    sigpos  = [];
  end
  if hasneg
    probneg = [stat.negclusters.prob];
    signeg  = probneg < cfg.alpha;
  else
    signeg  = [];
  end
  sigpos  = find(sigpos == 1);
  signeg  = find(signeg == 1);
  Nsigpos = length(sigpos);
  Nsigneg = length(signeg);
  Nsigall = Nsigpos + Nsigneg;

  if Nsigall == 0
    ft_error('no clusters present with a p-value lower than the specified alpha, nothing to plot')
  end

  % make clusterslabel matrix per significant cluster
  if haspos
    posCLM = stat.posclusterslabelmat;
    sigposCLM = zeros(size(posCLM));
    for iPos = 1:Nsigpos
      sigposCLM(:,:,iPos) = (posCLM == sigpos(iPos));
      hlsignpos(iPos) = prob2hlsign(probpos(iPos), cfg.highlightsymbolseries);
    end
  else
    sigposCLM = [];
    probpos = [];
  end

  if hasneg
    negCLM = stat.negclusterslabelmat;
    signegCLM = zeros(size(negCLM));
    for iNeg = 1:Nsigneg
      signegCLM(:,:,iNeg) = (negCLM == signeg(iNeg));
      hlsignneg(iNeg) = prob2hlsign(probneg(iNeg), cfg.highlightsymbolseries);
    end
  else % no negative clusters
    signegCLM = [];
    probneg = [];
  end

  fprintf('There are %d clusters smaller than alpha (%g)\n', Nsigall, cfg.alpha);

  if is2D
    % define time or freq window per cluster
    for iPos = 1:Nsigpos
      possum_perclus = sum(sigposCLM(:,:,iPos),1); %sum over chans for each time- or freq-point
      ind_min = find(possum_perclus~=0, 1 );
      ind_max = find(possum_perclus~=0, 1, 'last' );
      time_perclus = [time(ind_min) time(ind_max)];
      if hastime
        fprintf('%s%s%s%s%s%s%s%s%s%s%s\n', 'Positive cluster: ',num2str(sigpos(iPos)), ', pvalue: ',num2str(probpos(iPos)), ' (',hlsignpos(iPos), ')', ', t = ',num2str(time_perclus(1)), ' to ',num2str(time_perclus(2)))
      elseif hasfreq
        fprintf('%s%s%s%s%s%s%s%s%s%s%s\n', 'Positive cluster: ',num2str(sigpos(iPos)), ', pvalue: ',num2str(probpos(iPos)), ' (',hlsignpos(iPos), ')', ', f = ',num2str(time_perclus(1)), ' to ',num2str(time_perclus(2)))
      end
    end
    for iNeg = 1:Nsigneg
      negsum_perclus = sum(signegCLM(:,:,iNeg),1);
      ind_min = find(negsum_perclus~=0, 1 );
      ind_max = find(negsum_perclus~=0, 1, 'last' );
      time_perclus = [time(ind_min) time(ind_max)];
      if hastime
        time_perclus = [time(ind_min) time(ind_max)];
        fprintf('%s%s%s%s%s%s%s%s%s%s%s\n', 'Negative cluster: ',num2str(signeg(iNeg)), ', pvalue: ',num2str(probneg(iNeg)), ' (',hlsignneg(iNeg), ')', ', t = ',num2str(time_perclus(1)), ' to ',num2str(time_perclus(2)))
      elseif hasfreq
        fprintf('%s%s%s%s%s%s%s%s%s%s%s\n', 'Negative cluster: ',num2str(signeg(iNeg)), ', pvalue: ',num2str(probneg(iNeg)), ' (',hlsignneg(iNeg), ')', ', f = ',num2str(time_perclus(1)), ' to ',num2str(time_perclus(2)))
      end
    end

    % define time- or freq-window containing all significant clusters
    possum = sum(sum(sigposCLM,3),1); %sum over Chans for timevector
    negsum = sum(sum(signegCLM,3),1);

    if haspos && hasneg
      allsum = possum + negsum;
    elseif haspos
      allsum = possum;
    else
      allsum = negsum;
    end

    % first and last time points of any cluster
    ind_timewin_min = find(allsum~=0, 1);
    ind_timewin_max = find(allsum~=0, 1, 'last');
    timewin = time(ind_timewin_min:ind_timewin_max);

  else
    for iPos = 1:Nsigpos
      fprintf('%s%s%s%s%s%s%s\n', 'Positive cluster: ',num2str(sigpos(iPos)), ', pvalue: ',num2str(probpos(iPos)), ' (',hlsignpos(iPos), ')')
    end
    for iNeg = 1:Nsigneg
      fprintf('%s%s%s%s%s%s%s\n', 'Negative cluster: ',num2str(signeg(iNeg)), ', pvalue: ',num2str(probneg(iNeg)), ' (',hlsignneg(iNeg), ')')
    end
  end

  % setup highlight options for all clusters and make comment for 1D data
  compos = [];
  comneg = [];
  for iPos = 1:Nsigpos
    cfgtopo.highlight(iPos)         = prob2hlsign(probpos(iPos), cfg.highlightseries);
    cfgtopo.highlightsymbol{iPos}   = prob2hlsign(probpos(iPos), cfg.highlightsymbolseries);
    cfgtopo.highlightsize{iPos}     = prob2hlsign(probpos(iPos), cfg.highlightsizeseries);
    cfgtopo.highlightfontsize{iPos} = prob2hlsign(probpos(iPos), cfg.highlightfontsizeseries);
    cfgtopo.highlightcolor{iPos}    = cfg.highlightcolorpos;
    compos = strcat(compos,cfgtopo.highlightsymbol{iPos}, 'p=',num2str(probpos(iPos)), ' '); % make comment, only used for 1D data
  end

  for iNeg = 1:Nsigneg
    cfgtopo.highlight(Nsigpos+iNeg)         = prob2hlsign(probneg(iNeg), cfg.highlightseries);
    cfgtopo.highlightsymbol{Nsigpos+iNeg}   = prob2hlsign(probneg(iNeg), cfg.highlightsymbolseries);
    cfgtopo.highlightsize{Nsigpos+iNeg}     = prob2hlsign(probneg(iNeg), cfg.highlightsizeseries);
    cfgtopo.highlightfontsize{Nsigpos+iNeg} = prob2hlsign(probneg(iNeg), cfg.highlightfontsizeseries);
    cfgtopo.highlightcolor{Nsigpos+iNeg}    = cfg.highlightcolorneg;
    comneg = strcat(comneg,cfgtopo.highlightsymbol{Nsigpos+iNeg}, 'p=',num2str(probneg(iNeg)), ' '); % make comment, only used for 1D data
  end

  if isequal(cfg.toi, 'all')
    cfg.toi = timewin;
  end

  if is2D && isequal(cfg.toi, timewin)
    % old functionality, plot every (!) time slice
    Npl = length(cfg.toi);
    begs = ind_timewin_min-1+(1:numel(cfg.toi))';
    ends = ind_timewin_min-1+(1:numel(cfg.toi))';
  elseif is2D
    % new functionality, don't plot every time slice if requested
    Npl = length(cfg.toi);

    % compute the indices in the original time axis as center points
    ix = zeros(Npl,1);
    for k = 1:Npl
      if hastime
        ix(k) = nearest(stat.time, cfg.toi(k));
      elseif hasfreq
        ix(k) = nearest(stat.freq, cfg.toi(k));
      end
    end
    win  = ceil(mean(diff(ix))/2);
    begs = max(1,ix-win);
    ends = min(numel(time), ix+win-1);
  else
    Npl  = 1;
    begs = 1;
    ends = 1;
  end

  numSubplots = prod(cfg.subplotsize);
  Nfig = ceil(Npl/numSubplots);

  % put channel indices in list
  list = cell(1,Nsigpos+Nsigneg);
  if is2D
    for iPl = 1:Npl
      for iPos = 1:Nsigpos
        list{iPl}{iPos} = find(sum(sigposCLM(:,begs(iPl):ends(iPl),iPos),2) >= 1);
      end
      for iNeg = 1:Nsigneg
        list{iPl}{Nsigpos+iNeg} = find(sum(signegCLM(:,begs(iPl):ends(iPl),iNeg),2) >= 1);
      end
    end
  else
    for iPos = 1:Nsigpos
      list{1}{iPos} = find(sigposCLM(:,iPos) == 1);
    end
    for iNeg = 1:Nsigneg
      list{1}{Nsigpos+iNeg} = find(signegCLM(:,iNeg) == 1);
    end
  end

  count = 0;
  ft_progress('init', cfg.feedback, 'making subplots...');
  ft_progress(count/Npl, 'making subplot %d from %d', count, Npl);

  % make plots
  for iPl = 1:Nfig
    % open a new figure with the specified settings, note that here it must always open a new figure
    open_figure(keepfields(cfg, {'position', 'visible', 'renderer'}));
    if is2D
      if iPl < Nfig
        for iT = 1:numSubplots
          PlN = (iPl-1)*numSubplots + iT; % plotnumber
          cfgtopo.xlim = [time(begs(PlN)) time(ends(PlN))];
          cfgtopo.highlightchannel = list{PlN};
          if hastime
            cfgtopo.comment = strcat('time: ',num2str(mean(time(begs(PlN):(ends(PlN))))), ' s');
          elseif hasfreq
            cfgtopo.comment = strcat('freq: ',num2str(mean(time(begs(PlN):(ends(PlN))))), ' Hz');
          end
          cfgtopo.commentpos = 'title';
          cfgtopo.figure = subplot(cfg.subplotsize(1), cfg.subplotsize(2), iT);
          count = count+1;
          fprintf('making subplot %d from %d\n', count, Npl);
          ft_topoplotTFR(cfgtopo, stat);
        end
      elseif iPl == Nfig
        for iT = 1:Npl-(numSubplots*(Nfig-1))
          PlN = (iPl-1)*numSubplots + iT; % plotnumber
          cfgtopo.xlim = [time(begs(PlN)) time(ends(PlN))];
          cfgtopo.highlightchannel   = list{PlN};
          if hastime
            cfgtopo.comment = strcat('time: ',num2str(mean(time(begs(PlN):(ends(PlN))))), ' s');
          elseif hasfreq
            cfgtopo.comment = strcat('freq: ',num2str(mean(time(begs(PlN):(ends(PlN))))), ' Hz');
          end
          cfgtopo.commentpos = 'title';
          cfgtopo.figure = subplot(cfg.subplotsize(1), cfg.subplotsize(2), iT);
          count = count+1;
          fprintf('making subplot %d from %d\n', count, Npl);
          ft_topoplotTFR(cfgtopo, stat);
        end
      end
    else
      cfgtopo.highlightchannel = list{1};
      cfgtopo.comment = strcat(compos, comneg);
      cfgtopo.commentpos = 'title';
      count = count+1;
      fprintf('making subplot %d from %d\n', count, Npl);
      ft_topoplotTFR(cfgtopo, stat);
    end
    if isequal(cfg.saveaspng, 'no')
      % nothing to do
    else
      % save figure
      filename = strcat(cfg.saveaspng, '_fig', num2str(iPl));
      print(gcf, '-dpng', filename);
    end
  end
end

ft_progress('close');

% return to previous warning settings
ft_warning(ws);

% this is needed for the figure title
if isfield(cfg, 'dataname') && ~isempty(cfg.dataname)
  dataname = cfg.dataname;
elseif isfield(cfg, 'inputfile') && ~isempty(cfg.inputfile)
  dataname = cfg.inputfile;
elseif nargin>1
  dataname = arrayfun(@inputname, 2:nargin, 'UniformOutput', false);
else
  dataname = {};
end

% set the figure window title
if ~isempty(dataname)
  set(gcf, 'Name', sprintf('%d: %s: %s', double(gcf), mfilename, join_str(', ', dataname)));
else
  set(gcf, 'Name', sprintf('%d: %s', double(gcf), mfilename));
end
set(gcf, 'NumberTitle', 'off');

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble previous stat
ft_postamble provenance
ft_postamble savefig

% add a menu to the figure, but only if the current figure does not have subplots
menu_fieldtrip(gcf, cfg, false);

if ~ft_nargout
  % don't return anything
  clear cfg
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function sign = prob2hlsign(prob, hlsign)
if prob < 0.01
  sign = hlsign(1);
elseif prob < 0.05
  sign = hlsign(2);
elseif prob < 0.1
  sign = hlsign(3);
elseif prob < 0.2
  sign = hlsign(4);
elseif prob < 0.3
  sign = hlsign(5);
end


%% script for making plots used to demonstrate trx, per-frame, window features

%% set up path

addpath ../perframe;
addpath ../perframe/compute_perframe_features;
addpath ../misc;
addpath ../filehandling;
outfigdir = 'C:\Code\Jdetect\figures\OverviewOut';

%% parameters

rootdatadir = 'C:\Data\JAABA\FlyBowl';
experiment_name = 'pBDPGAL4U_TrpA_Rig1Plate15BowlB_20110922T145928';
fly = 4;
t = 6823;

%% load the data

trx = Trx('trxfilestr','registered_trx.mat','moviefilestr','movie.ufmf','perframedir','perframe');
expdir = fullfile(rootdatadir,experiment_name);
trx.AddExpDir(expdir);
moviename = trx.movienames{1};
[readframe,nframes,fid,headerinfo] = get_readframe_fcn(moviename);

%% show the trajectory

tradius = 50;

hfig = 1;
figure(hfig);
set(hfig,'Units','pixels','Position',[10 10 1026 964]);
clf;

plot(trx(fly).x,trx(fly).y,'-','color',[.4,.4,.4]);
hold on;
idx = t-tradius+trx(fly).off:t+tradius+trx(fly).off;
plot(trx(fly).x(idx),trx(fly).y(idx),'-','linewidth',3,'color','k');
axis equal ij;
axisalmosttight;
set(gca,'Box','off');

SaveFigLotsOfWays(hfig,fullfile(outfigdir,sprintf('ExampleTrajectory_%s_fly%02d',experiment_name,fly)));

%% choose per-frame features

T0 = min(cellfun(@(x)x(1),trx.timestamps));

files = dir(fullfile(trx.expdirs{1},'perframe','*.mat'));
perframefns = cellfun(@(x) x(1:end-4),{files.name},'UniformOutput',false);
ignorefns = {'a','b','x','y','theta','sex','timestamps','dt','x_mm','y_mm','xnose_mm','ynose_mm','arena_angle','arena_r','signdtheta'};
perframefns = setdiff(perframefns,ignorefns);
idx = ~cellfun(@isempty,regexp(perframefns,'^closestfly_.*$'));
perframefns(idx) = [];

%% plot per-frame features

naxr = 5;
naxc = 3;
nax = naxr*naxc;

t0 = t-tradius;
t1 = t+tradius;
i0 = t0+trx(fly).off;
i1 = t1+trx(fly).off;

hfig = 2;
fignum = 1;
for i = 1:nax:numel(perframefns),
  figure(hfig);
  set(hfig,'Units','pixels','Position',[20 20 2243 1030]);
  clf;
  hax = createsubplots(naxr,naxc,[.025,.025;.025,.01]);
  for j = i:min(numel(perframefns),i+nax-1),
    %axes(hax(j-i+1));
    fn = perframefns{j};
    plot(hax(j-i+1),trx(fly).timestamps(i0:i1)-T0,trx(fly).(fn)(i0:i1),'k-');
    axisalmosttight([],hax(j-i+1));
    set(hax(j-i+1),'XLim',[trx(fly).timestamps(i0-1)-T0,trx(fly).timestamps(i1-1)-T0],'Box','off');
    if mod(j-i,naxr) ~= naxr-1,
      set(hax(j-i+1),'XTickLabel',{});
    end
    ylabel(hax(j-i+1),perframefns{j},'Interpreter','none');
  end
  SaveFigLotsOfWays(hfig,fullfile(outfigdir,sprintf('ExamplePerFrameFeatures_%s_fly%02d_%d',experiment_name,fly,fignum)));
  hfig = hfig + 1;
  fignum = fignum+1;
end

%% compute window features

featureparamsfilename = '../perframe/params/WindowFeatures_Touch.xml';
featureconfigfilename = '../perframe/params/featureConfig.xml';

[windowfeaturesparams,windowfeaturescellparams,basicFeatureTable,featureWindowSize] = ...
  ReadPerFrameParams(featureparamsfilename,featureconfigfilename);

perframefns1 = fieldnames(windowfeaturescellparams);

x_curr_all = cell(1,numel(perframefns1));
feature_names_all = cell(1,numel(perframefns1));

for j = 1:numel(perframefns1),
  fn = perframefns1{j};

  % get per-frame data
  perframedata = trx(fly).(fn);

  i11 = min(i1,numel(perframedata));
  [x_curr,feature_names_curr] = ...
    ComputeWindowFeatures(perframedata,windowfeaturescellparams.(fn){:},'t0',i0,'t1',i11);
  if any(imag(x_curr(:)))
    fprintf('Feature values are complex, check input\n');
  end

  if i11 < i1,
    x_curr(:,end+1:end+i1-i11) = nan;
  end

  x_curr_all{j} = x_curr;
  feature_names_all{j} = feature_names_curr;

end

%% plot window features

rng(0);

hfig = 100;

fignum = 0;
nwindowfeatures = naxc;
for i = 1:numel(perframefns1),

  pfn = perframefns1{i};

  if mod(i,naxr) == 1,
    hfig = hfig+1;
    fignum = fignum+1;
    figure(hfig);
    set(hfig,'Units','pixels','Position',[98 143 1804 963]);
    clf;
    hax = reshape(createsubplots(naxr,naxc,[.03,.03;.04,.04]),[naxr,naxc]);
    r = 1;
  else
    r = r+1;
  end

  nw = numel(feature_names_all{i});
  js = sort(randsample(nw,nwindowfeatures))';

  for jj = 1:nwindowfeatures,
    j = js(jj);
    tmp = feature_names_all{i}{j}(5:end);
    tmp(2:2:end) = cellfun(@num2str,tmp(2:2:end),'UniformOutput',false);
    wfn = [sprintf('%s_%s',feature_names_all{i}{j}{2},feature_names_all{i}{j}{4}),...
      sprintf('_%s%s',tmp{:})];
    plot(hax(r,jj),trx(fly).timestamps(i0:i1)-T0,x_curr_all{i}(j,:),'k-');
    axisalmosttight([],hax(r,jj));
    set(hax(r,jj),'XLim',[trx(fly).timestamps(i0-1)-T0,trx(fly).timestamps(i1-1)-T0],'Box','off');
    if r ~= naxr,
      set(hax(r,jj),'XTickLabel',{});
    end
    %if jj == 1,
    ylabel(hax(r,jj),perframefns{i},'Interpreter','none');
    xlabel(hax(r,jj),wfn,'Interpreter','none');
    %else
    %  ylabel(hax(r,jj),wfn,'Interpreter','none');
    %end
  end
  if mod(i,naxr) == 0,
    SaveFigLotsOfWays(hfig,fullfile(outfigdir,sprintf('ExampleWindowFeatures_%s_fly%02d_%02d',experiment_name,fly,fignum)));
  end
end

%%

hfig = 200;
plotall_perframefns = {'velmag_ctr','dnose2ell'};
for fnii = 1:numel(plotall_perframefns),
  fignum = 1;
  pfn = plotall_perframefns{fnii};
  fni = find(strcmp(perframefns1,pfn),1);
  nw = numel(feature_names_all{fni});
  for i = 1:nax:nw,

    figure(hfig);
    set(hfig,'Units','pixels','Position',[98 143 1804 963]);
    clf;
    hax = createsubplots(naxr,naxc,[.03,.03;.04,.04]);
    for j = i:min(nw,i+nax-1),
      %axes(hax(j-i+1));
      tmp = feature_names_all{fni}{j}(5:end);
      tmp(2:2:end) = cellfun(@num2str,tmp(2:2:end),'UniformOutput',false);
      wfn = [sprintf('%s_%s',feature_names_all{fni}{j}{2},feature_names_all{fni}{j}{4}),...
        sprintf('_%s%s',tmp{:})];
      plot(hax(j-i+1),trx(fly).timestamps(i0:i1)-T0,x_curr_all{fni}(j,:),'k-');
      axisalmosttight([],hax(j-i+1));
      set(hax(j-i+1),'XLim',[trx(fly).timestamps(i0-1)-T0,trx(fly).timestamps(i1-1)-T0],'Box','off');
      if mod(j-i,naxr) ~= naxr-1,
        set(hax(j-i+1),'XTickLabel',{});
      end
      %ylabel(hax(j-i+1),pfn,'Interpreter','none');
      xlabel(hax(j-i+1),wfn,'Interpreter','none');
    end
    pause(1);
    SaveFigLotsOfWays(hfig,fullfile(outfigdir,sprintf('ExampleWindowFeatures_%s_%s_fly%02d_%d',pfn,experiment_name,fly,fignum)));
    hfig = hfig + 1;
    fignum = fignum+1;
  end
end


function [surfhandle, T2] = ft_plot_slice(dat, varargin)

% FT_PLOT_SLICE plots a single slice that cuts through a 3-D volume and interpolates
% the data if needed.
%
% Use as
%   ft_plot_slice(dat, ...)
% or
%   ft_plot_slice(dat, mask, ...)
% where dat and mask are equal-sized 3-D arrays.
%
% Additional options should be specified in key-value pairs and can be
%   'transform'    = 4x4 homogeneous transformation matrix specifying the mapping from
%                    voxel coordinates to the coordinate system in which the data are plotted.
%   'location'     = 1x3 vector specifying a point on the plane which will be plotted
%                    the coordinates are expressed in the coordinate system in which the
%                    data will be plotted. location defines the origin of the plane
%   'orientation'  = 1x3 vector specifying the direction orthogonal through the plane
%                    which will be plotted (default = [0 0 1])
%   'unit'         = string, can be 'm', 'cm' or 'mm (default is automatic)
%   'coordsys'     = string, assume the data to be in the specified coordinate system (default = 'unknown')
%   'resolution'   = number (default = 1 mm)
%   'datmask'      = 3D-matrix with the same size as the data matrix, serving as opacitymap
%                    If the second input argument to the function contains a matrix, this
%                    will be used as the mask
%   'maskstyle'    = string, 'opacity' or 'colormix', defines the rendering
%   'background'   = needed when maskstyle is 'colormix', 3D-matrix with
%                    the same size as the data matrix, serving as
%                    grayscale image that provides the background
%   'opacitylim'   = 1x2 vector specifying the limits for opacity masking
%   'interpmethod' = string specifying the method for the interpolation, see INTERPN (default = 'nearest')
%   'colormap'     = string, see COLORMAP
%   'clim'         = 1x2 vector specifying the min and max for the colorscale
%   'facealpha'    = transparency when no mask is specified, between 0 and 1 (default = 1)
%
% You can plot the slices from the volume together with an intersection of the slices
% with a triangulated surface mesh (e.g. a cortical sheet) using
%   'intersectmesh'       = triangulated mesh, see FT_PREPARE_MESH
%   'intersectcolor'      = string, color specification
%   'intersectlinestyle'  = string, line specification
%   'intersectlinewidth'  = number
%
% See also FT_PLOT_ORTHO, FT_PLOT_MONTAGE, FT_SOURCEPLOT

% Undocumented options
%   'plotmarker'     = Nx3 matrix with points to be plotted as markers, e.g. dipole positions
%   'markersize'
%   'markercolor'

% Copyrights (C) 2010-2014, Jan-Mathijs Schoffelen
% Copyrights (C) 2014-2022, Robert Oostenveld and Jan-Mathijs Schoffelen
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

persistent dim X Y Z

if isstruct(dat) && isfield(dat, 'anatomy') && isfield(dat, 'transform')
  % the input is an MRI structure, call this function recursively
  varargin = ft_setopt(varargin, 'transform', dat.transform);
  if isfield(dat, 'coordsys')
    varargin = ft_setopt(varargin, 'coordsys', dat.coordsys);
  end
  if isfield(dat, 'unit')
    varargin = ft_setopt(varargin, 'unit', dat.unit);
  end
  dat = dat.anatomy;
  [surfhandle, T2] = ft_plot_slice(dat, varargin{:});
  return
end

% the data can have up to 5 dimensions, including time and/or frequency
% the size function in MATLAB 2022b can take multiple input arguments, but not in 2018b
datsize = [size(dat, 1) size(dat, 2) size(dat, 3) size(dat, 4) size(dat, 5)];

if isequal(dim, datsize(1:3))
  % reuse the persistent variables to speed up subsequent calls with the same input
else
  % construct the persistent variables
  dim = datsize(1:3);
  [X, Y, Z] = ndgrid(1:dim(1), 1:dim(2), 1:dim(3));
end

if any(dim==1)
  ft_error('it is not possible to plot a volume that consists of a single slice');
end

% parse first input argument(s), it is either
% (dat, varargin)
% (dat, msk, varargin)
% (dat, [], varargin)

if numel(varargin)>0 && (isempty(varargin{1}) || isnumeric(varargin{1}) || islogical(varargin{1}))
  msk      = varargin{1};
  varargin = varargin(2:end);
end

% get the optional input arguments
transform           = ft_getopt(varargin, 'transform', eye(4));
loc                 = ft_getopt(varargin, 'location');
ori                 = ft_getopt(varargin, 'orientation', [0 0 1]);
coordsys            = ft_getopt(varargin, 'coordsys');
unit                = ft_getopt(varargin, 'unit');       % the default will be determined further down
resolution          = ft_getopt(varargin, 'resolution'); % the default depends on the units and will be determined further down
datmask             = ft_getopt(varargin, 'datmask');
maskstyle           = ft_getopt(varargin, 'maskstyle', 'opacity');
background          = ft_getopt(varargin, 'background');
opacitylim          = ft_getopt(varargin, 'opacitylim');
interpmethod        = ft_getopt(varargin, 'interpmethod', 'nearest');
cmap                = ft_getopt(varargin, 'colormap');
clim                = ft_getopt(varargin, 'clim');
doscale             = ft_getopt(varargin, 'doscale', true); % only scale when necessary (time consuming), i.e. when plotting as grayscale image & when the values are not between 0 and 1
surfhandle          = ft_getopt(varargin, 'surfhandle', []);
patchhandle         = ft_getopt(varargin, 'patchhandle', []);
tag                 = ft_getopt(varargin, 'tag', ''); % this is used to keep the thee intersecting slices or subplots apart

mesh                = ft_getopt(varargin, 'intersectmesh');
intersectcolor      = ft_getopt(varargin, 'intersectcolor', 'yrgbmyrgbm');
intersectlinewidth  = ft_getopt(varargin, 'intersectlinewidth', 2);
intersectlinestyle  = ft_getopt(varargin, 'intersectlinestyle');

plotmarker          = ft_getopt(varargin, 'plotmarker');
markersize          = ft_getopt(varargin, 'markersize', 'auto');
markercolor         = ft_getopt(varargin, 'markercolor', 'w');
facealpha           = ft_getopt(varargin, 'facealpha', 1); % only applies when no mask is specified

% convert from yes/no/true/false/0/1 into a proper boolean
doscale = istrue(doscale);

if ~isa(dat, 'double')
  dat = cast(dat, 'double');
end

if exist('msk', 'var') && isempty(datmask)
  ft_warning('using the second input argument as mask rather than the one from the varargin list');
  datmask = msk; clear msk;
end

% normalise the orientation vector to one
ori = ori./sqrt(sum(ori.^2));

% set the default location
if isempty(loc) && (isempty(transform) || isequal(transform, eye(4)))
  loc = (dim+1)./2;
elseif isempty(loc)
  loc = [0 0 0];
end

% shift the location to be along the orientation vector
loc = ori*dot(loc,ori);

dointersect = ~isempty(mesh);

% the mesh should be a cell-array
if isstruct(mesh)
  tmp = mesh;
  mesh = cell(size(tmp));
  for i=1:numel(tmp)
    mesh{i} = tmp(i);
  end
elseif isempty(mesh)
  mesh = {};
end

% replace pnt by pos
for k = 1:numel(mesh)
  mesh{k} = fixpos(mesh{k});
end

% the mesh should be a structure with both pos and tri
for k = 1:numel(mesh)
  if ~isfield(mesh{k}, 'pos') || ~isfield(mesh{k}, 'tri')
    mesh{k}.pos = [];
    mesh{k}.tri = [];
  end
end

domask = ~isempty(datmask);
if domask
  % check whether the mask is ok
  if ~isequal(size(dat), size(datmask)) && ~isequal(cmap, 'rgb')
    % the exception is when the functional data is to be interpreted as rgb
    ft_error('the mask data should have the same dimensions as the functional data');
  end
end

dobackground = ~isempty(background);
if dobackground
  % check whether the background is ok
  if ~isequal(size(dat), size(background))
    error('the background data should have the same dimensions as the functional data');
  end
end

% determine the voxel center
% voxel_center_vc = [X(:) Y(:) Z(:)];
% voxel_center_hc = ft_warp_apply(transform, voxel_center_vc);

% determine the edges, i.e. the corner points of each voxel
% [Xe, Ye, Ze] = ndgrid(0:dim(1), 0:dim(2), 0:dim(3));
% Xe = Xe+0.5;
% Ye = Ye+0.5;
% Ze = Ze+0.5;
% voxel_edge_vc = [Xe(:) Ye(:) Ze(:)];
% voxel_edge_hc = ft_warp_apply(transform, voxel_edge_vc);

% determine the corner points of the box that encompasses the whole data
% extend the box with half a voxel in all directions to get the outer edge
corner_vc = [
  0.5        0.5        0.5
  0.5+dim(1) 0.5        0.5
  0.5+dim(1) 0.5+dim(2) 0.5
  0.5        0.5+dim(2) 0.5
  0.5        0.5        0.5+dim(3)
  0.5+dim(1) 0.5        0.5+dim(3)
  0.5+dim(1) 0.5+dim(2) 0.5+dim(3)
  0.5        0.5+dim(2) 0.5+dim(3)
  ];
corner_hc = ft_warp_apply(transform, corner_vc);

if isempty(unit)
  if ~isequal(transform, eye(4))
    % estimate the geometrical units we are dealing with
    unit = ft_estimate_units(norm(range(corner_hc)));
  else
    % units are in voxels, these are assumed to be close to mm
    unit = 'mm';
  end
end

if isempty(resolution)
  % the default resolution is 1 mm
  resolution = ft_scalingfactor('mm', unit);
end

% determine whether interpolation is needed
dointerp = false;
dointerp = dointerp || sum(sum(transform-eye(4)))~=0;
dointerp = dointerp || ~all(round(loc)==loc);
dointerp = dointerp || sum(ori)~=1;
dointerp = dointerp || ~(resolution==round(resolution));
% determine the caller function and toggle dointerp to true, if ft_plot_slice has been called from ft_plot_montage
% this is necessary for the correct allocation of the persistent variables
st = dbstack;
if ~dointerp && numel(st)>1 && strcmp(st(2).name, 'ft_plot_montage'), dointerp = true; end

% define 'x' and 'y' axis in projection plane, the definition of x and y is more or less arbitrary
[x, y] = projplane(ori);
% z = ori;

% project the corner points onto the projection plane
corner_pc = zeros(size(corner_hc));
for i=1:8
  corner   = corner_hc(i, :) - loc(:)';
  corner_pc(i,1) = dot(corner, x);
  corner_pc(i,2) = dot(corner, y);
  corner_pc(i,3) = 0;
end

% get the transformation matrix from the projection plane to head coordinates
T2 = [x(:) y(:) ori(:) loc(:); 0 0 0 1];

% get the transformation matrix from projection plane to voxel coordinates
T3 = transform\T2;

min_corner_pc = min(corner_pc, [], 1);
max_corner_pc = max(corner_pc, [], 1);
% round the bounding box limits to the nearest mm
switch unit
  case 'm'
    min_corner_pc = ceil(min_corner_pc*100)/100;
    max_corner_pc = floor(max_corner_pc*100)/100;
  case 'cm'
    min_corner_pc = ceil(min_corner_pc*10)/10;
    max_corner_pc = floor(max_corner_pc*10)/10;
  case 'mm'
    min_corner_pc = ceil(min_corner_pc);
    max_corner_pc = floor(max_corner_pc);
end

% determine a grid of points in the projection plane
xplane = min_corner_pc(1):resolution:max_corner_pc(1);
yplane = min_corner_pc(2):resolution:max_corner_pc(2);
zplane = 0;
[Xi, Yi, Zi]      = ndgrid(xplane, yplane, zplane);
siz               = [size(squeeze(Xi)) size(dat,4)];
interp_center_pc  = [Xi(:) Yi(:) Zi(:)];
% interp_center_hc = ft_warp_apply(T2, interp_center_pc);

% get the positions of the points in the projection plane in voxel coordinates
interp_center_vc = ft_warp_apply(T3, interp_center_pc);

Xi = reshape(interp_center_vc(:, 1), siz(1:2));
Yi = reshape(interp_center_vc(:, 2), siz(1:2));
Zi = reshape(interp_center_vc(:, 3), siz(1:2));

% check whether the values in the axes are close enough to integer
tol = nanmean([diff(unique(Xi(:)));diff(unique(Yi(:)))])./100;
isintegerXi = issufficientlyinteger(Xi(:),tol);
isintegerYi = issufficientlyinteger(Yi(:),tol);
isintegerZi = issufficientlyinteger(Zi(:),tol);

% check whether it's possible to select an orthogonal plane
[islineXi, lineXi] = isline(Xi);
[islineYi, lineYi] = isline(Yi);
[islineZi, lineZi] = isline(Zi);

use_interpn = ~isequal(transform, eye(4)) || ~isequal(interpmethod, 'nearest') || ~all([isintegerXi isintegerYi isintegerZi]);
get_slice   = ~use_interpn && all([islineXi islineYi islineZi]) && all([isintegerXi isintegerYi isintegerZi]);
if use_interpn
  V  = interpn(X, Y, Z, dat, Xi, Yi, Zi, interpmethod);
  if domask,       Vmask = interpn(X, Y, Z, datmask,    Xi, Yi, Zi, interpmethod); end
  if dobackground, Vback = interpn(X, Y, Z, background, Xi, Yi, Zi, interpmethod); end
elseif get_slice
  % something more efficient than an interpolation can be done:
  % just select the appropriate plane, and permute to get the orientation
  % right in the plots, this has something to do with ndgrid vs meshgrid I think
  permutevec = [2 1];
  if ndims(dat)>3
    permutevec = [permutevec 3:ndims(dat)];
  end
  if numel(unique(lineXi(:)))==1
    lineXi = lineXi(1);
  elseif numel(unique(lineYi(:)))==1
    lineYi = lineYi(1);
  elseif numel(unique(lineZi(:)))==1
    lineZi = lineZi(1);
  end
  V = permute(reshape(dat(lineXi,lineYi,lineZi,:), siz(permutevec(1:ndims(dat)-1))), permutevec);
  if domask,       Vmask = permute(reshape(datmask(lineXi,lineYi,lineZi,:),       siz(permutevec(1:2))), [2 1]); end
  if dobackground, Vback = permute(reshape(background(lineXi,lineYi,lineZi,:), siz(permutevec(1:2))), [2 1]); end
else
  % use sub2ind in the unlikely case that it's an oblique plane, parallel
  % to one of the axes with only integer indices
  % this fails for rgb data
  V = dat(sub2ind(dim, Xi(:), Yi(:), Zi(:)));
  V = reshape(V, siz);
end

if all(isnan(V(:)))
  % the projection plane lies completely outside the box spanned by the data
else
  % trim the edges of the projection plane
  [sel1, sel2] = tight(V(:,:,1));
  V  = V (sel1,sel2,:);
  Xi = Xi(sel1,sel2);
  Yi = Yi(sel1,sel2);
  Zi = Zi(sel1,sel2);
  if domask
    Vmask = Vmask(sel1,sel2);
  end
  if dobackground
    Vback = Vback(sel1,sel2);
  end
end

if dobackground
  % convert the background plane to a grayscale image
  bmin  = nanmin(background(:));
  bmax  = nanmax(background(:));
  Vback = (Vback-bmin)./(bmax-bmin);
  Vback(~isfinite(Vback)) = 0;
  Vback = cat(3, Vback, Vback, Vback);
end

interp_center_vc = [Xi(:) Yi(:) Zi(:)]; clear Xi Yi Zi
interp_center_pc = ft_warp_apply(inv(T3), interp_center_vc);

% determine a grid of points in the projection plane
% this reconstruction is needed since the edges may have been trimmed off
xplane = min(interp_center_pc(:, 1)):resolution:max(interp_center_pc(:, 1));
yplane = min(interp_center_pc(:, 2)):resolution:max(interp_center_pc(:, 2));
zplane = 0;

[Xi, Yi, Zi] = ndgrid(xplane, yplane, zplane); % 2D cartesian grid of projection plane in plane voxels
siz          = size(squeeze(Xi));

% extend with one voxel along dim 1
Xi = cat(1, Xi, Xi(end,:)+mean(diff(Xi,[],1),1));
Yi = cat(1, Yi, Yi(end,:)+mean(diff(Yi,[],1),1));
Zi = cat(1, Zi, Zi(end,:)+mean(diff(Zi,[],1),1));
% extend with one voxel along dim 2
Xi = cat(2, Xi, Xi(:,end)+mean(diff(Xi,[],2),2));
Yi = cat(2, Yi, Yi(:,end)+mean(diff(Yi,[],2),2));
Zi = cat(2, Zi, Zi(:,end)+mean(diff(Zi,[],2),2));
% shift with half a voxel along dim 1 and 2
Xi = Xi-0.5*resolution;
Yi = Yi-0.5*resolution;
% Zi = Zi; % do not shift along this direction

interp_edge_pc = [Xi(:) Yi(:) Zi(:)]; clear Xi Yi Zi
interp_edge_hc = ft_warp_apply(T2, interp_edge_pc);

if false
  % plot all objects in head coordinates
  ft_plot_mesh(voxel_center_hc,   'vertexmarker', 'o')
  ft_plot_mesh(voxel_edge_hc,     'vertexmarker', '+')
  ft_plot_mesh(corner_hc,         'vertexmarker', '*')
  ft_plot_mesh(interp_center_hc,  'vertexmarker', 'o', 'vertexcolor', 'r')
  ft_plot_mesh(interp_edge_hc,    'vertexmarker', '+', 'vertexcolor', 'r')
  axis on
  grid on
  xlabel('x')
  ylabel('y')
  zlabel('z')
end

if isempty(cmap)
  % treat as gray value: scale and convert to rgb
  if doscale
    dmin = min(dat(:));
    dmax = max(dat(:));
    V    = (V-dmin)./(dmax-dmin);
    clear dmin dmax
  end
  V(~isfinite(V)) = 0;

  % deal with clim for RGB data here, where the purpose is to increase the
  % contrast range, rather than shift the average grey value
  if ~isempty(clim)
    V = (V-clim(1))./clim(2);
    V(V>1)=1;
  end

  % convert into RGB values, e.g. for the plotting of anatomy
  V = cat(3, V, V, V);
end

% get positions of the voxels in the interpolation plane in head coordinates
Xh = reshape(interp_edge_hc(:,1), siz+1);
Yh = reshape(interp_edge_hc(:,2), siz+1);
Zh = reshape(interp_edge_hc(:,3), siz+1);

% do the actual plotting of the slice
if ~domask
  % no masked slice to be plotted
  if isempty(surfhandle)
    % create surface object
    surfhandle = surface(Xh, Yh, Zh, V);
    set(surfhandle, 'linestyle', 'none');
    set(surfhandle, 'FaceAlpha', facealpha);
  else
    % update the colordata in the surface object
    set(surfhandle, 'Cdata', V);
    set(surfhandle, 'Xdata', Xh);
    set(surfhandle, 'Ydata', Yh);
    set(surfhandle, 'Zdata', Zh);
    set(surfhandle, 'FaceAlpha', facealpha);
  end
elseif domask
  % what should be done depends on the maskstyle
  switch maskstyle
    case 'opacity'
      if dobackground
        ft_warning('specifying maskstyle = ''opacity'' causes the supplied background image not to be used');
      end
      if isempty(surfhandle)
        % create surface object
        surfhandle = surface(Xh, Yh, Zh, V);
        set(surfhandle, 'linestyle', 'none');
      else
        % update the colordata in the surface object
        set(surfhandle, 'Cdata', V);
        set(surfhandle, 'Xdata', Xh);
        set(surfhandle, 'Ydata', Yh);
        set(surfhandle, 'Zdata', Zh);
      end
      if islogical(Vmask), Vmask = double(Vmask); end
      set(surfhandle, 'FaceColor', 'texture');
      set(surfhandle, 'FaceAlpha', 'texturemap'); %flat
      set(surfhandle, 'AlphaDataMapping', 'scaled');
      set(surfhandle, 'AlphaData', Vmask);
      if ~isempty(opacitylim)
        alim(opacitylim)
      end
    case 'colormix'
      if isempty(cmap), error('using ''colormix'' as maskstyle requires an explicitly defined colormap'); end
      if ischar(cmap),  cmap = strrep(cmap, 'default', 'parula'); cmap = ft_colormap(cmap); end
      V = bg_rgba2rgb(Vback,V,cmap,clim,Vmask,'rampup',opacitylim);
      if isempty(surfhandle)
        % create surface object
        surfhandle = surface(Xh, Yh, Zh, V);
        set(surfhandle, 'linestyle', 'none');
      else
        % update the colordata in the surface object
        set(surfhandle, 'Cdata', V);
        set(surfhandle, 'Xdata', Xh);
        set(surfhandle, 'Ydata', Yh);
        set(surfhandle, 'Zdata', Zh);
      end
    otherwise
      error('unsupported maskstyle');
  end
end

if ~isempty(coordsys) && ~strcmp(coordsys, 'unknown')
  % convert 'neuromag' to 'ras', etc.
  coordsys = generic(coordsys);

  % determine the center of each of the 6 faces of the box that encompasses the whole data
  % shift all of them inward by about 5%
  center_vc = [
    dim(1)*0.05 dim(2)*0.50 dim(3)*0.50
    dim(1)*0.95 dim(2)*0.50 dim(3)*0.50
    dim(1)*0.50 dim(2)*0.05 dim(3)*0.50
    dim(1)*0.50 dim(2)*0.95 dim(3)*0.50
    dim(1)*0.50 dim(2)*0.50 dim(3)*0.05
    dim(1)*0.50 dim(2)*0.50 dim(3)*0.95
    ];
  center_hc = ft_warp_apply(transform, center_vc);

  % the order of the center points is arbitrary and does not match coordsys/xyz
  % hence we have to determine them for each x/y/z direction
  minx = min(center_hc(:,1));
  maxx = max(center_hc(:,1));
  miny = min(center_hc(:,2));
  maxy = max(center_hc(:,2));
  minz = min(center_hc(:,3));
  maxz = max(center_hc(:,3));
  % also compute the midpoints, i.e. the center of the cube itself
  midx = mean(center_hc(:,1));
  midy = mean(center_hc(:,2));
  midz = mean(center_hc(:,3));

  if isequal(ori, [1 0 0])
    % the slice is perpendicular to the x-axis
    delete(findall(gca, 'Tag', ['coordsyslabel_' tag])) % remove the labels from the previous call
    text(loc(1), miny, midz, upper(flipletter(coordsys(2))), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(loc(1), maxy, midz, upper(           coordsys(2) ), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(loc(1), midy, minz, upper(flipletter(coordsys(3))), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(loc(1), midy, maxz, upper(           coordsys(3) ), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
  elseif isequal(ori, [0 1 0])
    % the slice is perpendicular to the y-axis
    delete(findall(gca, 'Tag', ['coordsyslabel_' tag])) % remove the labels from the previous call
    text(minx, loc(2), midz, upper(flipletter(coordsys(1))), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(maxx, loc(2), midz, upper(           coordsys(1) ), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(midx, loc(2), minz, upper(flipletter(coordsys(3))), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(midx, loc(2), maxz, upper(           coordsys(3) ), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
  elseif isequal(ori, [0 0 1])
    % the slice is perpendicular to the z-axis
    delete(findall(gca, 'Tag', ['coordsyslabel_' tag])) % remove the labels from the previous call
    text(minx, midy, loc(3), upper(flipletter(coordsys(1))), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(maxx, midy, loc(3), upper(           coordsys(1) ), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(midx, miny, loc(3), upper(flipletter(coordsys(2))), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
    text(midx, maxy, loc(3), upper(           coordsys(2) ), 'Color', 'y', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'Tag', ['coordsyslabel_' tag]);
  end
end % if coordsys

% plot the intersection with a mesh
if dointersect
  % determine three points on the plane
  inplane = eye(3) - (eye(3) * ori') * ori;
  v1 = loc + inplane(1,:);
  v2 = loc + inplane(2,:);
  v3 = loc + inplane(3,:);

  if isempty(patchhandle) || length(patchhandle)~=length(mesh)
    % try to find the handles of all patches
    patchhandle = findall(gca, 'tag', ['intersectmesh_' tag]);
  end

  if length(patchhandle)~=length(mesh)
    % the patch handles do not make sense, start from scratch
    delete(findall(gca, 'tag', ['intersectmesh_' tag]))
    patchhandle = nan(size(mesh));
  end

  for k = 1:numel(mesh)
    [xmesh, ymesh, zmesh] = intersect_plane(mesh{k}.pos, mesh{k}.tri, v1, v2, v3);

    % draw each individual line segment of the intersection
    if ~isempty(xmesh)
      if ~ishandle(patchhandle(k))
        patchhandle(k) = patch(xmesh', ymesh', zmesh', nan(1, size(xmesh, 1)));
        set(patchhandle(k), 'tag', ['intersectmesh_' tag]);
        if ~isempty(intersectcolor),     set(patchhandle(k), 'EdgeColor', intersectcolor(k));  end
        if ~isempty(intersectlinewidth), set(patchhandle(k), 'LineWidth', intersectlinewidth); end
        if ~isempty(intersectlinestyle), set(patchhandle(k), 'LineStyle', intersectlinestyle); end
      else
        set(patchhandle(k), 'XData', xmesh', 'YData', ymesh', 'ZData', zmesh', 'FaceVertexCdata', nan(size(xmesh,1),1));
      end
    end
  end
end

if ~isempty(cmap) && ~isequal(cmap, 'rgb') && ~isequal(maskstyle, 'colormix')
  ft_colormap(cmap);
  if ~isempty(clim)
    caxis(clim);
  end
end

if ~isempty(plotmarker)
  % determine three points on the plane
  inplane = eye(3) - (eye(3) * ori') * ori;
  v1 = loc + inplane(1,:);
  v2 = loc + inplane(2,:);
  v3 = loc + inplane(3,:);
  pr = nan(size(plotmarker,1), 3);
  d  = nan(size(plotmarker,1), 1);
  for k = 1:size(plotmarker,1)
    [pr(k,:), d(k,:)] = ptriprojn(v1, v2, v3, plotmarker(k,:));
  end
  sel = d<eps*1e8;
  if sum(sel)>0
    ft_plot_dipole(pr(sel,:), repmat([0;0;1], 1, size(pr,1)), 'length', 0, 'color', markercolor, 'diameter', markersize);
  end
end

% update the axes to ensure that the whole volume fits
ax = [min(corner_hc) max(corner_hc)];
axis(ax([1 4 2 5 3 6])); % reorder into [xmin xmax ymin ymaz zmin zmax]

st = dbstack;
if numel(st)>1
  % ft_plot_slice has been called from another function, probably ft_plot_ortho
  % assume the remainder of the axis settings to be handled there
else
  set(gca,'xlim',[min(Xh(:))-0.5*resolution max(Xh(:))+0.5*resolution]);
  set(gca,'ylim',[min(Yh(:))-0.5*resolution max(Yh(:))+0.5*resolution]);
  set(gca,'zlim',[min(Zh(:))-0.5*resolution max(Zh(:))+0.5*resolution]);

  set(gca,'dataaspectratio',[1 1 1]);
  % axis equal; % this for some reason does not work robustly when drawing intersections, replaced by the above
  axis vis3d
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [x, y] = projplane(z)
[u, s, v] = svd([eye(3) z(:)]);
x = u(:, 2)';
y = u(:, 3)';

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [sel1, sel2] = tight(V)
% make a selection to cut off the nans at the edges
sel1 = sum(~isfinite(V), 2)<size(V, 2);
sel2 = sum(~isfinite(V), 1)<size(V, 1);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function bool = issufficientlyinteger(X, tolerance)
%isinteger only checks for integer class, so will always return false with
%double integers
bool = all(abs(X-round(X))<tolerance);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [bool, lineX] = isline(X)
%isline returns an array of the values in X are columnwise or rowwise the
%same, otherwise returns false
if isequal(X(ones(1,size(X,1)),:),X)
  lineX = X(1,:);
  bool = true;
elseif isequal(X(:,ones(1,size(X,2))),X)
  lineX = X(:,1)';
  bool = true;
else
  lineX = [];
  bool = false;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function letter = flipletter(letter)
switch letter
  case 'a'
    letter = 'p';
  case 'p'
    letter = 'a';
  case 'l'
    letter = 'r';
  case 'r'
    letter = 'l';
  case 'i'
    letter = 's';
  case 's'
    letter = 'i';
  otherwise
    ft_error('incorrect letter')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function coordsys = generic(coordsys)
mapping = {
  'ctf',       'als'
  'bti',       'als'
  '4d',        'als'
  'yokogawa',  'als'
  'eeglab',    'als'
  'neuromag',  'ras'
  'itab',      'ras'
  'acpc',      'ras'
  'spm',       'ras'
  'mni',       'ras'
  'fsaverage', 'ras'
  'tal',       'ras'
  'scanras',   'ras'
  'scanlps',   'lps'
  'dicom',     'lps'
  };

sel = find(strcmp(mapping(:,1), coordsys));
if length(sel)==1
  coordsys = mapping{sel,2};
end

if ~all(ismember(coordsys, 'lrapis'))
  ft_error('cannot convert "%s" to a generic coordinate system label', coordsys);
end


function v1 = uminus(v1)
% overloads unitary minus

v1.x = -v1.x;
v1.y = -v1.y;
v1.z = -v1.z;


function [fx,dfdx,dfdP] = g_Id_phi(x,P,u,in)
% this function applies the identity mapping
fx = P;
dfdx = [];
dfdP = eye(size(P,1));


% CHANPROJ - make a detailed plot of data returned from PLOTPROJ
%              for given channel. Returns the data plotted.
% Usage:
%  >> [chandata] = chanproj(projdata,chan);
%  >> [chandata] = chanproj(projdata,chan,ncomps,framelist,limits,title,colors);
%
% Inputs:
%   projdata    = data returned from PLOTPROJ
%   chan        = single channel to plot
%   ncomps      = number of component projections in projdata
%
% Optional:
%   framelist   = data frames to plot per epoch Ex: [1:128] (0|def -> all)
%   limits      = [xmin xmax ymin ymax]  (x's in msec)
%                                        (0, or y's 0 -> data limits)
%   title       = fairly short single-quoted 'plot title'  (0|def -> chan)
%   colors      = file of color codes, 3 chars per line  (NB: '.' = space)
%                                        (0|def -> white is original data)
%
% Author: Scott Makeig, SCCN/INC/UCSD, La Jolla, 1996

% Copyright (C) 1996 Scott Makeig, SCCN/INC/UCSD, scott@sccn.ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

% 11-30-96 Scott Makeig  CNL / Salk Institute, La Jolla as plotprojchan.m
% 03-19-97 changed VAR to diag(COV) -sm
% 03-26-97 fix (== -> =) -sm
% 04-03-97 allow framelist to be a col vector, allow 32 traces, fix pvaf, made
%           ncomps mandatory  -sm
% 04-04-97 shortened name to CHANPROJ -sm
% 05-20-97 added read of icadefs.m -sm
% 11-05-97 disallowed white traces unless default axis color is white -sm & ch
% 11-13-97 rm'ed errcode variable -sm
% 12-08-97 added LineWidth 2 to data trace, changed whole plot color to BACKCOLOR -sm
% 01-25-02 reformated help & license -ad

function [chandata] = chanproj(projdata,chan,ncomps,framelist,limits,titl,colorfile)

icadefs; % read default MAXPLOTDATACHANS

if nargin < 7,
    colorfile =0;
end
if nargin < 6,
    titl = 0;
end
if nargin < 5,
    limits = 0;
end
if nargin < 4,
    framelist = 0;
end

if nargin < 3,
    fprintf('chanproj(): requires at least three arguments.\n\n');
    help chanproj
    return
end

[chans,framestot] = size(projdata);
frames = fix(framestot/(ncomps+1));

if ncomps < 1 || frames*(ncomps+1) ~= framestot,
    fprintf(...
  'chanproj(): data length (%d) not a multiple of ncomps (%d).\n',...
                                   framestot,ncomps);
    return
end

if chan> chans,
    fprintf(...
 'chanproj(): specified channel (%d) cannot be > than nchans (%d).\n',...
                            chan,chans);
    return
else
    chandata = projdata(chan,:);
    epochs = fix(length(chandata)/frames);
end
if epochs > MAXPLOTDATACHANS
  fprintf(...
 'chanproj(): maximum number of traces to plot is %d\n',...
              MAXPLOTDATACHANS);
   return
end
if framestot~=epochs*frames,
   fprintf('chanproj(): projdata is wrong length.\n');  % exit
   return
end

%
%%%%%%%%%%%%%%%% Find or read plot limits %%%%%%%%%%%%%%%%%%%%%%%%%%%
%
if limits==0,
  xmin=0;xmax=0;ymin=0;ymax=0;
else
  if length(limits)~=4,
      fprintf( ...
'chanproj():^G limits should be 0 or an array [xmin xmax ymin ymax].\n');
      return
  end
  xmin = limits(1);
  xmax = limits(2);
  ymin = limits(3);
  ymax = limits(4);
end

if xmin == 0 && xmax == 0,
  x = [0:frames-1];
  xmin = 0;
  xmax = frames-1;
else
  dx = (xmax-xmin)/(frames-1);
  x=xmin*ones(1,frames)+dx*(0:frames-1);          % construct x-values
end

if ymax == 0 && ymin == 0,
  ymax=max(max(projdata(chan,:)));
  ymin=min(min(projdata(chan,:)));
end
%
%%%%% Reshape the projdata for plotting and returning to user %%%%%%%%%
%
chandata=reshape(chandata,frames,epochs);
chandata=chandata';

if framelist ==0,
  framelist = [1:frames];  % default
end
if size(framelist,2)==1,
     if size(framelist,1)>1,
         framelist = framelist';
     else
         fprintf(...
 'chanproj(): framelist should be a vector of frames to plot per epoch.\n');
         return
     end
end

if framelist(1)<1 || framelist(length(framelist))>frames,
    fprintf('chanproj(): framelist values must be between 1-%d.\n',frames);
    return
else
    chandata=chandata(:,framelist);
end
%
%%%%%%%%%%%%%%%%%%% Read the color names %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
if colorfile ~=0,
    if ~ischar(colorfile)
       fprintf('chanproj(); color file name must be a string.\n');
       return
    end
    cid = fopen(colorfile,'r');
    if cid <3,
        fprintf('chanproj(): cannot open file %s.\n',colorfile);
        return
    end
    colors = fscanf(cid,'%s',[3 MAXPLOTDATACHANS]);
    colors = colors';
    [r c] = size(colors);
    for i=1:r
        for j=1:c
            if colors(i,j)=='.',
                colors(i,j)=' ';
            end
        end
    end
else    % default color order - no yellow
    colors =['w  ';'r  ';'b  ';'g  ';'c  ';'m  ';'r  ';'b  ';'g  ';'c  ';'m  ';'r  ';'b  ';'g  ';'c  ';'m  ';'r  ';'b  ';'g  ';'c  ';'m  ';'r  ';'b  ';'g  ';'c  ';'m  ';'r  ';'b  ';'g  ';'c  ';'m  ';'r  '];
end
%
% Make vector of x-values
%
x=[xmin:(xmax-xmin)/(frames-1):xmax+0.00001];
xmin=x(framelist(1));
xmax=x(framelist(length(framelist)));
x = x(framelist(1):framelist(length(framelist)));
%
%%%%%%%%% Compute percentage of variance accounted for %%%%%%%%%%%%%%
%
if epochs>1,
    sumdata = zeros(1,length(framelist));
    for e=2:epochs
        sumdata= sumdata + chandata(e,:);  % sum the component projections
    end
    sigvar = diag(cov(chandata(1,:)'));
    difvar = diag(cov(((chandata(1,:)-sumdata)')));
                                    % percent variance accounted for
    pvaf   = round(100.0*(1.0-difvar/sigvar));
end
%
%%%%%%%%%%%%%%%%%%%%%% Plot traces %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
fprintf('chanproj(): Drawing trace ');
for e=1:epochs,
    fprintf ('%d ',e);
    set(gcf,'Color',BACKCOLOR); % set the background color to grey
    set(gca,'Color','none');    % set the axis color = figure color
    if e==1
      plot(x,chandata(e,:),colors(e),'LineWidth',2);  % plot it!
    else
      plot(x,chandata(e,:),colors(e),'LineWidth',1);  % plot it!
    end
    hold on;
end
fprintf('\n');
%
%%%%%%%%%%%%%%%%%%%% Fix axis limits %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
axis([xmin xmax ymin-0.1*(ymax-ymin) ymax+0.1*(ymax-ymin)]);
%
%%%%%%%%%%%%%%%%%%% Add title and labels %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
if titl==0,
    titl = ['channel ' int2str(chan)];
end
titl = [ titl '   (p.v.a.f. ' int2str(pvaf) '%)' ];
title(titl);
xlabel('Time (msec)');
ylabel('Potential (uV)');


function gmt_map(sOutput, sPrefix, hAxes)
% function gmt_map(sOutput sPrefix, hAxes)
% ----------------------------------------
% Wrapper function for conversion of Matlab map-figures to GMT
%
% Input parameters:
%   sOutput         Filename without path information of the GMT script
%   sPrefix         Prefix of names of files generated by gmt_map (can contain path information)
%                   Default: empty string
%   hAxes           Handle of the axes for conversion (default: gca)
%
% Danijel Schorlemmer
% March 11, 2004

if ~exist('sPrefix', 'var')
  sPrefix = '';
end

if ~exist('hAxes', 'var')
  hAxes = gca;
end

gmt_init(hAxes, sOutput);

% Iterate thru the layers of the plot
vKids = get(hAxes, 'Children');
for nCnt = length(vKids):-1:1
  sType = lower(get(vKids(nCnt), 'Type'));
  switch sType
  case 'surface'
    [fXDiff, fYDiff] = gmt_pcolor(vKids(nCnt), sOutput, num2str(nCnt));
  case 'line'
    gmt_xy(vKids(nCnt), sOutput, num2str(nCnt));
  case 'text'
    gmt_text(vKids(nCnt), sOutput, num2str(nCnt));
  end
end

% Search for a colorbar and plot it
gmt_scale(hAxes, sOutput, '')

gmt_done(sOutput);

unix(['chmod u+x ' sOutput]);


function disp( f )
%DISP Display a BALLFUN object to the command line.
%
% See also DISPLAY.

% Copyright 2019 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

Nfunctions = numel(f);
% Vectorize the array
f = f(:);

% Display the object
if ( Nfunctions == 1 )
    % Get information that we want to display:
    display_ballfun(f);
else
    disp('   ballfun array containing')
    fprintf('\n');
    for i = 1:Nfunctions
        display_ballfun(f(i));
    end
end

end

% Display one ballfun function
function display_ballfun(f)

loose = strcmp(get(0, 'FormatSpacing'), 'loose');

if isempty(f)
    fprintf('    empty ballfun\n')
else
S = size(f);
vscl = vscale(f);
% Display the information:
disp('   ballfun object:')
fprintf('      domain           r    lambda    theta    vertical scale\n');
fprintf('     unit ball    %6i  %6i   %6i           %3.2g\n', S(1), S(2), S(3),vscl);
end

if ( loose )
    fprintf('\n');
end

end


function [tar,non] = get_tar_non(scr,key)
% Divides scores into target and non-target scores using
% information in a key.
% Inputs:
%   scr: A Scores object.
%   key: A Key object.
% Outputs:
%   tar: A vector of target scores.
%   non: A vector of non-target scores.

assert(nargin==2)
assert(isa(scr,'Scores'))
assert(isa(key,'Key'))
assert(scr.validate())
assert(key.validate())

scr = scr.align_with_ndx(key);
scores = scr.scoremat;
tarndx = key.tar & scr.scoremask;
nonndx = key.non & scr.scoremask;
tar = scores(tarndx(:))';
non = scores(nonndx(:))';
end


function ok = tt_subscheck(subs)
%TT_SUBSCHECK Checks for valid subscripts.
%
%  TT_SUBSCHECK(S) throws an error if S is not a valid subscript
%  array, which means that S is a matrix of real-valued, finite,
%  positive, integer subscripts.
%
%  X = TT_SUBSCHECK(S) returns true if S is a valid and false
%  otherwise.
%
%  See also TT_SIZECHECK, TT_VALSCHECK.
%
%MATLAB Tensor Toolbox.
%Copyright 2012, Sandia Corporation.

% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2012) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt

%
% Includes improvements offered by Marcus Brubaker.

if isempty(subs)
    ok = true;
elseif ndims(subs) == 2 && isreal(subs) ...
        && all(isfinite(subs(:)) & subs(:) > 0) ...
        && isequal(subs,round(subs))
    ok = true;
else
    ok = false;
end

if ~ok && nargout == 0
    error('Subscripts must be a matrix of real positive integers');
end


function CreateImageSeq(inData, filePath, format)
[height, width, layer] = size(inData{1});
for n = 1:layer
    Img(:,:,1) = inData{1}(:,:,n);
    Img(:,:,2) = inData{2}(:,:,n);
    Img(:,:,3) = inData{3}(:,:,n);
    fileName = [filePath, int2str(n), format];
    imwrite(Img, fileName);
end


%qrscorrect() -  Correct for false positive and negative QRS peaks
%   and align peaks for maximum correlation.
%
% Usage:
%   >>Peaks=qrscorrect(Peaks,Ecg,fs)
%
% Inputs:
%   Peaks: Indeces of located QRS peaks
%   Ecg: vector of ecg data
%   sampling frequency.
%
% Ouput:
%   Peaks: Corrected peaks
%
% Note: progress bar designed to work as part of fmrib_qrsdetect.
%
% Author: Rami Niazy, FMRIB Centre, University of Oxford.
%
%
%
% Copyright (c) 2004 Rami Niazy, FMRIB Centre, University of Oxford.
%

% Copyright (C) 2004 Rami K. Niazy, FMRIB Centre, University of Oxford
%                    rami@fmrib.ox.ac.uk
%
% 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
%
%

%   Dec 23, 2004
%   use binary prcorr2
%   instead of corrcoef

%   Dec 17, 2004
%   use corrcoef instead of
%   corr2

%   Dec 15, 2004
%   Median of all peaks used
%   for correction instead of
%   section median.

%   Nov 4, 2004
%   Changed order of correction
%   Added additional alignment

%   Nov 3, 2004
%   Added check and
%   adjustment of edge peaks clearance

%   Oct 26, 2004
%   Fixed bug for non-int fs case

function Peaks=qrscorrect(Peaks,Ecg,fs)

% init
%------
P=zeros(1,length(Ecg));
P(Peaks)=1;
dP_std=std(diff(Peaks));
dP_med=median(diff(Peaks));
pad=round(5*fs);
secL=round(10*fs);
sections=floor(length(P)/secL)+1;

if length(P((sections-1)*secL+1:end))<pad
    sections=sections-1;
end

barth=2;
barth_step=barth;
if isempty(javachk('desktop'))
    clearcom='clc';
else
    clearcom='home';
end

% Correct for FP
%---------------
for s=1:sections

    %wait bar
    if s==1
        barth=5;
        barth_step=barth;
        Flag25=0;
        Flag50=0;
        Flag75=0;
        fprintf('\nStage 2 of 5: Correcting for False Positive detection.\n');

    end
    %-----------------calc------------------------------------------

    f_flag=0;
    if s==1
        sec=P(1:secL+pad);
    elseif s==sections
        sec=P((s-1)*secL+1-pad:end);
    else
        sec=P((s-1)*secL+1-pad:s*secL+pad);
    end

    sec_P=find(sec==1);
    sec_dP=diff(sec_P);
%     dP_med=median(sec_dP);

    false_p=1;
    while ~isempty(false_p)
        false_p=find(sec_dP<(dP_med-3*dP_std));%-3*dP_std
        if ~isempty(false_p)
            f_flag=1;
            sec(sec_P(false_p(1)+1))=0;
            sec_P=find(sec==1);
            sec_dP=diff(sec_P);
            false_p=find(sec_dP<(dP_med-3*dP_std));
        end
    end

    if f_flag==1
        if s==1
            P(1:secL+pad)=sec;
        elseif s==sections
            P((s-1)*secL+1-pad:end)=sec;
        else
            P((s-1)*secL+1-pad:s*secL+pad)=sec;
        end
    end

    %---------------------------------------------------------------
    %update wait bar
    percentdone=floor(s*100/sections);
    if floor(percentdone)>=barth
        if percentdone>=25 & Flag25==0
            fprintf('25%% ')
            Flag25=1;
        elseif percentdone>=50 & Flag50==0
            fprintf('50%% ')
            Flag50=1;
        elseif percentdone>=75 & Flag75==0
            fprintf('75%% ')
            Flag75=1;
        elseif percentdone==100
            fprintf('100%%\n')
        else
            fprintf('.')
        end

        while barth<=percentdone
            barth=barth+barth_step;
        end
        if barth>100
            barth=100;
        end
    end
end

%Check Edge Peaks
%----------------
Peaks=find(P==1);
pc1=2;
pc2=1;

while (pc1>1 | pc2>0)
	dP=diff(Peaks);
	hwinl=round(mean(dP)/2);
	searchw=round(0.33*mean(dP));

	pc1=1;
	if length(Peaks)>5
        for p=1:5
            try
                tmp=Ecg(Peaks(p)-hwinl-searchw:Peaks(p)+hwinl);
                break;
            catch
                pc1=pc1+1;
            end
        end
	end

	if pc1>1
        Peaks(1:pc1-1)=[];
	end

	pc2=0;
	if length(Peaks)>5
        for p=length(Peaks):-1:length(Peaks-4)
            try
                tmp=Ecg(Peaks(p)-hwinl:Peaks(p)+hwinl+searchw);
                break;
            catch
                pc2=pc2+1;
            end
        end
	end

	if pc2>0
        Peaks(end-pc2:end)=[];
	end
end

%Finding corrected peaks
%-----------------------
searchwL=length(searchw);
peaksL=length(Peaks);
qrs=zeros(hwinl*2+1,peaksL);
qrs2=zeros(hwinl*2+1,peaksL);
barth=2;

% Find average Heart Beat for use in alignment
%---------------------------------------------
for p=1:peaksL
    qrs(:,p)=Ecg(Peaks(p)-hwinl:Peaks(p)+hwinl);
end

m_qrs=mean(qrs,2);
% figure;plot(qrs)

% Align Peaks (1)
%----------------
for p=1:peaksL

    %wait bar
    if p==1
        barth=5;
        barth_step=barth;
        Flag25=0;
        Flag50=0;
        Flag75=0;
        fprintf('\nStage 3 of 5: Aligning QRS Peaks  (1)\n');
    end

    %-----------------calc------------------------------------------

    ppn=1;
    for B=Peaks(p)-searchw:Peaks(p)+searchw
        C(ppn)=prcorr2(Ecg(B-hwinl:B+hwinl),m_qrs);;
        ppn=ppn+1;
	end
	[CV,CP]=max(C);
	Beta=CP-(searchw+1);
	Peaks(p)=Peaks(p)+Beta;

    %---------------------------------------------------------------
    %update wait bar
    percentdone=floor(p*100/(peaksL));
    if floor(percentdone)>=barth
        if percentdone>=25 & Flag25==0
            fprintf('25%% ')
            Flag25=1;
        elseif percentdone>=50 & Flag50==0
            fprintf('50%% ')
            Flag50=1;
        elseif percentdone>=75 & Flag75==0
            fprintf('75%% ')
            Flag75=1;
        elseif percentdone==100
            fprintf('100%%\n')
        else
            fprintf('.')
        end

        while barth<=percentdone
            barth=barth+barth_step;
        end
        if barth>100
            barth=100;
        end
    end
end
barth=2;
P=zeros(1,length(Ecg));
P(Peaks)=1;

% Correct for FN
%---------------
for s=1:sections

    %wait bar
    if s==1
        barth=5;
        barth_step=barth;
        Flag25=0;
        Flag50=0;
        Flag75=0;
        fprintf('\nStage 4 of 5: Correcting for False Negative detection.\n');
    end

    %-----------------calc------------------------------------------

    f_flag=0;
    if s==1
        sec=P(1:secL+pad);
    elseif s==sections
        sec=P((s-1)*secL+1-pad:end);
    else
        sec=P((s-1)*secL+1-pad:s*secL+pad);
    end

    sec_P=find(sec==1);
    sec_dP=diff(sec_P);
    dP_med=median(sec_dP);

    false_n=1;
    while ~isempty(false_n)
        false_n=find(sec_dP>(1.5*dP_med));
        if ~isempty(false_n)
            f_flag=1;
            sec(sec_P(false_n(1))+round(dP_med))=1;%sec_dP(false_n(1)-1)
            sec_P=find(sec==1);
            sec_dP=diff(sec_P);
            false_n=find(sec_dP>(1.5*dP_med));
        end
    end

    if f_flag==1
        if s==1
            P(1:secL+pad)=sec;
        elseif s==sections
            P((s-1)*secL+1-pad:end)=sec;
        else
            P((s-1)*secL+1-pad:s*secL+pad)=sec;
        end
    end

    %---------------------------------------------------------------
    %update wait bar
    percentdone=floor(s*100/sections);
    if floor(percentdone)>=barth
        if percentdone>=25 & Flag25==0
            fprintf('25%% ')
            Flag25=1;
        elseif percentdone>=50 & Flag50==0
            fprintf('50%% ')
            Flag50=1;
        elseif percentdone>=75 & Flag75==0
            fprintf('75%% ')
            Flag75=1;
        elseif percentdone==100
            fprintf('100%%\n')
        else
            fprintf('.')
        end

        while barth<=percentdone
            barth=barth+barth_step;
        end
        if barth>100
            barth=100;
        end
    end
end

barth=2;
Peaks=find(P==1);
peaksL=length(Peaks);
barth=2;
% Align Peaks (2)
%----------------
for p=2:peaksL-1

    %wait bar
    if p==2
        barth=5;
        barth_step=barth;
        Flag25=0;
        Flag50=0;
        Flag75=0;
         fprintf('\nStage 5 of 5: Aligning QRS Peaks  (2)\n');
    end

    %-----------------calc------------------------------------------

    ppn=1;
    for B=Peaks(p)-searchw:Peaks(p)+searchw
        C(ppn)=prcorr2(Ecg(B-hwinl:B+hwinl),m_qrs);
        ppn=ppn+1;
	end
	[CV,CP]=max(C);
	Beta=CP-(searchw+1);
	Peaks(p)=Peaks(p)+Beta;

    %---------------------------------------------------------------
    %update wait bar
    percentdone=floor((p-1)*100/(peaksL-2));
    if floor(percentdone)>=barth
        if percentdone>=25 & Flag25==0
            fprintf('25%% ')
            Flag25=1;
        elseif percentdone>=50 & Flag50==0
            fprintf('50%% ')
            Flag50=1;
        elseif percentdone>=75 & Flag75==0
            fprintf('75%% ')
            Flag75=1;
        elseif percentdone==100
            fprintf('100%%\n')
        else
            fprintf('.')
        end

        while barth<=percentdone
            barth=barth+barth_step;
        end
        if barth>100
            barth=100;
        end
    end
end

% for p=2:peaksL-1
%     qrs2(:,p-1)=Ecg(Peaks(p)-hwinl:Peaks(p)+hwinl);
% end
% figure;plot(qrs2);
return;


% axcopy() - Copy a Matlab figure axis and its graphic objects to a new pop-up window
%            using the left mouse button.
%
% Usage:  >> axcopy
%         >> axcopy(fig)
%         >> axcopy('noticks')
%         >> axcopy(fig, command)
%
% Notes:
%   1) Clicking the left mouse button on a Matlab figure axis copies the graphic objects
%      in the axis to a new (pop-up) figure window.
%   2) Option 'noticks' does not make x and y tickloabelmodes 'auto' in the pop-up.
%   2) The command option is an optional string that is evaluated in the new window
%      when it pops up. This allows the user to customize the pop-up display.
%   3) Deleting the pop-up window containing the copied axis leaves the selected axis
%      as the current graphic axis (gca).

% Authors: Tzyy-Ping Jung & Scott Makeig, SCCN/INC/UCSD, La Jolla, 2000

% Copyright (C) 2000 Tzyy-Ping Jung & Scott Makeig, SCCN/INC/UCSD, 3-16-00
%
% 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

% requires copyaxes.m
% 4-2-00 added 'noticks' -sm
% 01-25-02 reformated help & license -ad
% 02-16-02 debuged exist('fig') -> exist('fig') == 1 -ad
% 02-16-02 added the ignore parent size option -ad
% 03-11-02 remove size option and added command option -ad

function axcopy_modified(fig, command)
if (exist('fig') == 1) && strcmp(fig,'noticks')
   noticks = 1;
   if nargin> 1
     shift
   else
     fig = [];
   end
end
if ~(exist('fig') ==1) || isempty(fig) || fig == 0
   fig = gcf;
end
if ~strcmpi(get(fig, 'type'), 'axes')
    hndl= findobj('parent',fig,'type','axes');
else
    hndl = fig;
end;
offidx = [];
if exist('command') ~= 1
    comstr = 'copyaxis_modified';
else
   command_dbl = double(command);
   comstr = double(['copyaxis_modified(''' char(command_dbl) ''')']);
end;
for a=1:length(hndl)             % make all axes visible
    allobjs = findobj('parent',hndl(a));
    for index = 1:length(allobjs)
        if isempty(get(allobjs(index), 'ButtonDownFcn'))
            set(allobjs(index), 'ButtonDownFcn', char(comstr));
        end;
    end;
end
if ~strcmpi(get(fig, 'type'), 'axes')
    figure(fig);
else
    figure(get(fig, 'parent'));
end;
if exist('command') ~= 1
    set(hndl(a),'ButtonDownFcn','copyaxis_modified');
else
    % set(hndl,'ButtonDownFcn',['copyaxis(''' command ''')']);
    comstr = double(['copyaxis_modified(''' char(command_dbl) ''')']);
    set(hndl,'ButtonDownFcn',char(comstr));
end;
%set(hndl,'ButtonDownFcn','copyaxis');
%if ~exist('noticks')
  %axis on
  %set(gca,'xticklabelmode','auto')
  %set(gca,'yticklabelmode','auto')
%end