function [outim] = ft_sliceinterp(cfg, ininterp)

% FT_SLICEINTERP plots a 2D-montage of source reconstruction and anatomical MRI
% after these have been interpolated onto the same grid.
%
% Use as
%   ft_sliceinterp(cfg, interp)
%      or
%   [rgbimage] = ft_sliceinterp(cfg, interp), rgbimage is the monatage image
%
% where interp is the output of sourceinterpolate and cfg is a structure
% with any of the following fields:
%
% cfg.funparameter  string with the functional parameter of interest (default = 'source')
% cfg.maskparameter parameter used as opacity mask (default = 'none')
% cfg.clipmin       value or 'auto' (clipping of source data)
% cfg.clipmax       value or 'auto' (clipping of source data)
% cfg.clipsym       'yes' or 'no' (default) symmetrical clipping
% cfg.colormap      colormap for source overlay (default is jet(128))
% cfg.colmin        source value mapped to the lowest color (default = 'auto')
% cfg.colmax        source value mapped to the highest color (default = 'auto')
% cfg.maskclipmin   value or 'auto' (clipping of mask data)
% cfg.maskclipmax   value or 'auto' (clipping of mask data)
% cfg.maskclipsym   'yes' or 'no' (default) symmetrical clipping
% cfg.maskmap       opacitymap for source overlay
%                   (default is linspace(0,1,128))
% cfg.maskcolmin    mask value mapped to the lowest opacity, i.e.
%                   completely transparent (default ='auto')
% cfg.maskcolmin    mask value mapped to the highest opacity, i.e.
%                   non-transparent (default = 'auto')
% cfg.alpha         value between 0 and 1 or 'adaptive' (default)
% cfg.nslices       integer value, default is 20
% cfg.dim           integer value, default is 3 (dimension to slice)
% cfg.spacemin      'auto' (default) or integer (first slice position)
% cfg.spacemax      'auto' (default) or integer (last slice position)
% cfg.resample      integer value, default is 1 (for resolution reduction)
% cfg.rotate        number of ccw 90 deg slice rotations (default = 0)
% cfg.title         optional title (default is '')
% cfg.whitebg       'yes' or 'no' (default = 'yes')
% cfg.flipdim       flip data along the sliced dimension, 'yes' or 'no'
%                   (default = 'no')
% cfg.marker        [Nx3] array defining N marker positions to display
% cfg.markersize    radius of markers (default = 5);
% cfg.markercolor   [1x3] marker color in RGB (default = [1 1 1], i.e. white)
% cfg.interactive   'yes' or 'no' (default), interactive coordinates
%                   and source values
%
% if cfg.alpha is set to 'adaptive' the opacity of the source overlay
% linearly follows the source value: maxima are opaque and minima are
% transparent.
%
% if cfg.spacemin and/or cfg.spacemax are set to 'auto' the sliced
% space is automatically restricted to the evaluated source-space
%
% if cfg.colmin and/or cfg.colmax are set to 'auto' the colormap is mapped
% to source values the following way: if source values are either all
% positive or all negative the colormap is mapped to from
% min(source) to max(source). If source values are negative and positive
% the colormap is symmetrical mapped around 0 from -max(abs(source)) to
% +max(abs(source)).
%
% If cfg.maskparameter specifies a parameter to be used as an opacity mask
% cfg.alpha is not used. Instead the mask values are maped to an opacitymap
% that can be specified using cfg.maskmap. The mapping onto that
% opacitymap is controlled as for the functional data using the
% corresponding clipping and min/max options.
%
% if cfg.whitebg is set to 'yes' the function estimates the head volume and
% displays a white background outside the head, which can save a lot of black
% printer toner.
%
% if cfg.interactive is set to 'yes' a button will be displayed for
% interactive data evaluation and coordinate reading. After clicking the
% button named 'coords' you can click on any position in the slice montage.
% After clicking these coordinates and their source value are displayed in
% a text box below the button. The coordinates correspond to indeces in the
% input data array:
%
%   f = interp.source(coord_1,coord_2,coord_3)
%
% The coordinates are not affected by any transformations used for displaying
% the data such as cfg.dim, cfg.rotate,cfg.flipdim or cfg.resample.
%
% See also FT_SOURCEANALYSIS, FT_VOLUMERESLICE

% Copyright (C) 2004, Markus Siegel
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

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

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

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

% check if the input data is valid for this function
ininterp = ft_checkdata(ininterp, 'datatype', 'volume', 'feedback', 'yes');

% set the defaults
if ~isfield(cfg, 'clipmin');      cfg.clipmin = 'auto';        end
if ~isfield(cfg, 'clipmax');      cfg.clipmax = 'auto';        end
if ~isfield(cfg, 'clipsym');      cfg.clipsym = 'no';          end
if ~isfield(cfg, 'alpha');        cfg.alpha   = 'adaptive';    end
if ~isfield(cfg, 'nslices');      cfg.nslices = 20;            end
if ~isfield(cfg, 'dim');          cfg.dim = 3;                 end
if ~isfield(cfg, 'colormap');     cfg.colormap = jet(128);     end
if ~isfield(cfg, 'spacemin');     cfg.spacemin = 'auto';       end
if ~isfield(cfg, 'spacemax');     cfg.spacemax = 'auto';       end
if ~isfield(cfg, 'colmin');       cfg.colmin = 'auto';         end
if ~isfield(cfg, 'colmax');       cfg.colmax = 'auto';         end
if ~isfield(cfg, 'resample');     cfg.resample = 1;            end
if ~isfield(cfg, 'rotate');       cfg.rotate = 0;              end
if ~isfield(cfg, 'title');        cfg.title = '';              end
if ~isfield(cfg, 'whitebg');      cfg.whitebg = 'no';          end
if ~isfield(cfg, 'flipdim');      cfg.flipdim = 'no';          end
if ~isfield(cfg, 'marker');       cfg.marker = [];             end
if ~isfield(cfg, 'markersize');   cfg.markersize = 5;          end
if ~isfield(cfg, 'markercolor');  cfg.markercolor = [1,1,1];   end
if ~isfield(cfg, 'interactive');  cfg.interactive = 'no';      end
if ~isfield(cfg, 'maskclipmin');  cfg.maskclipmin = 'auto';           end
if ~isfield(cfg, 'maskclipmax');  cfg.maskclipmax = 'auto';           end
if ~isfield(cfg, 'maskclipsym');  cfg.maskclipsym = 'no';             end
if ~isfield(cfg, 'maskmap');      cfg.maskmap = linspace(0,1,128);    end
if ~isfield(cfg, 'maskcolmin');   cfg.maskcolmin = 'auto';            end
if ~isfield(cfg, 'maskcolmax');   cfg.maskcolmax = 'auto';            end
if ~isfield(cfg, 'maskparameter');cfg.maskparameter = [];             end

% perform some checks on the configuration for backward compatibility
if ~isfield(cfg, 'funparameter') && isfield(ininterp, 'source')
  % if present, the default behavior should be to use this field for plotting
  cfg.funparameter = 'source';
end

% make the selection of functional and mask data consistent with the data
cfg.funparameter  = parameterselection(cfg.funparameter, ininterp);
cfg.maskparameter = parameterselection(cfg.maskparameter, ininterp);
% only a single parameter should be selected
try, cfg.funparameter  = cfg.funparameter{1}; end
try, cfg.maskparameter = cfg.maskparameter{1}; end

% check anatomical data
if isfield(ininterp,'anatomy');
  interp.anatomy = reshape(ininterp.anatomy, ininterp.dim);
else
  ft_error('no anatomical data supplied');
end

% check functional data
if ~isempty(cfg.funparameter)
  interp.source = double(reshape(getsubfield(ininterp, cfg.funparameter), ininterp.dim));
else
  ft_error('no functional data supplied');
end

% check mask data
if ~isempty(cfg.maskparameter)
  interp.mask = double(reshape(getsubfield(ininterp,cfg.maskparameter), ininterp.dim));
  maskdat = 1;
else
  fprintf('no opacity mask data supplied\n');
  interp.mask = [];
  maskdat = 0;
end

% only work with the copy of the relevant parameters in "interp"
clear ininterp;

% convert anatomy data type and optimize contrast
if isa(interp.anatomy, 'uint8') || isa(interp.anatomy, 'uint16')
  fprintf('converting anatomy to floating point values...');
  interp.anatomy = double(interp.anatomy);
  fprintf('done\n');
end
fprintf('optimizing contrast of anatomical data ...');
minana = min(interp.anatomy(:));
maxana = max(interp.anatomy(:));
interp.anatomy = (interp.anatomy-minana)./(maxana-minana);
fprintf('done\n');

% store original data if 'interactive' mode
if strcmp(cfg.interactive,'yes')
  data.source = interp.source;
end

% place markers
marker = zeros(size(interp.anatomy));
if ~isempty(cfg.marker)
  fprintf('placing markers ...');
  [x,y,z] = ndgrid([1:size(interp.anatomy,1)],[1:size(interp.anatomy,2)],[1:size(interp.anatomy,3)]);
  for imarker = 1:size(cfg.marker,1)
    marker(find(sqrt((x-cfg.marker(iarker,1)).^2 + (y-cfg.marker(imarker,2)).^2 + (z-cfg.marker(imarker,3)).^2)<=cfg.markersize)) = 1;
  end
  fprintf('done\n');
end

% shift dimensions
fprintf('sorting dimensions...');
interp.anatomy = shiftdim(interp.anatomy,cfg.dim-1);
interp.source = shiftdim(interp.source,cfg.dim-1);
interp.mask = shiftdim(interp.mask,cfg.dim-1);
marker = shiftdim(marker,cfg.dim-1);
fprintf('done\n');

