function source = ft_read_cifti(filename, varargin)

% FT_READ_CIFTI read functional data or functional connectivity from a cifti-1 or
% cifti-2 file. The functional data can consist of a dense or a parcellated
% representation. The geometrical description of the brainordinates can consist of
% triangulated surfaces or voxels in a regular 3-D volumetric grid. If available,
% it also reads the geometrical description of the surfaces from the accompanying
% gifti files.
%
% Use as
%   data = ft_read_cifti(filename, ...)
%
% If the file contains a dense representation of functional data, the output data
% structure is organized according to the FT_DATATYPE_SOURCE or FT_DATATYPE_VOLUME
% definition.
%
% If the contains a parcellated representation of functional data, the output data
% structure is organized according to the FT_DATATYPE_TIMELOCK or FT_DATATYPE_FREQ
% definition. In addition, the description of the geometry wil be represented in a
% data.brainordinate field, which is organized according to the FT_DATATYPE_SOURCE
% or FT_DATATYPE_VOLUME definition.
%
% Any optional input arguments should come in key-value pairs and may include
%   'readdata'         = boolean, can be false or true (default depends on file size)
%   'readsurface'      = boolean, can be false or true (default = true)
%   'cortexleft'       = string, filename with left cortex (optional, default is automatic)
%   'cortexright'      = string, filename with right cortex (optional, default is automatic)
%   'hemisphereoffset' = number, amount in milimeter to move the hemispheres apart from each other (default = 0)
%   'mapname'          = string, 'field' to represent multiple maps separately, or 'array' to represent as array (default = 'field')
%   'debug'            = boolean, write a debug.xml file (default = false)
%
% See also FT_WRITE_CIFTI, FT_READ_MRI, FT_WRITE_MRI

% Known limitations and bugs
% - it will fail if multiple MatrixIndicesMap contain BrainModels
% - fibers (i.e. dfan and dfibersamp) are unsupported/untested
% - metadata is unsupported

% Copyright (C) 2013-2015, Robert Oostenveld
%
% 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$

readdata         = ft_getopt(varargin, 'readdata', []);   % the default depends on file size, see below
readsurface      = ft_getopt(varargin, 'readsurface', true);
cortexleft       = ft_getopt(varargin, 'cortexleft', {});
cortexright      = ft_getopt(varargin, 'cortexright', {});
hemisphereoffset = ft_getopt(varargin, 'hemisphereoffset', 0); % in mm, move the two hemispheres apart from each other
debug            = ft_getopt(varargin, 'debug', false);
mapname          = ft_getopt(varargin, 'mapname', 'field');
dataformat       = ft_getopt(varargin, 'dataformat', []);

if ft_filetype(filename, 'compressed')
  % the file is compressed, unzip on the fly
  inflated = true;
  origfile = filename;
  filename = inflate_file(filename);
else
  inflated = false;
  origfile = filename;
end

% read the header section
hdr = read_nifti2_hdr(filename);

% xml_offset = 540+12;
% xml_size   = hdr.vox_offset-xml_offset-8;

fid = fopen_or_error(filename, 'rb', hdr.endian);

% determine the file size, this is used to catch endian errors
fseek(fid, 0, 'eof');
filesize = ftell(fid);
fseek(fid, 0, 'bof');

if isempty(readdata)
  % set the default for readdata
  if filesize>1e9
    ft_warning('Not reading data by default in case filesize>1GB. Please specify the ''readdata'' option.');
    readdata = false;
  else
    readdata = true;
  end
else
  % convert 'yes'/'no' into boolean
  readdata = istrue(readdata);
end

fseek(fid, 540, 'bof');
hdrext = fread(fid, [1 4], 'int8');
if hdrext(1)~=1
  ft_error('cifti requires a header extension');
end

% determine the size of the header extension
esize = fread(fid, 1, 'int32=>int32');
etype = fread(fid, 1, 'int32=>int32');

hdrsize = 540;
voxsize = filesize-hdr.vox_offset;
if esize>(filesize-hdrsize-voxsize)
  ft_warning('the endianness of the header extension is inconsistent with the nifti-2 header');
  esize = swapbytes(esize);
  etype = swapbytes(etype);
end

if etype~=32 && etype~=swapbytes(int32(32)) % FIXME there is an endian problem
  ft_error('the header extension type is not cifti');
end

% read the extension content, subtract the 8 bytes from esize and etype
xmldata = fread(fid, [1 esize-8], 'uint8=>char');

% the size of the extension must be an integer multiple of 16 bytes according to http://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields/nifti1fields_pages/extension.html
% consequently there might be some zero-padding at the end of the XML section
if any(xmldata==0)
  xmldata = xmldata(xmldata>0);
end

if debug
  try
    % write the xml section to a temporary file
    xmlfile = 'debug.xml';
    tmp = fopen_or_error(xmlfile, 'w');
    fwrite(tmp, xmldata);
    fclose(tmp);
  end
end

% ensure that the external toolbox is present, this adds gifti/@xmltree
ft_hastoolbox('gifti', 1);

% convert the character data to an xmltree object
tree = xmltree(xmldata);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% convert the xmltree object to a generic structure
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

numericAttributeTypes = {'NumberOfMatrices', 'AppliesToMatrixDimension', 'IndexOffset', 'IndexCount', 'SurfaceNumberOfNodes', 'VolumeDimensions', 'SurfaceNumberOfVertices', 'SeriesStart', 'SeriesStep', 'NumberOfSeriesPoints', 'SeriesExponent', 'Vertices', 'MeterExponent'};

Cifti             = struct(); % the parent of the XML tree, it only contains version info
MatrixIndicesMap  = [];
Parcel            = [];
NamedMap          = [];
Surface           = [];
Volume            = [];
BrainModel        = struct([]); % this will be constructed both for dense and parcellated files

attr = attributes(tree, 'get', 1);
if ~iscell(attr), attr = {attr}; end % treat one attribute just like multiple attributes
for j=1:numel(attr)
  if any(strcmp(attr{j}.key, numericAttributeTypes))
    Cifti.(attr{j}.key) = str2num(attr{j}.val);
  else
    Cifti.(attr{j}.key) = attr{j}.val;
  end
end

uid_MatrixIndicesMap = find(tree,'/CIFTI/Matrix/MatrixIndicesMap');
for i=1:length(uid_MatrixIndicesMap)
  map = branch(tree, uid_MatrixIndicesMap(i));

  % get the attributes of each map
  attr = attributes(map, 'get', 1);
  for j=1:numel(attr)
    if any(strcmp(attr{j}.key, numericAttributeTypes))
      MatrixIndicesMap(i).(attr{j}.key) = str2num(attr{j}.val);
    else
      MatrixIndicesMap(i).(attr{j}.key) = attr{j}.val;
    end
  end

  switch Cifti.Version
    case {'1' '1.0'}
      uid_Volume = find(tree,'/CIFTI/Matrix/Volume');
    case {'2' '2.0'}
      uid_Volume = find(map,'/MatrixIndicesMap/Volume');
  end
  % the following will fail if there are multiple volumes
  if ~isempty(uid_Volume)
    switch Cifti.Version
      case {'1' '1.0'}
        volume = branch(tree, uid_Volume);
      case {'2' '2.0'}
        volume = branch(map, uid_Volume);
    end
    attr = attributes(volume, 'get', 1); % there should only be one attribute here
    if ~iscell(attr), attr = {attr}; end % treat one attribute just like multiple attributes
    for j=1:numel(attr)
      if any(strcmp(attr{j}.key, numericAttributeTypes))
        Volume.(attr{j}.key) = str2num(attr{j}.val);
      else
        Volume.(attr{j}.key) = attr{j}.val;
      end
    end
    uid_Transform = find(volume,'/Volume/TransformationMatrixVoxelIndicesIJKtoXYZ');
    if ~isempty(uid_Transform)
      transform = branch(volume, uid_Transform);
      attr = attributes(transform, 'get', 1);
      if isstruct(attr), attr = {attr}; end % treat one attribute just like multiple attributes
      for j=1:numel(attr)
        if any(strcmp(attr{j}.key, numericAttributeTypes))
          Volume.(attr{j}.key) = str2num(attr{j}.val);
        else
          Volume.(attr{j}.key) = attr{j}.val;
        end
      end
      Volume.Transform = str2num(get(transform, 2, 'value'));
      Volume.Transform = reshape(Volume.Transform, [4 4])'; % it needs to be transposed
    end
  end

  uid_Surface = find(map,'/MatrixIndicesMap/Surface');
  if ~isempty(uid_Surface)
    for j=1:length(uid_Surface)
      surface = branch(map, uid_Surface(j));

      % get the attributes of each surface
      attr = attributes(surface, 'get', 1);
      if isstruct(attr), attr = {attr}; end % treat one attribute just like multiple attributes
      for k=1:numel(attr)
        if any(strcmp(attr{k}.key, numericAttributeTypes))
          Surface(j).(attr{k}.key) = str2num(attr{k}.val);
        else
          Surface(j).(attr{k}.key) = attr{k}.val;
        end
      end % for

    end
  end

  uid_Parcel = find(map,'/MatrixIndicesMap/Parcel');
  if ~isempty(uid_Parcel)
    for j=1:length(uid_Parcel)
      parcel = branch(map, uid_Parcel(j));

      % get the attributes of each parcel
      attr = attributes(parcel, 'get', 1);
      if isstruct(attr), attr = {attr}; end % treat one attribute just like multiple attributes
      for k=1:numel(attr)
        if any(strcmp(attr{k}.key, numericAttributeTypes))
          Parcel(j).(attr{k}.key) = str2num(attr{k}.val);
        else
          Parcel(j).(attr{k}.key) = attr{k}.val;
        end
      end % for

      uid_VoxelIndicesIJK = find(parcel, '/Parcel/VoxelIndicesIJK');
      if ~isempty(uid_VoxelIndicesIJK)
        tmp = str2num(get(parcel, children(parcel, uid_VoxelIndicesIJK), 'value'));
        Parcel(j).VoxelIndicesIJK = reshape(tmp, 3, [])' + 1; % transpose, one offset
      else
        Parcel(j).VoxelIndicesIJK = [];
      end

      Parcel(j).BrainStructure = {};

      uid_Vertices = find(parcel, '/Parcel/Vertices');
      for k=1:length(uid_Vertices)
        vertices = branch(parcel, uid_Vertices(k));

        attr = attributes(vertices, 'get', 1);
        if isstruct(attr), attr = {attr}; end % treat one attribute just like multiple attributes
        for l=1:numel(attr)
          switch attr{l}.key
            case 'BrainStructure'
              Parcel(j).BrainStructure{k} = attr{l}.val;
          end
        end % for

        Parcel(j).Vertices{k} = str2num(get(vertices, children(vertices, find(vertices, 'Vertices')), 'value')) + 1; % one offset
      end
    end
  end

  uid_NamedMap = find(map, '/MatrixIndicesMap/NamedMap');
  if ~isempty(uid_NamedMap)
    for j=1:length(uid_NamedMap)

      namedmap = branch(map, uid_NamedMap(j));
      NamedMap(j).MapName = get(namedmap, children(namedmap, find(namedmap, '/NamedMap/MapName')), 'value');

      uid_LabelTable = find(namedmap, '/NamedMap/LabelTable');
      for k=1:length(uid_LabelTable);
        labeltable = branch(namedmap, uid_LabelTable(k));
        uid_Label = find(labeltable, '/LabelTable/Label');
        for l=1:length(uid_Label)
          % there are also potentially intersting atributes here, but I don't know what to do with them
          NamedMap(j).LabelTable.Label{l} = get(labeltable, children(labeltable, uid_Label(l)), 'value');
          attr = attributes(branch(labeltable, uid_Label(l)), 'get', 1);
          if isstruct(attr), attr = {attr}; end % treat one attribute just like multiple attributes
          for m=1:numel(attr)
            switch attr{m}.key
              case 'Key'
                NamedMap(j).LabelTable.Key(l)   = str2double(attr{m}.val);
              case 'Red'
                NamedMap(j).LabelTable.Red(l)   = str2double(attr{m}.val);
              case 'Green'
                NamedMap(j).LabelTable.Green(l) = str2double(attr{m}.val);
              case 'Blue'
                NamedMap(j).LabelTable.Blue(l)  = str2double(attr{m}.val);
              case 'Alpha'
                NamedMap(j).LabelTable.Alpha(l) = str2double(attr{m}.val);
            end
          end
        end
      end % for each LabelTable
    end % for each NamedMap
  end % if NamedMap

  uid_BrainModel = find(map, '/MatrixIndicesMap/BrainModel');
  for j=1:length(uid_BrainModel)
    brainmodel = branch(map, uid_BrainModel(j));

    % get the attributes of each model
    attr = attributes(brainmodel, 'get', 1);
    if isstruct(attr), attr = {attr}; end % treat one attribute just like multiple attributes
    for k=1:numel(attr)
      if any(strcmp(attr{k}.key, numericAttributeTypes))
        MatrixIndicesMap(i).BrainModel(j).(attr{k}.key) = str2num(attr{k}.val);
      else
        MatrixIndicesMap(i).BrainModel(j).(attr{k}.key) = attr{k}.val;
      end
    end % for each attr

    switch MatrixIndicesMap(i).BrainModel(j).ModelType
      case 'CIFTI_MODEL_TYPE_SURFACE'
        switch Cifti.Version
          case {'1' '1.0'}
            uid = find(brainmodel, '/BrainModel/NodeIndices');
            % use VertexIndices rather than NodeIndices
            try, MatrixIndicesMap(i).BrainModel(j).VertexIndices = str2num(get(brainmodel, children(brainmodel, uid), 'value')) + 1; end
            % also copy the global surface information to a higher level
            Surface(end+1).BrainStructure          = MatrixIndicesMap(i).BrainModel(j).BrainStructure;
            Surface(end  ).SurfaceNumberOfVertices = MatrixIndicesMap(i).BrainModel(j).SurfaceNumberOfNodes;

          case {'2' '2.0'}
            uid = find(brainmodel, '/BrainModel/VertexIndices');
            try, MatrixIndicesMap(i).BrainModel(j).VertexIndices = str2num(get(brainmodel, children(brainmodel, uid), 'value')) + 1; end
            % also copy the global surface information to a higher level
            Surface(end+1).BrainStructure          = MatrixIndicesMap(i).BrainModel(j).BrainStructure;
            Surface(end  ).SurfaceNumberOfVertices = MatrixIndicesMap(i).BrainModel(j).SurfaceNumberOfVertices;

          otherwise
            ft_error('unsupported cifti version');
        end % switch version

      case 'CIFTI_MODEL_TYPE_VOXELS'
        tmp = str2num(get(brainmodel, children(brainmodel, find(brainmodel, '/BrainModel/VoxelIndicesIJK')), 'value'));
        MatrixIndicesMap(i).BrainModel(j).VoxelIndicesIJK = reshape(tmp, 3, [])' + 1; % transpose, one offset

      otherwise
        ft_error('unsupported ModelType');
    end % switch ModelType
  end % for each BrainModel
