function som_trajectory(bmus,varargin)

%SOM_TRAJECTORY Launch a "comet" trajectory visualization GUI.
%
%  som_show(sM,'umat','all')
%  bmus = som_bmus(sM,sD);
%  som_trajectory(bmus)
%  som_trajectory(bmus, 'data1', sD, 'trajsize', [12 6 3 1]')
%  som_trajectory(bmus, 'data1', sD.data(:,[1 2 3]), 'name1', {'fii' 'faa' 'foo'})
%
% Input arguments ([]'s are optional):
%   bmus      (matrix) size Nx1, vector of BMUS
%   ['argID', (string) Other arguments can be given as 'argID', value
%    value]   (varies) pairs. See list below for valid values.
%
% NOTE: the GUI only works on a figure which has been made with SOM_SHOW.
%
% Here are the valid argument IDs (case insensitive) and associated values:
%  'color'      string 'xor' or ColorSpec, default: 'xor'.
%               (default: according to lattice as in som_cplane)
%  'TrajSize'   vector of size Nx1 to define the length of comet
%               (N) and size of the comet dots in points.
%               default: [16 12 10 8 6 4]'
%  'Data1'      SOM Toolbox data struct or matrix. The size of
%               data matrix (in data struct the field .data) is
%               Nxd, where N must be the same as the amount of
%               BMUS given in the first input argument 'bmus'
%               This data is shown in a new window in d subplots.
%               Default: BMU indices (first input argument)
%  'Name1'      cell array of d strings which contains names
%               for the components in 'Data1'. If 'Data1' is a SOM
%               Toolbox data struct, the existing component names
%               are overdone.
%  'Figure'     scalar that must be a handle to an existing figure
%               which has been made using SOM_SHOW function.
%               Default: current active figure (gcf).
%
% The following tools can be found in the 'Tools' -menu.
%
%  Remove Trajectory: removes trajectory from the map.
%  Dye Nodes        : opens GUI for selecting color for the nodes
%                     and points selected.
%  Clear Markers    : removes markers from map and data figure.
%  Save             : saves the current situation as a struct.
%  Load             : loads the struct from workspace and draws markers.
%
% Mouse operation
%
%  In data window: Left button is used to drag the operation point ruler
%                  if left button is on blank area, it starts
%  In map window : Left button starts a polygon; right button
%                  finishes; middle button toggles a unit.
%
% SOM_TRAJECTORY is an application for observing trajectory behavior.
%
% Using mouse the line in data figure can be dragged and the
% trajectory moves in the SOM SHOW figure. It is also possible to move
% trajectory by pressing keys '>' and '<' when mouse pointer is above
% data figure.
%
% Regions can be chosen from the data and the points in that region
% are mapped to the component planes. Regions can be chosen also in
% the map.  In this situation data points and map nodes are also
% marked (Left mouse button adds point to the polygon indicating the
% region and right button finals the polygon). By clicking a node (the
% middle button) that node is either added or removed from selection.
%
% It should be noticed that choosing intervals from data may cause
% situations that seem to be bugs. If there exisist marks of different
% color, removing them by clicking the map may left some marks in the
% data, because more than one point in the data is mapped to the same
% node in the map and the removing operation depends on the color of
% the marks. However, all the marks can be removed by using the 'Clear
% Markers' -operation.
%
% FEATURES
%
% The first input argument 'bmus' may also be a munits x N matrix
% In this case each column defines a "fuzzy response". That is,
% each column defines a hit histogram function). The element
% bmus(i,t) sets the size of marker on unit i at time t.
% NOTE: - in this case no regions can be selcted on the map!
%       - only > and < keys can be used to move the operation point
%         line: it can't be dragged
%       - "fuzzy response is always black (hope this will be fixed)
%
% It is possible to open a second data window showing different data:
% use indetifiers 'Data2' (and 'Name2'). The argument syntax is
% identical to 'Data1' (and 'Name1').
%
% See also SOM_SHOW, SOM_SHOW_ADD, SOM_BMUS.

% Contributed to SOM Toolbox 2.0, February 11th, 2000 by Johan
% Himberg and Juha Parhankangas
% Copyright (c) 2000 by the Johan Himberg and Juha Parhankangas
% http://www.cis.hut.fi/projects/somtoolbox/

% Check arguments

error(nargchk(1,Inf,nargin)); % Check no. of input arguments

%% Init input argument struct (see subfunction)
Traj=iniTraj(bmus);

% Check tentative BMU input validity

if ~vis_valuetype(bmus,{'nxm'}),
  error(['First input should be a vector of BMU indices or' ...
	 ' a "response matrix"']);
end

%% Check optional arguments
for i=1:2:length(varargin)
  identifier=lower(varargin{i});
  value=varargin{i+1};

  % Trajectory color
  switch identifier
   case 'color'
    if isempty(value)
      value='xor';
    end
    if vis_valuetype(value,{'colorstyle','xor'})
      Traj.color=value;
    else
      error('''Color'' has to be ColorSpec or string ''xor''.');
    end

   % 1st data
   case 'data1'
    if isempty(value),
      value=[];
    elseif vis_valuetype(value,{'nxm'})
      Traj.primary_data=value;
    elseif isstruct(value) && isfield(value,'type') && ...
	  ischar(value.type) && strcmp(value.type,'som_data'),
      Traj.primary_data=value.data;
      if isempty(Traj.primary_names),
	Traj.primary_names=value.comp_names;
      end
    end

    % 2nd data
   case 'data2'
    if isempty(value),
      value=[];
    elseif vis_valuetype(value,{'nxm'})
      Traj.secondary_data=value;
    elseif isstruct(value) && isfield(value,'type') && ...
	  ischar(value.type) && strcmp(value.type,'som_data'),
      Traj.secondary_data=value.data;
      if isempty(Traj.secondary_names),
	Traj.secondary_names=value.comp_names;
      end
     end

   % Trajectory length & size
   case 'trajsize'
    if isempty(value),
      Traj.size=[16 12 10 8 6 4]';
    end
    if vis_valuetype(value,{'nx1'})
      Traj.size=value
    else
      error('''TrajSize'' has to be a nx1 vector.');
    end

   % Names for first data variables
   case 'name1'
    if isempty(value),
      Traj.primary_names=[];
    elseif ~vis_valuetype(value,{'cellcolumn_of_char'}),
      error('''Name1'': variable names must be in a cell column array.')
    else
      Traj.primary_names = value;
    end
   % Names for 2nd data variables
   case 'name2'
    if isempty(value),
      Traj.secondary_names=[];
    elseif ~vis_valuetype(value,{'cellcolumn_of_char'}),
      error('''Name2'': variable names must be in a cell column array.')
    else
      Traj.secondary_names = value;
    end

   % Figure number
   case 'figure'
    if isempty(value)
      Traj.figure='gcf';
    end
    if vis_valuetype(value,{'1x1'})
      Traj.figure=value;
    else
      error('''Figure'' should be number of an existing figure.')
    end
  end
end

%% Get SOM data from figure
[h,msg,lattice,msize,dim]=vis_som_show_data('all',Traj.figure);

%% Not a SOM_SHOW figure?
if ~isempty(msg);
  error('Figure is invalid: use SOM_SHOW to draw the figure.');
end

% Get map size from figure data
Traj.lattice=lattice;
Traj.msize=msize;
if length(msize)>2,
  error(['This function works only with 2D maps: figure contains' ...
	 ' something else.']);
end
munits=prod(msize);

% Check BMU (or response) and map match

if vis_valuetype(bmus,{'nx1'});
  if max(bmus)>prod(msize) || min(bmus) <1
    error('BMU indexes out of range.')
  elseif any(round(bmus)~=bmus)
    error('BMU indexes must be integer.');
  elseif isempty(Traj.primary_data),
    Traj.primary_data=bmus;
  end
elseif size(bmus,1) ~= munits
  error(['Response matrix column number must match with the number of' ...
	 ' map units.']);
else
  bmus=bmus';
  if isempty(Traj.primary_data),
    Traj.primary_data=[1:size(bmus,1)]';
    Traj.primary_names={'BMU Index'};
  end
end

size1=size(Traj.primary_data);
size2=size(Traj.secondary_data);

% Data2 must not be defined alone

if isempty(Traj.primary_data)&&~isempty(Traj.secondary_data),
  error('If ''Data2'' is specified ''Data1'' must be specified, too.');
elseif ~isempty(Traj.secondary_data) ...
      && size1~= size2
  % If data1 and data2 exist both, check data1 and data2 match
  error('''Data1'' and ''Data2'' have different amount of data vectors.')
end

% Check BMU and data1 match (data2 matches with 1 anyway)

if ~isempty(Traj.primary_data) && size(bmus,1) ~= size1,
  error(['The number of data vectors in ''data1'' must match with' ...
	 ' the number of rows in the first input argument (bmus).']);
end

% Check that number of names and data dimension is consistent

if ~isempty(Traj.primary_names) && (size1(2)~=length(Traj.primary_names)),
  error('Number of component names and ''Data1'' dimension mismatch.');
end
if ~isempty(Traj.secondary_names) && ...
      (size2(2)~=length(Traj.secondary_names)),
  error('Number of component names and ''Data2'' dimension mismatch.');
end

%% Call the function that does the job
vis_trajgui(Traj);

%%% Subfunctions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function Traj=iniTraj(bmus)

Traj.figure=gcf;
Traj.primary_data=[];
Traj.secondary_data=[];
Traj.primary_names = [];
Traj.secondary_names = [];
Traj.size=[16 12 10 8 6 4]';
Traj.bmus=bmus;
Traj.color='xor';
Traj.msize=[];
Traj.lattice=[];


function blockface_correct_perspective(indir, files55, tform, outdir)
% blockface_correct_perspective  Correct perspective of Bewster angle
% blockface images.
%
% blockface_correct_perspective(INDIR, FILES55, TFORM, OUTDIR)
%
%   INDIR is a string with the directory where the input files are kept.
%
%   FILES55 is the result of a dir() command with a list of the files to
%   correct.
%
%   TFORM is a projective2d object with the transform correction for the
%   frames.
%
%   OUTDIR is a string with the directory for output images. INDIR and
%   OUTDIR must be different to avoid overwriting the input files.

% Author: Ramon Casero <rcasero@gmail.com>
% Copyright © 2014 University of Oxford
% Version: 0.2.0
%
% University of Oxford means the Chancellor, Masters and Scholars of
% the University of Oxford, having an administrative office at
% Wellington Square, Oxford OX1 2JD, UK.
%
% This file is part of Gerardus.
%
% 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. The offer of this
% program under the terms of the License is subject to the License
% being interpreted in accordance with English Law and subject to any
% action against the University of Oxford being under the jurisdiction
% of the English Courts.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see
% <http://www.gnu.org/licenses/>.

% check arguments
narginchk(4, 4);
nargoutchk(0, 0);

% safety to prevent us from overwriting the source files
if (strcmp(indir, outdir))
    error('Input and output directory are the same, and files would be overwritten')
end

% if destination directory doesn't exist, create it
if (isempty(dir(outdir)))
    ok = mkdir(outdir);
    if (~ok)
        error(['Cannot create output directory: ' outdir])
    end
end

% correct perspective
for I = 1:length(files55)

    % load image
    im55 = imread([indir filesep files55(I).name]);

    % make output image the same size and coordinates as input image
    ra = imref2d(size(im55));

    % correct perspective
    im55to90 = imwarp(im55, tform, 'cubic', ...
        'OutputView', ra, 'FillValues', double(min(im55(:))));

%     % plot results
%     subplot(2, 1, 1)
%     imagesc(im55)
%     subplot(2, 1, 2)
%     imagesc(im55to90)

    % save result
    imwrite(im55to90, [outdir filesep files55(I).name]);

end


% Copyright (C) 2017-2018 Titus Cieslewski, RPG, University of Zurich,
%   Switzerland
%   You can contact the author at <titus at ifi dot uzh dot ch>
% Copyright (C) 2017-2018 Siddharth Choudhary, College of Computing,
%   Georgia Institute of Technology, Atlanta, GA, USA
% Copyright (C) 2017-2018 Davide Scaramuzza, RPG, University of Zurich,
%   Switzerland
%
% This file is part of dslam_open.
%
% dslam_open 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.
%
% dslam_open 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 dslam_open. If not, see <http://www.gnu.org/licenses/>.

function [match_accepted, consistent_group] = ...
    acceptMatchIfConsistent(match, query_frame_i, query_robot_i, ...
    decentr_state, params)

this.frame_i = query_frame_i;
this.match = match;

robot_state = decentr_state{query_robot_i};
consistent_group = robot_state.consistent_groups{match.robot_i};

% Remove out-of-range matches from consistent group.
in_range = cellfun(@(x) areFramesWithinRange(robot_state, ...
            x.frame_i, query_frame_i, ...
            params.robust_relpose_consistency_range), ...
            consistent_group.members);
consistent_group.members = consistent_group.members(in_range);

% If all members were removed, group is forcibly floating again.
if (all(~in_range))
    consistent_group.floating = true;
end

% If consistent (or group empty), add current match.
consistent = cellfun(@(x) areMatchesConsistent(decentr_state, ...
    query_robot_i, x, this, params.robust_relpose_position_tolerance), ...
    consistent_group.members);
if (all(consistent))  % all([]) = true according to spec
    consistent_group.members = [consistent_group.members; this];
elseif (consistent_group.floating)
    % If not consistent and group was floating, replace group with this.
    consistent_group.members = {this};
    match_accepted = false;
    return;
else
    match_accepted = false;
    return;
end

% We only get here if the match has been added to the consitent group.

if ~consistent_group.floating
    match_accepted = true;
else
    if numel(consistent_group.members) > params.robust_relpose_min_group_size
        consistent_group.floating = false;
        match_accepted = true;
    else
        match_accepted = false;
    end
end

end


function brewermap_plot()
% Simple plot of all ColorBrewer colorscheme nodes in one figure.
%
% (c) 2014-2022 Stephen Cobeldick
%
%%% Syntax:
% brewermap_plot()
%
% See also BREWERMAP BREWERMAP_VIEW CUBEHELIX MAXDISTCOLOR
% LBMAP PARULA LINES RGBPLOT COLORMAP COLORBAR PLOT PLOT3 AXES SET

[mcs,nmn,pyt] = brewermap('list');
%
persistent cbh axh
%
xmx = max(nmn);
ymx = numel(pyt);
%
if ishghandle(cbh)
	figure(cbh);
	delete(axh);
else
	cbh = figure('HandleVisibility','callback', 'IntegerHandle','off',...
		'NumberTitle','off', 'Name',mfilename,'Color','white',...
		'MenuBar','figure', 'Toolbar','none', 'Tag',mfilename);
	set(cbh,'Units','pixels')
	pos = get(cbh,'Position');
	pos(1:2) = pos(1:2) - 123;
	pos(3:4) = max(pos(3:4),[842,532]);
	set(cbh,'Position',pos)
end
%
axh = axes('Parent',cbh, 'Color','none',...
	'XTick',0:xmx, 'YTick',0.5:ymx, 'YTickLabel',mcs, 'YDir','reverse');
title(axh,'ColorBrewer Color Schemes (brewermap.m)', 'Interpreter','none')
xlabel(axh,'Scheme Nodes')
ylabel(axh,'Scheme Name')
axf = get(axh,'FontName');
%
for y = 1:ymx
	N = nmn(y);
	M = brewermap(N,mcs{y});
	for x = 1:N
		patch([x-1,x-1,x,x],[y-1,y,y,y-1],1, 'FaceColor',M(x,:), 'Parent',axh)
	end
	text(xmx+0.1,y-0.5,pyt{y}, 'Parent',axh, 'FontName',axf)
end
%
drawnow()
%
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%brewermap_plot


function vline(x, varargin)

% VLINE plot a vertical line in the current graph

abc = axis;
x = [x x];
y = abc([3 4]);
if length(varargin)==1
  varargin = {'color', varargin{1}};
end
h = line(x, y);
set(h, varargin{:});


function view = measureCortMag(view, roiList, paramsFile)
%
% USAGE: view = measureCortMag(view, [roiList], [paramsFile])
%
%   view: can be either flat or gray.
%
%   roiList: a list of ROI indices specifying which ROIs to
%            use for the cortMag. Defaults to all ROIs.
%
%   paramsFile: a .mat file into which the parms will be saved.
%               If the file exists, it will be read in and those
%               params used as a starting point.
%
% PURPOSE:
%   Compute cortical magnification measurements.
%
% NOTE:
%   This code assumes that the ROI coords have been sorted such that the fovea
%   is at the beginning of the list! If you generate your ROIs with mrFindAreas,
%   you will be fine (it sorts the coords for you). If you draw them with the
%   'create line ROI' tool, you should be OK as long as you start all your lines
%   in the fovea. Otherwise, you are on your own.
%
% FIX THESE ISSUES:
%   * The inter-bin distance should be computed via paths only through layer 1.
%   * The inter-bin distance is currently computed from the bin edge, not the bin center.
%     This won't affect the distance measures, but it does bias the mean phase estimate
%     for each bin. It is tricky to adjust for this- the bins are not regularly spaced.
%
% HISTORY:
%   2002.01.22 RFD (bob@white.stanford.edu): wrote it, porting some
%   code from mrLoadRet-2.5. However, I've taken a fresh start and attempted
%   to streamline things and make them more modular.
%
% 7/16/02 djh, replaced mrSESSION.vAnatomyPath with global vANATOMYPATH

% We need vANATOMYPATH and mrSESSION.subject
global vANATOMYPATH;
global mrSESSION;
global dataTYPES;

if(~exist('roiList','var') | isempty(roiList))
    % default to all
    roiList = [1:length(view.ROIs)];
end

% We currently only support Flat view. All analyses are performed on
% flat view ROIs that _should_ be created by an atlas fit.
%
switch(view.viewType)
case {'Inplane','Volume','Gray'}
    error([mfilename,' doesn''t work for ',view.viewType,'.']);
case 'Flat'
    % Get a gray structure because we need the gray nodes.
    grayView = getSelectedGray;
    if isempty(grayView)
        grayView = initHiddenGray;
    end
    cortMag.ROIs = view.ROIs(roiList);
otherwise
    error([view.viewType,' is unknown!']);
end

if(~exist('paramsFile','var') | ~exist(paramsFile,'file'))
    cortMag.paramsFileName = [];
    nowNum = now;
    cortMag.subject = mrSESSION.subject;
    cortMag.timestamp = datestr(nowNum);
    cortMag.mmPerPix = readVolAnatHeader(vANATOMYPATH);
    cortMag.coThresh = getCothresh(view);
    cortMag.expNumber = getCurScan(view);
    cortMag.nodeIndices = {};
    cortMag.bins = {};
    cortMag.dataType = getDataTypeName(view);
    cortMag.subdir = view.subdir;
    % This is a crude method for determining the hemisphere- but it usually works.
    if(cortMag.ROIs(1).coords(3,1)==1)
        cortMag.hemisphere = 'left';
        cortMag.slice = 1;
    else
        cortMag.hemisphere = 'right';
        cortMag.slice = 2;
    end

else
    % this file _should_ contain a cortmag struct.
    % Note that this file, if it exists, will form the starting point.
    % We will overwrite many of it's fields below.
    tmp = load(paramsFile);
    if(isfield(tmp,'cortMag'))
        cortMag = tmp.cortMag;
    else
        warning(['Params file "',paramsFile,'" exists, but it does '...
                'not contain a cortMag struct! It will be ignored.']);
    end
    % Just in case the paramsFileName is not correct:
    cortMag.paramsFileName = paramsFile;
    % If we were really cool, we'd check the validity of the loaded cortmag struct.
end

% Make sure that the correct data is loaded in the grayView
grayView.curDataType = existDataType(cortMag.dataType);
grayView = loadCorAnal(grayView);

if(isempty(cortMag.paramsFileName))
    cortMag.paramsFileName = ['CortMag_',cortMag.subdir,'_',cortMag.dataType,'_',cortMag.hemisphere,'_',...
            datestr(nowNum,11),datestr(nowNum,5),datestr(nowNum,7),'.mat'];
end

% At this point, we have a gray struct and we have a set of ROIs in flat coordinates.
% Note that the ROI coords are assumed to be sorted already (with the fovea at the
% beginning of the list).

% Now, we should call a function that computes cortmag. But, I'd like to keep all UI code
% here, so we'll do it like this:

satisfied = 0;
while(~satisfied)
    resp = getCortMagParams(cortMag);
    if(~isstruct(resp) & resp==0)
        % a 0 means the user cancelled- they are aborting or are satisfied
        satisfied = 1;
    else
        % if it's not 0, then it's an updated cortMag struct
        oldCortMag = cortMag;
        cortMag = resp;

        % Finding the nodes for each coordinate in all the ROIs is slow. Once we do it, we
        % needn't do it again unless we get a new ROI added to the list. That is, none of the
        % cortMag parameters will affect the nodes that are found. So, we don't need to redo it
        % if a parameter changes.
        if(isempty(cortMag.nodeIndices))
            cortMag = buildCortMagNodes(cortMag, view, grayView);
            cortMag.bins = [];
        end
        % Finding the bins is somewhat slow. We only really need to redo it if the binSize changes.
        if( isempty(cortMag.bins) | cortMag.binDist ~= oldCortMag.binDist ...
            | cortMag.expNumber ~= oldCortMag.expNumber | cortMag.flatDataFlag ~= oldCortMag.flatDataFlag)
            cortMag = buildCortMagBins(cortMag, view, grayView);
            if(strmatch('Atlases', dataTYPES(view.curDataType).name))
                phaseScale = dataTYPES(view.curDataType).atlasParams(cortMag.expNumber).phaseScale(cortMag.slice);
                phaseShift = dataTYPES(view.curDataType).atlasParams(cortMag.expNumber).phaseShift(cortMag.slice);
                fprintf('Fixing atlas phases with shift (%.2f) and scale (%.2f)...', phaseShift, phaseScale);
                for(ii=1:length(cortMag.data))
                    cortMag.data{ii}.ph = mod((cortMag.data{ii}.ph - phaseShift) / phaseScale, 2*pi);
                end
            end
        end
        cortMag = computeCortMagFunction(cortMag, view, grayView);
        plotCortmagResults(cortMag);
    end
end

view.cortMag = cortMag;
if(~isempty(cortMag.paramsFileName))
    %if(~exist(cortMag.paramsFileName,'file') | strcmp(questdlg('Overwrite existing cortMag file?', 'Overwrite?', 'Yes', 'No', 'Yes'),'Yes'))
    if(exist(cortMag.paramsFileName,'file'))
        disp([cortMag.paramsFileName,' exists- launching gui to save it somewhere else.']);
        uisave({'cortMag'}, cortMag.paramsFileName);
    else
        save(cortMag.paramsFileName, 'cortMag');
        disp([cortMag.paramsFileName,' saved.']);
    end
end
return;


function [vid_ycbcr] = convert_rgb2ycbcr(vid_rgb)

N = length(vid_rgb);

for i = 1: N
    vid_ycbcr{i} = rgb2ycbcr(vid_rgb{i});
end


function x = esvm_hog(I, sbin)
%Return the current feature function, same as esvm_features

if nargin == 0
  x = 31;
  return
end

x = features_pedro(I,sbin);
%x = features_raw(I,sbin);


function result = parseBC(N, BC, type)
%PARSEBC  Parse boundary conditions for CHEBOP object.
%   This method is not intended for end users. For information about boundary
%   conditions in CHEBOPs, see CHEBOP.
%
%   This method is invoked by the set methods for LBC, RBC, and BC. The types
%   and meanings of the allowed input are described in the documentation for
%   CHEBOP. The result is either empty or a function handle that represents the
%   given condition as needed internally.

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

numIn = nargin(N);

if ( isempty(BC) )
    result = [];

elseif ( isnumeric(BC) )
    % We reach here if we get assigned BCs like N.lbc = [1; 3].
    %   * In the scalar case, this means setting up conditions so that the
    %     solution equals the first entry of the vector at the endpoint, its
    %     derivative the second entry etc. In the example above, for a
    %     second order problem on [0,1], this amounts to
    %         u(0) = 1, u'(0) = 3.
    %   * In the system case, this means that the value of the first unknown
    %     function equals the first entry of the vector, the second unknown
    %     function the second entry of the vector, etc. In the example above,
    %     for a coupled system on [0,1], this amounts to
    %         u(0) = 1, v(0) = 3.
    %     Higher order coupled systems are not supported, as that would require
    %     inspecting the diffOrder of the linearized chebop, a (relatively)
    %     computionally expensive operation. That information only becomes
    %     available for free once we've called \ for starting to solve the
    %     problem.
    result = @(varargin) setupNumericalConditions(BC, varargin);

elseif ( isa(BC, 'function_handle') )
    % If we are dealing with a scalar problem where the independent variable is
    % not specified in the function handle arguments, allow also passing an
    % input function handle that takes one argument. Otherwise, we request that
    % the number of input to the BC function handle is one less than the number
    % of arguments to the OP part. A special case is when BCs are specified as a
    % vector, this leads to nargin(BC) = -1 (since we assigned the boundary
    % conditions as an anonymous function with a varargin argument), if this is
    % the case and the number of inputs for the BC don't match the operator, a
    % meaningful error will be thrown later on.
    if ( (numIn == 0) || ( (numIn == 1) && (nargin(BC) == 1) ) || ...
            ( strcmp(type,'lrbc') && (nargin(BC) == (numIn - 1)) ) || ...
            ( strcmp(type,'lrbc') && (nargin(BC) == -1) ) || ...
            ( strcmp(type,'bc') && (nargin(BC) == numIn) ) || ...
            ( strcmp(type,'bc') && (nargin(BC) == numIn + 1) ) )
        % We've got an anonymous function we're happy with. Do we need to
        % vectorize it?
        if ( N.vectorize && ~strcmp(func2str(BC), vectorize(BC)) )
            result = N.vectorizeOp(BC);
        else
            result = BC;
        end
    else
        error('CHEBFUN:CHEBOP:parseBC:inputs', ...
            'Number of inputs to BCs do not match operator.');
    end

elseif ( strcmpi(BC, 'neumann') )
    % Homogeneous Neumann.
    if ( numIn <= 2 )
        result = @(u) diff(u);
    else
        error('CHEBFUN:CHEBOP:parseBC:neuman', ...
            'Can only assign scalar BCs to scalar problems.');
    end

elseif ( strcmpi(BC, 'dirichlet') )
    % Homogeneous Dirichlet.
    if ( numIn <= 2 )
        result = @(u) u;
    else
        error('CHEBFUN:CHEBOP:parseBC:dirichlet', ...
            'Can only assign scalar BCs to scalar problems.');
    end

elseif ( iscell(BC) ) && ( length(BC) == 2 ) && ( ischar(BC{2}) ) && ...
        ( isnumeric(BC{1}) )
    % A cell may have a numerical value followed by a keyword of 'dirichlet' or
    % 'neumann'.

    % This behavior is retained only for backward compatibility and only for
    % problems with one variable.
    warning('CHEBFUN:CHEBOP:parseBC:keywordbc',...
        ['Keyword/value specifications of boundary conditions are ', ...
         'deprecated and may be removed in future versions of Chebfun.'])

    if ( strcmpi(BC{2}, 'neumann') && ( numIn <= 2 ) )
        result = @(u) diff(u) - BC{1};
    elseif ( strcmpi(BC{2}, 'dirichlet') && ( numIn <= 2 ) )
        result = @(u) u - BC{1};
    else
         error('CHEBFUN:CHEBOP:parseBC:cell', ...
            'Unable to parse cell input to set BC.');
    end

else
    error('CHEBFUN:CHEBOP:parseBC:unknown', 'Unsupported format of BCs.')

end

end

function out = setupNumericalConditions(BC, varargin)
% SETUPNUMERICALCONDITIONS  Return anonymous function for evaluating BCs.
%
% We need this function so that we can construct a function handle that allows
% specifying boundary conditions with a vector. In the scalar case, we will be
% subtracting the nth entry of the BC vector from the (n-1)st derivative of the
% solution. In the system case, we will be subtracting the nth entry of the
% vector from the nth unknown function at the endpoint:

u = varargin{1};
% Check if we're dealing with scalar or system case:
if (length(u) == 1)
    % Scalar case, subtract entries of BC from U and its derivatives:
    u = u{1};
    % We must always have at least one condition
    out = u - BC(1);
    for bcCounter = 2:length(BC)
        % Add to the boundary conditions vector. It's tricky to initialize the
        % OUT argument to the correct dimensions, as we need to be able to
        % evaluate this both with CHEBFUNs, ADCHEBFUNs and TREEVARs, hence a
        % cheeky growth of the array.
        out = [out; diff(u, bcCounter-1) - BC(bcCounter)]; %#ok<AGROW>
    end
else
    assert(length(u) == length(BC), ...
        'CHEBFUN:CHEBOP:parseBC:numberOfConditions', ...
        ['The number of initial/final conditions appears to be greater than' ...
        ' the number of variables in problem. Specifying boundary ' ...
        'conditions as a vector for systems is only supported for first ' ...
        'order systems.']);
    % We must always have at least one condition
    out = u{1} - BC(1);
    for bcCounter = 2:length(BC)
        % Add to the boundary conditions vector. Like above, it's tricky to
        % initialize the OUT argument to the correct dimensions, as we need to
        % be able to evaluate this both with CHEBFUNs, ADCHEBFUNs and TREEVARs.
        % So, allow ourselves to grow the array, by subtracting elements from
        % the BC vector from the solution components.
        out = [out; u{bcCounter} - BC(bcCounter)]; %#ok<AGROW>
    end
end

end


function K = rbfXnoneKernCompute(rbfKern, noneKern, x1, x2)

% RBFXNONEKERNCOMPUTE Compute a cross kernel between RBF and NONE kernels.
% FORMAT
% DESC computes cross kernel terms between an RBF kernel and a dummy kernel for
% the multiple output kernel.
% ARG rbfKern : the kernel structure associated with the first
% rbf kernel.
% ARG noneKern : the kernel structure associated with the dummy kernel.
% ARG x : inputs for which kernel is to be computed.
% RETURN k : block of values from kernel matrix.
%
% FORMAT
% DESC computes cross kernel terms between two RBF kernels for
% the multiple output kernel.
% ARG rbfKern : the kernel structure associated with the
% rbf  kernel.
% ARG noneKern : the kernel structure associated with the dummy kernel.
% ARG x1 : row inputs for which kernel is to be computed.
% ARG x2 : column inputs for which kernel is to be computed.
% RETURN k : block of values from kernel matrix.
%
% SEEALSO : multiKernParamInit, multiKernCompute, rbfKernParamInit
%
% COPYRIGHT : Neil D. Lawrence, 2008

% KERN

if nargin < 4
  x2 = x1;
end

K = zeros(size(x1, 1), size(x2, 1));


function updateScattergroup(obj,scatterIndex)

%check: http://undocumentedmatlab.com/blog/undocumented-scatter-plot-behavior

%----SCATTER FIELDS----%

% x - [DONE]
% y - [DONE]
% r - [HANDLED BY SCATTER]
% t - [HANDLED BY SCATTER]
% mode - [DONE]
% name - [DONE]
% text - [NOT SUPPORTED IN MATLAB]
% error_y - [HANDLED BY ERRORBAR]
% error_x - [NOT SUPPORTED IN MATLAB]
% textfont - [NOT SUPPORTED IN MATLAB]
% textposition - [NOT SUPPORTED IN MATLAB]
% xaxis [DONE]
% yaxis [DONE]
% showlegend [DONE]
% stream - [HANDLED BY PLOTLYSTREAM]
% visible [DONE]
% type [DONE]
% opacity ---[TODO]

% MARKER
% marler.color - [DONE]
% marker.size - [DONE]
% marker.opacity - [NOT SUPPORTED IN MATLAB]
% marker.colorscale - [NOT SUPPORTED IN MATLAB]
% marker.sizemode - [DONE]
% marker.sizeref - [DONE]
% marker.maxdisplayed - [NOT SUPPORTED IN MATLAB]

% MARKER LINE
% marker.line.color - [DONE]
% marker.line.width - [DONE]
% marker.line.dash - [NOT SUPPORTED IN MATLAB]
% marker.line.opacity - [DONE]
% marker.line.smoothing - [NOT SUPPORTED IN MATLAB]
% marker.line.shape - [NOT SUPPORTED IN MATLAB]

% LINE
% line.color - [NA]
% line.width - [NA]
% line.dash - [NA]
% line.opacity [NA]
% line.smoothing - [NOT SUPPORTED IN MATLAB]
% line.shape - [NOT SUPPORTED IN MATLAB]
% connectgaps - [NOT SUPPORTED IN MATLAB]
% fill - [HANDLED BY AREA]
% fillcolor - [HANDLED BY AREA]

%-AXIS INDEX-%
axIndex = obj.getAxisIndex(obj.State.Plot(scatterIndex).AssociatedAxis);

%-SCATTER DATA STRUCTURE- %
scatter_data = get(obj.State.Plot(scatterIndex).Handle);

%-SCATTER CHILDREN-%
scatter_child = get(obj.State.Plot(scatterIndex).Handle,'Children');

%-SCATTER CHILDREN DATA-%
scatter_child_data = get(scatter_child);

%-CHECK FOR MULTIPLE AXES-%
[xsource, ysource] = findSourceAxis(obj,axIndex);

%-AXIS DATA-%
eval(['xaxis = obj.layout.xaxis' num2str(xsource) ';']);
eval(['yaxis = obj.layout.yaxis' num2str(ysource) ';']);

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

%-scatter xaxis-%
obj.data{scatterIndex}.xaxis = ['x' num2str(xsource)];

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

%-scatter yaxis-%
obj.data{scatterIndex}.yaxis = ['y' num2str(ysource)];

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

%-scatter type-%
if any(nonzeros(scatter_data.ZData))
    obj.data{scatterIndex}.type = 'scatter3d';
else
    obj.data{scatterIndex}.type = 'scatter';
end

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

%-scatter mode-%
obj.data{scatterIndex}.mode = 'markers';

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

%-scatter visible-%
obj.data{scatterIndex}.visible = strcmp(scatter_data.Visible,'on');

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

%-scatter name-%
obj.data{scatterIndex}.name = scatter_data.DisplayName;

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

%-scatter patch data-%
for m = 1:length(scatter_child_data)

    %reverse counter
    n = length(scatter_child_data) - m + 1;

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

    %-scatter x-%
    if length(scatter_child_data) > 1
        obj.data{scatterIndex}.x(m) = scatter_child_data(n).XData;
    else
        obj.data{scatterIndex}.x = scatter_child_data.XData;
    end

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

    %-scatter y-%
    if length(scatter_child_data) > 1
        obj.data{scatterIndex}.y(m) = scatter_child_data(n).YData;
    else
        obj.data{scatterIndex}.y = scatter_child_data.YData;
    end

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

    %-scatter z-%
    if any(nonzeros(scatter_data.ZData))
        if length(scatter_child_data) > 1
            obj.data{scatterIndex}.z(m) = scatter_child_data(n).ZData;
        else
            obj.data{scatterIndex}.z = scatter_child_data.ZData;
        end
    end

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

    %-scatter showlegend-%
    leg = get(scatter_data.Annotation);
    legInfo = get(leg.LegendInformation);

    switch legInfo.IconDisplayStyle
        case 'on'
            showleg = true;
        case 'off'
            showleg = false;
    end

    obj.data{scatterIndex}.showlegend = showleg;

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

    %-scatter marker-%
    childmarker = extractPatchMarker(scatter_child_data(n));

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

    %-line color-%
    if length(scatter_child_data) > 1
        if iscell(childmarker.line.color)
            obj.data{scatterIndex}.marker.line.color{m} = childmarker.line.color{1};
        else
            obj.data{scatterIndex}.marker.line.color{m} = childmarker.line.color;
        end
    else
        obj.data{scatterIndex}.marker.line.color = childmarker.line.color;
    end

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

    %-marker color-%
    if length(scatter_child_data) > 1
        if iscell(childmarker.color)
            obj.data{scatterIndex}.marker.color{m} = childmarker.color{1};
        else
            obj.data{scatterIndex}.marker.color{m} = childmarker.color;
        end
    else
        obj.data{scatterIndex}.marker.color = childmarker.color;
    end

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

    %-sizeref-%
    obj.data{scatterIndex}.marker.sizeref = childmarker.sizeref;

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

    %-sizemode-%
    obj.data{scatterIndex}.marker.sizemode = childmarker.sizemode;

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

    %-symbol-%
    obj.data{scatterIndex}.marker.symbol{m} = childmarker.symbol;

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

    %-size-%
    if length(scatter_child_data) > 1 || ischar(childmarker.color)
        obj.data{scatterIndex}.marker.size(m) = childmarker.size;
    else
        obj.data{scatterIndex}.marker.size(1:length(childmarker.color)) = childmarker.size;
    end

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

    %-line width-%

    if length(scatter_child_data) > 1 || ischar(childmarker.line.color)
        obj.data{scatterIndex}.marker.line.width(m) = childmarker.line.width;
    else
        obj.data{scatterIndex}.marker.line.width(1:length(childmarker.line.color)) = childmarker.line.width;
    end

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

end
end


classdef (Abstract, CaseInsensitiveProperties = true) functionalPlot < ...
        iosr.statistics.statsPlot
%FUNCTIONALPLOT Abstract superclass for functional plots
%
%   As an abstract class, this class cannot be instantiated. It provides no
%   public methods. The class is a super class for:
%       - iosr.statistics.functionalBoxPlot
%       - iosr.statistics.functionalSpreadPlot
%
%   The class does, however, provide various properties to subclasses.
%
%   IOSR.STATISTICS.FUNCTIONALSPREADPLOT properties:
%       mainLineColor           - Specifies the color of the central line.
%                                 See 'addPrctilesLineColor' for details of
%                                 how to specify colors. The default is
%                                 'auto'.
%       mainLineStyle           - Specifies the style of the central line.
%                                 The property may be specified as a char
%                                 array (e.g. '-'), or as a length-P cell
%                                 vector of strings (if passing line styles
%                                 for each functional plot). The default is
%                                 '-'.
%       mainLineWidth           - Specifies the width of the central line.
%                                 The property may be specified as a
%                                 length-P numeric vector, or as a cell
%                                 vector (if passing line styles for each
%                                 functional plot). The default is 1.
%       outerLineColor          - Specifies the color of the outer lines.
%                                 See 'mainLineStyle' for details of how to
%                                 specify colors. The default is 'auto'.
%       outerLineStyle          - Specifies the style of the outer lines.
%                                 See 'mainLineStyle' for details of how to
%                                 specify styles. The default is '-'.
%       outerLineWidth          - Specifies the width of the outer line.
%                                 See 'mainLineWidth' for details of how to
%                                 specify widths. The default is 1.
%       outlierLineWidth        - Specifies the width of the outlier marker
%                                 line. See 'mainLineWidth' for details of
%                                 how to specify widths. The default is 1.
%       showOutliers            - Specifies whether to show outliers. The
%                                 property must be a logical value. The
%                                 default is false.
%       spreadAlpha             - Set the alpha of the shaded region(s).
%                                 The default is 0.5.
%       spreadBorderLineColor   - Set the color of the shaded regions'
%                                 / region's border. See
%                                 'addPrctilesLineColor' for details of how
%                                 to specify colors. The default is 'none'.
%       spreadBorderLineWidth   - Set the width of the shaded regions'
%                                 / region's border. The default is 1.
%       spreadColor             - Set the color of the shaded region(s).
%                                 See 'addPrctilesLineColor' for details of
%                                 how to specify colors. The default is
%                                 'auto'.
%       whiskers                - Specify whether the box is connected to
%                                 the outer line via a whisker. The line is
%                                 drawn with the same style as the outer
%                                 line. The default is false.
%
%   These properties can be referenced using dot notation - e.g. H.BOXCOLOR
%   where H is an instance of the FUNCTIONALSPREADPLOT object - or using
%   the SET and GET methods - e.g. GET(H,'BOXCOLOR'). Both methods are
%   case-insensitive.
%
%   Note that some handles will be empty unless the associated option is
%   turned on.
%
%   See also IOSR.STATISTICS.BOXPLOT, IOSR.STATISTICS.QUANTILE,
%   IOSR.STATISTICS.FUNCTIONALBOXPLOT,
%   IOSR.STATISTICS.FUNCTIONALSPREADPLOT, IOSR.STATISTICS.STATSPLOT.

    properties (AbortSet)
        mainLineColor = 'auto'          % The color of the central line.
        mainLineStyle = '-'             % The style of the central line.
        mainLineWidth = 2               % The width of the central line.
        outerLineColor = 'auto'         % The color of the outer lines.
        outerLineStyle = '-'            % The style of the outer lines.
        outerLineWidth = 0.5            % The width of the outer lines.
        outlierLineWidth = 1            % Width of the outlier marker edges.
        showOutliers = true             % Turn outliers on and off.
        spreadAlpha = 0.5               % The alpha of the shaded regions.
        spreadColor = 'auto'            % The color of the shaded regions.
        spreadBorderLineWidth = 1       % The width of the shaded region borders.
        spreadBorderLineColor = 'none'  % The color of the shaded region borders.
        whiskers = false                % Specify whether a line connects the box to the outer lines.
    end

    methods

        function set.showOutliers(obj, val)
            assert(numel(val)==1 && islogical(val), 'iosr:functionalPlot:invalidShowOutliers', ...
                '''SHOWOUTLIERS'' must be true or false.')
            obj.showOutliers = val;
            obj.draw();
        end

        % main lines

        function val = get.mainLineColor(obj)
            val = obj.parseColor(obj.mainLineColor);
        end

        function set.mainLineColor(obj,val)
            obj.mainLineColor = val;
            obj.draw();
        end

        function val = get.mainLineStyle(obj)
            val = obj.parseProps(obj.mainLineStyle, false);
        end

        function set.mainLineStyle(obj,val)
            obj.mainLineStyle = val;
            obj.draw();
        end

        function val = get.mainLineWidth(obj)
            val = obj.parseProps(obj.mainLineWidth, false);
        end

        function set.mainLineWidth(obj,val)
            obj.mainLineWidth = val;
            obj.draw();
        end

        % outer lines

        function val = get.outerLineColor(obj)
            val = obj.parseColor(obj.outerLineColor);
        end

        function set.outerLineColor(obj,val)
            obj.outerLineColor = val;
            obj.draw();
        end

        function val = get.outerLineStyle(obj)
            val = obj.parseProps(obj.outerLineStyle, false);
        end

        function set.outerLineStyle(obj,val)
            obj.outerLineStyle = val;
            obj.draw();
        end

        function val = get.outerLineWidth(obj)
            val = obj.parseProps(obj.outerLineWidth, false);
        end

        function set.outerLineWidth(obj,val)
            obj.outerLineWidth = val;
            obj.draw();
        end

        % outlier settings

        function val = get.outlierLineWidth(obj)
            val = obj.parseProps(obj.outlierLineWidth, false);
        end

        function set.outlierLineWidth(obj,val)
            obj.outlierLineWidth = val;
            obj.draw();
        end

        % set spread alpha

        function set.spreadAlpha(obj,val)
            assert(isnumeric(val) && isscalar(val), 'iosr:functionalPlot:invalidSpreadAlpha', ...
                '''SPREADALPHA'' must be a numeric scalar')
            obj.spreadAlpha = val;
            obj.draw();
        end

        % get/set spread line colors

        function val = get.spreadBorderLineColor(obj)
            val = obj.parseColor(obj.spreadBorderLineColor);
        end

        function set.spreadBorderLineColor(obj,val)
            obj.spreadBorderLineColor = val;
            obj.draw();
        end

        % set spread border line width

        function set.spreadBorderLineWidth(obj,val)
            assert(isnumeric(val) && isscalar(val), 'iosr:functionalPlot:invalidspreadBorderLineWidth', ...
                '''SPREADBORDERLINEWIDTH'' must be a numeric scalar')
            obj.spreadBorderLineWidth = val;
            obj.draw();
        end

        % set/get spread color

        function val = get.spreadColor(obj)
            val = obj.parseColor(obj.spreadColor);
        end

        function set.spreadColor(obj,val)
            obj.spreadColor = val;
            obj.draw();
        end

        % whisker

        function set.whiskers(obj,val)
            assert(islogical(val) && isscalar(val), 'iosr:functionalPlot:invalidWhiskers', ...
                '''whisker'' must be a scalar and logical');
            obj.whiskers = val;
            obj.draw();
        end

    end

    methods (Access = protected)

        % Draw the plot
        function draw(obj)

            if isfield(obj.handles,'axes')

                axes(obj.handles.axes);
                hold on;

                if length(obj.outDims) > 2
                    nlines = obj.outDims(3);
                else
                    nlines = 1;
                end
                for n = nlines:-1:1
                    % draw inner patch
                    obj.handles.spreads(n) = patch(...
                        [obj.x obj.x(end:-1:1)], ...
                        [obj.statistics.inner_u(:,:,n) obj.statistics.inner_l(:,end:-1:1,n)], ...
                        obj.spreadColor{n} , ...
                        'FaceAlpha', obj.spreadAlpha, ...
                        'EdgeColor', obj.spreadBorderLineColor{n}, ...
                        'LineWidth', obj.spreadBorderLineWidth ...
                    );
                end

                for n = nlines:-1:1

                    % draw outer limits
                    obj.handles.outer_u(n) = line(obj.x, obj.statistics.outer_u(:,:,n), ...
                        'linestyle', obj.outerLineStyle{n}, ...
                        'linewidth', obj.outerLineWidth{n}, ...
                        'color', obj.outerLineColor{n} ...
                    );
                    obj.handles.outer_l(n) = line(obj.x, obj.statistics.outer_l(:,:,n), ...
                        'linestyle', obj.outerLineStyle{n}, ...
                        'linewidth', obj.outerLineWidth{n}, ...
                        'color', obj.outerLineColor{n} ...
                    );

                    % draw centre line
                    obj.handles.main(n) = line(obj.x, obj.statistics.main(:,:,n), ...
                        'linestyle', obj.mainLineStyle{n}, ...
                        'linewidth', obj.mainLineWidth{n}, ...
                        'color', obj.mainLineColor{n} ...
                    );

                end

                % draw whiskers
                if obj.whiskers
                    x_range = max(obj.x) - min(obj.x);
                    whisker_xlim = [(min(obj.x) + (1/3)*x_range) (max(obj.x) - (1/3)*x_range)];
                    if nlines==1
                        whisker_x = mean(whisker_xlim);
                    else
                        whisker_x = linspace(whisker_xlim(1),whisker_xlim(2),nlines);
                    end
                    for n = nlines:-1:1
                        % interpolate to find y
                        [~,x_ix] = min(abs(obj.x-whisker_x(n)));
                        x_interp_ix = [max(1,x_ix-1) x_ix min(length(obj.x),x_ix+1)];
                        x_interp_ix = unique(x_interp_ix);
                        y_i_l = interp1(obj.x(x_interp_ix), obj.statistics.inner_l(:,x_interp_ix,n), whisker_x(n));
                        y_i_u = interp1(obj.x(x_interp_ix), obj.statistics.inner_u(:,x_interp_ix,n), whisker_x(n));
                        y_o_l = interp1(obj.x(x_interp_ix), obj.statistics.outer_l(:,x_interp_ix,n), whisker_x(n));
                        y_o_u = interp1(obj.x(x_interp_ix), obj.statistics.outer_u(:,x_interp_ix,n), whisker_x(n));

                        % plot
                        obj.handles.whiskers_l(n) = line([whisker_x(n) whisker_x(n)],[y_i_l y_o_l], ...
                            'linestyle', obj.outerLineStyle{n}, ...
                            'linewidth', obj.outerLineWidth{n}, ...
                            'color', obj.outerLineColor{n});
                        obj.handles.whiskers_u(n) = line([whisker_x(n) whisker_x(n)],[y_i_u y_o_u], ...
                            'linestyle', obj.outerLineStyle{n}, ...
                            'linewidth', obj.outerLineWidth{n}, ...
                            'color', obj.outerLineColor{n});
                    end
                else
                    try
                        delete(obj.handles.whiskers_l(n))
                        delete(obj.handles.whiskers_u(n))
                    catch
                    end
                end
            end

        end

    end

    methods (Access = protected)

        % Make properties in to a cell array
        function val = parseProps(obj, prop, addP)

            nLines = obj.outDims(3);
            if ~addP
                nAdd = 1;
            else
                nAdd = length(obj.addPrctiles);
            end
            val = cell(nLines, nAdd);
            if isnumeric(prop)
                cellprop = num2cell(prop);
            elseif ischar(prop)
                cellprop = cellstr(prop);
            else
                cellprop = prop;
            end
            try
            for n = 1:nLines
                if ~addP
                    val(n) = cellprop(mod(n-1, numel(cellprop)) + 1);
                else
                    for p = 1:nAdd
                        ixn = mod(n-1, size(cellprop, 1)) + 1;
                        ixp = mod(p-1, size(cellprop, 2)) + 1;
                        val(n,p) = cellprop(ixn, ixp);
                    end
                end
            end
            catch
                keyboard
            end

        end

        % Put colors in to a cell array
        function val = parseColor(obj, inColor)

            nLines = obj.outDims(3);
            val = cell(nLines, 1);
            if isnumeric(inColor)
                if size(inColor, 2) ~= 3
                    error('iosr:functionalPlot:colorInvalid','Color must be an N-by-3 array, where N is any positive integer.')
                end
                for n = 1:nLines
                    val(n,:) = {inColor(mod(n, size(inColor, 1)) + 1, :)};
                end
            elseif ischar(inColor)
                if strcmp(inColor, 'auto')
                    colors = lines(nLines);
                    for n = 1:nLines
                        val(n,:) = {colors(n, :)};
                    end
                else
                    colors = cellstr(inColor);
                    for n = 1:nLines
                        val(n,:) = colors(mod(n, size(inColor, 1)) + 1);
                    end
                end
            elseif isa(inColor,'function_handle')
                colors = inColor(nLines);
                for n = 1:nLines
                    val(n,:) = {colors(mod(n, numel(inColor)) + 1)};
                end
            elseif iscellstr(inColor)
                for n = 1:nLines
                    val(n,:) = inColor(mod(n, numel(inColor)) + 1);
                end
            else
                error('iosr:functionalPlot:invalidColorFormat','Invalid color format specified.')
            end

        end

    end

end


function [ records, spectra, rawSpectra ] = hyperReadSpecpr( filename )
%HYPERREADSPECPR Reads USGS Specpr files.
%   hyperReadSpecpr reads USGS Specpr files.
%
% Usage
%   [ records, spectra, rawSpectra ] = hyperReadSpecpr( filename )
% Inputs
%   filename - Input filename
% Outputs
%   records - Individual records
%   spectra - The spectra post-processed
%   rawSpectra - The raw spectra
%
% References
%   http://speclab.cr.usgs.gov/specpr-format.html.
dbstop if error;
f = fopen(filename, 'r');
if (f == -1)
    error(sprintf('Failed to open file: %s'), filename);
end

% Ignore the first record.
r = uint32(fread(f, 1536, 'uint8'));

firstTime = 1;
records = {};
spectra = [];
rawSpectra = {};
numRawSpectra = 0;
numRecords = 0;

done = 0;
while (not(done))
    r = uint32(fread(f, 1536/4, 'uint32', 'ieee-be'));
    if (length(r) == 0)
        break;
    end
    numRecords
    r = swapbytes(r);
    r = typecast(r, 'uint8');
    % First two bits of file.  I am making this verbose here so it is clear what I
    % am doing.
    firstTwoBits = dec2bin(bitand(r(4), 3));

    %  Parse first two bits.
    if (firstTwoBits == '10')
        % This is a text record. Skip.
        numRecords = numRecords + 1;
    elseif (firstTwoBits == '0')
        % This is an actual (initial) data record.
        if (not(firstTime))
            numRecords = numRecords+1;
            records{record.irecno} = record;
        end
        record = [];
        data = [];
        firstTime = 0;
        iband = int32(zeros(2, 1));
        record.ititl = char(r(5:44)).';
        record.usernm = char(r(45:52)).';
        iscta = typecast(r(53:56), 'int32');
        isctb = typecast(r(57:60), 'int32');
        jdatea = typecast(r(61:64), 'int32');
        jdateb = typecast(r(65:68), 'int32');
        istb = typecast(r(69:72), 'int32');
        isra = typecast(r(73:76), 'int32');
        isdec = typecast(r(77:80), 'int32');
        record.itchan = swapbytes(typecast(r(81:84), 'int32'));
        irmas = typecast(r(85:88), 'int32');
        revs = typecast(r(89:92), 'int32');
        iband(1) = typecast(r(93:96), 'int32');
        iband(2) = typecast(r(97:100), 'int32');
        record.irwav = swapbytes(typecast(r(101:104), 'int32'));
        record.irespt = swapbytes(typecast(r(105:108), 'int32'));
        record.irecno = swapbytes(typecast(r(109:112), 'int32'));
        itpntr = typecast(r(113:116), 'int32');
        ihist = char(r(117:176)).';
        mhist = char(r(177:472)).';
        nruns = typecast(r(473:476), 'int32');
        siangl = typecast(r(477:480), 'int32');
        seangl = typecast(r(481:484), 'int32');
        sphase = typecast(r(485:488), 'int32');
        iwtrns = typecast(r(489:492), 'int32');
        itimch = typecast(r(493:496), 'int32');
        xnrm = typecast(r(497:500), 'int32');
        scatim = typecast(r(501:504), 'int32');
        timint = typecast(r(505:508), 'int32');
        tempd = typecast(r(509:512), 'int32');
        data = swapbytes(typecast(r(513:1536), 'single'));
        % Remove null data samples. Set to zero instead of -1.23e34.
        data(find(data < -1e34)) = 0;
        record.data = data;
    elseif (firstTwoBits == '1')
        % Continuation of data values.
        cData = swapbytes(typecast(r(5:1536), 'single'));
        cData(find(cData < -1e34)) = 0;
        data = [data; cData];
        record.data = data;
    else
        numRecords = numRecords + 1;
    end
end

% Convert to an array of signatures.
% Resample to model AVIRIS sensor
high = 2.40;
low = 0.4;
numBands = 224;
%d.data = sortrows(d.data, 1);
%[q, w, r ]= unique(d.data(:,1));
%d.data = d.data(w, :);
%lambda = d.data(:, 1);
%reflectance = d.data(:, 2);
s = length(records);
numSpectra = 0;
for q=1:s
    if (isempty(records{q}))
        continue;
    end
    if (records{q}.irwav == 0)
        continue;
    end

    if (not(isempty(strfind(records{q}.ititl, 'error'))))
        continue;
    end
    if (not(isempty(strfind(records{q}.ititl, 'Error'))))
        continue;
    end
    if (not(isempty(strfind(records{q}.ititl, 'Bandpass'))))
        continue;
    end
    if (not(isempty(strfind(records{q}.ititl, 'Wavelengths'))))
        continue;
    end
    % Find wavelengths
    if (isempty(records{records{q}.irwav}))
        continue;
    end
    lambdas = records{records{q}.irwav}.data;
    spectrum = records{q}.data;
    if (length(lambdas) ~= length(spectrum))
        fprintf('Error %d !!!\n', q);
        continue;
    end
    numRawSpectra = numRawSpectra + 1;
    rawSpectra{numRawSpectra}.name = records{q}.ititl;
    rawSpectra{numRawSpectra}.wavelengths = lambdas;
    rawSpectra{numRawSpectra}.reflectance = spectrum;
    goodIdx = find(lambdas > 0);
    lambdas = lambdas(goodIdx);
    spectrum = spectrum(goodIdx);
    % Ensure we have proper lower and upper bounds.
    if (lambdas(1) > low)
        %fprintf('Bad file: Lower wavelength value missing.');
        lambdas = [low; lambdas];
        spectrum = [spectrum(1); spectrum];
    end
    if (lambdas(end) < high)
        %fprintf('Bad file: Upper wavelength value missing.\n');
        %d.data = [];
        %fclose(fid);
        %return;
        lambdas = [lambdas; high];
        spectrum = [spectrum; spectrum(end)];
    end
    % Resample
    records{q}.ititl
    ts = timeseries(spectrum, lambdas);
    inc = (high-low) / (numBands-1);
    c = resample(ts, [low:inc:high], 'zoh');
    numSpectra = numSpectra + 1;
    spectra(numSpectra).data = [c.time c.data [1:numBands].'];
    spectra(numSpectra).name = records{q}.ititl;
end

return;


 function varargout = repout(in)
%function varargout = repout(in)
% extension of "deal" to take a single input to multiple outputs, e.g.,
% [a b c] = repout(in)

if nargin < 1, ir_usage, end
for ii=1:nargout
	varargout{ii} = in;
end


function [nodes, elements, nodedata, elementdata] = read_gmsh_binary(filename)

% READ_GMSH_BINARY reads a gmsh .msh binary file. Current support is only
% for version 2. There are some ASCII-readers floating around on the net,
% but they do not seem to work with the primary use case in FieldTrip (and
% the test data that I have available), which is SimNibs generated data.
%
% See also MESH_LOAD_GMSH4

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

fid = fopen(filename, 'rb');
txt = fgetl(fid);

if startsWith(txt, '$MeshFormat')
  % the file starts, as expected with the file header
  txt    = fgetl(fid);
  format = sscanf(txt, '%f %d %d');
  version  = format(1);
  isbinary = format(2)==1;
  dtype    = format(3);

  % read until the end of this chunk
  while 1
    % in a binary file, there's an endianness check byte, which is
    % currently not handled here.
    txt = fgetl(fid);
    if startsWith(txt, '$EndMeshFormat')
      break;
    end
  end
else
  ft_error('File %s cannot be read, header information is missing', filename);
end

if floor(version)~=2 || ~isbinary
  if ~isbinary
    str = 'not';
  else
    str = '';
  end
  ft_error('Currently only binary version 2 files can be read, the version is: %1.1f, and the file is %s binary', version, str);
end
if dtype~=8
  ft_error('Currently only 8-byte precision data is supported');
end

nodes       = [];
elements    = [];
nodedata    = [];
elementdata = [];

% read the contents of the file
while 1
  txt = fgetl(fid);
  if txt==-1
    % end of file
    fclose(fid);
    break;
  end
  % Nodes and Elements are expected to be minimally present, needed to
  % describe the mesh topology
  if startsWith(txt, '$Nodes')
    nodes = getnodes(fid);
  elseif startsWith(txt, '$Elements')
    elements = getelements(fid);
  elseif startsWith(txt, '$PhysicalNames')
    keyboard
  elseif startsWith(txt, '$Entities')
    keyboard
  elseif startsWith(txt, '$PartitionedEntities')
    keyboard
  elseif startsWith(txt, '$Periodic')
    keyboard
  elseif startsWith(txt, '$GhostElements')
    keyboard
  elseif startsWith(txt, '$Parametrizations')
    keyboard
  elseif startsWith(txt, '$NodeData')
    keyboard
  elseif startsWith(txt, '$ElementData')
    elementdata = getelementdata(fid);
  elseif startsWith(txt, '$ElementNodeData')
    keyboard
  elseif startsWith(txt, '$InterpolationScheme')
    keyboard
  else
    keyboard

  end
end

%%%%%%%%%%%%%%
% subfunctions

% $Nodes
function [nodes] = getnodes(fid)

% the chunk starts with an ascii-line with the number of elements
%
% the format of the nodes is 4-byte indx followed by 3x a float for the
% coordinates

txt = fgetl(fid);
N   = sscanf(txt, '%f');
ptr = ftell(fid);

fprintf('Reading position information for %d nodes\n', N);
nodes.indx = fread(fid, N, 'uint32', 24);
fseek(fid, ptr+4, 'bof');
nodes.nodes = fread(fid, 3*N, '3*double', 4);
nodes.nodes = reshape(nodes.nodes, 3, [])';
fseek(fid, ptr+28*N, 'bof');
txt = fgetl(fid);
assert(isequal(txt, '$EndNodes'), 'Reading of the nodes unexpectedly failed');

% $Elements
function [elements] = getelements(fid)

% the chunk starts with an ascii-line mentioning the overall number of elements, then 3 integers
% per sub-chunk of elements, that indicate the type, the number of elements
% in the sub-chunk, and the number of tags

txt = fgetl(fid);
N   = sscanf(txt, '%f');

fprintf('Reading element information for %d elements\n', N);
Ntotal = 0;
while N>Ntotal

  hdr  = fread(fid, 3, 'uint32');
  type = hdr(1);
  Nsub = hdr(2);
  ntag = hdr(3);

  [nnode, type_str] = type2nnode(type);

  fprintf('Reading %d %s with %d tags\n', Nsub, type_str, ntag);
  tmp = fread(fid, Nsub.*(nnode+ntag+1), '*uint32');
  tmp = reshape(tmp, nnode+ntag+1, [])';

  elements.(type_str)                     = tmp(:, (ntag+2):end);
  elements.(sprintf('%s_indx', type_str)) = tmp(:, 1);
  elements.(sprintf('%s_tag',  type_str)) = tmp(:, 1 + (1:ntag));
  Ntotal = Ntotal + Nsub;
end
txt = fgetl(fid);
assert(isequal(txt, '$EndElements'), 'Reading of the elements unexpectedly failed');

% $ElementData
function [elementdata] = getelementdata(fid)

% stringTags
txt = fgetl(fid);
N   = sscanf(txt, '%d');
for k = 1:N
  stringTag{k,1} = fgetl(fid);
end

% realTags
txt = fgetl(fid);
N   = sscanf(txt, '%d');
for k = 1:N
  txt = fgetl(fid);
  realTag(k,1) = sscanf(txt, '%d');
end

% integerTags
txt = fgetl(fid);
N   = sscanf(txt, '%d'); % N is kind of expected to be 4
for k = 1:N
  txt = fgetl(fid);
  integerTag(k,1) = sscanf(txt, '%d');
end
nfc = integerTag(2); % number of field components
N   = integerTag(3); % number of elements

fprintf('Reading elementdata for %d elements\n', N);
ptr = ftell(fid);
elementdata.indx = fread(fid, N, 'uint32', nfc*8);
fseek(fid, ptr+4, 'bof');
elementdata.value = fread(fid, nfc*N, sprintf('%d*double', nfc), 4);
elementdata.value = reshape(elementdata.value, nfc, [])';
fseek(fid, ptr+(4+nfc*8)*N, 'bof');

txt = fgetl(fid);
assert(isequal(txt, '$EndElementData'), 'Reading of the elementdata unexpectedly failed');

% helper function to get the number of nodes per element + a human interpretable name
function [nnode, type_str] = type2nnode(type)

switch type
  case 1
    % 2-node line
    nnode = 2; type_str = 'lines';
  case 2
    % 3-node triangle
    nnode = 3; type_str = 'triangles';
  case 3
    % 4-node quadrangle
    nnode = 4; type_str = 'quadrangles';
  case 4
    % 4-node tetrahedron
    nnode = 4; type_str = 'tetrahedra';
  case 5
    % 8-node hexahedron
    nnode = 8; type_str = 'hexahedra';
  case 6
    % 6-node prism
    nnode = 6; type_str = 'prisms';
  case 7
    % 5-node pyramid
    nnode = 5; type_str = 'pyramids';
  case 8
    % 3-node seconde order line (2 nodes associated with the vertices and 1 with the edge)
    nnode = 3; type_str = 'line';
  case 9
    % 6-node second order triangle (3 nodes associated with the vertices and 3 with the edges)
    nnode = 6; type_str = 'triangles';
  case 10
    % 9-node second order quadrangle (4 nodes associated with the vertices, 4 with the edges and 1 with the face)
    nnode = 9; type_str = 'quadrangles';
  case 11
    % 10-node second order tetrahedron (4 nodes associated with the vertices and 6 with the edges)
    nnode = 10; type_str = 'tetrahedra';
  case 12
    % 27-node second order hexahedron (8 nodes associated with the vertices, 12 with the edges,  6 with the faces and 1 with the volume)
    nnode = 27; type_str = 'hexahedra';
  case 13
    % 18-node second order prism (6 nodes associated with the vertices, 9 with the edges and 3 with the quadrangular faces)
    nnode = 18; type_str = 'prisms';
  case 14
    % 14-node seconde order pyramid (5 nodes associated with the vertices, 8 with the edges and 1 with the quadrangular face)
    nnode = 14; type_str = 'pyramids';
  case 15
    % 1-node point
    nnode = 1; type_str = 'points';
  otherwise
    %% FIXME add types 16-31, and 92 93
    keyboard
end


%  internal function

%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)

function save_nii_hdr(hdr, fid)

   if ~isequal(hdr.hk.sizeof_hdr,348),
      error('hdr.hk.sizeof_hdr must be 348.');
   end

   write_header(hdr, fid);

   return;					% save_nii_hdr

%---------------------------------------------------------------------
function write_header(hdr, fid)

        %  Original header structures
	%  struct dsr				/* dsr = hdr */
	%       {
	%       struct header_key hk;            /*   0 +  40       */
	%       struct image_dimension dime;     /*  40 + 108       */
	%       struct data_history hist;        /* 148 + 200       */
	%       };                               /* total= 348 bytes*/

   header_key(fid, hdr.hk);
   image_dimension(fid, hdr.dime);
   data_history(fid, hdr.hist);

   %  check the file size is 348 bytes
   %
   fbytes = ftell(fid);

   if ~isequal(fbytes,348),
      msg = sprintf('Header size is not 348 bytes.');
      warning(msg);
   end

   return;					% write_header

%---------------------------------------------------------------------
function header_key(fid, hk)

   fseek(fid,0,'bof');

	%  Original header structures
	%  struct header_key                      /* header key      */
	%       {                                /* off + size      */
	%       int sizeof_hdr                   /*  0 +  4         */
	%       char data_type[10];              /*  4 + 10         */
	%       char db_name[18];                /* 14 + 18         */
	%       int extents;                     /* 32 +  4         */
	%       short int session_error;         /* 36 +  2         */
	%       char regular;                    /* 38 +  1         */
	%       char dim_info;   % char hkey_un0;        /* 39 +  1 */
	%       };                               /* total=40 bytes  */

   fwrite(fid, hk.sizeof_hdr(1),    'int32');	% must be 348.

   % data_type = sprintf('%-10s',hk.data_type);	% ensure it is 10 chars from left
   % fwrite(fid, data_type(1:10), 'uchar');
   pad = zeros(1, 10-length(hk.data_type));
   hk.data_type = [hk.data_type  char(pad)];
   fwrite(fid, hk.data_type(1:10), 'uchar');

   % db_name   = sprintf('%-18s', hk.db_name);	% ensure it is 18 chars from left
   % fwrite(fid, db_name(1:18), 'uchar');
   pad = zeros(1, 18-length(hk.db_name));
   hk.db_name = [hk.db_name  char(pad)];
   fwrite(fid, hk.db_name(1:18), 'uchar');

   fwrite(fid, hk.extents(1),       'int32');
   fwrite(fid, hk.session_error(1), 'int16');
   fwrite(fid, hk.regular(1),       'uchar');	% might be uint8

   % fwrite(fid, hk.hkey_un0(1),    'uchar');
   % fwrite(fid, hk.hkey_un0(1),    'uint8');
   fwrite(fid, hk.dim_info(1),      'uchar');

   return;					% header_key

%---------------------------------------------------------------------
function image_dimension(fid, dime)

	%  Original header structures
	%  struct image_dimension
	%       {                                /* off + size      */
	%       short int dim[8];                /* 0 + 16          */
	%       float intent_p1;   % char vox_units[4];   /* 16 + 4       */
	%       float intent_p2;   % char cal_units[8];   /* 20 + 4       */
	%       float intent_p3;   % char cal_units[8];   /* 24 + 4       */
	%       short int intent_code;   % short int unused1;   /* 28 + 2 */
	%       short int datatype;              /* 30 + 2          */
	%       short int bitpix;                /* 32 + 2          */
	%       short int slice_start;   % short int dim_un0;   /* 34 + 2 */
	%       float pixdim[8];                 /* 36 + 32         */
	%			/*
	%				pixdim[] specifies the voxel dimensions:
	%				pixdim[1] - voxel width
	%				pixdim[2] - voxel height
	%				pixdim[3] - interslice distance
	%				pixdim[4] - volume timing, in msec
	%					..etc
	%			*/
	%       float vox_offset;                /* 68 + 4          */
	%       float scl_slope;   % float roi_scale;     /* 72 + 4 */
	%       float scl_inter;   % float funused1;      /* 76 + 4 */
	%       short slice_end;   % float funused2;      /* 80 + 2 */
	%       char slice_code;   % float funused2;      /* 82 + 1 */
	%       char xyzt_units;   % float funused2;      /* 83 + 1 */
	%       float cal_max;                   /* 84 + 4          */
	%       float cal_min;                   /* 88 + 4          */
	%       float slice_duration;   % int compressed; /* 92 + 4 */
	%       float toffset;   % int verified;          /* 96 + 4 */
	%       int glmax;                       /* 100 + 4         */
	%       int glmin;                       /* 104 + 4         */
	%       };                               /* total=108 bytes */

   fwrite(fid, dime.dim(1:8),        'int16');
   fwrite(fid, dime.intent_p1(1),  'float32');
   fwrite(fid, dime.intent_p2(1),  'float32');
   fwrite(fid, dime.intent_p3(1),  'float32');
   fwrite(fid, dime.intent_code(1),  'int16');
   fwrite(fid, dime.datatype(1),     'int16');
   fwrite(fid, dime.bitpix(1),       'int16');
   fwrite(fid, dime.slice_start(1),  'int16');
   fwrite(fid, dime.pixdim(1:8),   'float32');
   fwrite(fid, dime.vox_offset(1), 'float32');
   fwrite(fid, dime.scl_slope(1),  'float32');
   fwrite(fid, dime.scl_inter(1),  'float32');
   fwrite(fid, dime.slice_end(1),    'int16');
   fwrite(fid, dime.slice_code(1),   'uchar');
   fwrite(fid, dime.xyzt_units(1),   'uchar');
   fwrite(fid, dime.cal_max(1),    'float32');
   fwrite(fid, dime.cal_min(1),    'float32');
   fwrite(fid, dime.slice_duration(1), 'float32');
   fwrite(fid, dime.toffset(1),    'float32');
   fwrite(fid, dime.glmax(1),        'int32');
   fwrite(fid, dime.glmin(1),        'int32');

   return;					% image_dimension

%---------------------------------------------------------------------
function data_history(fid, hist)

	% Original header structures
	%struct data_history
	%       {                                /* off + size      */
	%       char descrip[80];                /* 0 + 80          */
	%       char aux_file[24];               /* 80 + 24         */
	%       short int qform_code;            /* 104 + 2         */
	%       short int sform_code;            /* 106 + 2         */
	%       float quatern_b;                 /* 108 + 4         */
	%       float quatern_c;                 /* 112 + 4         */
	%       float quatern_d;                 /* 116 + 4         */
	%       float qoffset_x;                 /* 120 + 4         */
	%       float qoffset_y;                 /* 124 + 4         */
	%       float qoffset_z;                 /* 128 + 4         */
	%       float srow_x[4];                 /* 132 + 16        */
	%       float srow_y[4];                 /* 148 + 16        */
	%       float srow_z[4];                 /* 164 + 16        */
	%       char intent_name[16];            /* 180 + 16        */
	%       char magic[4];   % int smin;     /* 196 + 4         */
	%       };                               /* total=200 bytes */

   % descrip     = sprintf('%-80s', hist.descrip);     % 80 chars from left
   % fwrite(fid, descrip(1:80),    'uchar');
   pad = zeros(1, 80-length(hist.descrip));
   hist.descrip = [hist.descrip  char(pad)];
   fwrite(fid, hist.descrip(1:80), 'uchar');

   % aux_file    = sprintf('%-24s', hist.aux_file);    % 24 chars from left
   % fwrite(fid, aux_file(1:24),   'uchar');
   pad = zeros(1, 24-length(hist.aux_file));
   hist.aux_file = [hist.aux_file  char(pad)];
   fwrite(fid, hist.aux_file(1:24), 'uchar');

   fwrite(fid, hist.qform_code,    'int16');
   fwrite(fid, hist.sform_code,    'int16');
   fwrite(fid, hist.quatern_b,   'float32');
   fwrite(fid, hist.quatern_c,   'float32');
   fwrite(fid, hist.quatern_d,   'float32');
   fwrite(fid, hist.qoffset_x,   'float32');
   fwrite(fid, hist.qoffset_y,   'float32');
   fwrite(fid, hist.qoffset_z,   'float32');
   fwrite(fid, hist.srow_x(1:4), 'float32');
   fwrite(fid, hist.srow_y(1:4), 'float32');
   fwrite(fid, hist.srow_z(1:4), 'float32');

   % intent_name = sprintf('%-16s', hist.intent_name);	% 16 chars from left
   % fwrite(fid, intent_name(1:16),    'uchar');
   pad = zeros(1, 16-length(hist.intent_name));
   hist.intent_name = [hist.intent_name  char(pad)];
   fwrite(fid, hist.intent_name(1:16), 'uchar');

   % magic	= sprintf('%-4s', hist.magic);		% 4 chars from left
   % fwrite(fid, magic(1:4),           'uchar');
   pad = zeros(1, 4-length(hist.magic));
   hist.magic = [hist.magic  char(pad)];
   fwrite(fid, hist.magic(1:4),        'uchar');

   return;					% data_history


function fgWrite(fg,name,type)
% Write out a fiber group structure for use with Quench. Pdb format.
%
%  fgWrite(fg,[name],[type])
%
% INPUTS:
%     fg - Fiber group structure.
%   name - Name used to save the fiber group. Can bee a full path
%          terminating in a name. Defaults to the current directory with
%          the name in the fg.name field.
%   type - File type to save as.
%            Options are:
%             'pdb'    - quench file format [DEFAULT]
%             'quench' - same as 'pdb'
%             'mat'    - matlab file format
%             'tck'    - MRTRIX file format (also 'mrtrix'/'.tck')
%
%          * If "name" ends in either .pdb or .mat the correct file type
%            will be used.
%
% WEB RESOURCES:
%   mrvBrowseSVN('fgWrite');
%
% EXAMPLE USAGE:
%   fgWrite(fg,fg.name,'pdb');
%
% See Also:
%   fgRead.m
%
%
% (C) Stanford VISTA, 2011
%
%     Brent McPherson and Franco Pestilli
%     Indiana University 2017

%% Check inputs

% Check that fg is a structure
if ~isstruct(fg) || ~isfield(fg, 'fibers')
    error('fg must be a fiber group structure with a fibers field.');
end

% Should we do this?
% This will set fg.params = [] and fg.pathwayInfo = [].
% fg = dtiClearQuenchStats(fg);

% Check for name input variable and use fg.name if empty
if ~exist('name','var') || isempty(name)
    name = fgGet(fg,'name');
end

% Check type and set to pdb by default
if ~exist('type','var') || isempty(type)
    type = 'pdb';
end

% If the name ends in '.mat' set type to 'mat'
[p n e] = fileparts(name);
if strcmp(e,'.mat')
    type = 'mat';
end

%% Write out the fibers
switch type
    case {'pdb', 'quench'}
        % Arguments are:
        % fg, fileName, xformToAcPc, xform, fiberToStrFlag, version
        mtrExportFibers(fg, name, [], [], [], 3);
    case 'mat'
        dtiWriteFiberGroup(fg, name);
    case {'tck', 'mrtrix', '.tck'}
        dtiExportFibersMrtrix(fg, name);
end

return


function excelrecord3fcn(stringdates1, endo, Y, ss_estimates, n, pref)
% script excelrecord3
% records the information contained in the worksheet 'steady state' of the excel spreadsheet 'results.xls'

% create the cell that will be saved on excel
sscell={};

% build preliminary elements: space between the tables
vertspace=repmat({''},size(stringdates1,1)+3,1);

% loop over variables (horizontal dimension)
for ii=1:n
   % create cell of steady-state record for variable ii
   temp=['steady-state and actual: ' endo{ii,1}];
   ss_i=[temp {''} {''} {''} {''};{''} {''} {''} {''} {''};{''} {'sample'} {'lw. bound'} {'median'} {'up. bound'};stringdates1 num2cell(Y(:,ii)) num2cell(ss_estimates{ii,1}')];
sscell=[sscell ss_i vertspace];
end

% trim
sscell=sscell(:,1:end-1);

% write in excel
if pref.results==1
    bear.xlswritegeneral(fullfile(pref.results_path, [pref.results_sub '.xlsx']),sscell,'steady state','B2');
end


function nthreads = SetUpMatlabPoolforJAABAPlot()

% limitations:
% computation_threads >= 1
% framecache_threads >= 1
% computation_threads + framecache_threads <= NumWorkers
% computation_threads <= numCores
% framecache_threads <= numCores

if verLessThan('matlab','8.3.0.532'),

  c=parcluster;
  numCores = feature('numCores');
  NumWorkers = c.NumWorkers;
  matlabpool_size = matlabpool('size');

  % load in rc file
  isrcfile = exist('most_recent_config.mat','file');
  if isrcfile,
    rc=load('most_recent_config.mat');  % would've been nice to be able to just call LoadRC
  else
    rc = struct;
  end

  % if matlabpool has already been started, use this as the number of
  % computation threads
  if matlabpool_size > 0,
    rc.handles.computation_threads = min(matlabpool_size,NumWorkers);
  end

  % if both computation_threads and framecache_threads are set in rc file,
  % just adjust them so that they are legal values
  if isfield(rc,'handles') && isfield(rc.handles,'computation_threads'),
    rc.handles.computation_threads = max(1,min([rc.handles.computation_threads,numCores,NumWorkers]));
  else
    rc.handles.computation_threads = min(numCores,NumWorkers);
  end

  nthreads = rc.handles.computation_threads;
  fprintf('Number of threads allocated for computation: %d\n',nthreads);

  if matlabpool_size>0 && matlabpool_size ~= rc.handles.computation_threads,
    matlabpool close;
    matlabpool_size = matlabpool('size');
  end

  % start up matlabpool if necessary
  if (numCores>1) && (matlabpool_size<1),
    matlabpool('open',rc.handles.computation_threads);
  end

  if isrcfile,
    save('most_recent_config.mat','-struct','rc')
  end
else

  c=parcluster;
  numCores = feature('numCores');
  NumWorkers = c.NumWorkers;
  curpool = gcp('nocreate');
  if isempty(curpool)
    curpool_size = 0;
  else
    curpool_size = curpool.NumWorkers;
  end
  % load in rc file
  isrcfile = exist('most_recent_config.mat','file');
  if isrcfile,
    rc=load('most_recent_config.mat');  % would've been nice to be able to just call LoadRC
  else
    rc = struct;
  end

  % if matlabpool has already been started, use this as the number of
  % computation threads
  if curpool_size > 0,
    rc.handles.computation_threads = min(curpool_size,NumWorkers);
  end

  % if both computation_threads and framecache_threads are set in rc file,
  % just adjust them so that they are legal values
  if isfield(rc,'handles') && isfield(rc.handles,'computation_threads'),
    rc.handles.computation_threads = max(1,min([rc.handles.computation_threads,numCores,NumWorkers]));
  else
    rc.handles.computation_threads = min(numCores,NumWorkers);
  end

  nthreads = rc.handles.computation_threads;
  fprintf('Number of threads allocated for computation: %d\n',nthreads);

  if curpool_size>0 && curpool_size ~= rc.handles.computation_threads,
    delete(gcp);
    curpool_size = 0;
  end

  % start up matlabpool if necessary
  if (numCores>1) && (curpool_size<1),
    parpool(rc.handles.computation_threads);
  end

  if isrcfile,
    save('most_recent_config.mat','-struct','rc')
  end

end


function tP = minus(tP,xy)
% shift triple points in x/y direction
%
% Syntax
%
%   % shift in x direction
%   tP = gB + [100,0]
%
% Input
%  tP - @triplePointList
%  xy - x and y coordinates of the shift
%
% Output
%  tP - @triplePointList

tP = tP + (-xy);