% flip dimensions
if strcmp(cfg.flipdim,'yes')
  fprintf('flipping dimensions...');
  interp.anatomy = flipdim(interp.anatomy,1);
  interp.source = flipdim(interp.source,1);
  interp.mask = flipdim(interp.mask,1);
  marker = flipdim(marker,1);
  fprintf('done\n');
end

% set slice space
if ischar(cfg.spacemin)
  fprintf('setting first slice position...');
  spacemin = min(find(~isnan(max(max(interp.source,[],3),[],2))));
  fprintf('%d...done\n',spacemin);
else
  spacemin = cfg.spacemin;
end

if ischar(cfg.spacemax)
  fprintf('setting last slice position...');
  spacemax = max(find(~isnan(max(max(interp.source,[],3),[],2))));
  fprintf('%d...done\n',spacemax);
else
  spacemax = cfg.spacemax;
end

% clip funtional data
if ~ischar(cfg.clipmin)
  fprintf('clipping functional minimum...');
  switch cfg.clipsym
  case 'no'
    interp.source(find(interp.source<cfg.clipmin)) = nan;
  case 'yes'
    interp.source(find(abs(interp.source)<cfg.clipmin)) = nan;
  end
  fprintf('done\n');
end
if ~ischar(cfg.clipmax)
  fprintf('clipping functional maximum...');
  switch cfg.clipsym
  case 'no'
    interp.source(find(interp.source>cfg.clipmax)) = nan;
  case 'yes'
    interp.source(find(abs(interp.source)>cfg.clipmax)) = nan;
  end
  fprintf('done\n');
end

% clip mask data
if maskdat
  if ~ischar(cfg.maskclipmin)
    fprintf('clipping mask minimum...');
    switch cfg.maskclipsym
    case 'no'
      interp.mask(find(interp.mask<cfg.maskclipmin)) = nan;
    case 'yes'
      interp.mask(find(abs(interp.mask)<cfg.maskclipmin)) = nan;
    end
    fprintf('done\n');
  end
  if ~ischar(cfg.maskclipmax)
    fprintf('clipping mask maximum...');
    switch cfg.maskclipsym
    case 'no'
      interp.mask(find(interp.mask>cfg.maskclipmax)) = nan;
    case 'yes'
      interp.mask(find(abs(interp.mask)>cfg.maskclipmax)) = nan;
    end
    fprintf('done\n');
  end
end

% scale functional data
fprintf('scaling functional data...');
fmin = min(interp.source(:));
fmax = max(interp.source(:));
if ~ischar(cfg.colmin)
  fcolmin = cfg.colmin;
else
  if sign(fmin)==sign(fmax)
    fcolmin = fmin;
  else
    fcolmin = -max(abs([fmin,fmax]));
  end
end
if ~ischar(cfg.colmax)
  fcolmax = cfg.colmax;
else
  if sign(fmin)==sign(fmax)
    fcolmax = fmax;
  else
    fcolmax = max(abs([fmin,fmax]));
  end
end
interp.source = (interp.source-fcolmin)./(fcolmax-fcolmin);
if ~ischar(cfg.colmax)
  interp.source(find(interp.source>1)) = 1;
end
if ~ischar(cfg.colmin)
  interp.source(find(interp.source<0)) = 0;
end
fprintf('done\n');

% scale mask data
if maskdat
  fprintf('scaling mask data...');
  fmin = min(interp.mask(:));
  fmax = max(interp.mask(:));
  if ~ischar(cfg.maskcolmin)
    mcolmin = cfg.maskcolmin;
  else
    if sign(fmin)==sign(fmax)
      mcolmin = fmin;
    else
      mcolmin = -max(abs([fmin,fmax]));
    end
  end
  if ~ischar(cfg.maskcolmax)
    mcolmax = cfg.maskcolmax;
  else
    if sign(fmin)==sign(fmax)
      mcolmax = fmax;
    else
      mcolmax = max(abs([fmin,fmax]));
    end
  end
  interp.mask = (interp.mask-mcolmin)./(mcolmax-mcolmin);
  if ~ischar(cfg.maskcolmax)
    interp.mask(find(interp.mask>1)) = 1;
  end
  if ~ischar(cfg.maskcolmin)
    interp.mask(find(interp.mask<0)) = 0;
  end
  fprintf('done\n');
end

% merge anatomy, functional data and mask
fprintf('constructing overlay...');
if ischar(cfg.colormap)
  % replace string by colormap using standard MATLAB function
  cfg.colormap = colormap(cfg.colormap);
end
cmap = cfg.colormap;
cmaplength = size(cmap,1);
maskmap = cfg.maskmap(:);
maskmaplength = size(maskmap,1);
indslice = round(linspace(spacemin,spacemax,cfg.nslices));
nvox1 = length(1:cfg.resample:size(interp.anatomy,2));
nvox2 = length(1:cfg.resample:size(interp.anatomy,3));
if mod(cfg.rotate,2)
  dummy = nvox1;
  nvox1 = nvox2;
  nvox2 = dummy;
end
out = zeros(nvox1,nvox2,3,cfg.nslices);
for islice = 1:cfg.nslices
  sel1 = 1:cfg.resample:size(interp.anatomy,2);
  sel2 = 1:cfg.resample:size(interp.anatomy,3);

  dummy1 = reshape(interp.anatomy(indslice(islice),sel1,sel2), [numel(sel1) numel(sel2)]);
  dummy2 = reshape(interp.source(indslice(islice),sel1,sel2),  [numel(sel1) numel(sel2)]);
  indmarker = find(reshape(marker(indslice(islice),sel1,sel2), [numel(sel1) numel(sel2)]));
  indsource = find(~isnan(dummy2));
  if maskdat
    dummymask = reshape(interp.mask(indslice(islice),sel1,sel2), [numel(sel1) numel(sel2)]);
    indsource = find(~isnan(dummy2) & ~isnan(dummymask));
  end
  for icol = 1:3
    dummy3 = dummy1;
    if not(maskdat)
      if ~ischar(cfg.alpha)
        try
          dummy3(indsource) = ...
            (1-cfg.alpha) * dummy3(indsource) + ...
            cfg.alpha * cmap(round(dummy2(indsource)*(cmaplength-1))+1,icol);
        end
      else
        try
          dummy3(indsource) = ...
            (1-dummy2(indsource)) .* dummy3(indsource) + ...
            dummy2(indsource) .* cmap(round(dummy2(indsource)*(cmaplength-1))+1,icol);
        end
      end
    else
      dummy3(indsource) = ...
        (1-maskmap(round(dummymask(indsource)*(maskmaplength-1))+1)).* ...
        dummy3(indsource) + ...
        maskmap(round(dummymask(indsource)*(maskmaplength-1))+1) .* ...
        cmap(round(dummy2(indsource)*(cmaplength-1))+1,icol);
    end
    dummy3(indmarker) = cfg.markercolor(icol);
    out(:,:,icol,islice) = rot90(dummy3,cfg.rotate);
  end
  if strcmp(cfg.whitebg,'yes')
    bgmask = zeros(nvox1,nvox2);
    bgmask(find(conv2(mean(out(:,:,:,islice),3),ones(round((nvox1+nvox2)/8))/(round((nvox1+nvox2)/8).^2),'same')<0.1)) = 1;
    for icol = 1:3
      out(:,:,icol,islice) = bgmask.*ones(nvox1,nvox2) + (1-bgmask).* out(:,:,icol,islice);
    end
  end
end
fprintf('done\n');

clf;
fprintf('plotting...');
axes('position',[0.9 0.3 0.02 0.4]);
image(permute(cmap,[1 3 2]));
set(gca,'YAxisLocation','right');
set(gca,'XTick',[]);
set(gca,'YDir','normal');
set(gca,'YTick',linspace(1,cmaplength,5));
set(gca,'YTickLabel',linspace(fcolmin,fcolmax,5));
set(gca,'Box','on');
axes('position',[0.01 0.01 0.88 0.90]);
[h,nrows,ncols]=slicemon(out);
xlim=get(gca,'XLim');
ylim=get(gca,'YLim');
text(diff(xlim)/2,-diff(ylim)/100,cfg.title,'HorizontalAlignment','center','Interpreter','none');
drawnow;
fprintf('done\n');
if nargout > 0
  outim=get(h,'CData');
end

if strcmp(cfg.interactive,'yes')
  data.sin = size(interp.source);
  data.nrows = nrows;
  data.ncols = ncols;
  data.out = out;
  data.indslice = indslice;
  data.cfg = cfg;
  data.hfig = gcf;
  uicontrol('Units','norm', 'Position', [0.9 0.2 0.08 0.05], 'Style','pushbutton', 'String','coords',...
    'Callback',@getcoords,'FontSize',7);
  data.hcoords = uicontrol('Units','norm', 'Position', [0.9 0.05 0.08 0.13], 'Style','text', 'String','','HorizontalAlign','left','FontSize',7);
  guidata(data.hfig,data);
end

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble trackconfig
ft_postamble history ininterp
ft_postamble provenance

% ---------------- subfunctions ----------------

function getcoords(h,eventdata,handles,varargin)
data = guidata(gcf);
[xi,yi] = ginput(1);

co(2,1) = round(mod(yi,size(data.out,1)));
co(3,1) = round(mod(xi,size(data.out,2)));
switch mod(data.cfg.rotate,4)
case 1
  t1 = co(2);
  co(2) = co(3);
  co(3) = data.sin(3)-t1;
case 2
  co(2) = data.sin(2)-co(2);
  co(3) = data.sin(3)-co(3);
case 3
  t1 = co(3);
  co(3) = co(2);
  co(2) = data.sin(2)-t1;
end

try
  co(1) = data.indslice(fix(xi/size(data.out,2)) + fix(yi/size(data.out,1))*data.ncols + 1);
catch
  co(1) = NaN;