end % for each MatrixIndicesMap

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% read the voxel data section
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if readdata
  fseek(fid, hdr.vox_offset, 'bof');
  switch hdr.datatype
    case   2, [voxdata, nitemsread] = fread(fid, inf, 'uchar');   assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    case   4, [voxdata, nitemsread] = fread(fid, inf, 'short');   assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    case   8, [voxdata, nitemsread] = fread(fid, inf, 'int');     assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    case  16, [voxdata, nitemsread] = fread(fid, inf, 'float');   assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    case  64, [voxdata, nitemsread] = fread(fid, inf, 'double');  assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    case 512, [voxdata, nitemsread] = fread(fid, inf, 'ushort');  assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    case 768, [voxdata, nitemsread] = fread(fid, inf, 'uint');    assert(nitemsread==prod(hdr.dim(2:end)), 'could not read all data');
    otherwise, ft_error('unsupported datatype');
  end
  % hdr.dim(1) is the number of dimensions
  % hdr.dim(2) is reserved for the x-dimension
  % hdr.dim(3) is reserved for the y-dimension
  % hdr.dim(4) is reserved for the z-dimension
  % hdr.dim(5) is reserved for the time-dimension
  % hdr.dim(6:8) are used for CIFTI
  voxdata = reshape(voxdata, hdr.dim(6:end));
end
fclose(fid);

if inflated
  % compressed file has been unzipped on the fly, clean up
  delete(filename);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% convert to FieldTrip source representation, i.e. according to FT_DATATYPE_SOURCE and FT_DATATYPE_PARCELLATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

dimord = cell(size(MatrixIndicesMap));

% MatrixIndicesMap.IndicesMapToDataType can be
% CIFTI_INDEX_TYPE_BRAIN_MODELS The dimension represents one or more brain models.
% CIFTI_INDEX_TYPE_PARCELS      The dimension represents a parcellation scheme.
% CIFTI_INDEX_TYPE_SERIES       The dimension represents a series of regular samples.
% CIFTI_INDEX_TYPE_SCALARS      The dimension represents named scalar maps.
% CIFTI_INDEX_TYPE_LABELS       The dimension represents named label maps.

for i=1:length(MatrixIndicesMap)
  switch MatrixIndicesMap(i).IndicesMapToDataType
    case 'CIFTI_INDEX_TYPE_BRAIN_MODELS'
      dimord(MatrixIndicesMap(i).AppliesToMatrixDimension+1) = {'pos'};

      % concatenate all into an array of brain models
      for j=1:length(MatrixIndicesMap(i).BrainModel)
        BrainModel = cat(1, BrainModel, MatrixIndicesMap(i).BrainModel(j));
      end

    case 'CIFTI_INDEX_TYPE_PARCELS'
      dimord(MatrixIndicesMap(i).AppliesToMatrixDimension+1) = {'chan'};

      % construct an array of brain models
      IndexOffset = 0;
      BrainModelParcelName = {};

      for j=1:numel(Parcel)

        for k=1:numel(Parcel(j).BrainStructure)
          sel = strcmp({Surface(:).BrainStructure}, Parcel(j).BrainStructure{k});
          BrainModel(end+1).IndexOffset             = IndexOffset;
          BrainModel(end  ).IndexCount              = numel(Parcel(j).Vertices{k});
          BrainModel(end  ).BrainStructure          = Parcel(j).BrainStructure{k};
          BrainModel(end  ).ModelType               = 'CIFTI_MODEL_TYPE_SURFACE';
          BrainModel(end  ).SurfaceNumberOfVertices = Surface(sel).SurfaceNumberOfVertices;
          BrainModel(end  ).VertexIndices           = Parcel(j).Vertices{k};
          BrainModel(end  ).VoxelIndicesIJK         = [];
          IndexOffset = IndexOffset + numel(Parcel(j).Vertices{k});
          BrainModelParcelName{end+1} = Parcel(j).Name;
        end % for each BrainStructure

        if ~isempty(Parcel(j).VoxelIndicesIJK)
          BrainModel(end+1).IndexOffset             = IndexOffset;
          BrainModel(end  ).IndexCount              = size(Parcel(j).VoxelIndicesIJK,1);
          BrainModel(end  ).BrainStructure          = 'CIFTI_STRUCTURE_INVALID';
          BrainModel(end  ).ModelType               = 'CIFTI_MODEL_TYPE_VOXELS';
          BrainModel(end  ).SurfaceNumberOfVertices = [];
          BrainModel(end  ).VertexIndices           = [];
          BrainModel(end  ).VoxelIndicesIJK         = Parcel(j).VoxelIndicesIJK;
          IndexOffset = IndexOffset + size(Parcel(j).VoxelIndicesIJK,1);
          BrainModelParcelName{end+1} = Parcel(j).Name;
        end

      end % for each Parcel

    case 'CIFTI_INDEX_TYPE_SERIES'
      % this only applies to cifti version 2
      switch MatrixIndicesMap(i).SeriesUnit
        case 'SECOND'
          dimord(MatrixIndicesMap(i).AppliesToMatrixDimension+1) = {'time'};
          Cifti.time = (((1:MatrixIndicesMap(i).NumberOfSeriesPoints)-1) * MatrixIndicesMap(i).SeriesStep + MatrixIndicesMap(i).SeriesStart) * 10^MatrixIndicesMap(i).SeriesExponent;
        case 'HERTZ'
          dimord(MatrixIndicesMap(i).AppliesToMatrixDimension+1) = {'freq'};
          Cifti.freq = (((1:MatrixIndicesMap(i).NumberOfSeriesPoints)-1) * MatrixIndicesMap(i).SeriesStep + MatrixIndicesMap(i).SeriesStart) * 10^MatrixIndicesMap(i).SeriesExponent;
          % case 'METER'
          % case 'RADIAN'
        otherwise
          ft_error('unsupported SeriesUnit');
      end % switch SeriesUnit

    case 'CIFTI_INDEX_TYPE_SCALARS'
      dimord{MatrixIndicesMap(i).AppliesToMatrixDimension+1} = []; % scalars are not explicitly represented
      for j=1:length(NamedMap)
        Cifti.mapname{j} = fixname(NamedMap(j).MapName);
      end

    case 'CIFTI_INDEX_TYPE_LABELS'
      dimord{MatrixIndicesMap(i).AppliesToMatrixDimension+1} = []; % labels are not explicitly represented
      for j=1:length(NamedMap)
        key = NamedMap(j).LabelTable.Key;
        lab = NamedMap(j).LabelTable.Label;
        sel = key>0;
        if isfield(NamedMap(j).LabelTable, 'Red')
          % assume rgba to be also specified
          rgba = [NamedMap(j).LabelTable.Red(:) ...
                  NamedMap(j).LabelTable.Green(:) ...
                  NamedMap(j).LabelTable.Blue(:) ...
                  NamedMap(j).LabelTable.Alpha(:)];
          Cifti.rgba{j} = rgba(sel,:);
        end
        Cifti.labeltable{j}(key(sel)) = lab(sel);
        Cifti.mapname{j} = fixname(NamedMap(j).MapName);
      end

    case 'CIFTI_INDEX_TYPE_TIME_POINTS'
      % this only applies to cifti-1, in cifti-2 this has been replaced by CIFTI_INDEX_TYPE_SERIES
      dimord(MatrixIndicesMap(i).AppliesToMatrixDimension+1) = {'time'};
      switch MatrixIndicesMap(i).TimeStepUnits
        case 'NIFTI_UNITS_SEC'
          Cifti.fsample = 1/str2double(MatrixIndicesMap(i).TimeStep);
        otherwise
          % other units should be trivial to implement
          ft_error('unsupported TimeStepUnits "%s"', MatrixIndicesMap(i).TimeStepUnits);
      end % switch TimeStepUnits

    otherwise
      ft_error('unsupported IndicesMapToDataType');
  end % switch IndicesMapToDataType
end % for each MatrixIndicesMap

dimord = dimord(~cellfun(@isempty, dimord));
source.dimord = sprintf('%s_', dimord{:});
source.dimord(end) = [];