end

if strcmp(data.cfg.flipdim, 'yes')
  co(1) = data.sin(1) - co(1) + 1;
end
co = co(:);

co(2:3) = round(co(2:3)*data.cfg.resample);
for ishift = 1:data.cfg.dim-1
 co = [co(3);co(1);co(2)];
end
set(data.hcoords,'String',sprintf('1: %d\n2: %d\n3: %d\nf: %0.4f',co(1),co(2),co(3),data.source(co(1),co(2),co(3))));

function [h,nrows,ncols] = slicemon(a) % display the montage w/o image_toolbox
siz = [size(a,1) size(a,2) size(a,4)];
nn = sqrt(prod(siz))/siz(2);
mm = siz(3)/nn;
if (ceil(nn)-nn) < (ceil(mm)-mm)
  nn = ceil(nn); mm = ceil(siz(3)/nn);
else
  mm = ceil(mm); nn = ceil(siz(3)/mm);
end
b = a(1,1);
b(1,1) = 0;
b = repmat(b, [mm*siz(1), nn*siz(2), size(a,3), 1]);
rows = 1:siz(1); cols = 1:siz(2);
for i=0:mm-1
  for j=0:nn-1
    k = j+i*nn+1;
    if k<=siz(3)
      b(rows+i*siz(1),cols+j*siz(2),:) = a(:,:,:,k);
    end
  end
end
hh = image(b);
axis image;
box off;
set(gca,'XTick',[],'YTick',[],'Visible','off');
if nargout > 0
  h = hh;
  nrows = mm;
  ncols = nn;
end


function [handles,msh] = dtiMrMeshAddFGs(handles,msh,showTheseFGs)
%Add the selected fiber groups to the mrMesh window.
%
% [handles,msh] = dtiMrMeshAddFGs(handles,msh,[showTheseFGs])
%
%   handles is from dtiFiberUI (guidata)
%
%   The show selection list is determined from data in the handles
%   structure. You can over-ride that by specifying a vector of 0s and 1s
%   in showTheseFGs. 0 means don't show, 1 means show.  The length is equal
%   to the number of fiber groups.
%
% See also:  dtiFGShowList, dtiRefreshFigure; dtiMrMesh3AxisImage
%
% HISTORY:
% 2004.09.?? Dougherty & Wandell wrote it.
% 2006.11.21 RFD: fixed a couple of little bugs that affected the rendering
% of fibers with few points
%
% (c) Stanford VISTA Team 2005

if ~exist('showTheseFGs','var')||isempty(showTheseFGs),
    showTheseFGs =  dtiFGShowList(handles);
    if(isempty(showTheseFGs)), return; end
end

% This is the list of fiber groups to show
fiberGroups = handles.fiberGroups(showTheseFGs);
defaultAlpha = 255;

stepSizeMm = sqrt(mean(sum(diff(fiberGroups(1).fibers{1},1,2).^2,1)));
skipPoints = round(2./stepSizeMm);

% Display the fiber groups
numGrps = length(fiberGroups);
totalNumActors = 0;
for grpNum=1:numGrps
    fg = fiberGroups(grpNum);
    if(fg.visible)
        fgRgba = uint8(fg.colorRgb);
        sz = size(fgRgba);
        if(sz(2)==3), fgRgba(:,4) = repmat(defaultAlpha,sz(1),1); end
        if(fg.thickness>0)
            % Positive thickness means render as tubes
            clear t;
            t.class = 'mesh';
            [id,s,t] = mrMesh(msh.host, msh.id, 'add_actor', t);
            totalNumActors = totalNumActors+1;
            msh.fiberGroupActors(totalNumActors) = t.actor;

            % This value could change to make really beautiful fibers.  But
            % for exploration, this is fine.  It makes little
            % square/rectangular extrusions.
            t.sides = 5;
            t.radius = fg.thickness;
            t.cap = 1;
            t.points = [];
            % t.points are assembled into a set of tubes separated by a
            % [999,999,999] value.  Inelegant, but OK for now.
            t.color = fgRgba(1,:);
            for ii=1:length(fg.fibers)
                if(size(fg.fibers{ii},2)>skipPoints*10)
                    % We now ensure that we always include the first and last
                    % point. This matters more with metrotrac paths that are
                    % sampled at 2mm.
                    fg.fibers{ii} = horzcat(fg.fibers{ii}(:,1:skipPoints:end-1),fg.fibers{ii}(:,end));
                end
                fg.fibers{ii}(:,end+1) = [999;999;999];
            end
            t.points = horzcat(fg.fibers{:});
            [id,s,r] = mrMesh(msh.host, msh.id, 'tube', t);
        else
            % A negative thickness means render fibers as polylines
            clear t
            t.class = 'polyline';
            t.width = abs(fg.thickness);
            if(sz(1)==1)
                [id,s,r] = mrMesh(msh.host, msh.id, 'add_actor', t);
                totalNumActors = totalNumActors+1;
                msh.fiberGroupActors(totalNumActors) = r.actor(1);
                t.actor = r.actor;
                t.color = uint8(fgRgba);
                % Work-around for fiber color bug in mrMesh. With polylines, it
                % adds a random luminance offset in the range of +/- 8 to the
                % color of each fiber to make individual polylines distinct.
                % But, it donesn't clamp the values at 0,255, so if any of the
                % RGB values are too close to 0,255 the value might wrap and
                % make rainbow colors.
                t.color(t.color(1:3)<8) = 8;
                t.color(t.color(1:3)>247) = 247;
                t.points = [];
                for(ii=1:length(fg.fibers))
                    fg.fibers{ii}(:,end) = [999;999;999];
                end
                t.points = horzcat(fg.fibers{:});
                [id,s,r] = mrMesh(msh.host, msh.id, 'set', t);
            else
                for ii=1:length(fg.fibers)
                    [id,s,r] = mrMesh(msh.host, msh.id, 'add_actor', t);
                    totalNumActors = totalNumActors+1;
                    msh.fiberGroupActors(totalNumActors) = r.actor(1);
                    t.actor = r.actor;
                    if(size(fg.fibers{ii},2)>skipPoints*10)
                        fg.fibers{ii} = horzcat(fg.fibers{ii}(:,1:skipPoints:end-1),fg.fibers{ii}(:,end));
                    end
                    t.points = fg.fibers{ii};
                    t.color = fgRgba(ii,:);
                    t.color(t.color(1:3)<8) = 8;
                    t.color(t.color(1:3)>247) = 247;
                    [id,s,r] = mrMesh(msh.host, msh.id, 'set', t);
                end

            end
        end
    end
end

return;


function h = set(h,varargin)
%SET: Set drumplot properties
%   val = get(drumplot,prop_name,prop_val)
%
%   Valid property names:
%       WAVE, CATALOG, MPL, TRACE_COLOR, EVENT_COLOR, SCALE
%
%   See also DRUMPLOT, DRUMPLOT/SET
%
% Author: Dane Ketner, Alaska Volcano Observatory
% Modified: Glenn Thompson 2016/04/20
% $Date$
% $Revision$

if ~isa(h,'drumplot')
   error('DRUMPLOT/SET: Not a valid drumplot object')
end

if ~rem(nargin-1,2) == 0
      error(['DRUMPLOT/SET: Arguments after h must appear in ',...
             'property name/val pairs'])
end

for n = 1:2:nargin-2
   nw = numel(h.wave); % Number of waveforms in h
   pn = varargin{n};   % Property name
   pv = varargin{n+1}; % Property value
   nv = numel(pv);     % Number of elements in property value
   sv = size(pv);      % Size of property value

   switch lower(pn)
      case 'wave'
         if ~isa(pv,'waveform')
            error('DRUMPLOT/SET: wave property is not a waveform')
         end
         if numel(pv)~=nw
            error(['DRUMPLOT/SET: new waveform dimensions must match',...
                   ' existing waveform dimensions'])
         end
         h.wave = pv;

      case 'catalog'
         if isa(pv,'Catalog')
             h.catalog = pv;
         elseif isempty(pv)
            h.catalog = Catalog();
         else
            error('DRUMPLOT/SET: wrong Catalog format')
         end

      case 'mpl'
         if isnumeric(pv) && nv==1
            h.mpl = pv;
         else
            error('DRUMPLOT/SET: wrong mpl format')
         end

      case 'trace_color'
         if sv(2)==3 && isnumeric(pv)
            pv = {pv};
            nv = 1;
         end
         if (nw==nv) && iscell(pv)
            for m = 1:nv
               if size(pv{m},2)==3
                  for mm = 1:numel(pv{m})
                     if ~(pv{m}(mm)<=1 && pv{m}(mm)>=0)
                        error(['DRUMPLOT/SET: trace_color RGB values must ',...
                           'be between 0 and 1'])
                     end
                  end
               end
            end
            h.trace_color = pv;
         else
            error('DRUMPLOT/SET: wrong trace_color format')
         end

      case 'event_color'
         h.event_color = pv;

      case 'scale'
         if isnumeric(pv) && nv==nw
            h.scale = pv;
         elseif isnumeric(pv) && nv==1
            h.scale = pv*ones(1,nw);
         else
            error('DRUMPLOT/SET: wrong scale format')
         end

      otherwise
         error('DRUMPLOT/GET: Not a valid drumplot property')
    end
end


function tuned = TuneArtefactTimes(x,times,varargin)