if ~isempty(BrainModel)
  % the BrainModel is constructed both for dense and parcellated data

  dataIndex           = cell(size(BrainModel));
  greynodeIndex       = cell(size(BrainModel));
  brainstructureIndex = cell(size(BrainModel));
  surfaceIndex        = nan(size(BrainModel)); % remains nan if it maps into the volume

  geombeg = [BrainModel.IndexOffset]+1;
  geomend = geombeg + [BrainModel.IndexCount] - 1;

  for i=1:numel(BrainModel)
    dataIndex{i} = geombeg(i):geomend(i);
    switch BrainModel(i).ModelType
      case 'CIFTI_MODEL_TYPE_SURFACE'

        switch Cifti.Version
          case {'1' '1.0'}
            try
              greynodeIndex{i}     = BrainModel(i).VertexIndices;
            catch
              greynodeIndex{i}     = 1:BrainModel(i).SurfaceNumberOfNodes;
            end
            brainstructureIndex{i} = 1:BrainModel(i).SurfaceNumberOfNodes;
          case {'2', '2.0'}
            greynodeIndex{i}       = BrainModel(i).VertexIndices;
            brainstructureIndex{i} = 1:BrainModel(i).SurfaceNumberOfVertices;
          otherwise
            ft_error('unsupported cifti version');
        end % switch

        sel = find(strcmp({Surface(:).BrainStructure}, BrainModel(i).BrainStructure));
        assert(numel(sel)==1);
        surfaceIndex(i) = sel;

      case 'CIFTI_MODEL_TYPE_VOXELS'
        greynodeIndex{i}       = 1:BrainModel(i).IndexCount;
        brainstructureIndex{i} = 1:BrainModel(i).IndexCount;
        surfaceIndex(i) = nan; % does not map onto surface

      otherwise
        ft_error('unsupported ModelType "%s"', BrainModel(i).ModelType);
    end
  end % for each BrainModel

  pos      = zeros(0,3);
  posIndex = zeros(0,1);

  % concatenate all vertices of all surfaces, including vertices that do not have data
  for i=1:numel(Surface)
    pos      = cat(1, pos, nan(Surface(i).SurfaceNumberOfVertices, 3));
    posIndex = cat(1, posIndex, i*ones(Surface(i).SurfaceNumberOfVertices, 1));
  end

  % it would be possible to represent all voxels, but for efficiency we only include voxel positions with data
  if ~isempty(Volume) && any(isnan(surfaceIndex))
    tmp       = ft_warp_apply(Volume.Transform, cat(1, BrainModel(isnan(surfaceIndex)).VoxelIndicesIJK));
    pos       = cat(1, pos, tmp);
    posIndex  = cat(1, posIndex, nan(size(tmp,1),1));
  end

  % the surface vertices come before the voxels
  if ~isempty(Surface)
    voxeloffset = sum([Surface.SurfaceNumberOfVertices]);
  else
    voxeloffset = 0;
  end

  greynodeOffset = nan(size(BrainModel));
  for i=1:numel(BrainModel)
    if strcmp(BrainModel(i).ModelType, 'CIFTI_MODEL_TYPE_SURFACE')
      sel = find(strcmp({Surface(:).BrainStructure}, BrainModel(i).BrainStructure));
      greynodeOffset(i) = find(posIndex==sel, 1, 'first') - 1;
    else
      sel = strcmp({BrainModel(1:i-1).ModelType}, 'CIFTI_MODEL_TYPE_VOXELS');
      greynodeOffset(i) = voxeloffset + sum([BrainModel(sel).IndexCount]);
    end
    % shift the greynodes to become consistent with the voxel data
    greynodeIndex{i} = greynodeIndex{i} + greynodeOffset(i);
    % shift the brainstructures to become consistent with the voxel data
    brainstructureIndex{i} = brainstructureIndex{i} + greynodeOffset(i);
  end

end % if BrainModel

brainordinate.brainstructure      = zeros(size(pos,1),1);
brainordinate.brainstructurelabel = {};

for i=1:numel(BrainModel)
  indx = find(strcmp(brainordinate.brainstructurelabel, BrainModel(i).BrainStructure));
  if isempty(indx)
    indx = length(brainordinate.brainstructurelabel)+1;
  end
  brainordinate.brainstructure(brainstructureIndex{i}) = indx;
  brainordinate.brainstructurelabel{indx} = BrainModel(i).BrainStructure;
end

list1 = {
  'CIFTI_STRUCTURE_CORTEX_LEFT'
  'CIFTI_STRUCTURE_CORTEX_RIGHT'
  'CIFTI_STRUCTURE_CEREBELLUM'
  'CIFTI_STRUCTURE_ACCUMBENS_LEFT'
  'CIFTI_STRUCTURE_ACCUMBENS_RIGHT'
  'CIFTI_STRUCTURE_ALL_GREY_MATTER'
  'CIFTI_STRUCTURE_ALL_WHITE_MATTER'
  'CIFTI_STRUCTURE_AMYGDALA_LEFT'
  'CIFTI_STRUCTURE_AMYGDALA_RIGHT'
  'CIFTI_STRUCTURE_BRAIN_STEM'
  'CIFTI_STRUCTURE_CAUDATE_LEFT'
  'CIFTI_STRUCTURE_CAUDATE_RIGHT'
  'CIFTI_STRUCTURE_CEREBELLAR_WHITE_MATTER_LEFT'
  'CIFTI_STRUCTURE_CEREBELLAR_WHITE_MATTER_RIGHT'
  'CIFTI_STRUCTURE_CEREBELLUM_LEFT'
  'CIFTI_STRUCTURE_CEREBELLUM_RIGHT'
  'CIFTI_STRUCTURE_CEREBRAL_WHITE_MATTER_LEFT'
  'CIFTI_STRUCTURE_CEREBRAL_WHITE_MATTER_RIGHT'
  'CIFTI_STRUCTURE_CORTEX'
  'CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_LEFT'
  'CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_RIGHT'
  'CIFTI_STRUCTURE_HIPPOCAMPUS_LEFT'
  'CIFTI_STRUCTURE_HIPPOCAMPUS_RIGHT'
  'CIFTI_STRUCTURE_INVALID'
  'CIFTI_STRUCTURE_OTHER'
  'CIFTI_STRUCTURE_OTHER_GREY_MATTER'
  'CIFTI_STRUCTURE_OTHER_WHITE_MATTER'
  'CIFTI_STRUCTURE_PALLIDUM_LEFT'
  'CIFTI_STRUCTURE_PALLIDUM_RIGHT'
  'CIFTI_STRUCTURE_PUTAMEN_LEFT'
  'CIFTI_STRUCTURE_PUTAMEN_RIGHT'
  'CIFTI_STRUCTURE_THALAMUS_LEFT'
  'CIFTI_STRUCTURE_THALAMUS_RIGHT'
  };

list2 = {
  'CORTEX_LEFT'
  'CORTEX_RIGHT'
  'CEREBELLUM'
  'ACCUMBENS_LEFT'
  'ACCUMBENS_RIGHT'
  'ALL_GREY_MATTER'
  'ALL_WHITE_MATTER'
  'AMYGDALA_LEFT'
  'AMYGDALA_RIGHT'
  'BRAIN_STEM'
  'CAUDATE_LEFT'
  'CAUDATE_RIGHT'
  'CEREBELLAR_WHITE_MATTER_LEFT'
  'CEREBELLAR_WHITE_MATTER_RIGHT'
  'CEREBELLUM_LEFT'
  'CEREBELLUM_RIGHT'
  'CEREBRAL_WHITE_MATTER_LEFT'
  'CEREBRAL_WHITE_MATTER_RIGHT'
  'CORTEX'
  'DIENCEPHALON_VENTRAL_LEFT'
  'DIENCEPHALON_VENTRAL_RIGHT'
  'HIPPOCAMPUS_LEFT'
  'HIPPOCAMPUS_RIGHT'
  'INVALID'
  'OTHER'
  'OTHER_GREY_MATTER'
  'OTHER_WHITE_MATTER'
  'PALLIDUM_LEFT'
  'PALLIDUM_RIGHT'
  'PUTAMEN_LEFT'
  'PUTAMEN_RIGHT'
  'THALAMUS_LEFT'
  'THALAMUS_RIGHT'
  };

% replace the long name with the short name, i.e remove 'CIFTI_STRUCTURE_' where applicable
[dum, indx1, indx2] = intersect(brainordinate.brainstructurelabel, list1);
brainordinate.brainstructurelabel(indx1) = list2(indx2);

if ~isempty(Parcel)
  brainordinate.parcellation = zeros(size(pos,1),1);
  brainordinate.parcellationlabel = {};
  for i=1:numel(Parcel)
    brainordinate.parcellationlabel{i} = Parcel(i).Name;
    sel = find(strcmp(BrainModelParcelName, Parcel(i).Name));
    for j=1:numel(sel)
      brainordinate.parcellation(greynodeIndex{sel(j)}) = i; % FIXME should this be greynodeIndex or brainstructureIndex?
    end
  end
end

if readdata
  if isempty(Parcel)
    % the data is dense, make it consistent with the graynode positions
    dataIndex     = [dataIndex{:}];
    greynodeIndex = [greynodeIndex{:}];
    Ngreynodes    = size(pos,1);
  else
    % the data is defined on parcels
    Ngreynodes    = length(Parcel);
    dataIndex     = 1:Ngreynodes;
    greynodeIndex = 1:Ngreynodes;
  end

  switch source.dimord
    % the following representations are directly consistent with FieldTrip
    case {'pos' 'chan'}
      [m, n] = size(voxdata);
      if m>n
        dat = nan(Ngreynodes,n);
        dat(greynodeIndex(dataIndex),:) = voxdata;
      else
        dat = nan(Ngreynodes,m);
        dat(greynodeIndex(dataIndex),:) = transpose(voxdata);
      end
    case {'pos_time' 'chan_time'}
      Ntime = size(voxdata,2);
      dat = nan(Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),:) = voxdata;
    case {'pos_pos' 'chan_chan'}
      dat = nan(Ngreynodes,Ngreynodes);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex)) = voxdata;
    case {'pos_pos_time' 'chan_chan_time'}
      Ntime = size(voxdata,3);
      dat = nan(Ngreynodes,Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex),:) = voxdata;
    case {'pos_pos_freq' 'chan_chan_freq'}
      Ntime = size(voxdata,3);
      dat = nan(Ngreynodes,Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex),:) = voxdata;

      % the following representations need to be transposed to be consistent with FieldTrip
    case 'time_pos'
      Ntime = size(voxdata,1);
      dat = nan(Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),:) = transpose(voxdata);
      source.dimord = 'pos_time';
    case 'time_chan'
      Ntime = size(voxdata,1);
      dat = nan(Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),:) = transpose(voxdata);
      source.dimord = 'chan_time';
    case 'time_pos_pos'
      Ntime = size(voxdata,1);
      dat = nan(Ngreynodes,Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex),:) = permute(voxdata, [2 3 1]);
      source.dimord = 'pos_pos_time';
    case 'time_chan_chan'
      Ntime = size(voxdata,1);
      dat = nan(Ngreynodes,Ngreynodes,Ntime);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex),:) = permute(voxdata, [2 3 1]);
      source.dimord = 'chan_chan_time';
      case 'freq_pos'
      Nfreq = size(voxdata,1);
      dat = nan(Ngreynodes,Nfreq);
      dat(greynodeIndex(dataIndex),:) = transpose(voxdata);
      source.dimord = 'pos_freq';
    case 'freq_chan'
      Nfreq = size(voxdata,1);
      dat = nan(Ngreynodes,Nfreq);
      dat(greynodeIndex(dataIndex),:) = transpose(voxdata);
      source.dimord = 'chan_freq';
    case 'freq_pos_pos'
      Nfreq = size(voxdata,1);
      dat = nan(Ngreynodes,Ngreynodes,Nfreq);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex),:) = permute(voxdata, [2 3 1]);
      source.dimord = 'pos_pos_freq';
    case 'freq_chan_chan'
      Nfreq = size(voxdata,1);
      dat = nan(Ngreynodes,Ngreynodes,Nfreq);
      dat(greynodeIndex(dataIndex),greynodeIndex(dataIndex),:) = permute(voxdata, [2 3 1]);
      source.dimord = 'chan_chan_freq';

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

  if isfield(Cifti, 'mapname') && isfield(Cifti, 'labeltable') && strcmp(mapname, 'array')
    allthesame = true;
    for i=2:length(Cifti.labeltable)
      allthesame = allthesame && isequal(Cifti.labeltable{1}, Cifti.labeltable{i});
    end
    if allthesame
      ft_warning('using the same labels for all maps in the array');
      source.datalabel = Cifti.labeltable{1};
      Cifti = rmfield(Cifti, 'labeltable');
    else
      ft_error('multiple maps cannot be represented as array in the presence of different labeltables');
    end
  end

  if isfield(Cifti, 'mapname') && (length(Cifti.mapname)>1 || isfield(Cifti, 'labeltable'))
    switch mapname
      case 'field'
        % use distict names if there are multiple scalars or labels
        for i=1:length(Cifti.mapname)
          fieldname = Cifti.mapname{i};
          if isfield(Cifti, 'labeltable')
            if length(fieldname)>58
              % truncate it, needed to be able to append 'label' to the end
              fieldname = fieldname(1:58);
            end
            % append 'label' to the end
            source.([fieldname 'label']) = Cifti.labeltable{i}(:);
          end
          if isfield(Cifti, 'rgba')
            source.([fieldname 'rgba']) = Cifti.rgba{i};
          end
          source.(fieldname) = dat(:,i);
        end
      case 'array'
        source.mapname = {NamedMap.MapName}; % keep the original names, not the field names
        source.data    = dat;
        source.dimord  = [source.dimord '_mapname'];
      otherwise
        ft_error('incorrect specification of mapname "%s"', mapname);
    end % switch mapname
  else
    % the name of the data will be based on the filename
    source.data = dat;
  end