%TuneArtefactTimes - Fine-tune artefact (e.g. stimulation) times.
%
%  Fine tuning can be done using either the LFP (faster), or the wideband
%  data (slower but more accurate). The latter is especially useful in cases
%  where the artefacts are no longer visible in the LFP signal due to low-
%  pass filtering.
%
%  USAGE
%
%    % Using LFP (pass the actual samples)
%    times = TuneArtefactTimes(lfp,artefacts,<options>)
%
%    % Using wideband data (pass the channel ID because the data would not fit
%    % in memory, so it will have to be read piecewise from disk)
%    times = TuneArtefactTimes(channel,artefacts,<options>)
%
%    lfp            local field potentials
%    channel        recording channel (wideband data)
%    artefacts      approximate artefact times
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'method'      artefacts can be detected using either the derivative
%                   ('slope', default) or the amplitude ('amplitude') of the
%                   signal
%     'durations'   interval (in s) around each approximate artefact time
%                   where the actual artefact should be sought
%                   (default = [-0.0075 0.0075])
%     'verbose'     display information about ongoing processing
%                   (default = 'off')
%    =========================================================================
%
%  SEE
%
%    See also RemoveArtefacts.

% Copyright (C) 2008-2011 by Michaël Zugaro
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.

% Default values
durations = [-0.0075 0.0075];
verbose = 'off';
nChunks = 100;
method = 'slope';

% Check number of parameters
if nargin < 2 | mod(length(varargin),2) ~= 0,
  error('Incorrect number of parameters (type ''help TuneArtefactTimes'' for details).');
end

% LFP or wideband?
if isiscalar(x),
	source = 'wideband';
	channel = x;
else
	source = 'lfp';
	lfp = x;
	if size(lfp,2) ~= 2,
		error('Parameter ''lfp'' is not a Nx2 matrix (type ''help TuneArtefactTimes'' for details).');
	end
end

% Check parameter sizes
if ~isdvector(times),
	error('Parameter ''times'' is not a vector (type ''help TuneArtefactTimes'' for details).');
end

% Parse parameter list
for i = 1:2:length(varargin),
	if ~ischar(varargin{i}),
		error(['Parameter ' num2str(i+1) ' is not a property (type ''help TuneArtefactTimes'' for details).']);
	end
	switch(lower(varargin{i})),
		case 'durations',
			durations = varargin{i+1};
			if ~isdvector(durations,'#2','>'),
			error('Incorrect value for property ''durations'' (type ''help TuneArtefactTimes'' for details).');
			end
		case 'method',
			method = lower(varargin{i+1});
			if ~isstring_FMAT(method,'slope','amplitude'),
				error('Incorrect value for property ''method'' (type ''help Sync'' for details).');
			end
		case 'verbose',
			verbose = lower(varargin{i+1});
			if ~isstring_FMAT(verbose,'on','off'),
				error('Incorrect value for property ''verbose'' (type ''help Sync'' for details).');
			end
		otherwise,
			error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help TuneArtefactTimes'' for details).']);

	end
end

% Discard duplicates
cleaned = unique(times);
if length(times) ~= length(cleaned),
	warning('Data contains duplicates - these will be discarded.');
end
times = cleaned;

if strcmp(source,'lfp'),
	% Process LFP all at once
	% Time intervals where the artefacts should be sought
	intervals = [times+durations(1) times+durations(2)];
	tuned = process(lfp,times,intervals,verbose);
else
	% Split data in chunks
	start = times(1) + durations(1);
	stop = times(end) + durations(2);
	totalTime = stop-start;
	dt = totalTime/nChunks;
	tuned = [];
	for i = 1:nChunks,
		% Time intervals where the artefacts should be sought
		t = Restrict(times,[start start+dt]);
		intervals = [t+durations(1) t+durations(2)];
		data = GetWidebandData(channel,'intervals',[start start+dt]);
		tuned = [tuned;process(data,t,intervals,method,verbose)];
		start = start + dt;
	end
end

% Actual processing

function tuned = process(signal,times,intervals,method,verbose)

% Differentiate signal
if strcmp(method,'slope'),
	signal(:,2) = [0;diff(signal(:,2))];
end

% Select the samples within these intervals
[in,i,j] = InIntervals(signal(:,1),intervals,'verbose',verbose);
% Transform this into a matrix where each line contains the data for one artefact, and find minima
m = full(sparse(i(i~=0),j(j~=0),signal(in,2)));
infs = ~logical(full(sparse(i(i~=0),j(j~=0),1)));
m(infs) = inf;
[unused,where] = min(m,[],2);
% Build the same matrix for data timestamps
t = full(sparse(i(i~=0),j(j~=0),signal(in,1)));
% Times
s1 = size(m,1);
s2 = size(m,2);
where = logical(full(sparse(1:s1,where,1,s1,s2)));
tuned = sort(t(where));


function [geom_data, valid_ids] = Read_geom_files_dynamic(users, params_data_dir)

    geom_data = [];
    valid_ids = [];

    load('../../pca_generation/pdm_68_aligned_wild.mat');

    user1 = {'train_001', 'train_002', 'train_003', 'train_004', 'train_005',...
            'train_006', 'train_007', 'train_008', 'train_009', 'train_010',...
            'train_011', 'train_012', 'train_013', 'train_014', 'train_015',...
            'train_016', 'train_017', 'train_018'};

    user2 = {'train_019', 'train_020', 'train_021', 'train_022', 'train_023',...
             'train_024', 'train_025', 'train_026', 'train_027', 'train_028',...
             'train_029', 'train_030', 'train_031', 'train_032'};

    user3 = {'train_033', 'train_034', 'train_035', 'train_036', 'train_037',...
             'train_038', 'train_039', 'train_040', 'train_041'};

    user4 = {'train_042', 'train_043', 'train_044', 'train_045', 'train_046',...
             'train_047', 'train_048', 'train_049', 'train_050', 'train_051',...
             'train_052', 'train_053', 'train_054', 'train_055', 'train_056'};

    user5 = {'train_057', 'train_058', 'train_059', 'train_060', 'train_061',...
             'train_062', 'train_063'};

    user6 = {'train_064', 'train_065', 'train_066', 'train_067', 'train_068',...
             'train_069', 'train_070', 'train_071', 'train_072', 'train_073',...
             'train_074', 'train_075', 'train_076', 'train_077', 'train_078', 'train_079'};

    user7 = {'train_080', 'train_081', 'train_082', 'train_083', 'train_084',...
             'train_085', 'train_086', 'train_087'};

    users_group = cat(1, {user1}, {user2}, {user3}, {user4}, {user5}, {user6}, {user7});
    user_inds = [];

    for i=1:numel(users)

        geom_file = [params_data_dir, '/au_training_' users{i} '.csv'];

        if(i == 1)
            tab = readtable(geom_file);
            column_names = tab.Properties.VariableNames;
            valid_ind = cellfun(@(x) ~isempty(x) && x==1, strfind(column_names, 'success'));
            shape_inds = cellfun(@(x) ~isempty(x) && x==1, strfind(column_names, 'p_'));
        end

        res = dlmread(geom_file, ',', 1, 0);

        % Check the confidence of detection
        valid = logical(res(:, valid_ind));
        res = res(:, shape_inds);

        % Do not consider global parameters
        res = res(:, 7:end);

        for k=1:numel(users_group)
            if(~isempty(strmatch(users{i}, users_group{k})))
                user_inds = cat(1, user_inds, k * ones(size(valid,1),1));
            end
        end

        actual_locs = res * V';
        res = cat(2, actual_locs, res);

        valid_ids = cat(1, valid_ids, valid);

        geom_data = cat(1, geom_data, res);

    end

    if(numel(users) > 0)

        % Perform normalization here
        u_id = unique(user_inds)';
        for u=u_id
            geom_data(user_inds==u, :) = bsxfun(@plus, geom_data(user_inds==u, :), -median(geom_data(user_inds==u & valid_ids, :)));
        end
    end

end


function [exp,changed,rules] = exp_replaceall(exp,rules)
% Apply substitution rules to some expression.
% [Out-Expression,Changed,Remaining-Rules] = exp_replaceall(In-Expression, Rule/Rule-List)
%
% Each part of the expression receives at most a single replacement, and each rule is used at most
% once rules may contain blanks and placeholders.
%
% In:
%   In-Expression : some pure expression in which substitution shall take place
%   Rule-List     : cell array of substitution rules
%
% Out:
%   Out-Expression     : In-Expression with substitutions performed
%
%   Changed            : whether a replacement has taken place
%   Remaining-Rules    : the list of rules that have not been applied
%
% Notes:
%   Impure expressions degrade into pure expressions, when substitutions are performed on them (i.e.
%   they lose their attached value). Named patterns inside the right-hand-sides of substitution
%   rules are treated as if just their symbols were given instead.
%
% See also:
%   exp_replacerepeated, exp_match
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-04-19

if ~exp_beginfun('symbolic') return; end

[exp,changed,rules] = utl_replaceall(exp,rules);

exp_endfun;


function ASAversionbkup(funct_name)
%ASAVERSIONBKUP Version-tagged function backup
%   ASAVERSIONBKUP(FUNCTION) copies a m-file FUNCTION, that is provided
%   with an ARMASA version identifier, into a directory 'Backup' and
%   renames the file by appending the version identifier (that has been
%   numerically converted) to its name. For more details about the
%   numeric conversion, see ASAVERSION2NUMSTR. FUNCTION must be a
%   character string, containing the name of the m-file to backup. The
%   directory 'Backup' must be located in the directory where the m-file
%   resides, or otherwise it is created.
%
%   See also: ASAVERSION2NUMSTR

[funct_root,funct_name,funct_ext]=fileparts(which(funct_name));
ASAcontrol.run=0;
ASAcontrol.version_chk=0;
ASAcontrol=feval(funct_name,ASAcontrol);
funct_version=ASAversion2numstr(ASAcontrol.is_version);
bkup_name=[funct_name '_' strrep(funct_version,'.','_')];
if ~exist([funct_root filesep 'Backup'],'dir')
   mkdir(funct_root,'Backup');
end
copyfile([funct_root filesep funct_name funct_ext], ...
   [funct_root filesep 'Backup' filesep bkup_name funct_ext]);