end % if readdata

source = copyfields(Cifti, source, {'time', 'freq'});
source.hdr = hdr;
source.unit = 'mm'; % per definition

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% try to get the geometrical information from the corresponding gifti files
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% use the filename prior to decompression
filename = origfile;

[p, f, x] = fileparts(filename);
t = tokenize(f, '.');

subject  = 'unknown';
dataname = 'unknown';
geomodel = '';

% the following assumes HCP/WorkBench/Caret file naming conventions
if length(t)==2
  subject  = t{1};
  dataname = t{2};
elseif length(t)==3
  subject  = t{1};
  dataname = t{2};
  content  = t{3};
elseif length(t)==4
  subject  = t{1};
  dataname = t{2};
  geomodel = t{3};
  content  = t{4};
elseif length(t)==5
  subject  = t{1};
  dataname = [t{2} '.' t{3}];
  geomodel = t{4};
  content  = t{5};
else
  ft_warning('cannot parse file name');
end

if readsurface
  % construct a list of possible file names for the surface geometry
  Lfilelist = {
    [subject '.L' '.midthickness'  '.' geomodel '.surf.gii']
    [subject '.L' '.pial'          '.' geomodel '.surf.gii']
    [subject '.L' '.white'         '.' geomodel '.surf.gii']
    [subject '.L' '.inflated'      '.' geomodel '.surf.gii']
    [subject '.L' '.very_inflated' '.' geomodel '.surf.gii']
    [subject '.L' '.sphere'        '.' geomodel '.surf.gii']
    [subject '.L' '.'              '.' geomodel '.surf.gii']
    [subject '.L' '.midthickness'               '.surf.gii']
    [subject '.L' '.pial'                       '.surf.gii']
    [subject '.L' '.white'                      '.surf.gii']
    [subject '.L' '.inflated'                   '.surf.gii']
    [subject '.L' '.very_inflated'              '.surf.gii']
    [subject '.L' '.sphere'                     '.surf.gii']
    [subject '.L'                               '.surf.gii']
    [subject '.CORTEX_LEFT'                     '.surf.gii']
    };

  Rfilelist = {
    [subject '.R' '.midthickness'  '.' geomodel  '.surf.gii']
    [subject '.R' '.pial'          '.' geomodel  '.surf.gii']
    [subject '.R' '.white'         '.' geomodel  '.surf.gii']
    [subject '.R' '.inflated'      '.' geomodel  '.surf.gii']
    [subject '.R' '.very_inflated' '.' geomodel  '.surf.gii']
    [subject '.R' '.sphere'        '.' geomodel  '.surf.gii']
    [subject '.R' '.'              '.' geomodel  '.surf.gii']
    [subject '.R' '.midthickness'                '.surf.gii']
    [subject '.R' '.pial'                        '.surf.gii']
    [subject '.R' '.white'                       '.surf.gii']
    [subject '.R' '.inflated'                    '.surf.gii']
    [subject '.R' '.very_inflated'               '.surf.gii']
    [subject '.R' '.sphere'                      '.surf.gii']
    [subject '.R'                                '.surf.gii']
    [subject '.CORTEX_RIGHT'                     '.surf.gii']
    };

  Bfilelist = {
    [subject '.midthickness'  '.' geomodel '.surf.gii']
    [subject '.pial'          '.' geomodel '.surf.gii']
    [subject '.white'         '.' geomodel '.surf.gii']
    [subject '.inflated'      '.' geomodel '.surf.gii']
    [subject '.very_inflated' '.' geomodel '.surf.gii']
    [subject '.sphere'        '.' geomodel '.surf.gii']
    [subject                  '.' geomodel '.surf.gii']
    [subject '.midthickness'               '.surf.gii']
    [subject '.pial'                       '.surf.gii']
    [subject '.white'                      '.surf.gii']
    [subject '.inflated'                   '.surf.gii']
    [subject '.very_inflated'              '.surf.gii']
    [subject '.sphere'                     '.surf.gii']
    [subject                               '.surf.gii']
    [subject '.CORTEX'                     '.surf.gii']
    };

  % assume that the surface files are in the same directory as the cifti file
  for i=1:numel(Lfilelist)
    Lfilelist{i} = fullfile(p, Lfilelist{i});
  end
  for i=1:numel(Rfilelist)
    Rfilelist{i} = fullfile(p, Rfilelist{i});
  end
  for i=1:numel(Bfilelist)
    Bfilelist{i} = fullfile(p, Bfilelist{i});
  end

  Lfilelist = cat(1, cortexleft, Lfilelist);
  Rfilelist = cat(1, cortexright, Rfilelist);

  tri = zeros(0,3);
  for i=1:length(Surface)

    switch Surface(i).BrainStructure
      case 'CIFTI_STRUCTURE_CORTEX_LEFT'
        for j=1:length(Lfilelist)
          if exist(Lfilelist{j}, 'file')
            fprintf('reading CORTEX_LEFT surface from %s\n', Lfilelist{j});
            mesh = ft_read_headshape(Lfilelist{j}, 'unit', 'mm'); % volume and surface should be in consistent units, gifti is defined in mm, wb_view also expects mm
            mesh.pos(:,1) = mesh.pos(:,1) - hemisphereoffset;
            pos(posIndex==i,:) = mesh.pos;
            tri = cat(1, tri, mesh.tri + find(posIndex==i, 1, 'first') - 1);
            break
          end
        end % for each Lfilelist

      case 'CIFTI_STRUCTURE_CORTEX_RIGHT'
        for j=1:length(Rfilelist)
          if exist(Rfilelist{j}, 'file')
            fprintf('reading CORTEX_RIGHT surface from %s\n', Rfilelist{j});
            mesh = ft_read_headshape(Rfilelist{j}, 'unit', 'mm'); % volume and surface should be in consistent units, gifti is defined in mm, wb_view also expects mm
            mesh.pos(:,1) = mesh.pos(:,1) + hemisphereoffset;
            pos(posIndex==i,:) = mesh.pos;
            tri = cat(1, tri, mesh.tri + find(posIndex==i, 1, 'first') - 1);
            break
          end
        end % for each Rfilelist

      otherwise
        for j=1:length(Bfilelist)
          if exist(Bfilelist{j}, 'file')
            fprintf('reading %s surface from %s\n', Surface(i).BrainStructure(17:end), Bfilelist{j});
            mesh = ft_read_headshape(Bfilelist{j}, 'unit', 'mm'); % volume and surface should be in consistent units, gifti is defined in mm, wb_view also expects mm
            pos(posIndex==i,:) = mesh.pos;
            tri = cat(1, tri, mesh.tri + find(posIndex==i, 1, 'first') - 1);
            break
          end
        end % for each Bfilelist

    end % switch BrainStructure
  end
else
  tri = [];
end % if readsurface

% add the vertex and voxel positions
brainordinate.pos = pos;
if ~isempty(tri)
  % add the surface triangulations
  brainordinate.tri = tri;
end

if ~isempty(Volume)
  brainordinate.dim        = Volume.VolumeDimensions;
  brainordinate.transform  = Volume.Transform;
end

if isempty(Parcel)
  % copy the geometrical description of the brainordinates into the main structure
  source = copyfields(brainordinate, source, fieldnames(brainordinate));
else
  % it is a parcellated source structure, i.e. represented by one channel per parcel
  % copy the geometrical description of the brainordinates into a sub-structure
  source.brainordinate = brainordinate;
  source.label = {Parcel(:).Name}';
end

% haslabeltable = false;
% if ~isempty(NamedMap)
%   % the following assumes a single NamedMap
%   if isfield(NamedMap, 'LabelTable')
%     % use the key-label combination in the label table
%     haslabeltable    = true;
%     key              = NamedMap.LabelTable.Key;
%     source.datalabel = NamedMap.LabelTable.Label(:);
%   end
% end

if readdata
  if isfield(source, 'data')
    % rename the data field
    source.(fixname(dataname)) = source.data;
    source = rmfield(source, 'data');
%     % adopt FT convention for parcel-to-label mapping
%     if haslabeltable
%       tempdata = nan+zeros(size(source.data));
%       for k = 1:numel(key)
%         tempdata(source.data==key(k)) = k;
%       end
%       source.data = tempdata;
%     end
%     source = rmfield(source, 'data');
  end

  % rename the datalabel field
  if isfield(source, 'datalabel')
    source.(fixname([dataname 'label'])) = source.datalabel;
    source = rmfield(source, 'datalabel');
  end

end % if readdata


function path = vl_setup(varargin)
% VL_SETUP Add VLFeat Toolbox to the path
%   PATH = VL_SETUP() adds the VLFeat Toolbox to MATLAB path and
%   returns the path PATH to the VLFeat package.
%
%   VL_SETUP('NOPREFIX') adds aliases to each function that do not
%   contain the VL_ prefix. For example, with this option it is
%   possible to use SIFT() instead of VL_SIFT().
%
%   VL_SETUP('TEST') or VL_SETUP('XTEST') adds VLFeat unit test
%   function suite. See also VL_TEST().
%
%   VL_SETUP('QUIET') does not print the greeting message.
%
%   See also: VL_ROOT(), VL_HELP().

% Authors: Andrea Vedaldi and Brian Fulkerson

% Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

noprefix = false ;
quiet = true ;
xtest = false ;
demo = false ;

for ai=1:length(varargin)
  opt = varargin{ai} ;
  switch lower(opt)
    case {'noprefix', 'usingvl'}
      noprefix = true ;
    case {'test', 'xtest'}
      xtest = true ;
    case {'demo'}
      demo = true ;
    case {'quiet'}
      quiet = true ;
    case {'verbose'}
      quiet = false ;
    otherwise
      error('Unknown option ''%s''.', opt) ;
  end
end

% Do not use vl_root() to avoid conflicts with other VLFeat
% installations.

[a,b,c] = fileparts(mfilename('fullpath')) ;
[a,b,c] = fileparts(a) ;
root = a ;

addpath(fullfile(root,'toolbox'             )) ;
addpath(fullfile(root,'toolbox','aib'       )) ;
addpath(fullfile(root,'toolbox','geometry'  )) ;
addpath(fullfile(root,'toolbox','imop'      )) ;
addpath(fullfile(root,'toolbox','kmeans'    )) ;
addpath(fullfile(root,'toolbox','misc'      )) ;
addpath(fullfile(root,'toolbox','mser'      )) ;
addpath(fullfile(root,'toolbox','plotop'    )) ;
addpath(fullfile(root,'toolbox','quickshift')) ;
addpath(fullfile(root,'toolbox','sift'      )) ;
addpath(fullfile(root,'toolbox','special'   )) ;
addpath(fullfile(root,'toolbox','slic'      )) ;
addpath(fullfile(root,'toolbox','gmm'       )) ;
addpath(fullfile(root,'toolbox','vlad'      )) ;
addpath(fullfile(root,'toolbox','fisher'    )) ;

if vl_isoctave()
  addpath(genpath(fullfile(root,'toolbox','mex','octave'))) ;
  warning('off', 'Octave:possible-matlab-short-circuit-operator') ;
  pkg load image ;
else
  bindir = mexext ;
  if strcmp(bindir, 'dll'), bindir = 'mexw32' ; end
  addpath(fullfile(root,'toolbox','mex',bindir)) ;
end

if noprefix
  addpath(fullfile(root,'toolbox','noprefix')) ;
end

if xtest
  addpath(fullfile(root,'toolbox','xtest')) ;
end

if demo
  addpath(fullfile(root,'toolbox','demo')) ;
end

if ~quiet
  if exist('vl_version') == 3
    fprintf('VLFeat %s ready.\n', vl_version) ;
  else
    warning('VLFeat does not seem to be installed correctly. Make sure that the MEX files are compiled.') ;
  end
end

if nargout == 0
  clear path ;
end


%% ODE_BATCH_LOCAL uses the BATCH command to run the ODE code locally.
%
%  Discussion:
%
%    The ODE code is a function, so first we must write a script
%    called ODE_SCRIPT that runs the function.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 May 2012
%
%  Author:
%
%    John Burkardt
%
  workers = 3;

  fprintf ( 1, '\n' );
  fprintf ( 1, 'ODE_BATCH_LOCAL:\n' );
  fprintf ( 1, '  Run ODE_SCRIPT locally.\n' );
%
%  BATCH defines the job and sets it for execution.
%
  my_job = batch ( 'ode_script', ...
    'CaptureDiary', true, ...
    'FileDependencies', { 'ode_fun', 'ode_system' }, ...
    'Configuration', 'local', ...
    'matlabpool', workers );
%
%  WAIT pauses the MATLAB session til the job completes.
%
  wait ( my_job );
%
%  DIARY displays any messages printed during execution.
%
  diary ( my_job );
%
%  LOAD makes the script's workspace available.
%
%  bVals = B values
%  kVals = K values
%  peakVals = peak values
%
  load ( my_job );
%
%  Display the results.
%
  ode_display ( bVals, kVals, peakVals );
%
%  DESTROY cleans up data about the job we no longer need.
%
  destroy ( my_job );

  fprintf ( 1, '\n' );
  fprintf ( 1, 'ODE_BATCH_LOCAL:\n' );
  fprintf ( 1, '  Normal end of execution.\n' );


%kextractor 'Interactive image Region of Interest (ROI) extraction'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros extractor.pane file
%
% Parameters:
% InputFile: i 'Input File', required: 'File containing image from which to extract ROI'
% OutputFile: o 'Output ROI ', optional: 'Output image in which to save ROI'
% MultiChoice: shape 'ROI Shape:', default: 1: 'Desired shape for ROI'
%    Choices are:
%   1: 'Rectangle'
%   2: 'Polyline'
%   3: 'Circle'
%   4: 'Ellipse'
%   5: 'Line'
%   6: 'Freehand'
% Integer: x 'X Placement', default: -1: 'X location for GUI autoplacement'
% Integer: y 'Y Placement', default: -1: 'Y location for GUI autoplacement'
% Double: update 'Update time', default: 1: 'How often to check input file for modification'
%
% Example: o = kextractor(i, {'i','';'o','';'shape',1;'x',-1;'y',-1;'update',1})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% extractor - Interactive image Region of Interest (ROI) extraction
%
%  DESCRIPTION
%
% The extractor program allows you to display an image, and interactively
% extract regions of interest (ROI's) from it.  The region of interest shapes
% supported include rectangle, line, polyline, signal, circle, curve, ellipse,
% and freehand.  The region of interest extracted may be defined by the region
% inside the shape, by the region outside the shape, or by the outline of the
% shape.  When a region of interest is extracted, it may be written to an output
% file, displayed in a separate window, or both.
%
% When running extractor, you must provide an input image.  If desired, you may
% specify the shape to be used in ROI extraction using the [-shape] argument.
% The [-disp] argument may be used to specify whether the extracted ROI is
% automatically displayed in a separate window.  The [-o] argument may be used
% to specify an output file.  Note that the extracted ROI may be displayed in
% a separate window, written to an output file, or both.  The ROI Extraction
% Policy dictates whether the region of interest is to be specified by the
% area inside the shape, by the area outside the shape, or by the outline of the
% shape; this can be specified using the [-policy] argument.
%
% For all ROI extraction, you will first indicate the desired region of interest
% by drawing the desired shape in the input image.  For all ROI shapes except
% signal (for which it does not apply) a mask is employed to obscure the pixels
% which are not inside the region of interest before the ROI is written to disk
% or displayed.  How the mask is applied depends on the ROI Extraction Policy.  ROI's may be
% defined by the area inside the shape drawn, the area outside the shape drawn,
% or by the outline of the shape drawn.
%
% Once the input image is displayed, the desired ROI shape is specified, the
% desired ROI Extraction Policy is given, and an optional output filename is
% entered (all of which can be done using the arguments to extractor) you
% may immediately begin the ROI extraction process. The procedure used to
% specify the region of interest in the input image will differ depending on
% the ROI shape being used. The following is a summary of the available ROI
% shapes and the procedure used for the extraction of each.
%
%
%  "Rectangle"
% The region of interest will be defined by a simple rectangle. For an
% ROI Extraction policy of "Inside Shape", the subimage defined by the rectangle
% is extracted.  For an ROI Extraction policy of "Shape Outline", the bounding
% box of the rectangle is calculated, and all pixel values inside the bounding
% box but not on the outline of the rectangle are masked out.  For an ROI
% Extraction policy of "Outside Shape", the pixel values within the rectangle
% are masked out, while the remainder of the image is preserved.
%
%
% To extract a rectangular region of interest, rubberband the rectangle
% surrounding the region of interest in the input image.  To rubberband the
% rectangle, click the mouse button on a corner of the desired region of interest.
% Holding the mouse button down, move the mouse to the opposite corner of the
% desired rectangle.  The rectangle will refreshed as the mouse moves, or
% "rubberbanded" after the mouse.
%
%
%  "Line"
% The region of interest will be defined by a line.  For ROI Extraction Policies
% of "Inside Shape" and "Shape Outline", the bounding box is calculated from the
% endpoints of the line and all pixel values within the bounding box but not on
% the line are masked out.  For an ROI Extraction Policy of "Outside Shape",
% all pixels on the line itself are masked out, while the rest of the image is
% preserved.
%
%
% To extract a line region of interest, rubberband the line defining
% the region of interest in the input image.  To rubberband the line, click the
% mouse button on one endpoint of the desired region of interest.  Holding the
% mouse button down, move the mouse to the other endpoint of the line.
%
%
%  "Signal"
% The region of interest will be defined by a line; however, rather than
% computing the bounding box of the line and masking out all unwanted pixels,
% the pixel values on the line are written out "alone" to the output image.
% Thus, the extraction of signal ROI's is a useful way of creating VIFF images
% containing single lines of image data that can be used in a 2D plot.  The
% ROI Extraction Policy does not apply to the signal ROI.
%
%
% Extraction of a signal is identical to extraction of a line (see above).
%
%
%  "Polyline"
% The region of interest will be defined by a polyline. For ROI Extraction
% Policy of "Inside Shape", the bounding box is calculated from the vertices
% of the polyline and all pixel values within the bounding box but not inside
% the polygon are masked out. For an ROI Extraction policy of "Shape Outline",
% the bounding box is calculated from the vertices of the polyline and all pixel
% values not on the polyline itself are masked out.  For ROI Extraction policy of
% "Outside Shape", all pixels within the polyline are masked out, while the
% rest of the image is preserved.
%
%
% To extract a polyline region of interest, begin by clicking the mouse on
% one vertex of the desired polyline. As you move the mouse, a line will be
% rubberbanded after it.  Continue to button click on each successive vertex,
% until the polyline appears as desired; double click to end the polyline.
% The first and last vertices of the polyline will be joined in order to define
% a closed region of interest.
%
%
%  "Circle"
% The region of interest will be defined by a circle. For ROI Extraction
% Policy of "Inside Shape", the bounding box is calculated from the dimensions
% of the circle and all pixel values within the bounding box but not inside
% the circle are masked out. For an ROI Extraction policy of "Shape Outline",
% the bounding box is calculated from the vertices of the polyline and all pixel
% values not on the circle itself are masked out.  For ROI Extraction policy of
% "Outside Shape", all pixels within the circle are masked out, while the
% rest of the image is preserved.
%
%
% To extract a circular region of interest, rubberband the circle defining
% the region of interest in the input image.  To rubberband the circle, click
% the mouse button on the center of the desired region of interest.  Holding
% the mouse button down, move the mouse until the circle has the desired radius.
%
%
%  "Ellipse"
% The ellipse region of interest works similarly to that of a circle (see above).
% To extract an ellipsoid region of interest, rubberband the ellipse defining
% the region of interest in the input image.  Rubberbanding an ellipse is similar
% to rubberbanding a circle, except that the width and height of the ellipse will
% be changed with horizontal and vertical mouse movement.
%
%
%
%
%
%  EXAMPLES
% % extractor -i image:mandril
%
%  "SEE ALSO"
% editimage(1)
%
%  RESTRICTIONS
%
%  REFERENCES
% The extractor program is covered in detail in Chapter 5 of the Envision Manual.
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kextractor(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kextractor(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'o', '__output';'shape', 1;'x', -1;'y', -1;'update', 1};
maxval={0,1,0,1000,1000,1};
minval={0,1,0,-1,-1,1};
istoggle=[0,1,0,1,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','OutputFile','MultiChoice','Integer','Integer','Double'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=0; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'extractor"  '],Inputs,narglist);


function test_bug168

% MEM 2gb
% WALLTIME 00:10:00
% DEPENDENCY ft_realtime_topography

[ftver, ftpath] = ft_version;
cd(ftpath);

% ensure that these compat folders are presaent on the path
% although they should not be used
ft_hastoolbox('COMPAT', 2);
ft_hastoolbox('UTILITIES/COMPAT', 2);
ft_hastoolbox('FILEIO/COMPAT', 2);
ft_hastoolbox('PREPROC/COMPAT', 2);
ft_hastoolbox('FORWARD/COMPAT', 2);
ft_hastoolbox('PLOTTING/COMPAT', 2);

% this list was compiled on 1 May 2012 from the fieldtrip/realtime directory
% functions that will be added future don't have to be tested persee
inlist = {
  'acquisition/matlab/ft_realtime_asaproxy.m'
  'acquisition/matlab/ft_realtime_brainampproxy.m'
  'acquisition/matlab/ft_realtime_ctfproxy.m'
  'acquisition/matlab/ft_realtime_dicomproxy.m'
  'acquisition/matlab/ft_realtime_fileproxy.m'
  'acquisition/matlab/ft_realtime_fmriproxy.m'
  'acquisition/matlab/ft_realtime_neuralynxproxy.m'
  'acquisition/matlab/ft_realtime_pooraudioproxy.m'
  'acquisition/matlab/ft_realtime_signalproxy.m'
  'acquisition/matlab/private/encode_nifti1.m'
  'acquisition/matlab/private/tcpread.m'
  'acquisition/siemens/compile.m'
  'acquisition/siemens/matlab/replay_dicoms.m'
  'buffer/matlab/buffer.m'
  'buffer/matlab/compile.m'
  'example/ft_realtime_average.m'
  'example/ft_realtime_benchmark.m'
  'example/ft_realtime_classification.m'
  'example/ft_realtime_downsample.m'
  'example/ft_realtime_fmriviewer.m'
  'example/ft_realtime_heartratemonitor.m'
  'example/ft_realtime_packettimer.m'
  'example/ft_realtime_powerestimate.m'
  'example/ft_realtime_selectiveaverage.m'
  'example/ft_realtime_signalviewer.m'
  'example/ft_realtime_topography.m'
  'online_eeg/ft_realtime_oddball.m'
  'online_eeg/ft_realtime_ouunpod.m'
  'online_eeg/private/compile_midiOut.m'
  'online_eeg/private/controlfunction.m'
  'online_eeg/private/midiOut.m'
  'online_meg/ft_realtime_coillocalizer.m'
  'online_meg/ft_realtime_headlocalizer.m'
  'online_mri/ft_omri_align_init.m'
  'online_mri/ft_omri_align_scan.m'
  'online_mri/ft_omri_info_from_header.m'
  'online_mri/ft_omri_pipeline.m'
  'online_mri/ft_omri_pipeline_nuisance.m'
  'online_mri/ft_omri_quality.m'
  'online_mri/ft_omri_quality_plot.m'
  'online_mri/ft_omri_slice_time_apply.m'
  'online_mri/ft_omri_slice_time_init.m'
  'online_mri/ft_omri_smoothing_kernel.m'
  'online_mri/ft_omri_volume_to_mosaic.m'
  'online_mri/private/coords.m'
  'online_mri/private/encode_nifti1.m'
  'online_mri/private/hom2six.m'
  'online_mri/private/kspace3d.m'
  'online_mri/private/make_A.m'
  'online_mri/private/reslice_vol.m'
  'online_mri/private/rls_init.m'
  'online_mri/private/rls_predict.m'
  'online_mri/private/rls_update.m'
  'online_mri/private/shear_decomp.m'
  'online_mri/private/smooth_vol.m'
  'tutorial/bcifun_latidx.m'
  'tutorial/ft_realtime_asynchronous.m'
  'tutorial/ft_realtime_synchronous.m'
  };

[outlist, depmat] = mydepfun(inlist);

problem = ~cellfun(@isempty, regexp(outlist, 'compat/obsolete'));
problem = outlist(problem)  % display the output;

if ~isempty(problem)
  error('there are some files that depend on the compat functions');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% this was the version of the test script prior to 1 May 2012
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% if isempty(which('ft_realtime_topography'))
%   addpath(fullfile(fileparts(which('ft_defaults')), 'realtime/example'));
% end
%
% cfg = [];
% cfg.dataset = dccnpath('/home/common/matlab/fieldtrip/data/ftp/test/ctf/Subject01.ds');
% cfg.bufferdata = 'first';
% cfg.layout = 'CTF151.lay';
%
% ft_realtime_topography(cfg);


function mia_Ypixval(arg1, arg2)
% mia_Ypixval display information about image pixels in coronal image
% in the case of 3Dcursor mode
%
% Matlab library function for mia_gui utility.
% University of Debrecen, PET Center/LB 2003

%   PIXVAL ON turns on interactive display of information about image pixels
%   in the current figure. PIXVAL installs a black bar at the bottom of the
%   figure which displays the (x,y) coordinates for whatever pixel the
%   cursor is currently over, and the color information for that pixel. If
%   the image is binary or intensity, the color information is a single
%   intensity value. If the image is indexed or RGB, the color information
%   is an RGB triplet. The values displayed are the actual data values,
%   regardless of the class of the image array, or whether the data is in
%   normal image range.
%
%   If you click on the image and hold down the mouse button while you move
%   the cursor, PIXVAL also displays the Euclidean distance between the
%   point you clicked on and current cursor location. PIXVAL draws a line
%   between these points to indicate the distance being measured. When you
%   release the mouse button, the line and the distance display disappear.
%
%   You can move the display bar by clicking on it and dragging it to
%   another place in the figure.
%
%   PIXVAL OFF turns interactive display off in the current figure. You can
%   also turn off the display by clicking the button on the right side of
%   the display bar.
%
%   PIXVAL toggles interactive display on or off in the current figure.
%
%   PIXVAL(FIG,OPTION) applies the PIXVAL command to the figure specified
%   by FIG. OPTION is string containing 'on' or 'off'.
%
%   See also IMPIXEL, IMPROFILE.

%   Copyright 1993-2001 The MathWorks, Inc.
%   $Revision: 1.19 $  $Date: 2001/01/18 15:30:19 $

error(nargchk(0,2,nargin));

if nargin==0
   arg1 = gcf;
end

if ischar(arg1) % execute a callback
   switch lower(arg1)
   case 'pixvalmotionfcn'
      PixvalMotionFcn;
   case 'buttondownonimage'
      ButtonDownOnImage;
   case 'backtonormalpixvaldisplay'
      BackToNormalPixvalDisplay;
   case 'movedisplaybar'
      MoveDisplayBar;
   case 'on'
      mia_Ypixval(gcf, 'on');
   case 'off'
      mia_Ypixval(gcf, 'off');
   case 'deletedisplaybar'
      DeleteDisplayBar;
   otherwise
      error(['Unknown PIXVAL command or callback: ' arg1 '.']);
   end

elseif ishandle(arg1)
   images = findobj(arg1, 'type', 'image');
   if isempty(images)
      return
   end

   figureHandle = get(get(images(1), 'parent'), 'parent');

   if nargin>1   % mia_Ypixval(handle, 'on|off')
      if ischar(arg2)
         if strcmp(lower(arg2), 'off')
            action = 'destroy';
         elseif strcmp(lower(arg2), 'on')
            action = 'create';
         else
            error(['Unknown PIXVAL input string: ' arg2]);
         end
      else
         error('Invalid input arguments');
      end
   else  % mia_Ypixval(handle) or mia_Ypixval
      % Toggle the state
      displayBar = findobj(figureHandle, 'tag', 'pixel value display bar');
      if isempty(displayBar)
         action = 'create';
      else
         action = 'destroy';
      end
   end

   % Put things in the Images' UserData so that pixel value display works
   switch action
   case 'destroy'
      displayBar = findobj(figureHandle, 'tag', 'pixel value display bar');
      if ~isempty(displayBar)
         DeleteDisplayBar(displayBar);
      end

   case 'create'
      otherDisplayBar = findobj(figureHandle, 'tag', 'pixel value display bar');
      if ~isempty(otherDisplayBar)
         % mia_Ypixval is already ON for this figure.
         return
      end

      % Save the interactive state of the figure. UICLEARMODE now
      % does everything that UISUSPEND used to do. It calls
      % SCRIBECLEARMODE to take care of SCRIBE.  We WILL be
      % restoring the state - this is a change.  UICLEARMODE also
      % registers a way to disable mia_Ypixval
      dbud.oldFigureUIState = uiclearmode(figureHandle,'mia_Ypixval',figureHandle,'off');
      AxisHandle = findobj(figureHandle, 'type', 'axes');
      dbud.xLim = get(AxisHandle,'XLim');% for zoom mode
      dbud.yLim = get(AxisHandle,'YLim');% for zoom mode
      dbud.old_pt = 0;% for zoom mode

      % Make sure the SCRIBE toolbar doesn't go away
      plotedit(figureHandle,'locktoolbarvisibility');

      % Make sure that the Motion function doesn't interrupt itself
      set(figureHandle, 'Interruptible', 'off', 'busyaction', 'queue');

      % Make sure that the Image's Button Down & Up functions will queue
      set(images, 'Interruptible', 'off', 'BusyAction', 'Queue');

      % dbud is the UserData structure which is going into the display bar
      dbud.figureHandle = figureHandle;
      dbud.displayMode = 'normal';

      figPos = get(dbud.figureHandle, 'position');
      tempStr = sprintf(' %g, %g = %6.4f,%6.4f,%6.4f  dist = %3.3f', ...
        -0.9999,-0.9999,pi/10,pi/10,pi/10,pi*100);

      % Calculate the position vector of the display bar
      tempTxt = uicontrol('Parent', dbud.figureHandle, 'horiz', 'left', ...
         'Style', 'text', 'Units', 'pixels', 'visible', 'off', 'fontname', ...
         'FixedWidth','String', tempStr, 'position', [0 0 figPos(3) 20]);
      extent= get(tempTxt, 'extent');
      txtHt = extent(4);
      txtWid = extent(3)+txtHt; % Leave room for the close button
      delete(tempTxt);

      % Create position vectors for Text, Frame, and Close Button
      pos = [0 0 (min(txtWid,figPos(3))-txtHt)*1/2 txtHt];
      btnSide = pos(4)-4;  % In pixels
      btnPos = [pos(1)+pos(3)+2 pos(2)+2 btnSide btnSide];
      frmPos = [pos(1)+pos(3) 0 txtHt txtHt];

      % Create a frame for the display bar. I do this because of a bug in
      % MATLAB 5.2 where the text field pops to the front of the button each
      % time it is updated.  It should stay behind in which case, all we would
      % need is the text uicontrol and the button.  We can take the frame out
      % if the bug gets fixed.  Make sure the frame doesn't fall behind the
      % text control, otherwise you can't execute the text control's buttondown
      % function (another MATLAB 5.2 bug).

      dbud.DisplayFrame = uicontrol('Parent', figureHandle, ...
         'style', 'frame', ...
         'Units', 'pixels', ...
         'Position', frmPos, ...
         'Background', [0 0 0], ...
         'BusyAction', 'queue', ...
         'Interruptible', 'off');

      % Create the display bar
      dbud.buttonDownImage = 0;  % Image 0 never exists
      DisplayBar = uicontrol('Parent', figureHandle, ...
         'Style','text', ...
         'Units','pixels', ...
         'Position',pos, ...
         'Foreground', [1 1 .5], ...
         'Background', [0 0 0], ...
         'Horiz','left', ...
         'Tag', 'pixel value display bar', ...
         'String','', ...
         'fontname', 'FixedWidth', ...
         'BusyAction', 'queue', ...
         'enable', 'inactive', ...
         'Interruptible', 'off');
%          'ButtonDownFcn', 'mia_Ypixval(''MoveDisplayBar'')', ...
%          'DeleteFcn', 'mia_Ypixval(''DeleteDisplayBar'')', ...

      % Create the close button
      dbud.CloseButton = uicontrol('Parent', figureHandle, ...
         'style', 'pushbutton', ...
         'units', 'pixels', ...
         'position', btnPos, ...
         'BusyAction', 'queue', ...
         'Interruptible', 'off');
%          'String', 'X', ...
%          'Callback', 'mia_Ypixval(''DeleteDisplayBar'')', ...
%          'DeleteFcn', 'mia_Ypixval(''DeleteDisplayBar'')', ...

      % Register the motion function and button up function
      set(figureHandle, 'WindowButtonMotionFcn',...
         'mia_Ypixval(''PixvalMotionFcn'')')

      set(DisplayBar, 'UserData', dbud);
      PixvalMotionFcn(DisplayBar);
   end

else
   error('Invalid input argument.')
end

%%%
%%%  Sub-function - PixvalMotionFcn
%%%

function PixvalMotionFcn(displayBar)

if nargin < 1
   displayBar = findobj(gcbf, 'Tag', 'pixel value display bar');
end

if isempty(displayBar)
   % Pixval is in a half-broken state.  Another function (like zoom) has
   % restored pixval callbacks and PIXVAL has already been uninstalled.
   % Call uisuspend to gracefully get rid of the callbacks.

   % Note 7/21/98 - Since we are now using UICLEARMODE instead of
   % UISUSPEND, I think that we should never get into this
   % state.  It will only happen if a user writes a function
   % which calls UIRESTORE without ever calling UICLEARMDOE or
   % SCRIBECLEARMODE.  We'll leave this code here just to be safe.
   uisuspend(gcbf);
   return
end

dbud = get(displayBar, 'UserData');
pt = get(dbud.figureHandle, 'CurrentPoint');
x = pt(1,1); y = pt(1,2);

if strcmp(dbud.displayMode, 'normal')
   % See if we're above the displayBar
   dbpos = get(displayBar, 'Position');
   left   = dbpos(1);
   bottom = dbpos(2);
   width  = dbpos(3);
   height = dbpos(4);
   % On the line below, we look to see if x<=left+width+height because
   % there is a frame next to the text object, see note above about the
   % MATLAB 5.2 bug we're working around
   if x>=left & x<=left+width+height & y>=bottom & y<=bottom+height
      % We're hovering above the display bar

      % Look to see if we are above the Close button
      cp = get(dbud.CloseButton, 'Position');
      if  x>=cp(1) & x<=cp(1)+cp(3) & y>=cp(2) & y<=cp(2)+cp(4)
         if isfield(dbud, 'oldPointer') % Switch back to the default pointer
            set(dbud.figureHandle, 'Pointer', dbud.oldPointer, ...
               'PointerShapeCData', dbud.oldPointerShapeCData, ...
               'PointerShapeHotSpot', dbud.oldPointerShapeHotSpot);
            dbud = rmfield(dbud, {'oldPointer', ...
                  'oldPointerShapeCData', 'oldPointerShapeHotSpot'});
         end
      else
         % Save the default pointer if there is one
         if ~isfield(dbud, 'oldPointer') % We still have the default pointer
            dbud.oldPointer = get(dbud.figureHandle, 'Pointer');
            dbud.oldPointerShapeCData = get(dbud.figureHandle, 'PointerShapeCData');
            dbud.oldPointerShapeHotSpot = get(dbud.figureHandle, 'PointerShapeHotSpot');
         end
         setptr(dbud.figureHandle, 'hand');
      end
   else
      % We're not hovering over the display bar
      [imageHandle,imageType,img,x,y] = OverImage(dbud.figureHandle);
      if imageHandle ~= 0
         % Save old pointer and get a new one (use Custom pointer for non-Windows machine)
         if ~isfield(dbud, 'oldPointer') % We still have the default pointer
            dbud.oldPointer = get(dbud.figureHandle, 'Pointer');
            dbud.oldPointerShapeCData = get(dbud.figureHandle, 'PointerShapeCData');
            dbud.oldPointerShapeHotSpot = get(dbud.figureHandle, 'PointerShapeHotSpot');
         end
         if strcmp(computer, 'PCWIN')
            set(dbud.figureHandle, 'Pointer', 'cross');
         else
            [pointerShape, pointerHotSpot] = CreatePointer;
            set(dbud.figureHandle, 'Pointer', 'custom', ...
               'PointerShapeCData', pointerShape, ...
               'PointerShapeHotSpot', pointerHotSpot);
         end

         % Update the Pixel Value display
         UpdatePixelValues(imageHandle, imageType, displayBar,img,x,y );
      else
         if isfield(dbud, 'oldPointer') % Switch back to the default pointer
            set(dbud.figureHandle, 'Pointer', dbud.oldPointer, ...
               'PointerShapeCData', dbud.oldPointerShapeCData, ...
               'PointerShapeHotSpot', dbud.oldPointerShapeHotSpot);
            dbud = rmfield(dbud, {'oldPointer', ...
                  'oldPointerShapeCData', 'oldPointerShapeHotSpot'});
            set(displayBar, 'String', '');
         end
      end
   end
elseif strcmp(dbud.displayMode, 'distance')
   % If we're in distance mode and in another image, clean up a bit.
   [imageHandle,imageType,img,x,y]= OverImage(dbud.figureHandle);
   if imageHandle~=0
      if imageHandle==dbud.buttonDownImage
         UpdatePixelValues(imageHandle, imageType, displayBar,img,x,y);
      end
   end
end
set(displayBar, 'UserData', dbud);

%%%
%%%  Sub-function - OverImage
%%%

function [imageHandle,imageType,img,x,y] = OverImage(figHandle)
% Return the index of which image we are over, and return a 0 if we
% aren't above an image.

images = findobj(figHandle, 'type', 'image');
if isempty(images)
   imageHandle=0; imageType=''; img=[]; x=0; y=0;
   return
end
% Make sure that the Image's Button Down & Up functions will queue
set(images, 'ButtonDownFcn', 'mia_Ypixval(''ButtonDownOnImage'')', ...
   'Interruptible', 'off', 'BusyAction', 'Queue');
axHandles = get(images, {'Parent'});
axPositions = get([axHandles{:}], {'Position'});
axCurPt = get([axHandles{:}], {'CurrentPoint'});

% Loop over the axes, see if we are above any of them
imageHandle = 0;
for k=1:length(axHandles)
   XLim = get(axHandles{k}, 'XLim');
   YLim = get(axHandles{k}, 'YLim');
   pt = axCurPt{k};
   x = pt(1,1); y = pt(1,2);
   if x>=XLim(1) & x<=XLim(2) & y>=YLim(1) & y<=YLim(2)
      imageHandle = images(k);
      break;
   end
end

% Figure out image type
if imageHandle ~= 0
   [img,flag] = getimage(imageHandle);

   switch flag
   case 1
      imageType = 'indexed';
   case {2,3}  % Grayscale or binary
      imageType = 'intensity';
   case 4
      imageType = 'rgb';
   otherwise
      error(['Invalid image, GETIMAGE returned flag = ' flag '.']);
   end
else
   imageHandle=0; imageType=''; img=[]; x=0; y=0;
end

%%%
%%%  Sub-function - UpdatePixelValues
%%%

function UpdatePixelValues(imageHandle, imageType, displayBar, img, x, y)
%   This is the motion callback for when we are displaying
%   pixels.  Either we are in automatic display mode and the
%   mouse pointer is moving around or we are in normal mode
%   and there has been a button-down but not yet a button up.
%   I get the current point and update the string.

global old_pt_zoom_y x0_pan_y;%temp for zoom and pan

dbud = get(displayBar, 'UserData');
[rows, cols, colors] = size(img);
y = min(y,rows);% y > rows happens in some case /LB
rp = axes2pix(rows, get(imageHandle,'YData'),y);
cp = axes2pix(cols, get(imageHandle,'XData'),x);
r = min(rows, max(1, round(rp)));
c = min(cols, max(1, round(cp)));

if strcmp(imageType,'indexed')
   map=get(dbud.figureHandle, 'Colormap');
   idx = img(r,c);
   if ~isa(idx, 'double'),
      idx = double(idx)+1;
   end
   idx = round(idx);
   if idx<=size(map,1)
      pixel = map(idx,:);
   else
      pixel = map(end,:);
   end
else
   pixel = double(img(r,c,:));
end

% figure out the new string
figureHandle = get(get(imageHandle, 'parent'), 'parent');
switch dbud.displayMode
case 'normal'   % Just display intensity information
	FigDataIn = get(figureHandle,'Userdata');
    ImVolume = get(FigDataIn.ImaHandlerX,'UserData');
    %pixel(1) = ImVolume.PixInt;

	if isa(img, 'uint8')
     pixval_str = sprintf(' %g,%g,%g = %3d',round(y),round(ImVolume.Y), round(x),pixel(1));
	elseif isa(img, 'int16')
     pixval_str = sprintf(' %g,%g,%g = %5d',round(y),round(ImVolume.Y), round(x), pixel(1));
	else
     pixval_str = sprintf(' %g,%g,%g = %5.1f',round(y),round(ImVolume.Y), round(x),pixel(1));
	end
    set(displayBar, 'String', pixval_str, 'UserData', dbud);
case 'distance'
    mouseclick = get(gcf,'SelectionType');
    if strcmp(mouseclick,'normal')
		FigDataIn = get(figureHandle,'Userdata');
		fighandlerZ = FigDataIn.ImaHandlerZ;
		fighandlerX = FigDataIn.ImaHandlerX;
		FigData = FigDataIn.CData;

        set(fighandlerZ,'CData',squeeze(FigData(:,:,size(FigData,3) - round(rp) + 1)) );
        set(fighandlerX,'CData',rot90(squeeze(FigData(:,round(cp),:))));

        ImVolume = get(FigDataIn.ImaHandlerX,'UserData');
        ImVolume.Z = y;
        ImVolume.X = x;
        ImVolume.PixInt = pixel(1);
        set(FigDataIn.ImaHandlerX,'UserData',ImVolume);

		if isa(img, 'uint8')
         pixval_str = sprintf(' %g,%g,%g = %3d',round(y),round(ImVolume.Y), round(x),pixel(1));
		elseif isa(img, 'int16')
         pixval_str = sprintf(' %g,%g,%g = %5d', round(y),round(ImVolume.Y), round(x), pixel(1));
		else
         pixval_str = sprintf(' %g,%g,%g = %5.1f',round(y),round(ImVolume.Y), round(x),pixel(1));
		end
        set(displayBar, 'String', pixval_str, 'UserData', dbud);
    elseif strcmp(mouseclick,'extend') % zoom mode
        CurrentAxes = findobj(figureHandle, 'type', 'axes');
        %set(CurrentAxes,'Units','pixel');
        new_pt = get(0,'PointerLocation');
        dy = (new_pt(2) - old_pt_zoom_y(2))/abs(old_pt_zoom_y(2))*5;
        zoomFactor = (1-dy);
        xLim=get(CurrentAxes,'XLim');
        yLim=get(CurrentAxes,'YLim');
     	xLimNew = [0 zoomFactor*diff(xLim)] + xLim(1) + (1-zoomFactor)*diff(xLim)/2;
        yLimNew = [0 zoomFactor*diff(yLim)] + yLim(1) + (1-zoomFactor)*diff(yLim)/2;
		set(CurrentAxes,'XLim',xLimNew,'YLim',yLimNew);
        old_pt_zoom_y  = new_pt;
    elseif strcmp(mouseclick,'alt') % pan mode
        currentPoint = get(figureHandle,'CurrentPoint');
        CurrentAxes = findobj(figureHandle, 'type', 'axes');
        set(CurrentAxes,'Units','pixel');
        dx = currentPoint - x0_pan_y;
        x0_pan_y = currentPoint;
        ap = get(CurrentAxes,'Position');
        xLim = get(CurrentAxes,'XLim');
        yLim = get(CurrentAxes,'YLim');
        set(CurrentAxes,'XLim',xLim-(diff(xLim)*dx(1)/ap(3)), ...
           'YLim',yLim+(diff(yLim)*dx(2)/ap(4)));
       set(CurrentAxes,'Units','normalized');
    elseif strcmp(mouseclick,'open')% restore the original image
        xLimNew = dbud.xLim;
        yLimNew = dbud.yLim;
        CurrentAxes = findobj(gcf, 'type', 'axes');
        set(CurrentAxes,'XLim',xLimNew,'YLim',yLimNew);
    end
end

%%%
%%%  Sub-function - ButtonDownOnImage
%%%

function ButtonDownOnImage

global old_pt_zoom_y x0_pan_y;%temp for zoom

%imageHandle = gcbo;
currentHandle = gcbo;
if strcmp(get(currentHandle,'type'),'image')
    imageHandle = currentHandle;
else
    currentParentHandle = get(currentHandle,'Parent');
    currentChildrensHandles = get(currentParentHandle,'children');
    imageHandle = currentChildrensHandles(3);
end
figureHandle = get(get(imageHandle,'Parent'),'Parent');
displayBar = findobj(figureHandle, 'Tag', 'pixel value display bar');
dbud = get(displayBar, 'UserData');
axesHandle = get(imageHandle, 'Parent');

mouseclick = get(gcf,'SelectionType');
if strcmp(mouseclick,'normal')% case of distance measuring
	%
elseif strcmp(mouseclick,'alt') % pan mode
    setptr(figureHandle,'closedhand');
elseif strcmp(mouseclick,'extend') % zoom mode
    setptr(figureHandle,'glass');
end

%
% Turn on the PlotEdit options on X,Y slice images
%
FigDataIn = get(figureHandle,'Userdata');

%delete the positioning line
ImVolume = get(FigDataIn.ImaHandlerX,'UserData');
if ishandle(ImVolume.Xxline)
    delete(ImVolume.Xxline);
    delete(ImVolume.Xyline);
    delete(ImVolume.Yxline);
    delete(ImVolume.Yyline);
    delete(ImVolume.Zxline);
    delete(ImVolume.Zyline);
end
set(FigDataIn.ImaHandlerX,'UserData',ImVolume);

% Set the initial point (x0,y0)

%pt = get(axesHandle, 'CurrentPoint');
%dbud.x0 = pt(1,1);
%dbud.y0 = pt(1,2);
%dbud.line = line('Parent', axesHandle, ...
%  'erasemode', 'xor', ...
%   'color', [1 0 0], ...
%   'Xdata', [dbud.x0 dbud.x0], ...
%   'Ydata', [dbud.y0 dbud.y0]);
dbud.displayMode = 'distance';
old_pt_zoom_y = get(0,'PointerLocation');% for zoom mode
x0_pan_y = get(figureHandle,'CurrentPoint');% for pan mood
dbud.buttonDownImage = imageHandle;
set(displayBar, 'UserData', dbud);
set(dbud.figureHandle, 'WindowButtonUpFcn', 'mia_Ypixval(''BackToNormalPixvalDisplay'')');
PixvalMotionFcn(displayBar);

%%%
%%%  Sub-function - BackToNormalPixvalDisplay
%%%

function BackToNormalPixvalDisplay

displayBar = findobj(gcbo, 'Tag', 'pixel value display bar');
dbud = get(displayBar, 'UserData');
imageHandle = dbud.buttonDownImage;
%
% Turn off the PlotEdit options on X,Y slice images and
% write the last X,Y,Z coordinates to the FigDataIn struct
%
[imageHandleTmp,imageType,img,x,y]= OverImage(dbud.figureHandle);
figureHandle = get(get(imageHandle,'Parent'),'Parent');
FigDataIn = get(figureHandle,'Userdata');
axesHandleX = get(FigDataIn.ImaHandlerX, 'Parent');
axesHandleY = get(FigDataIn.ImaHandlerY, 'Parent');
axesHandleZ = get(FigDataIn.ImaHandlerZ, 'Parent');

%draw the positioning line
Xyrange = get(axesHandleX,'Ylim');
Xxrange = get(axesHandleX,'Xlim');
Yyrange = get(axesHandleY,'Ylim');
Yxrange = get(axesHandleY,'Xlim');
Zyrange = get(axesHandleZ,'Ylim');
Zxrange = get(axesHandleZ,'Xlim');

ImVolume = get(FigDataIn.ImaHandlerX,'UserData');
if ishandle(ImVolume.Xxline)
    delete(ImVolume.Xxline);
    delete(ImVolume.Xyline);
    delete(ImVolume.Yxline);
    delete(ImVolume.Yyline);
    delete(ImVolume.Zxline);
    delete(ImVolume.Zyline);
end

LineWidthCur = 2;
%lines in saggital slice
ImVolume.Xxline=line('Parent', axesHandleX,'color', [0 1 1],'EraseMode','xor', ....
    'LineWidth',LineWidthCur,'Xdata',[ImVolume.Y ImVolume.Y],'Ydata',[0 Xyrange(2)], ...
    'ButtonDownFcn','mia_Xpixval(''ButtonDownOnImage'')');
ImVolume.Xyline=line('Parent', axesHandleX,'color', [0 1 1],'EraseMode','xor', ...
'LineWidth',LineWidthCur,'Xdata',[0 Xxrange(2)],'Ydata', [ImVolume.Z ImVolume.Z], ...
    'ButtonDownFcn','mia_Xpixval(''ButtonDownOnImage'')');

%lines in axial slice
ImVolume.Zxline=line('Parent', axesHandleZ,'color', [0 1 1],'EraseMode','xor', ....
    'LineWidth',LineWidthCur,'Xdata',[ImVolume.X ImVolume.X],'Ydata',[0 Zyrange(2)], ...
    'ButtonDownFcn','mia_Zpixval(''ButtonDownOnImage'')');
ImVolume.Zyline=line('Parent', axesHandleZ,'color', [0 1 1],'EraseMode','xor', ...
    'LineWidth',LineWidthCur,'Xdata',[0 Zxrange(2)],'Ydata', [ImVolume.Y ImVolume.Y], ...
    'ButtonDownFcn','mia_Zpixval(''ButtonDownOnImage'')');

% set(FigDataIn.ImaHandlerX,'UserData',ImVolume);
%lines in coronal slice
ImVolume.Yxline=line('Parent', axesHandleY,'color', [0 1 1],'EraseMode','xor', ....
    'LineWidth',LineWidthCur,'Xdata',[ImVolume.X ImVolume.X],'Ydata',[0 Xyrange(2)], ...
    'ButtonDownFcn','mia_Ypixval(''ButtonDownOnImage'')');
ImVolume.Yyline=line('Parent', axesHandleY,'color', [0 1 1],'EraseMode','xor', ...
    'LineWidth',LineWidthCur,'Xdata',[0 Yxrange(2)],'Ydata', [ImVolume.Z ImVolume.Z], ...
    'ButtonDownFcn','mia_Ypixval(''ButtonDownOnImage'')');

set(FigDataIn.ImaHandlerX,'UserData',ImVolume);

%delete(dbud.line);
%dbud.line = [];
%dbud.x0 = []; dbud.y0 = [];
set(dbud.figureHandle, 'WindowButtonUpFcn', '');
dbud.displayMode = 'normal';
dbud.buttonDownImage = 0;
set(displayBar, 'UserData', dbud);
PixvalMotionFcn( displayBar);

%%%
%%%  Sub-function - MoveDisplayBar
%%%

function MoveDisplayBar

displayBar = gcbo;
dbud = get(displayBar, 'UserData');
oldWindowMotionFcn = get(dbud.figureHandle, 'WindowButtonMotionFcn');
set(dbud.figureHandle, 'WindowButtonMotionFcn', '');
oldPointer = get(dbud.figureHandle, 'Pointer');
oldPointerShapeCData = get(dbud.figureHandle, 'PointerShapeCData');
oldPointerShapeHotSpot = get(dbud.figureHandle, 'PointerShapeHotSpot');
setptr(dbud.figureHandle,'closedhand');

txtpos = get(displayBar, 'Position');
frmPos = get(dbud.DisplayFrame, 'Position');
position = txtpos + [0 0 frmPos(3) 0];
position2 = dragrect(position);
dx = position2(1)-position(1);
dy = position2(2)-position(2);

txtpos = txtpos + [dx dy 0 0];
set(displayBar, 'Position', txtpos);

frmPos = frmPos + [dx dy 0 0];
set(dbud.DisplayFrame, 'Position', frmPos);

btnPos = get(dbud.CloseButton, 'Position');
btnPos = btnPos + [dx dy 0 0];
set(dbud.CloseButton, 'Position', btnPos);

set(dbud.figureHandle, 'Pointer', oldPointer, ...
   'PointerShapeCData', oldPointerShapeCData, ...
   'PointerShapeHotSpot', oldPointerShapeHotSpot)
set(dbud.figureHandle, 'WindowButtonMotionFcn', oldWindowMotionFcn);

%%%
%%%  Sub-function - DeleteDisplayBar
%%%

function DeleteDisplayBar(displayBar)
%  Take apart the pixel value display - get rid of the text
%  uicontrol and clean up the image objects (remove their
%  UserData's and get rid of their ButtonDown & Delete Fcn's)

if nargin<1
   displayBar = findobj(gcbf, 'Tag', 'pixel value display bar');
end

if ~isempty(displayBar) % if it's empty, there's no work to do.
   dbud = get(displayBar, 'UserData');
   set(displayBar, 'DeleteFcn', '');  % Make sure there's no recursion
   if ishandle(displayBar)
      delete(displayBar);
   end
   if ishandle(dbud.CloseButton)
      delete(dbud.CloseButton);
   end
   if ishandle(dbud.DisplayFrame)
      delete(dbud.DisplayFrame);
   end

   % We are once again restoring the figure state, since
   % UIRESTORE also will reactivate the SCRIBE toolbar.
   uirestore(dbud.oldFigureUIState);

   % This is what we did for IPT 2.1/ML 5.2 :
   %   uisuspend(dbud.figureHandle);
end

%%%
%%%  Sub-function - CreatePointer
%%%

function [pointerShape, pointerHotSpot] = CreatePointer

pointerHotSpot = [8 8];
pointerShape = [ ...
      NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   1   1   1   1   1   1   2 NaN   2   1   1   1   1   1   1   1
   2   2   2   2   2   2   2 NaN   2   2   2   2   2   2   2   2
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
   2   2   2   2   2   2   2 NaN   2   2   2   2   2   2   2   2
   1   1   1   1   1   1   2 NaN   2   1   1   1   1   1   1   1
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN   1   2 NaN   2   1 NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN];


function newPrevPermute = removeDimPrevPermute(prevPermute, dimNumber)
    newPrevPermute = prevPermute;

    for iDim = 1:size(prevPermute, 2)
        dimensionNumber = prevPermute{1, iDim};
        if(dimensionNumber > dimNumber)
            newPrevPermute{1, iDim} = dimensionNumber - 1;
        elseif(dimensionNumber == dimNumber)
            removeIndex = iDim;
        end
    end
    newPrevPermute(:, removeIndex) = [];
end


function CreateCenteredMIPanel(handles)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

    %CenteredMI Panel
    handles.centeredMI = uipanel('Parent',handles.toolPanel,'Units','normalize','FontSize',10,'Title','MI Optimizer Setup',...
                                    'Tag','centeredMIPanel','Clipping','off',...
                                    'Position',[0 0 1 1],...
                                    'Visible','on');
    dx = 0; dy = -90;
    h337 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[8+dx 370-5+dy 133 29],...
    'String','Histogram Bins:',...
    'Style','text',...
    'Tag','text68');

    handles.centeredMI_histbins = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 368+dy 116 30],...
    'String','32',...
    'Style','edit',...
    'Tag','centeredMI_histbins');

    h339 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[6+dx 332-8+dy 135 32],...
    'String','Initial Radius:',...
    'Style','text',...
    'Tag','text69');

    handles.centeredMI_initradius = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 329+dy 116 30],...
    'String','0.05',...
    'Style','edit',...
    'Tag','centeredMI_initradius');

    h341 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[6+dx 295-5+dy 135 30],...
    'String','Epsilon:',...
    'Style','text',...
    'Tag','text70');

    handles.centeredMI_epsilon = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 291+dy 116 30],...
    'String','0.001',...
    'Style','edit',...
    'Tag','centeredMI_epsilon');

    h343 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[6+dx 260-5+dy 135 28],...
    'String','Max Iteration Number:',...
    'Style','text',...
    'Tag','text71');

    handles.centeredMI_iternum = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 256+dy 116 30],...
    'String','1500',...
    'Style','edit',...
    'Tag','centeredMI_iternum');