%Program history
%======================================================================
%
% Version                Programmer(s)          E-mail address
% -------                -------------          --------------
% [2000 12 30 20 0 0]    W. Wunderink           wwunderink01@freeler.nl


function var = kernGetVariance(kern)

% KERNGETVARIANCE Get the signal associated with a the kernel.

% KERN

fhandleStr = ([kern.type 'KernGetVariance']);
if exist(fhandleStr)==2
  fhandle = str2func(fhandleStr);
  var = fhandle(kern);
  return
else
  if(strcmp(kern.type, 'white'))
    var = 0;
    return
  else
    if(isfield(kern, 'variance'))
      var = kern.variance;
    else
      error(['No field variance or file ' fhandleStr])
    end
    return
  end
end


1     % problem
4     % grid parameter
1     % uniform/stretched grid
2     % domain size
1     % approximation

%% Data file for test problem P1


function spm_check_registration(varargin)
% A visual check of image registration quality
% FORMAT spm_check_registration
% FORMAT spm_check_registration(images)
% Orthogonal views of one or more images are displayed. Clicking in
% any image moves the centre of the orthogonal views. Images are
% shown in orientations relative to that of the first selected image.
% The first specified image is shown at the top-left, and the last at
% the bottom right. The fastest increment is in the left-to-right
% direction (the same as you are reading this).
%__________________________________________________________________________
% Copyright (C) 1997-2019 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_check_registration.m 7759 2019-12-19 11:50:50Z guillaume $

SVNid = '$Rev: 7759 $';

%-Get input
%--------------------------------------------------------------------------
if nargin
    if nargin > 1 && iscellstr(varargin{2})
        images = varargin{1};
    elseif isstruct(varargin{1})
        images = [varargin{:}];
    else
        images = char(varargin);
    end
else
    [images, sts] = spm_select([1 24],'image','Select images');
    if ~sts, return; end
end

if ischar(images), images = spm_vol(images); end
if numel(images) > 24
    if ~isdeployed, addpath(fullfile(spm('Dir'),'spm_orthviews')); end
    img = cell(1,numel(images));
    for i=1:numel(images)
        img{i} = [images(i).fname ',' num2str(images(i).n(1))];
    end
    spm_ov_browser('ui',char(img));
    return
end
images = images(1:min(numel(images),24));