%     h345 = uicontrol(...
%     'Parent',handles.centeredMI,...
%     'FontSize',10,...
%     'HorizontalAlignment','right',...
%     'Position',[8+dx 219+dy 133 30],...
%     'String','Initial Angle:',...
%     'Style','text',...
%     'Tag','text72');
%
%     h346 = uicontrol(...
%     'Parent',handles.centeredMI,...
%     'FontSize',10,...
%     'HorizontalAlignment','right',...
%     'Position',[7+dx 188+dy 134 27],...
%     'String','Initial TranslationX:',...
%     'Style','text',...
%     'Tag','text73');
%
%     handles.centeredMI_initangle = uicontrol(...
%     'Parent',handles.centeredMI,...
%     'FontSize',10,...
%     'HorizontalAlignment','left',...
%     'Position',[148+dx 223+dy 79 25],...
%     'String','0',...
%     'Style','edit',...
%     'Tag','centeredMI_initangle');
%
%     handles.centeredMI_initTx = uicontrol(...
%     'Parent',handles.centeredMI,...
%     'FontSize',10,...
%     'HorizontalAlignment','left',...
%     'Position',[148+dx 189+dy 79 26],...
%     'String','0',...
%     'Style','edit',...
%     'Tag','centeredMI_initTx');
%
%     h349 = uicontrol(...
%     'Parent',handles.centeredMI,...
%     'FontSize',10,...
%     'HorizontalAlignment','right',...
%     'Position',[6+dx 156+dy 135 26],...
%     'String','Initial TranslationY:',...
%     'Style','text',...
%     'Tag','text74');
%
%     handles.centeredMI_initTy = uicontrol(...
%     'Parent',handles.centeredMI,...
%     'FontSize',10,...
%     'HorizontalAlignment','left',...
%     'Position',[148+dx 157+dy 79 25],...
%     'String','0',...
%     'Style','edit',...
%     'Tag','centeredMI_initTy');

    h351 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[6+dx 153-3+dy 135 28],...
    'String','Rotation Scale:',...
    'Style','text',...
    'Tag','text75');

    handles.centeredMI_rs = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 153+dy 116 30],...
    'String','0.6',...
    'Style','edit',...
    'Tag','centeredMI_rs');

    h353 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[8+dx 180+dy 133 30],...
    'String','Center Scale X:',...
    'Style','text',...
    'Tag','text76');

    h354 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[7+dx 213+dy 134 27],...
    'String','Center Scale Y:',...
    'Style','text',...
    'Tag','text77');

    handles.centeredMI_rcsx = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 189+dy 79 25],...
    'String','0',...
    'Style','edit',...
    'Tag','centeredMI_rcsx');

    handles.centeredMI_rcsy = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 219+dy 79 26],...
    'String','0',...
    'Style','edit',...
    'Tag','centeredMI_rcsy');

    h357 = uicontrol(...
    'Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','right',...
    'Position',[6+dx 121+dy 135 26],...
    'String','Translation Scale:',...
    'Style','text',...
    'Tag','text78');

    handles.centeredMI_ts = uicontrol('Parent',handles.centeredMI,...
    'FontSize',10,...
    'HorizontalAlignment','left',...
    'Position',[148+dx 123+dy 79 25],...
    'String','0.001',...
    'Style','edit',...
    'Tag','centeredMI_ts');

    guidata(handles.mainframe, handles);

end


function tapas_print_logo()
%% Print the logo of tapas.

%
% aponteeduardo@gmail.com
% copyright (C) 2017
%

disp( '_______            _____                _____       _____           ____                ');
disp( '   |              |     |              |_____|     |     |         |                    ');
disp( '   |              |-----|              |           |-----|          ----|               ');
disp(['   | RANSLATIONAL |     |LGORITHMS FOR |SYCHIATRY  |     |DVANCING  ____|CIENCE         ','']);
fprintf('\n');

end


function params = RightStancePreProcess(sys, t, x, controller, params) %#ok<INUSL>

    y = struct2array(sys.VirtualConstraints);

    for i=1:numel(y)
        if strcmp(y(i).PhaseType,'TimeBased')
            params.(y(i).PhaseParamName) = t + params.(y(i).PhaseParamName);
        end
    end

end


function opengz(gz)
% Open imtool3D by double click on a .nii.gz file in Matlab file browser
if strcmp(gz(max(end-6,1):end),'.nii.gz')
    imtool3D_nii_3planes(gz);
else
    gunzip(gz);
end