%-Print
%--------------------------------------------------------------------------
spm('FnBanner',mfilename,SVNid);                                        %-#
exactfname  = @(f) [f.fname ',' num2str(f.n(1))];
cmddispone  = 'spm_image(''display'',''%s'')';
cmddispall  = 'spm_check_registration(''%s'')';
if spm_platform('desktop')
    str     = '';
    for i=1:numel(images)
        str = [str sprintf('''%s'',',exactfname(images(i)))];
    end
    dispall = [' (' spm_file('all','link',sprintf(cmddispall,str(2:end-2))) ')  '];
else
    dispall = '        ';
end
for i=1:numel(images)
    if i==1,     fprintf('Display ');                                   %-#
    elseif i==2, fprintf('%s',dispall);
    else         fprintf('        '); end
    fprintf('%s\n',spm_file(exactfname(images(i)),'link',cmddispone));  %-#
end

%-Display
%--------------------------------------------------------------------------
fg = spm_figure('GetWin','Graphics');
spm_figure('Clear',fg);
spm_orthviews('Reset');
dm = get(fg,'Position');
mn = length(images);

minwasted = Inf;
m = 0; n = 0;
for m1=1:mn
    n1 = ceil(mn/m1);
    s  = max(dm(4)/m1,dm(3)/n1);
    wasted = (s*m1-dm(4))*dm(3) + (s*n1-dm(3))*dm(4) + (m1*n1-mn)*s^2*1.01;
    if wasted < minwasted
        minwasted = wasted;
        m = m1;
        n = n1;
    end
end

w  = 1/n;
h  = 1/m;
ds = (w+h)*0.02;
for ij=1:mn
    i = 1-h*(floor((ij-1)/n)+1);
    j = w*rem(ij-1,n);
    handle = spm_orthviews('Image', images(ij),...
        [j+ds/2 i+ds/2 w-ds h-ds]);
    if ij==1, spm_orthviews('Space'); end
    spm_orthviews('AddContext',handle);
end

%-Backward compatibility with spm_check_registration(images,captions)
%--------------------------------------------------------------------------
if nargin > 1 && iscellstr(varargin{2})
    spm_orthviews('Caption',varargin{2}, varargin{3:end});
    %for ij=1:numel(varargin{2})
    %    spm_orthviews('Caption', ij, varargin{2}{ij}, varargin{3:end});
    %end
end


function mtrExportFibersFromMatrix(pathwayMatrix, pathwayLengthVec, statsVec, statsNames, fileName, xformToMm)
%Exports pathway matrix (3,max_path_length,num_paths) to .pdb format
%
% This routine does not appear to be used much.
%
%   mtrExportFibersFromMatrix(pathwayMatrix, pathwayLengthVec, fileName, xformToAcPc, xform)
%
% See also: mtrNotes
%
% Examples
%
%
% HISTORY:
% 2008.01.13 AJS: wrote it.
%
%
% Stanford VISTA Team

if(~exist('xformToMm','var')),  xformToMm = eye(4); end

% Create blank database for finding some of the sizes to write out
pdb = mtrPathwayDatabase();

% PDB structure will have stat header info, but fg will retain the actual
% statistics per path to record
mmPerStep = sqrt(sum((pathwayMatrix(:,1,1)-pathwayMatrix(:,2,1)).^2));
[pdb,fg] = makeStatsFromStatsVec(pdb,pathwayLengthVec,mmPerStep, statsVec, statsNames);
numstats = length( pdb.pathway_statistic_headers );
numalgs = 0;

% Redo offset
offset = 4*4 + 16*8 + numstats * getStatisticsHeaderSizeWordAligned(pdb) + numalgs * getAlgorithmsHeaderSizeWordAligned(pdb); % 4 uint, 16 doubles

fid = fopen(fileName, 'wb');

% Writing header
fwrite(fid,offset,'uint');
xformToMm = xformToMm';
fwrite(fid,xformToMm(:),'double');
fwrite(fid,numstats,'uint');

% Write stat header
saveStatisticsHeader(pdb,fid);

fwrite(fid,numalgs,'uint');
% Write algorithm header
%saveAlgorithmHeader(pdb,fid);

% Write version info
version = 1;
fwrite(fid,version,'uint');

%Writing path info
numpaths = length(pathwayLengthVec);
fwrite(fid,numpaths,'uint');
fileOffsets = zeros(1,numpaths);
for ff = 1:numpaths
    % Record current file position to write later
    fileOffsets(ff) = ftell(fid);

    path_offset = 3 * 4  + numstats*8; % 4 int, numstats double
    fwrite(fid,path_offset,'int');
    nn = pathwayLengthVec(ff);
    fwrite(fid,nn,'int');
    fwrite(fid,0,'int'); % algo type HACK
    fwrite(fid,1,'int'); % seedpointindex HACK ???

    % Write stats per path
    for as = 1:numstats
        fwrite(fid,fg.params{as}.stat(ff),'double');
    end

    % Writing path nodes
    %pos = fg.fibers{ff}.coords;
    pos = pathwayMatrix(:,1:pathwayLengthVec(ff),ff);
    %pos = mrAnatXformCoords(xformFromAcpc, pos')';
    % Change from 1 based to 0 based positions in mm space
    %pos = pos - repmat([2,2,2]',1,size(pos,2));
    fwrite(fid,pos,'double');

    % Writing stats values per position
    for as = 1:numstats
        % Just write garbage as fiber groups can't handle per point stats
        if( pdb.pathway_statistic_headers(as).is_computed_per_point )
            error('Cannot export fiber group with per point statistics!');
            %fwrite(fid,this.pathways(p).point_stat_array(as,:),'double');
        end
    end
end

% Database Footer
fwrite(fid,fileOffsets,'uint32');

fclose(fid);
return;

function [pdb, fg] = makeStatsFromStatsVec(pdb, lengthVec, mmPerStep, statsVec, statsNames)

fg = dtiNewFiberGroup();
fg.params = {};
fg.params{1}.name = 'Length';
for ff=1:length(lengthVec)
    fg.params{1}.stat(ff) = mmPerStep*(lengthVec(ff)-1);
end
s=length(pdb.pathway_statistic_headers)+1;
pdb = addStatisticHeader(pdb,'Length','NA', 1, 0, 1, 8877+s);

for ii=2:min(length(statsNames)+1,3)
    % Add up to two stats from the provided vectors
    fg.params{ii}.name = statsNames{ii-1};
    for ff=1:size(statsVec,2)
        fg.params{ii}.stat(ff) = statsVec(ii-1,ff);
    end
    s=length(pdb.pathway_statistic_headers)+1;
    pdb = addStatisticHeader(pdb,statsNames{ii-1},'NA', 1, 0, 1, 8877+s);
end

for ii=(length(statsNames)+2):3
    % Add blank stats to fill up to 3 required by pdb format.
    pdb = addBlankStat(pdb,'Blank');
    fg.params{ii}.name = 'Blank';
    fg.params{ii}.stat = zeros(1,length(lengthVec));
end

return;

function pdb = addBlankStat(pdb,agg_name)
s=length(pdb.pathway_statistic_headers)+1;
pdb = addStatisticHeader(pdb,agg_name,'NA', 1, 0, 1, 8877+s);
return;


function fwriten(fid,a,N)
%FWRITEN Write a value to a file in text in a fixed length field.
%
% If A is an integer, it is padded with zeros to the left.  If A is a char,
% it is padded with spaces to the right.  N is the length (in characters)
% of the field.
%
% Written by: Tom Krauss, NGA/IDT
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

if isa(a,'integer')
    % This is the reasonable way to zeropad integers using sprintf, but it
    % doesn't seem to work for very large values of 'a' for versions of
    % MATLAB prior to 2011b.  Must have been a bug in the Matlab sprintf
    % implementation.
    % x = sprintf('%0*d', N, a);
    % The following workaround seems to work for all sizes of integers even
    % on versions of MATLAB prior to the 2011b fix.
    x = sprintf('%0*.0f', N, a);
else
    x = sprintf('%-*s', N, a);
end
fwrite(fid,x(1:N),'uint8');

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function out = numeric(fa)
% Convert to numeric form
% FORMAT numeric(fa)
% fa - a file_array
%__________________________________________________________________________
% Copyright (C) 2005-2018 Wellcome Trust Centre for Neuroimaging

%
% $Id: numeric.m 7501 2018-11-30 12:16:58Z guillaume $

out = full(fa);


% Wrapper for vl_nnlogScale2Scale block
% inputs{1} :   s     : 1 x 1 x 1 x b
% outputs{1}:   y     : 1 x 1 x 1 x b

classdef logScale2Scale < dagnn.Layer
    methods
        function outputs = forward(~, inputs, ~)

            useGPU = isa(inputs{1}, 'gpuArray');
            if useGPU
                outputs{1} = gpuArray( vl_nnlogScale2Scale(gather(inputs{1})) );
            else
                outputs{1} = vl_nnlogScale2Scale(inputs{1});
            end

        end

        function [derInputs, derParams] = backward(~, inputs, ~, derOutputs)

            useGPU = isa(inputs{1}, 'gpuArray');
            if useGPU
                derInputs{1} = gpuArray( vl_nnlogScale2Scale(gather(inputs{1}), gather(derOutputs{1})) );
            else
                derInputs{1} = vl_nnlogScale2Scale(inputs{1}, derOutputs{1});
            end

            derParams = {};
        end

        function outputSizes = getOutputSizes(~, inputSizes)
            outputSizes = inputSizes{1};
        end

        function obj = logScale2Scale(varargin)
            obj.load(varargin);
        end
    end
end


function Model = buildRxnEquations(Model)
% Takes a COBRA model and creates reaction equations based
% on the metabolites list, `S` matrix and bound information. Returns the
% model with the additinal field `rxnEquations`. Is called by `cleanTmodel`, `compareCbModels`, `readCbTmodel`, `verifyModel`, `addSEEDInfo`.
%
% USAGE:
%
%    Model = buildRxnEquations(Model)
%
% INPUTS:
%    Model:     COBRA format model or `Tmodel`
%
% OUTPUTS:
%    Model:     Model with corrected `rxnEquations` field.
%
% Please cite:
% `Sauls, J. T., & Buescher, J. M. (2014). Assimilating genome-scale
% metabolic reconstructions with modelBorgifier. Bioinformatics
% (Oxford, England), 30(7), 1036?8`. http://doi.org/10.1093/bioinformatics/btt747
%
% ..
%    Edit the above text to modify the response to help addMetInfo
%    Last Modified by GUIDE v2.5 06-Dec-2013 14:19:28
%    This file is published under Creative Commons BY-NC-SA.
%
%    Correspondance:
%    johntsauls@gmail.com
%
%    Developed at:
%    BRAIN Aktiengesellschaft
%    Microbial Production Technologies Unit
%    Quantitative Biology and Sequencing Platform
%    Darmstaeter Str. 34-36
%    64673 Zwingenberg, Germany
%    www.brain-biotech.de

if isstruct(Model.lb) % If the function is fed Tmodel, use reaction bounds from first model.
    modelNames = fieldnames(Model.Models) ;
    firstModel = modelNames{1} ;
    lb = Model.lb.(firstModel) ;
    ub = Model.ub.(firstModel) ;
else
    lb = Model.lb ;
    ub = Model.ub ;
end

%% Make the equations.
for iRxn = 1:length(Model.rxns)
    rxnCode = [] ;

    educts   = find(Model.S(:,iRxn) < 0) ;
    products = find(Model.S(:,iRxn) > 0) ;
    if lb(iRxn) < 0 && ub(iRxn) > 0
        arrow = '<==>' ;
    elseif lb(iRxn) >= 0 && ub(iRxn) > 0
        arrow = '-->' ;
    elseif lb(iRxn) < 0 && ub(iRxn) <= 0
        arrow = '<--' ;
    elseif lb(iRxn) == 0 && ub(iRxn) == 0
        arrow = '|||' ;
    else
        disp(['Do not understand directionality of ' Model.rxns{iRxn}])
    end

    if ~isempty(educts)
        for ie = 1:length(educts)
            if Model.S(educts(ie), iRxn) ~= -1
                rxnCode = [rxnCode '(' num2str(-Model.S(educts(ie),iRxn)) ...
                          ') ' Model.mets{educts(ie)} ] ;
            else
                rxnCode = [rxnCode Model.mets{educts(ie)}] ;
            end
            rxnCode = [rxnCode ' + '] ;
        end
        % Note how just 2 positions are substracted from the end, leaving a
        % space.
        rxnCode = [rxnCode(1:end - 2) arrow] ;
    end
    if ~isempty(products)
        rxnCode = [rxnCode ' '] ;
        for ip = 1:length(products)
            if Model.S(products(ip), iRxn) ~= 1
                rxnCode = [rxnCode '(' num2str(Model.S(products(ip), iRxn))...
                          ') ' Model.mets{products(ip)}] ;
            else
                rxnCode = [rxnCode Model.mets{products(ip)}] ;
            end
            rxnCode = [rxnCode ' + '] ;
        end
        rxnCode = rxnCode(1:end - 3) ;
    end

    Model.rxnEquations{iRxn, 1} = rxnCode ;
    clear educts ; clear products; clear ie ; clear ip ; clear rxcode ;
end


function [res] = fiff_pick_channels_evoked(orig,include,exclude)
%
% [res] = fiff_pick_channels_evoked(orig,include,exclude)
%
% Pick desired channels from evoked-response data
%
% orig      - The original data
% include   - Channels to include (if empty, include all available)
% exclude   - Channels to exclude (if empty, do not exclude any)
%
%

%
%   Author : Matti Hamalainen, MGH Martinos Center
%   License : BSD 3-clause
%
%
%   Revision 1.9  2006/10/04 20:12:37  msh
%   Added fiff_read_evoked_all
%   Modified fiff_pick_channels_evoked to handle multiple data sets
%   Added bad channel writing to fiff_write_evoked
%
%   Revision 1.8  2006/06/29 22:12:28  msh
%   Fixed errors in channel picking
%
%   Revision 1.7  2006/04/26 00:14:24  msh
%   Return empty structure from fiff_pick_channels evoked and fiff_pick_types_evoked if there is no match.
%   Accuracy checking was incorrect in mne_add_coil_defs
%
%   Revision 1.6  2006/04/23 15:29:40  msh
%   Added MGH to the copyright
%
%   Revision 1.5  2006/04/22 10:59:30  msh
%   Added fiff_pick_info
%
%   Revision 1.4  2006/04/20 23:33:20  msh
%   Separated fiff_pick_channels and fiff_pick_types from the _evoked versions
%
%   Revision 1.3  2006/04/18 20:44:46  msh
%   Added reading of forward solution.
%   Use length instead of size when appropriate
%
%   Revision 1.2  2006/04/14 15:49:49  msh
%   Improved the channel selection code and added ch_names to measurement info.
%
%   Revision 1.1  2006/04/14 00:45:42  msh
%   Added channel picking and fiff_invert_transform
%
%

me='MNE:fiff_pick_channels_evoked';

if nargin == 1
    res = orig;
    return;
elseif nargin == 2
    exclude = [];
elseif nargin ~= 3
    error(me,'Incorrect number of arguments');
end

if isempty(include) && isempty(exclude)
    res = orig;
    return;
end

sel = fiff_pick_channels(orig.info.ch_names,include,exclude);
if isempty(sel)
    res = [];
    fprintf(1,'Warning : No channels match the selection.\n');
    return;
end

res = orig;
%
%   Modify the measurement info
%
res.info = fiff_pick_info(res.info,sel);
%
%   Create the reduced data set
%
for k = 1:length(res.evoked)
    res.evoked(k).epochs = res.evoked(k).epochs(sel,:);
end

return;

end


function data = seloverdim(data, seldim, sel, fb)

if nargin<4,
  fb = 1;
end

% get all XXXdimord fields
fn    = fieldnames(data);
selfn = find(~cellfun('isempty', strfind(fn, 'dimord')));
fn    = fn(selfn);
for k = 1:numel(fn)
  fndimord{k} = data.(fn{k});
end

% check which XXXdimord fields contain the seldim and keep only those
selx     = ~cellfun('isempty', strfind(fndimord, seldim));
fn       = fn(selx);
fndimord = fndimord(selx);

% extract the selected dimension as number
for k = 1:numel(fn)
  dimtok       = tokenize(fndimord{k}, '_');
  seldimnum{k} = find(strcmp(seldim, dimtok)); % the selected dimension as number
  if numel(seldimnum{k})<1 && strcmp(seldim, 'rpt'),
    %try rpttap
    seldimnum{k} = find(strcmp([seldim,'tap'], dimtok));
    tapflag      = 1;
  else
    tapflag      = 0;
  end
end

% if sum(~cellfun('isempty', seldimnum))<1
%   ft_error('the "%s" dimension is not present in the data', seldim)
% elseif any(cellfun(@numel, seldimnum)>1)
%   ft_error('cannot select over multiple dimensions at the same time')
% end

[reduceddim, fntmp] = dimlength(data);
selx       = find(ismember(fntmp, fn));
reduceddim = reduceddim(selx);
fntmp      = fntmp(selx);

% extract the fieldnames of the parameters of interest
if numel(fntmp)==1 && strcmp(fntmp{1}, 'dimord'),
  % data is not source data
  param      = selparam(data);
  reduceddim = repmat(reduceddim, [1 numel(param)]);
  seldimnum  = repmat(seldimnum,  [1 numel(param)]);
else
  for k = 1:numel(fntmp)
    param{1,k} = fntmp{k}(1:end-6);
  end
end

% make the subselection
for i = 1:numel(param)
  if numel(seldimnum{i})==1,
    if fb, fprintf('selection %s along dimension %d\n', param{i}, seldimnum{i}); end
  else
    if fb, fprintf('selection %s along dimensions %d and %d\n', param{i}, seldimnum{i}(1), seldimnum{i}(2)); end
  end

  reduceddim{i}(seldimnum{i}) = numel(sel);
  tmp       = data.(param{i});
  iscelltmp = iscell(tmp);
  if ~iscelltmp,
    %temporarily convert to cell
    tmp = {tmp};
  else
    %keep cells but reduce seldimnum
    seldimnum{i}  = seldimnum{i} - 1; %FIXME this only works if cell is 1D
    reduceddim{i} = [reduceddim{i}(2:end) 1];
  end

  if seldimnum{i}>0,
    % subselection from each of the cells
    for j = 1:numel(tmp)
      if ~isempty(tmp{j}) && numel(seldimnum{i})==1,
        switch seldimnum{i}
          case 1
            tmp{j} = tmp{j}(sel,:,:,:,:,:,:,:,:);
          case 2
            tmp{j} = tmp{j}(:,sel,:,:,:,:,:,:,:);
          case 3
            tmp{j} = tmp{j}(:,:,sel,:,:,:,:,:,:);
          case 4
            tmp{j} = tmp{j}(:,:,:,sel,:,:,:,:,:);
          case 5
            tmp{j} = tmp{j}(:,:,:,:,sel,:,:,:,:);
          case 6
            tmp{j} = tmp{j}(:,:,:,:,:,sel,:,:,:);
          case 7
            tmp{j} = tmp{j}(:,:,:,:,:,:,sel,:,:);
          case 8
            tmp{j} = tmp{j}(:,:,:,:,:,:,:,sel,:);
          case 9
            tmp{j} = tmp{j}(:,:,:,:,:,:,:,:,sel);
          otherwise
            ft_error('the number of dimensions is too high');
        end
        tmp{j} = reshape(tmp{j}, reduceddim{i});

      elseif ~isempty(tmp{j}) && numel(seldimnum{i})>1,
        if all(seldimnum{i}==[1 2])
          tmp{j} = tmp{j}(sel,sel,:,:,:,:,:,:,:,:);
        elseif all(seldimnum{i}==[2 3])
          tmp{j} = tmp{j}(:,sel,sel,:,:,:,:,:,:,:);
        else
          ft_error('selection of 2 dimensions simultaneously only works for [1 2] or [2 3] at present');
        end
        tmp{j} = reshape(tmp{j}, reduceddim{i});
      end
    end
  else
    % subselection of cells
    tmp = tmp(sel);
  end

  if ~iscelltmp,
    tmp = tmp{1};
  end
  data.(param{i}) = tmp;
end

switch seldim
  case 'rpt'
    if tapflag && isfield(data, 'cumtapcnt') && ~isempty(sel),
      sumtapcnt = cumsum([0;data.cumtapcnt(:)]);
      tapers    = zeros(1, sumtapcnt(end));
      for i=1:length(data.cumtapcnt)
        tapers(sumtapcnt(i)+1:sumtapcnt(i+1)) = i;
      end
      tmpsel    = [];
      tmpsel(1) = tapers(sel(1));
      for i=2:length(sel)
        if tapers(sel(i))~=tapers(sel(i-1))
	        tmpsel(end+1) = tapers(sel(i));
	      end
      end
    else
      tmpsel = sel;
    end
    if isfield(data, 'cumtapcnt'), data.cumtapcnt = data.cumtapcnt(tmpsel,:); end
    if isfield(data, 'cumsumcnt'), data.cumsumcnt = data.cumsumcnt(tmpsel,:); end

    % also try to adjust the trialinfo in the data
    if isfield(data, 'trialinfo')
      data.trialinfo = data.trialinfo(tmpsel, :);
    end

  case 'rpttap'
    % nothing to do
  case 'chan'
    data.label = data.label(sel);
  case 'freq'
    data.freq = data.freq(sel);
  case 'time'
    data.time = data.time(sel);
  case 'pos'
    data      = fixinside(data, 'logical');
    data.pos  = data.pos(sel,:);
    data.inside = data.inside(sel);
    data      = fixinside(data, 'index');
  otherwise
    ft_error('unknown dimension "%s"', seldim);
end


function [curImg] = read_current_image(curFolderPath, imgFilesPathList, iCurImg)
%     NeuroSLAM System Copyright (C) 2018-2019
%     NeuroSLAM: A Brain inspired SLAM System for 3D Environments
%
%     Fangwen Yu (www.yufangwen.com), Jianga Shang, Youjian Hu, Michael Milford(www.michaelmilford.com)
%
%     The NeuroSLAM V1.0 (MATLAB) was developed based on the OpenRatSLAM (David et al. 2013).
%     The RatSLAM V0.3 (MATLAB) developed by David Ball, Michael Milford and Gordon Wyeth in 2008.
%
%     Reference:
%     Ball, David, Scott Heath, Janet Wiles, Gordon Wyeth, Peter Corke, and Michael Milford.
%     "OpenRatSLAM: an open source brain-based SLAM system." Autonomous Robots 34, no. 3 (2013): 149-176.
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.

    % curFolderPath is the path of current sub folder
    % imgFilesPathList is the path set of all image files in current sub
    % folder
    % iCurImg is the index of current image in the path list

    % get the current image name
    curImgName = imgFilesPathList(iCurImg).name;

    % read current image
    curImg =  imread(strcat(curFolderPath,curImgName));

end


function [msh,lights,tenseMsh] = fs_FSMesh2MLR(fsMeshName,mmPerVox, host, id, varargin);
%
%   [msh,lights,tenseMsh] = fs_fs_FSMesh2MLR(fsMeshName,mmPerVox, host, id, varargin);
%
%
% Author: ARW (based on RFD's mrmBuildMesh)
% Purpose:
%  Take a mesh in Freesurfer sorf format and convert it into a mrLoadRet /
%  mrMesh-type mesh. Allows you to do the usual mrMesh type things like
%  relaxation...
%  Additional processing options can be set as well.  These
%  options include:
%     * 'RelaxIterations'- the next value specifies how many extra smoothing iterations.
%     * 'SavetenseMsh'- if you specify any RelaxIterations and add this option, then
% you will get two meshes- the relaxed mesh and the unrelaxed (tense) mesh.
%
% Returns:
% The mesh structure and a lights structure.
% You can also get the unrelaxed mesh out (tenseMsh).
%
% See Also
%  mrmMapVerticesToGray
%
mrGlobals;

summaryParams = 1;
meshName = '';
QueryFlag = 1;
relaxIter = 1;
saveTense = 0;
backColor = [0.2 0 0];  % CHanged from Gray - partly just to keep track of EMSE meshes...

if (ieNotDefined('fsMeshName'))
   [fsMeshName,fsPath]=uigetfile('*.*','Pick a freesurfer mesh file');
   fsMeshName=fullfile(fsPath,fsMeshName);

end
if ieNotDefined('mmPerVox'),
    mmPerVox=[1 1 1];
    disp('Assuming 1 1 1 mm per vox');
end
if ieNotDefined('host'), host = 'localhost'; end
if ieNotDefined('id'), id = 1; end

if(nargout>2),  saveTense = 1; end

% Parse the varargin values
for(ii=1:length(varargin))
    if    (strcmpi(varargin{ii}, 'RelaxIterations')), relaxIter = varargin{ii+1};
    elseif(strcmpi(varargin{ii}, 'QueryFlag')),       QueryFlag = varargin{ii+1};
    elseif(strcmpi(varargin{ii}, 'MeshName')),        meshName = varargin{ii+1};
    elseif (strcmpi(varargin{ii},'Background')),      backColor = varargin{ii+1};
    end
end

% Set initial parameters for the mesh.
msh = meshDefault(host,id,mmPerVox,relaxIter,meshName);

if QueryFlag,  msh = meshQuery(msh,summaryParams); end

% If the window is already open, no harm is done.
msh = mrmInitHostWindow(msh)

[msh, lights] = mrmInitMesh(msh,backColor);

disp('Importing mesh data from Freesurfer')
%

% ***************************************************************
% We need to construct a basic msh structure from the file data.

[path,name,ext,ver]=fileparts(fsMeshName);

if (strcmp(upper(ext),'.TRI'))
    [vertex,face]=freesurfer_read_tri(fsMeshName);
else

[vertex,face]=freesurfer_read_surf(fsMeshName);
end

% Now  I'm a little curious about this: It seemed to me that the faces in
% regular FS mesh files are in quad format (4 vertices per face). And so I
% don't understand how mrMesh can deal with this. But using mris_convert
% you can generate triangle mesh files from regular freesurfer meshes so I
% propose to do this and avoid all confusion.

size(vertex)
size(face)
vertex=vertex'+128;
face=face';
%face=face([3 2 1],:);

% Vertices come back rotated in an odd way. The entire cortex is rotated 90
% degrees about the L/R axis (i.e. the sag view is rotated 90degrees
% anticlockwise).
vertex=vertex([2 3 1],:);

v=squeeze(vertex([1,2],:));
v=v-128;

rotMat=[-1 0;0 -1];
v=v'*rotMat;
vertex([1,2],:)=v'+128;

nVerts=length(vertex);

p.vertices=(vertex);
p.triangles=face;
p.triangles = p.triangles - 1
p.class='mesh';

% Sometimes we pass in the whole array of meshes.  Mostly, just one,
% though.
host = meshGet(msh,'host');
windowID = meshGet(msh,'windowid');

if isempty(host), host = 'localhost'; end

if isempty(windowID), error('Mesh must specify a window'); end
[id, status, result] = mrMesh ('localhost', windowID, 'add_actor', p)
p.scale = meshGet(msh,'mmPerVox');
p = setSmooth(p,msh,1);
p = setDecimate(p,msh,1);
p.actor = meshGet(msh,'actor');
disp(p);

p.colors=ones(4,length(p.vertices))*255;

[a,b,c]=mrmesh('localhost',id,'set_mesh',p)

        p.scale = meshGet(msh,'mmPerVox');
        p = setSmooth(p,msh,1);
        p = setDecimate(p,msh,1);
        %p.actor = meshGet(msh,'actor');
        p.normals=mrmGet(msh,'normals');
        p.actor = meshGet(msh,'actor');

%disp('Building smoothed and decimated mesh for display...');
%mrmSet(msh,'buildMeshAndDecimateAndSmooth',voxels);
%[msh] = mrmSet(msh,'smooth')

% This is a little 'center object' routine.  We could put this into mrmSet,
% really.

vertices = mrmGet(msh,'vertices');
%keyboard
mrmSet(msh,'origin',-mean(vertices'));

% Save these unsmoothed data.
unSmoothedData = mrmGet(msh,'data');
msh.initVertices=vertices;
msh.grayLayers=3;
view=getSelectedVolume;
    disp('Finding vertex to gray map');
    % You have to do this before smoothing...

v2gMap = mrmMapVerticesToGray(vertices,view.nodes,[1 1 1],view.edges,4);

msh.vertexGrayMap=v2gMap;
msh.grayToVertexMap = mrmMapGrayToVertices(view.nodes,vertices, [1 1 1]);

% Now also try to find the other mapping: the mapping from the mesh to all
% the gray nodes: So that we can ask: for any arbitray gray node, which
% mesh point is it closest to?

% If we smooth, the mesh, it is done here.
if(relaxIter>0)

    mrmSet(msh,'smooth');

    % We get the curvature colors from the uninflated mesh
    % We should figure out the correct value to use as our threshold. The mean
    % isn't ideal, since it is moved around by the large areas with arbitrary
    % curvature (eg. corpus callosum). What we really want is the value that
    % corresponds to zero curvature.
    % maybe make the specific curvature map colors adjustable?
    % We now use the actual curvature values, so we know that zero is zeros
    % curvature.
    disp('Setting up the curvature colors');

    % Attach curvature data to the mesh.  We turn on the color later, I think.
    msh=mrmSet(msh,'curvature');
    p.colors=[repmat(msh.curvature,3,1); ones(1,length(msh.curvature))]*255;
    %[a,b,c]=mrmesh('localhost',id,'set_mesh',p)

    curvColorIntensity = 128*meshGet(msh,'curvatureModDepth'); % mesh.curvature_mod_depth;

    monochrome = uint8(round((double(msh.curvature>0)*2-1)*curvColorIntensity+127.5));

    msh = mrmSet(msh,'colors',monochrome);

    disp('Storing the smoothed mesh data computed by mrMesh...');
    data = mrmGet(msh,'data');
    msh = meshSet(msh,'data',data);
    msh = meshSet(msh,'connectionMatrix',1);

else
    % We don't smooth the data with mrMesh.  We just assign it.
    msh = meshSet(msh,'data',unSmoothedData);
    msh = meshSet(msh,'connectionMatrix',1);
end

% In either case, we return a version of the data without smoothing.  This
% mesh is used to register with the gray coordinates.  This is a little
% disorganized.  If we don't smooth, tenseMesh is identically msh.
tenseMsh = msh;
tenseMsh = meshSet(tenseMsh,'data',unSmoothedData);

return;

%---------------------------------------
function p = setSmooth(p,mesh,val)
if val
    p.do_smooth = 1;
    p.smooth_iterations = meshGet(mesh,'smoothiterations');
    p.smooth_relaxation = meshGet(mesh,'smoothrelaxation');
    p.smooth_sinc_method = meshGet(mesh,'smoothmethod');
    p.do_smooth_pre = meshGet(mesh,'smooth_pre');
else
    p.do_smooth = 0;
    p.do_smooth_pre = 0;
end

return;

%----------------------------
function p = setDecimate(p,mesh,val)

if val
    p.do_decimate = 1;
    p.decimate_reduction = meshGet(mesh,'decimatereduction');
    p.decimate_iterations = meshGet(mesh,'decimateiterations');
else
    p.do_decimate = 0;
end

return;


function [roiFile, labelFile] = roiSaveAllAsNifti(vw, fname)
% Export all current ROIs in a mrVista view as a nifti segmentation file,
% with each ROI corresponding to a different layer (integer) in the nifti
% file.
%
% [roiFile, labelFile] = roiSaveAllAsNifti(vw, fname, roiColor)
%
% Oct 2008: JW
%
% See roiSaveAsNifti.m

% global variables
mrGlobals;

% get the view struct
if notDefined('vw'), vw = getCurView; end

% check that it's a gray or volume view
viewType = viewGet(vw, 'viewtype');
switch lower(viewType)
    case {'gray', 'volume'}
        nROIs = numel(vw.ROIs);
    otherwise
        error('[%s]: Must be in gray view', mfilename);
end

% check the name of the file to save
if notDefined('fname'),
    fname = [fileparts(vANATOMYPATH) filesep  'ROIs-' datestr(now,1) '.nii.gz'];
end

% make a 3D image with all points set to zero except ROI = roiColor
roiData = zeros(size(vw.anat));

% loop through all ROIs in view struct
for rois = 1:nROIs
    vw = viewSet(vw, 'selected ROI', rois);

    %get ROI coords
    coords = getCurROIcoords(vw);
    nVoxels = size(coords, 2);

    % assign all voxels within the ROI a unique value (the roinum). this
    % will be the label number if the nifti file is imported to itkGray.
    thelabel = rois;

    for voxel = 1:nVoxels
        roiData(coords(1,voxel), coords(2,voxel),coords(3,voxel)) = thelabel;
    end

end

% save the file
roiFile = niftiSaveVistaVolume(vw, roiData, fname);

% create a label file for itkGray
useV1V2V3V4colors = true;
labelFile = saveLabels(vw, useV1V2V3V4colors);

message = sprintf...
    ('ROI file saved as %s.\n\nLabel file save as %s.', roiFile, labelFile);
disp(message);

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

end

%------------------------------------------------------------------------
function fname = saveLabels(vw, useV1V2V3V4colors)
% create and save an itkGray-compatible label file

mrGlobals
if notDefined('useV1V2V3V4colors'), useV1V2V3V4colors = true; end

% create a blank file
fname = [fileparts(vANATOMYPATH) filesep  'ROIs-' datestr(now,1), '.lbl'];
fid = fopen(fname, 'w');

% print some typical headers
h{1} = '################################################';
h{2} = '# ITK-SnAP Label Description File';
h{3} = '# File format:';
h{4} = '# IDX   -R-  -G-  -B-  -A--  VIS MSH  LABEL';
h{5} = '# Fields:';
h{6} = '#    IDX:   Zero-based index ';
h{7} = '#    -R-:   Red color component (0..255)';
h{8} = '#    -G-:   Green color component (0..255)';
h{9} = '#    -B-:   Blue croiSaveAllForItkGrayolor component (0..255)';
h{10} = '#    -A-:   Label transparency (0.00 .. 1.00)';
h{11} = '#    VIS:   Label visibility (0 or 1)';
h{12} = '#    IDX:   Label mesh visibility (0 or 1)';
h{13} = '#  LABEL:   Label description ';
h{14} = '################################################';
for ii = 1:14
    fwrite(fid, sprintf('%s\n',h{ii}));
end

% count the ROIs
nROIs = length(viewGet(vw, 'ROIs'));

% make some colors for the different labels
theColors   = hsv(nROIs);

% type out a line of text into the label file for each ROI
for roi = 1:nROIs

    c = theColors(roi, :);
    rname = vw.ROIs(roi).name;
    % -----------------------------------------------------------
    if useV1V2V3V4colors
        % force a color scheme on the labels for v1/v2/v3/v4
        if strfind(lower(rname), 'v1')
            c = [255 0 0 ];
        elseif strfind(lower(rname), 'v2')
            c = [255 255 0 ];
        elseif strfind(lower(rname), 'v3a')
            c = [255 255 255];
        elseif strfind(lower(rname), 'v3b')
            c = [0 255 255];
        elseif strfind(lower(rname), 'v3')
            c = [0 255 0 ];
        elseif strfind(lower(rname), 'v4')
            c = [0 0 255 ];
        elseif strfind(lower(rname), 'vo1')
            c = [255 255 255 ];
        elseif strfind(lower(rname), 'vo2')
            c = [0 255 255 ];
        elseif strfind(lower(rname), 'lo1')
            c = [255 0 255 ];
        elseif strfind(lower(rname), 'lo2')
            c = [127 127 255 ];
        elseif strfind(lower(rname), 'to1')
            c = [0 255 0];
        elseif strfind(lower(rname), 'to2')
            c = [255 0 0 ];
        end
    end
    %------------------------------------------------------------

    a = sprintf('%d\t%d\t%d\t%d\t1\t1\t1\t"%s"\n', ...
        roi, c(1), c(2), c(3), rname);
    fwrite(fid, a);
end
%fclose('all');

end