function [err,Info, BRIKinfo] = BrikInfo (BrickName)
%
%   [err,Info] = BrikInfo (BrickName)
%
%Purpose:
%
%   returns some field values in the .HEAD files
%
%Input Parameters:
%   Brick filename
%  (also works for 1D files but Info has very limited info.
%   You should really avoid using BrikInfo on 1D file
%   because the 1D file is read in its entirety (and then cleared)
%   before Info can be returned. Better used BrikLoad directly or
%   Read_1D)
%
%
%Output Parameters:
%   err : 0 No Problem
%       : 1 Mucho Problems
%   Info is a structure with the following fields
%           Fieldnames in UPPER CASE correspond to the fields in AFNI
%           Field names in lower case correspond to some interpretation of the uppercase ones
%      .RootName : the brik name without .HEAD or .BRIK.
%      .TypeName: The interpretation of BRICK_TYPES (short, int, floats, etc...)
%      .TypeBytes : The corresponding byte size of BRICK_TYPES
%		 .ByteOrder: interpretation of BYTEORDER_STRING
%      .Orientation : interpretation of ORIENT_SPECIFIC, Orientation(:,1)' forms the three letter orientation code
%        The ith row of Orientation describes the orientation along that dimension.
%   see AFNI's README.attributes for a complete description of these fields
%   MANDATORY FIELDS AS SPECIFIED IN: ~cox/README.attributes (added April 6 2001)
%      .DATASET_RANK : ASK BOB. I think the first is always 3 (3D) and the other is the number of sub-bricks
%      .DATASET_DIMENSIONS : Number of voxels in each of the three dimensions
%      .TYPESTRING: Determines if the data set is of type Anat (underlay) or Func (overlay)
%      .SCENE_DATA : The three integer codes describing the dataset type
%		 .ORIENT_SPECIFIC : orintation code
%      .ORIGIN : The xyz coordinates of the center of the (0, 0, 0) voxel in the data set
%      .DELTA : the increment (in mm) to go from one voxel to the next (could be +ve or -ve depending on slices)
%      .TAXIS_NUMS: see readme file
%      .TAXIS_FLOATS
%      .TAXIS_OFFSETS
%   ALMOST MANDATORY FIELDS
%      .IDCODE_STRING
%      .IDCODE_DATE
%      .BYTEORDER_STRING : Byte order string
%      .BRICK_STATS : range of values in brick (min to max)
%         (Do not apply the scaling factor to these values)
%      .BRICK_TYPES : types of values in .BRIK
%      .BRICK_FLOAT_FACS : float factors to apply to bricks for recovering original values
%      .BRICK_LABS : The Sub-brick labels (~delimited)
%      .BRICK_STATAUX : Auxilliary Statistical Information
%      .STAT_AUX
%      .HISTORY_NOTE
%      .NOTES_COUNT
%      .NOTE_NUMBER_001
%      .TAGALIGN_MATVEC
%      .VOLREG_CENTER_OLD
%      .VOLREG_CENTER_BASE
%      .VOLREG_ROTPARENT_IDCODE
%      .VOLREG_ROTPARENT_NAME
%      .VOLREG_GRIDPARENT_IDCODE
%      .VOLREG_GRIDPARENT_NAME
%      .VOLREG_INPUT_IDCODE
%      .VOLREG_INPUT_NAME
%      .VOLREG_BASE_IDCODE
%      .VOLREG_BASE_NAME
%      .VOLREG_ROTCOM_NUM
%      .VOLREG_MATVEC_xxxxxx
%      .VOLREG_ROTCOM_xxxxxx
%      .IDCODE_ANAT_PARENT
%      .TO3D_ZPAD
%      .IDCODE_WARP_PARENT
%      .WARP_TYPE
%      .WARP_DATA
%      .MARKS_XYZ
%      .MARKS_LAB
%      .MARKS_HELP
%      .MARKS_FLAGS
%      .TAGSET_NUM
%      .TAGSET_FLOATS
%      .TAGSET_LABELS
%      .LABEL_1
%      .LABEL_2
%      .DATASET_NAME
%      .DATASET_KEYWORDS
%      .BRICK_KEYWORDS
%      .HISTORY_NOTE
%      .NOTES_COUNT
%      .NOTE_NUMBER_xxx
%      .TEMPLATE_SPACE
%      .ATLAS_LABEL_TABLE
%
%  To implement in the future,
%      VOLREG_MATVEC_xxxxxx , VOLREG_ROTCOM_xxxxxx
%
%  The following fields were added to support 1D file format.
%      .FileFormat: 'BRIK' ('1D' is allowed for 1D files but 1D files do not use BrikInfo)
%      .Extension_1D: The extension of the 1D filename
%  BrikLoad and WriteBrik now read and write 1D files
%
%  If the .HEAD contains a TEMPLATE_SPACE and ATLAS_LABEL_TABLE, these are added as well
%
%Key Terms:
%   The 1st, second and third dimensions refer to the dimensions the slices were entered into to3d
%More Info :
%    afni's README.attributes
%
%     Author : Ziad Saad
%     Date : Sun Oct 17 00:13:49 CDT 1999 , Major modification, April 06 2001

%Define the function name for easy referencing
FuncName = 'BrikInfo';

%Debug Flag
DBG = 1;

%initailize return variables
err = 1;
Info = [];

%check if this is a 1D file ..
is1D = 0;
[St, xtr] = Remove1DExtension(BrickName);
if (~isempty(xtr))
   is1D = 1;
end

if (is1D) % 1D land
   [err, V, Info] = Read_1D(BrickName, 1);
   if (err)
      ErrMessage = sprintf ('%s: Failed to read %s file', FuncName, BrickName);
      err = ErrEval(FuncName,'Err_1D file could not be read');
      return;
   end
   clear V; %biggest waste since 2002
   return;
end

	vtmp = findstr(BrickName,'.BRIK');
	if (~isempty(vtmp)) %remove .BRIK
		BrickName = BrickName(1:vtmp(1)-1);
	end

	vtmp = findstr(BrickName,'.HEAD');
	if (isempty(vtmp)) %add .HEAD
		BrickName = sprintf('%s.HEAD',BrickName);
	end

	if (exist(BrickName) ~= 2)
		err = ErrEval(FuncName,'Err_Could not find data set');
		return;
	end

	%store the name without the extension
		vtmp = findstr(BrickName,'.HEAD');
		Info.RootName = BrickName(1:vtmp-1);

	fidin=fopen(BrickName,'r');
	if (fidin < 0),	err = ErrEval(FuncName,'Err_Could not read .HEAD file'); return;	end
		BRIKinfo = fscanf(fidin,'%c');
	fclose(fidin);

%get subBrik info
	%Nx, Ny, Nz
		[err,Info.DATASET_DIMENSIONS] = BrikInfo_SectionValue (BRIKinfo, 'DATASET_DIMENSIONS');

	%DATASET_RANK
		[err, Info.DATASET_RANK] = BrikInfo_SectionValue(BRIKinfo, 'DATASET_RANK');

	%BRICK_TYPES
		[err, Info.BRICK_TYPES] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_TYPES');

	%BRICK_STATS
		[err, Info.BRICK_STATS] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_STATS');

	%BRICK_FLOAT_FACS
		[err, Info.BRICK_FLOAT_FACS] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_FLOAT_FACS');

	%BYTEORDER_STRING
		[err, Info.BYTEORDER_STRING] = BrikInfo_SectionValue(BRIKinfo, 'BYTEORDER_STRING');

	%ORIENT_SPECIFIC
		[err, Info.ORIENT_SPECIFIC] = BrikInfo_SectionValue(BRIKinfo, 'ORIENT_SPECIFIC');

	%ORIGIN
		[err, Info.ORIGIN] = BrikInfo_SectionValue(BRIKinfo, 'ORIGIN');

	%DELTA
		[err, Info.DELTA] = BrikInfo_SectionValue(BRIKinfo, 'DELTA');

	%BRICK_LABS
		[err, Info.BRICK_LABS] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_LABS');

	%BRICK_KEYWORDS
		[err, Info.BRICK_KEYWORDS] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_KEYWORDS');

	%SCENE_DATA
		[err, Info.SCENE_DATA] = BrikInfo_SectionValue(BRIKinfo, 'SCENE_DATA');

	%TYPESTRING
		[err, Info.TYPESTRING] = BrikInfo_SectionValue(BRIKinfo, 'TYPESTRING');

	%TAXIS_NUMS
		[err, Info.TAXIS_NUMS] = BrikInfo_SectionValue(BRIKinfo, 'TAXIS_NUMS');

	%TAXIS_FLOATS
		[err, Info.TAXIS_FLOATS] = BrikInfo_SectionValue(BRIKinfo, 'TAXIS_FLOATS');

	%TAXIS_OFFSETS
		[err, Info.TAXIS_OFFSETS] = BrikInfo_SectionValue(BRIKinfo, 'TAXIS_OFFSETS');

	%IDCODE_STRING
		[err, Info.IDCODE_STRING] = BrikInfo_SectionValue(BRIKinfo, 'IDCODE_STRING');

	%IDCODE_DATE
		[err, Info.IDCODE_DATE] = BrikInfo_SectionValue(BRIKinfo, 'IDCODE_DATE');

	%BRICK_STATAUX
		[err, Info.BRICK_STATAUX] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_STATAUX');

	%STAT_AUX
		[err, Info.STAT_AUX] = BrikInfo_SectionValue(BRIKinfo, 'STAT_AUX');

	%HISTORY_NOTE
		[err, Info.HISTORY_NOTE] = BrikInfo_SectionValue(BRIKinfo, 'HISTORY_NOTE');

	%NOTES_COUNT
		[err, Info.NOTES_COUNT] = BrikInfo_SectionValue(BRIKinfo, 'NOTES_COUNT');

	%NOTE_NUMBER_001
		[err, Info.NOTE_NUMBER_001] = BrikInfo_SectionValue(BRIKinfo, 'NOTE_NUMBER_001');

	%TAGALIGN_MATVEC
		[err, Info.TAGALIGN_MATVEC] = BrikInfo_SectionValue(BRIKinfo, 'TAGALIGN_MATVEC');

	%VOLREG_CENTER_OLD
		[err, Info.VOLREG_CENTER_OLD] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_CENTER_OLD');

	%VOLREG_CENTER_BASE
		[err, Info.VOLREG_CENTER_BASE] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_CENTER_BASE');

	%VOLREG_ROTPARENT_IDCODE
		[err, Info.VOLREG_ROTPARENT_IDCODE] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_ROTPARENT_IDCODE');

	%VOLREG_ROTPARENT_NAME
		[err, Info.VOLREG_ROTPARENT_NAME] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_ROTPARENT_NAME');

	%VOLREG_GRIDPARENT_IDCODE
		[err, Info.VOLREG_GRIDPARENT_IDCODE] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_GRIDPARENT_IDCODE');

	%VOLREG_GRIDPARENT_NAME
		[err, Info.VOLREG_GRIDPARENT_NAME] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_GRIDPARENT_NAME');

	%VOLREG_INPUT_IDCODE
		[err, Info.VOLREG_INPUT_IDCODE] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_INPUT_IDCODE');

	%VOLREG_INPUT_NAME
		[err, Info.VOLREG_INPUT_NAME] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_INPUT_NAME');

	%VOLREG_BASE_IDCODE
		[err, Info.VOLREG_BASE_IDCODE] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_BASE_IDCODE');

	%VOLREG_BASE_NAME
		[err, Info.VOLREG_BASE_NAME] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_BASE_NAME');

	%VOLREG_ROTCOM_NUM
		[err, Info.VOLREG_ROTCOM_NUM] = BrikInfo_SectionValue(BRIKinfo, 'VOLREG_ROTCOM_NUM');

   %Inbal's matrix,
   %TO get AFNI's RAI coords from AFNI i, j, k, indices (start at 0):
   % M = reshape(Info.IJK_TO_DICOM_REAL, 4, 3)'; I = [i j k 1]';
   % X = M*I
      [err, Info.IJK_TO_DICOM_REAL ] = BrikInfo_SectionValue(BRIKinfo, 'IJK_TO_DICOM_REAL');

	if (~err)
		for i=1:1:Info.VOLREG_ROTCOM_NUM
			sp = pad_strn(num2str(i-1), '0', 6, 1);
			spad = sprintf('[err, Info.VOLREG_MATVEC_%s] = BrikInfo_SectionValue(BRIKinfo, ''VOLREG_MATVEC_%s'');' , sp, sp);
			eval([ spad ]);
			spad = sprintf('[err, Info.VOLREG_ROTCOM_%s] = BrikInfo_SectionValue(BRIKinfo, ''VOLREG_ROTCOM_%s'');' , sp, sp);
			eval([ spad ]);
		end
	end

	%IDCODE_ANAT_PARENT
		[err, Info.IDCODE_ANAT_PARENT] = BrikInfo_SectionValue(BRIKinfo, 'IDCODE_ANAT_PARENT');

	%TO3D_ZPAD
		[err, Info.TO3D_ZPAD] = BrikInfo_SectionValue(BRIKinfo, 'TO3D_ZPAD');

	%IDCODE_WARP_PARENT
		[err, Info.IDCODE_WARP_PARENT] = BrikInfo_SectionValue(BRIKinfo, 'IDCODE_WARP_PARENT');

	%WARP_TYPE
		[err, Info.WARP_TYPE] = BrikInfo_SectionValue(BRIKinfo, 'WARP_TYPE');

	%WARP_DATA
		[err, Info.WARP_DATA] = BrikInfo_SectionValue(BRIKinfo, 'WARP_DATA');

	%MARKS_XYZ
		[err, Info.MARKS_XYZ] = BrikInfo_SectionValue(BRIKinfo, 'MARKS_XYZ');

	%MARKS_LAB
		[err, Info.MARKS_LAB] = BrikInfo_SectionValue(BRIKinfo, 'MARKS_LAB');

	%MARKS_HELP
		[err, Info.MARKS_HELP] = BrikInfo_SectionValue(BRIKinfo, 'MARKS_HELP');

	%MARKS_FLAGS
		[err, Info.MARKS_FLAGS] = BrikInfo_SectionValue(BRIKinfo, 'MARKS_FLAGS');

	%TAGSET_NUM
		[err, Info.TAGSET_NUM] = BrikInfo_SectionValue(BRIKinfo, 'TAGSET_NUM');

	%TAGSET_FLOATS
		[err, Info.TAGSET_FLOATS] = BrikInfo_SectionValue(BRIKinfo, 'TAGSET_FLOATS');

	%TAGSET_LABELS
		[err, Info.TAGSET_LABELS] = BrikInfo_SectionValue(BRIKinfo, 'TAGSET_LABELS');

	%LABEL_1
		[err, Info.LABEL_1] = BrikInfo_SectionValue(BRIKinfo, 'LABEL_1');

	%LABEL_2
		[err, Info.LABEL_2] = BrikInfo_SectionValue(BRIKinfo, 'LABEL_2');

	%DATASET_NAME
		[err, Info.DATASET_NAME] = BrikInfo_SectionValue(BRIKinfo, 'DATASET_NAME');

	%DATASET_KEYWORDS
		[err, Info.DATASET_KEYWORDS] = BrikInfo_SectionValue(BRIKinfo, 'DATASET_KEYWORDS');

	%BRICK_KEYWORDS
		[err, Info.BRICK_KEYWORDS] = BrikInfo_SectionValue(BRIKinfo, 'BRICK_KEYWORDS');

	%HISTORY_NOTE
		%[err, Info.HISTORY_NOTE] = BrikInfo_SectionValue(BRIKinfo, 'HISTORY_NOTE');

	%NOTES_COUNT
		%[err, Info.NOTES_COUNT] = BrikInfo_SectionValue(BRIKinfo, 'NOTES_COUNT');

	if (~err)
		for i=1:1:Info.NOTES_COUNT
			sp = pad_strn(num2str(i), '0', 3, 1);
			spad = sprintf('[err, Info.NOTE_NUMBER_%s] = BrikInfo_SectionValue(BRIKinfo, ''NOTE_NUMBER_%s'');' , sp, sp);
			eval([ spad ]);
		end
	end

	%SOMETHING
		%[err, Info.SOMETHING] = BrikInfo_SectionValue(BRIKinfo, 'SOMETHING');

	%DOF
		[err, Info.WORSLEY_DF] = BrikInfo_SectionValue(BRIKinfo, 'WORSLEY_DF');

	%NONJ
		[err, Info.WORSLEY_NCONJ] = BrikInfo_SectionValue(BRIKinfo, 'WORSLEY_NCONJ');

	%FWHM
		[err, Info.WORSLEY_FWHM] = BrikInfo_SectionValue(BRIKinfo, 'WORSLEY_FWHM');

        %TEMPLATE_SPACE
		[err, Info.TEMPLATE_SPACE] = BrikInfo_SectionValue(BRIKinfo, 'TEMPLATE_SPACE');

        %ATLAS_LABEL_TABLE (if present)
                [err, atlastable] = BrikInfo_SectionValue(BRIKinfo, 'ATLAS_LABEL_TABLE');

    try
      Info.ATLAS_LABEL_TABLE = parse_atlastable(atlastable);
    end

    itype = unique(Info.BRICK_TYPES);

 	if (length(itype) > 1)
		Info.TypeName = 'Mutliple Types';
	else
		switch itype
			case 0
				Info.TypeName = 'byte';
				Info.TypeBytes = 1;
			case 1
				Info.TypeName = 'short';
				Info.TypeBytes = 2; %Platform dependent
			case 2
				Info.TypeName = 'int';
				Info.TypeBytes = 4; %Platform dependent
			case 3
				Info.TypeName = 'float';
				Info.TypeBytes = 4; %Platform dependent
			otherwise
				Info.TypeName = 'Dunno';
				Info.TypeBytes = 0;
		end
	end

	 if (isempty(Info.BYTEORDER_STRING)) %field not found go native
	 	Info.ByteOrder = 'unspecified';
	 else
		 if (~isempty(strmatch('MSB_FIRST', Info.BYTEORDER_STRING)))
				Info.ByteOrder = 'ieee-be'; %Big Endian
		else
			if (~isempty(strmatch('LSB_FIRST', Info.BYTEORDER_STRING)))
					Info.ByteOrder = 'ieee-le'; %Little Endian
			else
				err = ErrEval(FuncName,'Err_Could not understand BYTEORDER_STRING');
				return;
			end
		end
	 end

	for i=1:1:3
		switch Info.ORIENT_SPECIFIC(i)
			case 0
				Info.Orientation(i,:) = 'RL';	%right to left
			case 1
				Info.Orientation(i,:) = 'LR';
			case 2
				Info.Orientation(i,:) = 'PA';
			case 3
				Info.Orientation(i,:) = 'AP';
			case 4
				Info.Orientation(i,:) = 'IS';
			case 5
				Info.Orientation(i,:) = 'SI';
      otherwise
				err = ErrEval(FuncName,'Err_Cannot understand Orientation code');
				return;
		end
	end

   Info.FileFormat = 'BRIK';
   Info.Extension_1D = '';
err = 0;
return;

function T = parse_atlastable(atlastable)

% subfunction to convert a string (read from an afni .HEAD file) with
% atlastable information into a struct

% assume that the string is well-behaved, and that it consists of a series
% of atlas points that has the attributes STRUCT, VAL, OKEY, GYoAR, COG
ap = regexp(atlastable, '<ATLAS_POINT');
st = regexp(atlastable, 'STRUCT');
v  = regexp(atlastable, 'VAL');
o  = regexp(atlastable, 'OKEY');
g  = regexp(atlastable, 'GYoAR');
c  = regexp(atlastable, 'COG');

% the sub-brick label is optional, only present for probabilistic atlases
s  = regexp(atlastable, 'SB_LABEL');
isprobabilistic = ~isempty(s);

assert(numel(ap)==numel(st));
assert(numel(ap)==numel(v));
assert(numel(ap)==numel(o));
assert(numel(ap)==numel(g));
assert(numel(ap)==numel(c));
if isprobabilistic
  assert(numel(ap)==numel(s));
end

ap(end+1) = numel(atlastable);

T = [];
for k = 1:numel(st)
  T(k,1).struct = getcontent(atlastable(st(k):(v(k)-1)));
  T(k,1).val    = str2double(getcontent(atlastable(v(k):(o(k)-1))));
  T(k,1).okey   = str2double(getcontent(atlastable(o(k):(g(k)-1))));
  T(k,1).gyoar  = str2double(getcontent(atlastable(g(k):(c(k)-1))));
  if isprobabilistic
    T(k,1).cog = str2num(getcontent(atlastable(c(k):(s(k)-1))));
    tmp        = getcontent(atlastable(s(k):(ap(k+1)-1)));
    % the sub-brick label can be numeric, or string
    if ~isempty(str2num(tmp))
      T(k,1).sb_label = str2num(tmp);
    else
      T(k,1).sb_label = tmp;
    end
  else
    T(k,1).cog = str2num(getcontent(atlastable(c(k):(ap(k+1)-1))));
  end
end

function out = getcontent(substring)

% anecdotally, I have come across 2 situations: one with the values between
% double quotation marks, or without, try to accommodate both situations
sel = regexp(substring, '"');
if isempty(sel)
  sel = regexp(substring, '=');
  out = deblank(substring((sel+1):end));
elseif numel(sel)>=2
  out = substring((sel(1)+1):(sel(2)-1));
else
  error('something unexpected happened');
end


function letitsnow(figure_hdl, sf_color)
% LETITSNOW - Make the figure snow!
%
% Usage:
% ------
% letitsnow              -  Create snow in the CURRENT figure. If no figure
%                           exists, an empty figure will be created.
% letitsnow(figure_hdl)  -  Create snow in the figure represented by
%                           figure_hdl
% letitsnow(figure_hdl, color) - Create snow with the specified color
%                                'color' is a scalar ranged 0~1
%
% How to stop the snow
% -----
% Press 's' on the figure or directly close the figure
%
% Mingjing Zhang @ Simon Fraser University, Canada
% Copyright(C)Stellari Studio, 2011

%% Update History
% -------------------------------------------------------------------------
% Date                  Updater                 Modification
% -------------------------------------------------------------------------
% Dec 19, 2010          M. Zhang                wrote it
%%

%% TODO:
% The frost effect

%% Error Check
if ~exist('figure_hdl', 'var')||isempty(figure_hdl)
    figure_hdl = gcf;
%     axes
end
if ~exist('sf_color', 'var') || isempty(sf_color)
    sf_color = 0.9;
end

if strcmp(get(figure_hdl, 'UserData'), 'sn0w1ng')
    return;
end
%% Constant Declaration
fps = 25;
% - For snowflakes -
num_snowflakes = 120;
snowflakes_size = 11;
snowflakes_color = sf_color.*[1 1 1];
height_range = 3;

snow_timer = timer('TimerFcn', @updateSnow_Callback_nest,...
    'ExecutionMode','fixedRate',...
    'Period', 1./fps, 'BusyMode', 'queue');

% Position of snowflakes
snowflakes_x = rand(1, num_snowflakes);

snowflakes_y = rand(1, num_snowflakes);
snowflakes_y = (snowflakes_y .* height_range.^3).^(1/3)+1;
snowflakes_pos = [snowflakes_x; snowflakes_y];

% Velocity of snowflakes
snowflakes_v = rand(1, num_snowflakes).*0.1 + 0.1;  % (distance per second)
snowflakes_theta = (rand(1, num_snowflakes).*60 + 240).*pi/180;
snowflakes_vel = [snowflakes_v.*cos(snowflakes_theta); snowflakes_v.*sin(snowflakes_theta)];

% parameters for the frost image (not implemented for this version)
% frost_image_res = [300 400];
% frost_image_data = ones(frost_image_res(1), frost_image_res(2), 3);
% frost_alpha_data = zeros(frost_image_res); %rand(frost_image_res).*0.4+0.6;
% frost_snowpile_top = ones(1, frost_image_res(2)).*frost_image_res(1);

% direction_vectors = [1 0;1 -1;0 -1;-1 -1;-1 0;-1 1;0 1;1 1];

%% Create two axes (above all the existing axes, if any)
% Modify the figure (just a little bit)
original_figure_title = get(figure_hdl, 'Name');
set(figure_hdl, 'Name', [original_figure_title ' - Happy Holidays!'],...
                'Renderer', 'opengl',...
                'UserData','sn0w1ng',...
                'KeyReleaseFcn', @figureKeyUp_Callback, ...
                'CloseRequestFcn', @figureCloseReq_Callback);

% The axes for the snowing effect
snow_axes_hdl = axes('Parent', figure_hdl, ...
                     'Units', 'normalized', ...
                     'Position', [0 0 1 1], ...
                     'Color', [0 0 0], ...
                     'XLim', [0 1], ...
                     'YLim', [0 1], ...
                     'NextPlot','add', ...
                     'Visible', 'off', ...
                     'XTick', [], ...
                     'YTick', []);

% The axes for the frost effect (not implemented for this version)
frost_axes_hdl = axes('Parent', figure_hdl, ...
                     'Units', 'normalized', ...
                     'Position', [0 0 1 1], ...
                     'Color', [0 0 0], ...
                     'XLim', [0 1], ...
                     'YLim', [0 1], ...
                     'NextPlot','add', ...
                     'Visible', 'off', ...
                     'XTick', [], ...
                     'YTick', []);

%% Create the image objects
snow_plot_hdl = zeros(1,4);

% One snowflake is virtually four different markers overlapped
snow_plot_hdl(1) = plot(snowflakes_x, snowflakes_y, 'h', 'MarkerSize',snowflakes_size);
snow_plot_hdl(2) = plot(snowflakes_x, snowflakes_y, 'x', 'MarkerSize',snowflakes_size);
snow_plot_hdl(3) = plot(snowflakes_x, snowflakes_y, '+', 'MarkerSize',snowflakes_size.*3/4);
snow_plot_hdl(4) = plot(snowflakes_x, snowflakes_y, 's', 'MarkerSize',snowflakes_size.*3/5);

set(snow_plot_hdl, 'Color', snowflakes_color);
% set(snow_plot_hdl, 'Color', snowflakes_color, 'XDataSource', 'snowflakes_x', 'YDataSource', 'snowflakes_y');

% frost_image_hdl = image([0 1], [0 1], frost_image_data, ...
%                         'Parent', frost_axes_hdl,...
%                         'AlphaData', frost_alpha_data, ...
%                         'Visible', 'on');
% frost_alpha_data(150, 200) = 1;
start_time = tic;
last_time = toc(start_time);
uistack(snow_axes_hdl, 'top');
uistack(frost_axes_hdl, 'top');

% Start the falling snow!
start(snow_timer);

%% TimerFcn Callback function for updating the falling snow
    function updateSnow_Callback_nest(obj, event)
        current_time = toc(start_time);
        elapsed_time = current_time - last_time;

        % Update the position
        snowflakes_pos = snowflakes_pos + snowflakes_vel.*elapsed_time;

        % Find the snowflakes falling on the ground, and let them reappear
        % from the top of the sky.
        ind_snowflakes_outofbound = snowflakes_pos(2,:)<-0.05;
        snowflakes_pos(2, ind_snowflakes_outofbound) = snowflakes_pos(2, ind_snowflakes_outofbound) + 1.1;

        % Update the x/y data source
        snowflakes_x = snowflakes_pos(1,:);
        snowflakes_y = snowflakes_pos(2,:);
        %         refreshdata(snow_plot_hdl, 'caller'); % BROKEN IN R2012b
        set(snow_plot_hdl, 'XData', snowflakes_x, 'YData', snowflakes_y);
        drawnow;
        last_time = current_time;

    end
%% KeyReleaseFcn: stop the snow when 's' is pressed
    function figureKeyUp_Callback(obj, event)
        key = get(figure_hdl, 'CurrentKey');
        if strcmp(key, 's')
            stopsnow;
            set(figure_hdl, 'KeyReleaseFcn', []);
        end
    end

%% CloseRequestFcn: stop the snow when the figure window is closed
    function figureCloseReq_Callback(obj, event)
        if strcmp(get(figure_hdl, 'UserData'), 'sn0w1ng')
            stopsnow;
        end
        closereq;
    end

%% Clean up function
    function stopsnow()
        if isvalid(snow_timer)
            stop(snow_timer);
            delete(snow_timer);
            delete(snow_axes_hdl);
            delete(frost_axes_hdl);
        end

        set(figure_hdl, 'UserData', []);
        set(figure_hdl, 'Name', original_figure_title);
    end
end


function [keypointsAll, imgidxs_missing] = det2keypointsImgMulticut(expidx,parts,annolist_gt,bAllPartsOn)

fprintf('det2keypointsImgMulticut()\n');

if (nargin < 4)
    bAllPartsOn = false;
end

p = rcnn_exp_params(expidx);

predDir = p.multicutDir;
%{
if (isfield(p,'predDir'))
    predDir = [p.predDir '/multicut/'];
else
    conf = rcnn_config('sub_dir', '/cachedir/test', 'exp_dir', [p.expDir '/' p.shortName]);
    predDir = [conf.cache_dir '/multicut/'];
end
%}
keypointsAll = repmat(struct('imgname','','det',nan(16,3)), length(annolist_gt), 1);

if (isfield(p,'oracleClusEval'))
    oracleClusEval = p.oracleClusEval;
else
    oracleClusEval = false;
end

if (isfield(p,'oracleEval'))
    oracleEval = p.oracleEval;
else
    oracleEval = false;
end

if (isfield(p,'oracleSymmEval'))
    oracleSymmEval = p.oracleSymmEval;
else
    oracleSymmEval = false;
end

sim_class_id = [
    6
    5
    4
    3
    2
    1
    12
    11
    10
    9
    8
    7
    13
    14
];

cidxs = p.cidxs;

nmissing = 0;
imgidxs_missing = [];
for imgidx = 1:length(annolist_gt)

    fprintf('.');

    keypointsAll(imgidx).imgname = annolist_gt(imgidx).image.name;

    fname = [predDir '/imgidx_' padZeros(num2str(imgidx),4) '_cidx_' num2str(cidxs(1)) '_' num2str(cidxs(end))];
    try
        load(fname,'unLab','unPos','unProb');
    catch
        for i = 1:length(p.pidxs)
            pidx = p.pidxs(i);
            jidx = parts(pidx+1).pos(1);
            keypointsAll(imgidx).det(jidx+1,:) = [inf inf -1];
        end
        nmissing = nmissing + 1;
        imgidxs_missing = [imgidxs_missing; imgidx];
        continue;
    end

    img = imread(annolist_gt(imgidx).image.name);
    [Y,X,~] = size(img);
    Y = 0.5*Y; X = 0.5*X;

%     X = annolist_gt(imgidx).annorect.objpos.x;
%     Y = annolist_gt(imgidx).annorect.objpos.y;

    rect = annolist_gt(imgidx).annorect;
    refDist = util_get_torso_size(rect);
    for i = 1:length(p.pidxs)
        if (~ismember(i,cidxs))
            continue;
        end
        pidx = p.pidxs(i);
        assert(parts(pidx+1).pos(1) == parts(pidx+1).pos(2));
        jidx = parts(pidx+1).pos(1);

%         detidxs = find(unLab(:,1) == i-1);
        detidxs = find(unLab(:,1) == find(cidxs == i)-1);
        if (isempty(detidxs))
            if (bAllPartsOn)
%                 [val,idx] = max(unProb(:,i));
                [val,idx] = max(unProb(:,find(cidxs == i)));
                det = unPos(idx,:);
            else
                det = [inf inf];
            end
        else
            clusidxs = unLab(detidxs,2);
            clusidxsUniq = unique(clusidxs);
            nExClus = zeros(length(clusidxsUniq),1);
            clusMean = zeros(length(clusidxsUniq),2);
            for j = 1:length(clusidxsUniq)
                nExClus(j) = length(find(unLab(:,2) == clusidxsUniq(j)));
                clusMean(j,:) = mean(unPos(unLab(:,2) == clusidxsUniq(j),:),1);
            end
            d = sqrt(sum((clusMean - repmat([X Y],size(clusMean,1),1)).^2,2));
            [val,id] = sort(d,'ascend');
%             detidxs = find(unLab(:,1) == i-1 & unLab(:,2) == clusidxsUniq(id(1)));
            detidxs = find(unLab(:,1) == find(cidxs == i)-1 & unLab(:,2) == clusidxsUniq(id(1)));

%             mean between the two closest
%             d = inf(length(detidxs),length(detidxs));
%             for j = 1:length(detidxs)
%                 d(j,:) = sqrt(sum((repmat(unPos(detidxs(j),:),length(detidxs),1) - unPos(detidxs,:)).^2,2));
%             end
%             for j = 1:length(detidxs)
%                 d(j,j) = inf;
%             end
%             idx = find(d==min(min(d)));
%             [rx,cx] = ind2sub(size(d),idx);
%             det = mean(unPos(detidxs([rx(1) cx(1)]),:),1);

%             weighted sum excluding outliers
%             if (length(detidxs) > 2)
%                 d = inf(length(detidxs),length(detidxs));
%                 for j = 1:length(detidxs)
%                     d(j,:) = sqrt(sum((repmat(unPos(detidxs(j),:),length(detidxs),1) - unPos(detidxs,:)).^2,2));
%                 end
%                 dd = sum(d,2);
%                 [val,id2] = sort(dd,'descend');
%                 detidxs = detidxs(2:end);
%             end
%             w = unProb(detidxs,i);
%             w = w./sum(w);
%             det = sum(unPos(detidxs,:).*[w w],1);

%             medoid
%             m = mean(unPos(detidxs,:),1);
%             d = sqrt(sum((repmat(m,length(detidxs),1) - unPos(detidxs,:)).^2,2));
%             [val,id2] = min(d);
%             det = unPos(detidxs(id2),:);

%             maximum
%             [val,idx] = max(unProb(detidxs));
%             det = unPos(detidxs(idx),:);

%             weighted sum
%             w = unProb(detidxs,i);
            w = unProb(detidxs,find(cidxs == i));
            w = w./sum(w);
            det = sum(unPos(detidxs,:).*[w w],1);

            if (oracleEval || oracleClusEval || oracleSymmEval)
                if (oracleEval)
                    detidxs = 1:size(unPos,1);
                elseif (oracleSymmEval)
%                     detidxs = [detidxs; find(unLab(:,1) == sim_class_id(i)-1)];
                    detidxs = [detidxs; find(unLab(:,1) == sim_class_id(find(cidxs == i))-1)];
                end
                pp = util_get_annopoint_by_id(rect.annopoints.point, jidx);
                d = sqrt(sum((repmat([pp.x pp.y],size(unPos(detidxs,:),1),1) - unPos(detidxs,:)).^2,2))./refDist;
                [val,id] = min(d);
                det = unPos(detidxs(id),:);
            end

            % DEBUG
%             [val,id] = max(unProb(detidxs,i));
%             det = unPos(detidxs(id),:);
%             figure(100); clf; imagesc(img); axis equal; hold on;
%             detidxsAll = unLab(:,2) == clusidxsUniq(id(1));
%             plot(unPos(detidxsAll,1),unPos(detidxsAll,2),'r+','Markersize',10);
%             plot(unPos(detidxs,1),unPos(detidxs,2),'b+','Markersize',10);
%             plot(det(:,1),det(:,2),'y+','Markersize',10);
        end

        % part is a joint
        keypointsAll(imgidx).det(jidx+1,:) = [det,-1];
    end
    if (~mod(imgidx, 100))
        fprintf(' %d/%d\n',imgidx,length(keypointsAll));
    end
end
fprintf(' done\n');
fprintf('nmissing: %d\n',nmissing);

end


% --- Executes on button press in multinetdect.
function multinetdect_Callback(hObject, eventdata, handles, SingleDetect)

if isempty(handles.audiofiles)
    errordlg('No Audio Selected')
    return
end
if isempty(handles.networkfiles)
    errordlg('No Network Selected')
    return
end
if exist(handles.data.settings.detectionfolder,'dir')==0
    errordlg('Please Select Output Folder')
    uiwait
    load_detectionFolder_Callback(hObject, eventdata, handles)
    handles = guidata(hObject);  % Get newest version of handles
end

%% Do this if button Multi-Detect is clicked
if ~SingleDetect
    audioselections = listdlg('PromptString','Select Audio Files:','ListSize',[500 300],'ListString',handles.audiofilesnames);
    if isempty(audioselections)
        return
    end
    networkselections = listdlg('PromptString','Select Networks:','ListSize',[500 300],'ListString',handles.networkfilesnames);
    if isempty(audioselections)
        return
    end

    %% Do this if button Single-Detect is clicked
elseif SingleDetect
    audioselections = get(handles.AudioFilespopup,'Value');
    networkselections = get(handles.neuralnetworkspopup,'Value');
end

Settings = [];
for k=1:length(networkselections)
    prompt = {'Total Analysis Length (Seconds; 0 = Full Duration)','Frequency Cut Off High (kHZ)','Frequency Cut Off Low (kHZ)','Score Threshold (0-1)','Append Date to FileName (1 = yes)'};
    dlg_title = ['Settings for ' handles.networkfiles(networkselections(k)).name];
    num_lines=[1 100]; options.Resize='off'; options.WindowStyle='modal'; options.Interpreter='tex';
    def = handles.data.settings.detectionSettings;
    current_settings = str2double(inputdlg(prompt,dlg_title,num_lines,def,options));

    if isempty(current_settings) % Stop if user presses cancel
        return
    end

    Settings = [Settings, current_settings];
    handles.data.settings.detectionSettings = sprintfc('%g',Settings(:,1))';
end

if isempty(Settings)
    return
end

% Save the new settings
handles.data.saveSettings();

update_folders(hObject, eventdata, handles);
handles = guidata(hObject);  % Get newest version of handles

%% For Each File
for j = 1:length(audioselections)
    CurrentAudioFile = audioselections(j);
    % For Each Network
    Calls = [];
    for k=1:length(networkselections)
        h = waitbar(0,'Loading neural network...');

        AudioFile = fullfile(handles.audiofiles(CurrentAudioFile).folder,handles.audiofiles(CurrentAudioFile).name);

        networkname = handles.networkfiles(networkselections(k)).name;
        networkpath = fullfile(handles.networkfiles(networkselections(k)).folder,networkname);
        NeuralNetwork=load(networkpath);%get currently selected option from menu
        close(h);

        Calls = [Calls; SqueakDetect(AudioFile,NeuralNetwork,handles.audiofiles(CurrentAudioFile).name,Settings(:,k),j,length(audioselections),networkname)];

    end

    [~,audioname] = fileparts(AudioFile);
    detectiontime=datestr(datetime('now'),'yyyy-mm-dd HH_MM PM');

    if isempty(Calls)
        fprintf(1,'No Calls found in: %s \n',audioname)
        continue
    end

    h = waitbar(1,'Saving...');
    Calls = Automerge_Callback(Calls, [], AudioFile);

    %% Save the file
    % Save the Call table, detection metadata, and results of audioinfo

    % Append date to filename
    if Settings(5)
        fname = fullfile(handles.data.settings.detectionfolder,[audioname ' ' detectiontime '.mat']);
    else
        fname = fullfile(handles.data.settings.detectionfolder,[audioname '.mat']);
    end

    % Display the number of calls
    fprintf(1,'%d Calls found in: %s \n',height(Calls),audioname)

    if ~isempty(Calls)
        detection_metadata = struct(...
            'Settings', Settings,...
            'detectiontime', detectiontime,...
            'networkselections', {handles.networkfiles(networkselections).name});
        audiodata = audioinfo(AudioFile);
        save(fname,'Calls', 'detection_metadata', 'audiodata' ,'-v7.3', '-mat');
    end

    delete(h)
end
update_folders(hObject, eventdata, handles);
guidata(hObject, handles);


% Example script showcasing the functionality of slocstat
%
% @author B. Schauerte
% @date   2012

%%
% You can call it on a filename
call = 'slocstat.m';
[sloc,stat] = slocstat(call);
fprintf('Total number of lines %d for ''%s''\n',sloc,call);

%%
% You can call it on a function name
call = 'slocstat';
[sloc,stat] = slocstat(call);
fprintf('Total number of lines %d for ''%s''\n',sloc,call);

% ... and of course of any (non-builtin) function in Matlab's search path,
% e.g., "dct"
call = 'dct';
[sloc,stat] = slocstat(call);
fprintf('Total number of lines %d for ''%s''\n',sloc,call);

%%
% You can call it on a folder name and then it also makes sense to use
% printslocstat
call = '.';
[sloc,stat] = slocstat(call);
fprintf('Total number of lines %d for ''%s''\n',sloc,call);
printslocstat(stat);


% Initialize trial with default values
%
function trial = initTrial()
    % Recording system. Possible values are 'Axona' and 'NeuraLynx'.
    trial.system = nan;

    % Session base name. I.e. for single session '2103201101'. For combined sessions
    % it will be '2103201101+02'. More than two combined sessions are not supported.
    trial.basename = '';

    % Session path, normally the folder of the first session
    trial.path = '';

    trial.positions = []; % Matrix Nx5, N - number of entries.
                                % columns:
                                % 1 timestamps
                                % 2 LED1 x values
                                % 3 LED1 y values
                                % 4 LED2 x values, also known as x2
                                % 5 LED2 y values, also knows as y2

    trial.spikes = [];    % Matrix Nx3, N - number of entries. 1 column stores timestamps, spike data.
                                % 2 column contains tetrode number (integer), 3 column - unit
                                % number (integer).

    % Extra information, structure. Could contain fields
    % room - string with information about recording room;
    % shape - struct with information about recording box shape, fields:
    %         descr - raw string from file
    %         type - integer. 1 = box, 2 = cylinder, 3 = linear track
    %         value - double. Side length or diameter of the arena. If dimensions are not equal,
    %                 then value will have several values. For example for box arena value(1)
    %                 defines x-dimension, and value(2) defines y-dimension.
    % startTime - string
    % calibration - structure with following fields:
    %         file - Path to file with camera calibration information
    %         data - calibration data
    %
    % innerSize - structure with following fields:
    %         rec - Path to recording with LEDs that define arena inner size
    %         data - extracted inner size data
    % outerSize - structure with following fields
    %         rec - Path to recording with LEDs that define arena outer size
    %         data - extracted outer size data
    % subjectId - string, animal/subject ID
    %
    trial.extraInfo = struct();

    % Sampling time. Default values are from Axona system.
    trial.sampleTime = 0.02; % 50 Hz
    trial.videoSamplingRate = 50;

    trial.sessions = {}; % session names
    trial.cuts = {}; % cut filenames. Matrix of size NxM. Each row contains cut filenames
                     % for a particular tetrode (tetrodes are obtained from trial.units).
                     % Values in columns allow to load multiple cut files for cells,
                     % or have multiple cuts per tetrode in case of combined sessions.
    % Information about cut files that have been used to define units.
    % The key is an integer that represents the cell: each tetrode and cell number is
    % mapped in a single value by function helpers.tetrodeCellLinearIndex.
    % Each value is a Nx3 cell array. N is the number of cut files that have been used
    % to define this cell. First column contains path to cut file, second column
    % contains corresponding MD5 hash value. Both columns are strings.
    % Third column contains session index of a session to which this cut file belongs.
    % This is needed to adjust spike offset in case of multiple session loading.
    % Used to determine if cut files have been changed since last load of data in Matlab.
    trial.cutHash = containers.Map('KeyType', 'int64', 'ValueType', 'any');

    trial.tetrode = [];
    trial.units = []; % Matrix of units, Nx2. First column contains
                      % tetrode numbers, second - cell numbers.

    trial.startIndices = []; % When multiple sessions are combined, it can be
                             % useful to track position of each session individually.
                             % This vector stores starting index of each session. Index
                             % is applied to position matrix.

     % List of LFP channels to load/use. -1 has a special meaning being
     % 'all available channels'.
     trial.lfpChannels = [];

     % Flag that determines if this trial inherits units of a previous
     % trial or not. Default behaviour is that units are not inherited.
     trial.inheritUnits = false;

     % Identifier of cutting software. See also bntConstants.CutApp.
     trial.cutApp = [];

     % Fields that are going to be initialized later if at all:
     % lfpInfo - instance of io.LfpInfoInterface class
end


%              Apart = extractA(At,Ajc,blk0,blk1,blkstart[,blkstart2])
% EXTRACTA  Fast alternative to
%  Apart = At(blkstart(1):blkstart(2)-1,:).
%  Instead of blkstart(2), it takes "blkstart2" (if supplied) or
%  size(At,1)+1 (if neither blkstart(2) nor blkstart2) are available.
%
%  Extract submatrix of
%  A with subscripts "blkstart(1):blkstart(2)-1" and has its nonzeros per column
%  bounded bij Ajc1:Ajc2. If Ajc1 = [] (Ajc2=[]) then start at column start
%  (end at column end) of A.
%
% ******************** INTERNAL FUNCTION OF SEDUMI ********************
%
%  See also partitA

function Apart = extractA(At,Ajc,blk0,blk1,blkstart,blkstart2) %#ok
%
% This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko
% Copyright (C) 2005 McMaster University, Hamilton, CANADA  (since 1.1)
%
% Copyright (C) 2001 Jos F. Sturm (up to 1.05R5)
%   Dept. Econometrics & O.R., Tilburg University, the Netherlands.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% Affiliation SeDuMi 1.03 and 1.04Beta (2000):
%   Dept. Quantitative Economics, Maastricht University, the Netherlands.
%
% Affiliations up to SeDuMi 1.02 (AUG1998):
%   CRL, McMaster University, Canada.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
% 02110-1301, USA

disp('The SeDuMi binaries are not installed.')
disp('In Matlab, launch "install_sedumi" in the folder you put the SeDuMi files.')
disp('For more information see the file Install.txt.')
error(' ')


function display(X)
%DISPLAY   Display a CHEBFUN2V.
%   DISPLAY(F) outputs important information about the CHEBFUN2V F to the
%   command window, including its domain of definition, length (number of pivots
%   used to represent it), and a summary of its structure.
%
%   It is called automatically when the semicolon is not used at the end of a
%   statement that results in a CHEBFUN2V.

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

if ( isequal(get(0, 'FormatSpacing'), 'compact') )
	disp([inputname(1), ' =']);
	disp(X);
else
	disp(' ');
	disp([inputname(1), ' =']);
	disp(' ');
    disp(X);
end

end


function [voitype]=dicomrt_checkvoitype(VOI)
% dicomrt_checkvoi(VOI)
%
% Check if the VOI has its own original format (set of 2D contours defined in the Z axis)
% or if it has been modified to contain outlines defined also in the X and Y planes.
%
% See also: dicomrt_checkinput, dicomrt_loadvoi
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

VOI_pointer=VOI{1,1};
VOI_array=VOI{2,1};

nvois=size(VOI_array,1);

for i=1:nvois
    n3d(i)=isempty(strfind(VOI_array{i,1},'3D'));
end

if find(n3d)
    voitype='2D';
else
    voitype='3D';
end


%% S2FunHarmonicSym


function v = cvx_value( x )
v = value( x );

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


classdef (Abstract) nme_branch < mp.nm_element

%   MATPOWER
%   Copyright (c) 2019-2022, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%     properties
%     end

    methods
        function name = name(obj)
            name = 'branch';
        end

        function np = np(obj)
            np = 2;     %% this is a 2 port element
        end
    end     %% methods
end         %% classdef


function val = get(A, prop_name)
%
%  Get psfMatrix properties from specified object and return
%  the value.
%
%     val = get(A, prop_name);
%
%  Valid choices for prop_name are:
%    'psf', 'matdata', 'type', 'boundary', 'transpose'
%

%  J. Nagy  5/2/01

switch prop_name
case 'psf'
  val = A.psf;
case 'matdata'
  val = A.matdata;
case 'type'
  val = A.type;
case 'boundary'
  val = A.boundary;
case 'transpose'
  val = A.transpose;
otherwise
  error([prop_name, 'Is not valid psfMatrix property'])
end


function [retval] = getbatch

% GETBATCH returns a number that can be used to distinguish the subsequent
% batches. The number automatically increases over subsequent calls.

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

persistent batch
if isempty(batch)
  batch = 0;
end
batch = batch + 1;
retval = batch;


function w = waveform(TC)

%WAVEFORM Get waveforms from a threecomp object
%   W = WAVEFORM(TC) Extracts waveforms from a threecomp object where W is
%   a waveform object and TC is a threecomp object.

% Author: Michael West, Geophysical Institute, Univ. of Alaska Fairbanks
% $Date$
% $Revision$

w = TC.traces;


%   AUTHORSHIP
%   Primary Developer: Stephen Meehan <swmeehan@stanford.edu>
%   Math Lead & Secondary Developer:  Connor Meehan <connor.gw.meehan@gmail.com>
%   Bioinformatics Lead:  Wayne Moore <wmoore@stanford.edu>
%   Provided by the Herzenberg Lab at Stanford University
%   License: BSD 3 clause
%

classdef Plots < handle
    properties(Constant)
        DOING_ALL='doingAll';
        DEBUGGING=false;
    end

    properties(SetAccess=private)
        is3D;
        legendHs;
        mdns;
        stds;
        legendH;
        otherPlots={};
    end

    properties
        Hs;
        otherHs;
        cnts;
        names;
        N;
        distFactor;
        mx;
        mxI;
        mn;
        mnI;
        ax;
        javaLegend;
        otherPlotMap;
    end

    methods
        function this=Plots()
            this.Hs=[];
        end

        function setHs(this, Hs)
            this.Hs=Hs;
            this.N=length(this.Hs);
            this.cnts=zeros(1, this.N);
            if isempty(this.names)
                this.names=cell(1, this.N);
                for i=1:this.N
                    this.cnts(i)=length(get(this.Hs(i), 'XData'));
                    this.names{i}=get(this.Hs(i), 'DisplayName');
                end
            else
                for i=1:this.N
                    this.cnts(i)=length(get(this.Hs(i), 'XData'));
                end
            end
            this.setMinMax;
            this.ax=get(Hs(1), 'Parent');
        end

        function setMinMax(this)
            [this.mx, this.mxI]=max(this.cnts);
            [this.mn, this.mnI]=min(this.cnts);
        end

        function setOtherHs(this, Hs)
            this.otherHs=Hs;
        end

        function setLegendHs(this, Hs)
            this.legendHs=Hs;
        end

        function initStats(this)
            if isempty(this.mdns)
                if isempty(this.Hs)
                    return;
                end
                [this.mdns, this.stds, this.is3D]=Plots.Stats(this.Hs);
                if this.is3D
                    this.distFactor=6;
                else
                    this.distFactor=3;
                end
            end
        end

        function setNames(this, names)
            this.names=names;
        end

        function names=getNames(this)
            names=this.names;
        end

        function toggleVisibility(this, idx)
            if ~isempty(this.otherHs)
                R=size(this.otherHs,1);
                for r=1:R
                    plotH=this.otherHs(r, idx);
                    if strcmp('off', get(plotH, 'visible'))
                        set(plotH(r), 'visible', 'on');
                    else
                        set(plotH(r), 'visible', 'off');
                    end
                end
            end
        end

        function clear(this)
            try
                N_=length(this.Hs);
                for i=1:N_
                    delete(this.Hs(i));
                end
                N_=length(this.otherHs);
                for i=1:N_
                    delete(this.otherHs(i));
                end
            catch ex
                %ex.getReport
            end
        end

        function addOtherPlots(this, other)
            this.otherPlots{end+1}=other;
        end

        function flashOtherPlots(this, idx, on)
           if ~isempty(this.otherPlots)
               name=edu.stanford.facs.swing.Basics.RemoveXml(String.RemoveTex(this.names{idx}));
               lIdx=String.LastIndexOf(name, ' training ');
               if lIdx>0
                   name=name.substring(0,lIdx-1);
               end
               nOthers=length(this.otherPlots);
               for i=1:nOthers
                   other=this.otherPlots{i};
                   nNames=length(other.names);
                   for j=1:nNames
                       if strcmp(name, edu.stanford.facs.swing.Basics.RemoveXml(String.RemoveTex(other.names{j})))
                           perc=other.cnts(j)/sum(other.cnts);
                           otherH=other.Hs(j);
                           if nargin<3
                               if isequal(get(otherH, 'visible'), 'on')
                                   set(otherH, 'visible', 'off');
                                   times=3;
                               else
                                   set(otherH, 'visible', 'on');
                                   times=4;
                               end
                           else
                               if ~on
                                   set(otherH, 'visible', 'off');
                                   times=3;
                               else
                                   set(otherH, 'visible', 'on');
                                   times=4;
                               end
                           end
                           Plots.Flash(otherH, perc, times);
                           break;
                       end
                   end
               end
           end
           if nargin>2
               this.setOtherPlotMapVisible(idx, on)
           end
        end

        function yes=isInOtherPlotMap(this, idx)
            if ~isempty(this.otherPlotMap)
                H=this.Hs(idx);
                yes=this.otherPlotMap.isKey(H);
            else
                yes=false;
            end
        end

        function setOtherPlotMapVisible(this, idx, on)
            if ~isempty(this.otherPlotMap)
                H=this.Hs(idx);
                if on
                    vis='on';
                else
                    vis='off';
                end
                if this.otherPlotMap.isKey(H)
                    v=this.otherPlotMap(H);
                    if iscell(v)
                        v=v{1};
                    end
                    if any(v<=0)
                        v(v<0)=v;
                    end
                    set(v, 'visible', vis);
                end
            end
        end

        function setOtherPlotVisible(this, idx, on)
           if ~isempty(this.otherPlots)
               name=edu.stanford.facs.swing.Basics.RemoveXml(String.RemoveTex(this.names{idx}));
               lIdx=String.LastIndexOf(name, ' training ');
               if lIdx>0
                   name=name.substring(0,lIdx-1);
               end
               nOthers=length(this.otherPlots);
               for i=1:nOthers
                   other=this.otherPlots{i};
                   nNames=length(other.names);
                   for j=1:nNames
                       if strcmp(name, edu.stanford.facs.swing.Basics.RemoveXml(String.RemoveTex(other.names{j})))
                           perc=other.cnts(j)/sum(other.cnts);
                           otherH=other.Hs(j);
                           if ~on
                               set(otherH, 'visible', 'off');
                           else
                               set(otherH, 'visible', 'on');
                           end
                       end
                   end
               end
           end
           this.setOtherPlotMapVisible(idx, on)
        end
    end

    methods(Static)

        function [mdns, stds, is3D]=Stats(plotHs)
            N=length(plotHs);
            mdns=zeros(N,3);
            stds=zeros(N,3);
            is3D=true;
            for ii=1:N
                plotH=plotHs(ii);
                if ~ishandle(plotH)
                    return;
                end
                x_=get(plotH, 'XData');
                y_=get(plotH, 'YData') ;
                z_=get(plotH, 'ZData');
                if isempty(z_)
                    [RR,CC]=size(get(plotHs(ii), 'XData'));
                    z_=ones(RR, CC);
                    is3D=false;
                end
                d=[x_; y_; z_]';
                mdns(ii,:)=median(d);
                stds(ii,:)=std(d);
            end
        end

        function Flash(plotH, perc, times)
            vi=get(plotH, 'Visible');
            ms=get(plotH, 'MarkerSize');
            if perc<.02
                set(plotH, 'MarkerSize', ms+7)
            elseif perc<.05
                set(plotH, 'MarkerSize', ms+5)
            elseif perc<.1
                set(plotH, 'MarkerSize', ms+3);
            elseif perc<.2
                set(plotH, 'MarkerSize', ms+2);
            end
            for i=1:times
                %MatBasics.RunLater(@(h,e)go, .1);
                go
            end
            %MatBasics.RunLater(@(h,e)done, .51);
            done
            function go
                set(plotH, 'visible', 'off');
                pause(0.1);
                set(plotH, 'visible', 'on');
                pause(0.05);
            end
            function done
                set(plotH, 'visible', vi);
                set(plotH, 'MarkerSize', ms);
            end
        end

        function [HL, fcnMotion, btns, sortI, plots]=Legend(plotHs, names, ...
                idxsWithName, xMargin, yMargin, doMotion, freq, priorFcn,...
                doubleClicker, doJavaLegend, oldJavaBtns, southComponent,...
                selectedCallback)

            if nargin<13
                selectedCallback=[];
                if nargin<12
                    southComponent=[];
                    if nargin<11
                        oldJavaBtns=[];
                        if nargin<10
                            if nargin<9
                                doubleClicker=[];
                                if nargin<8
                                    priorFcn=[];
                                    if nargin<7
                                        freq=[];
                                        if nargin<6
                                            doMotion=true;
                                            if nargin<5
                                                yMargin=[];
                                                if nargin<4
                                                    xMargin=[];
                                                    if nargin<3
                                                        idxsWithName=[];
                                                        if nargin<2
                                                            names={};
                                                        end
                                                    end
                                                end
                                            end
                                        end
                                    end
                                end
                            end
                            doJavaLegend=false;
                        end
                    end
                end
            end
            app=BasicMap.Global;
            if doJavaLegend
                sup1=app.supStart;
                sup2=app.supEnd;
            end
            btns=[];
            if isa(plotHs, 'Plots')
                that=plotHs;
                plots=plotHs;
                N=plots.N;
            else
                that=[];
                plots=Plots;
                plots.setNames(names);
                if iscell(plotHs)
                    N=length(plotHs);
                    plotHs2=zeros(1,N);
                    for i=1:N
                        plotHs2(i)=plotHs{i};
                    end
                    plotHs=plotHs2;
                else
                    [R,N]=size(plotHs);
                    if R>1
                        otherPlotHs=plotHs(2:end,:);
                        plotHs=plotHs(1,:);
                        plots.setOtherHs(otherPlotHs);
                    end
                end
                plots.setHs(plotHs);
                names=plots.names;
            end
            if isempty(doJavaLegend)
                HL=[];
                fcnMotion=[];
                sortI=[];
                return;
            end
            if N<1
                return;
            end
            if isempty(idxsWithName)
                idxsWithName=1:N;
            end
            assert(N>=length(idxsWithName), ...
                '# of plotHs must be >= # of idxsWithName');
            ax=get(plots.Hs(1), 'Parent');
            fig=get(ax, 'Parent');
            curOver=0;
            overH=[];
            try
                set(fig,'WindowButtonMotionFcn', @legMotion);
            catch ex
            end
            fcnMotion=@legMotion;
            X=xlim(ax);
            Y=ylim(ax);
            assert( length(names) == length(idxsWithName), ...
                'Must be as many namedPlotIdxs as names');

            mx=plots.mx;
            cnts=plots.cnts;
            if isempty(freq)
                Cnt=sum(cnts);
            else
                if length(freq)==length(cnts)
                    if any(isnan(freq))
                        freq(isnan(freq))=-1;
                    end
                    Cnt=sum(freq(freq>=0));
                    cnts=freq;
                    mx=max(cnts);
                else
                    Cnt=freq;
                end
            end
            nNames=length(names);
            cnts2=zeros(1, nNames);
            cntPref=app.getNumeric('cntPref', 2);
            if ~doJavaLegend
                legendHs=zeros(1, nNames);
                for i=1:nNames
                    idx=idxsWithName(i);
                    cnt=cnts(idx);
                    cnts2(i)=cnt;
                    if cnt>=0
                        strCnt=['    ^{\color{blue}' ...
                            String.encodePercent(cnt, Cnt, 1) '  '...
                            '\color[rgb]{.2 .5 .2} ' ...
                            String.encodeCount(cnt, cntPref) '} '];
                        mkSz=6 + (cnt/mx*15);
                        clr=get(plots.Hs(idx), 'MarkerEdgeColor');

                        legendHs(i)=plot(ax, X(1)-X(2), Y(1)-Y(2), 's', ...
                            'MarkerSize', mkSz, 'Color',  clr, ...
                            'MarkerFaceColor', clr, 'LineStyle', 'none');
                    else
                        if isa(plotHs, 'Plots')
                            clr=get(plots.Hs(idx), 'MarkerEdgeColor');
                            legendHs(i)=plot(ax, X(1)-X(2), Y(1)-Y(2), 's', ...
                                'MarkerSize', 6, 'Color',  clr, ...
                                'MarkerFaceColor', clr, 'LineStyle', 'none');
                        else
                            legendHs(i)=plotHs(i);
                        end
                        names{i}=names{i};
                    end

                end
                xlim(ax,X);
                ylim(ax,Y);
                [~,sortI]=sort(cnts2, 'descend');
                HL=legend(ax, legendHs(sortI), names(sortI), ...
                    'Location', 'northeast','AutoUpdate', 'off');
                if ~isempty(xMargin) && ~isempty(yMargin)
                    p=get(HL, 'position');
                    p2=Gui.GetNormalized(ax);
                    xNudge=1-(p2(1)+p2(3));
                    xNudge=xNudge-xMargin;
                    yNudge=1-(p2(2)+p2(4));
                    yNudge=yNudge-yMargin;
                    set(HL, 'position', [p(1)+xNudge, p(2)+yNudge p(3) p(4)]);
                end
                plots.setLegendHs(legendHs);
                HL.ItemHitFcn=@(h,event)Plots.LegendClick(plots, event,...
                    idxsWithName, cnts2);
            else
                for i=1:nNames
                    idx=idxsWithName(i);
                    cnt=cnts(idx);
                    cnts2(i)=cnt;
                    clr=get(plots.Hs(idx), 'MarkerEdgeColor');
                    if cnt>=0
                        strSymbol=['•'];
                        f=11+(cnt/mx*15);
                        f= ceil((f-4)/4)+3;
                        if f>=10
                            f=10;
                        end
                        f=String.encodeInteger(f);
                        str=['' strSymbol ''];
                        strCnt=[sup1 ' ' ...
                            String.encodePercent(cnt,Cnt,1) ''...
                            '  ' ...
                            String.encodeCount(cnt, cntPref) '' ...
                            sup2 ];
                    else
                        ls=get(plots.Hs(idx), 'LineStyle');
                        if strcmpi(ls, 'none')
                            str=['• '];
                        else
                            str=['− '];
                        end
                        strCnt='';
                    end
                    names{i}=['' str names{i} '  '...
                        strCnt Html.EncodeSort('name', strtrim(char(...
                        edu.stanford.facs.swing.Basics.RemoveXml(...
                        lower(names{i}))))) ...
                        Html.EncodeSort('frequency', cnt) ...
                        ''];
                end
                [~, sortI]=sort(cnts2, 'descend');
                [outerPnl, ~, btns,sortGui]=...
                    Radio.Panel3(names(sortI), 1:nNames, 13, ...
                    @(h,e, i)Plots.JavaLegendClick(plots, i, sortI,...
                    idxsWithName, plots.Hs, cnts2, h, selectedCallback), ...
                    true,Html.Wrap(['Deselecting '...
                    'hides items in plot & selecting '...
                    'unhides']));
                sortGui.setProperties(app, 'javaLegend.');
                if ~isempty(oldJavaBtns)
                    it=oldJavaBtns.iterator;
                    while it.hasNext
                        btn=it.next;
                        if ~btn.isSelected
                            k=StringArray.IndexOf(names,char(btn.getText));
                            m=find(sortI==k, 1);
                            if ~isempty(m)
                                b=btns.get(m-1);
                                b.setSelected(false);
                                set(plots.Hs(k), 'visible', 'off');
                                disp('was OFF');
                            end
                        end
                    end
                    sortGui.setAllChbText;
                elseif isequal(idxsWithName, 1:nNames)
                    for i=1:nNames
                        if isequal('off', get(plots.Hs(i), 'visible'))
                            b=btns.get(i-1);
                            b.setSelected(false);
                        end
                    end
                end
                if ~isempty(southComponent)
                    bp=Gui.BorderPanel(0,0);
                    bp.add(outerPnl, 'Center');
                    bp.add(southComponent, 'South');
                    outerPnl=bp;
                end
                if ~isempty(oldJavaBtns) && oldJavaBtns.size>0
                    HL=Gui.WindowAncestor(oldJavaBtns.get(0));
                    HL.setContentPane(outerPnl);
                    HL.pack;
                else
                    pu=showMsg(outerPnl, 'Legend', 'north east+', false, ...
                        false, 0, false);
                    HL=pu.dlg;
                end
                sortGui.dlg=HL;
                btns.get(0).getParent.scrollRectToVisible(java.awt.Rectangle(0,0,1,1))
                if ismac
                    if 1<size(get(0, 'MonitorPositions'), 1)
                        if isequal('on', get(fig, 'visible'))
                            MatBasics.RunLater(@(h,e)relocate(getjframe(fig)), .52)
                        else
                            disp('huh');
                        end
                    end
                end
            end
            if ~isempty(that)
                that.legendH=HL;
            end
            function relocate(ref)
                Gui.LocateJava(HL, ref, 'north east+');
                HL.setVisible(true);
            end
            function legMotion(hObj, event)
                if ~isvalid(ax)
                    return;
                end
                cp=get(ax, 'CurrentPoint');
                if isempty(cp)
                    return;
                end
                C=size(cp,2);
                x=cp(2,1);
                y=cp(2,2);
                z=cp(2,3);
                try
                    e=Gui.GetPixels(HL);
                catch ex
                    e=[];
                end
                %[normX normY normFigX normFigY e]
                if ~isempty(e)
                    cp2=get(get(HL, 'Parent'), 'currentpoint');
                    if cp2(2)<=e(2)+e(4) && cp2(2)>=e(2)
                        if cp2(1)>=e(1) && cp2(1)<= e(1)+e(3)
                            if ~isempty(doubleClicker)
                                doubleClicker.stopListening;
                            end
                            return;
                        end
                    end
                end
                if ~isempty(doubleClicker)
                    doubleClicker.startListening;
                end
                if ~doMotion
                    if ~isempty(priorFcn)
                        feval(priorFcn, hObj, event);
                    end
                    return;
                end
                plots.initStats;
                if plots.is3D
                    [D,II]=pdist2(plots.mdns, [x y z], 'euclidean', 'Smallest', 1);
                    limit=pdist2(plots.mdns(II,:), ...
                        plots.mdns(II,:)+(plots.stds(II,:)*plots.distFactor));
                else
                    [D,II]=pdist2(plots.mdns(:,1:2), [x y], 'euclidean', 'Smallest', 1);
                    limit=pdist2(plots.mdns(II,1:2), plots.mdns(II,1:2)+...
                        (plots.stds(II,1:2)*plots.distFactor));
                end
                if Plots.DEBUGGING
                    %is3D
                    [x y z ; II D limit]
                    plots.names{II}
                    %cp
                    %[plots.mdns(II,:);plots.stds(II,:)]
                    if II==plots.mxI
                        disp('hey');
                    end
                end
                if D<=limit
                    over=II;
                else
                    over=0;
                end
                if ~ishandle(overH)
                    return;
                end
                if curOver ~= over
                    curOver=over;
                    if over>0
                        %disp(['Over ' names{II}]);
                        nameIdx=find(idxsWithName==II,1);
                        if nameIdx>0
                            nm=names{nameIdx};
                            if doJavaLegend
                                nm=strrep(nm, '', '_{{');
                                nm=strrep(nm, '', '}');
                                nm=char(...
                                    edu.stanford.facs.swing.Basics.RemoveXml(nm));
                                nm=strrep(nm, '_{{', '^{');
                                nm=strrep(nm, '•','');
                            end
                            if isempty(overH)
                                if plots.is3D
                                    overH=text(ax, x, y, z, nm, ...
                                        'fontSize', 9, ...
                                        'color', [0 0 .5],...
                                        'EdgeColor', 'red', ...
                                        'FontName', 'Arial', ...
                                        'backgroundColor', [255/256 252/256 170/256]);
                                else
                                    overH=text(ax, x, y, nm, ...
                                        'fontSize', 9, 'color', [0 0 .5], 'EdgeColor',...
                                        'red', 'FontName', 'Arial', ...
                                        'backgroundColor', [255/256 252/256 170/256]);
                                end
                            else
                                if plots.is3D
                                    set(overH, 'visible', 'on', 'Position', ...
                                        [x y z], 'String', nm);
                                    %uistack(overH, 'top');
                                else
                                    set(overH, 'visible', 'on', 'Position', ...
                                        [x y 0], 'String', nm);
                                end
                            end
                        else
                            disp('background');
                        end
                    else
                        %disp('Over NOTHING');
                        if ~isempty(overH)
                            set(overH, 'visible', 'off');
                        end
                    end
                end
                if ~isempty(priorFcn)
                    feval(priorFcn, hObj, event);
                end
            end
        end

        function LegendClick(this, event, idxsWithName, nums)
            H=get(event.Peer, 'UserData');
            idx_=find(this.legendHs==event.Peer, 1);
            idx=idxsWithName(idx_);
            if ~isempty(H)
                set(event.Peer, 'UserData', []);
                delete(H);
                this.toggleVisibility(idx);
                return;
            end
            this.initStats;
            plotH=this.Hs(idx);
            perc= nums(idx_)/max(nums);
            Plots.Flash(plotH, perc, 4);
            X=this.mdns(idx, 1);
            Y=this.mdns(idx, 2);
            ax=get(this.Hs(idx), 'Parent');
            str=get(event.Peer, 'DisplayName');
            fsz=11;
            if this.is3D
                Z=this.mdns(idx, 3);
                Plots.ShowLegendTip(ax, event.Peer, X, Y, str, fsz, 0, Z);
                [X Y Z]
            else
                Plots.ShowLegendTip(ax, event.Peer, X, Y, str, fsz, 0);
            end
            this.toggleVisibility(idx);
            this.flashOtherPlots(idx);
        end

        function ok=JavaLegendClick(this, idx_, sortI, idxsWithName, plotHs, ...
                nums, h, selectedCallback)
            idx=idxsWithName(sortI(idx_));
            plotH=plotHs(idx);
            if ~ishandle(plotH)
                msg('Supervisors not showing', 5, 'east+');
                ok=false;
                return;
            end
            ok=true;
            hasOtherMap=this.isInOtherPlotMap(idx);
            doingAll=nargin==8 && isequal(Plots.DOING_ALL, ...
                    char(h.getActionCommand));
            if ~h.isSelected
                set(plotH, 'visible', 'off');
                times=3;
            else
                if doingAll && hasOtherMap
                    %nope
                    if this.otherPlotMap.size<length(this.Hs)
                        h.setSelected(false)
                        return;
                    end
                end
                set(plotH, 'visible', 'on');
                times=4;
            end
            if doingAll
                if ~isempty(selectedCallback)
                    feval(selectedCallback, h, idx, true);
                end
                this.setOtherPlotVisible(idx, h.isSelected);
                return;
            end
            perc=nums(idx)/max(nums);
            Plots.Flash(plotH, perc, times);
            if ~isempty(selectedCallback)
                feval(selectedCallback, h, idx, false);
            end
            this.flashOtherPlots(idx, h.isSelected);
        end

        function ShowLegendTip(ax, hLegend, posX, posY, str, fsz, secs, posZ)
            if nargin<8
                [normX, normY]=Gui.DataToAxNorm(ax, posX, posY);
                tipH=text(normX+.03, normY+.03, str, ...
                    'fontSize', fsz, 'color', [0 0 .5], 'EdgeColor',...
                    'red', 'FontName', 'Arial', 'parent', ax, ...
                    'FontAngle', 'italic', 'units', 'normalized',...
                    'backgroundColor', [255/256 252/256 170/256]);
            else
                tipH=text(ax, posX, posY, posZ, str, ...
                    'fontSize', fsz, 'color', [0 0 .5], 'EdgeColor',...
                    'red', 'FontName', 'Arial', 'FontAngle', 'italic',...
                    'backgroundColor', [255/256 252/256 170/256]);

            end
            set(tipH, 'ButtonDownFcn', @(h,e)freeze(h, hLegend));
            if secs>0
                MatBasics.RunLater(@(h,e)closeTip, secs);
            else
                freeze(tipH, hLegend);
            end

            function freeze(hText, hLegend)
                if isempty(get(tipH, 'UserData'))
                    set(hText, 'UserData', true);
                    set(hText, 'FontSize', fsz-2);
                    set(hText, 'FontAngle', 'normal');
                    set(hLegend, 'UserData', hText);
                else
                    set(hLegend, 'UserData', []);
                    delete(hText);
                end
            end

            function closeTip
                if isempty(get(tipH, 'UserData'))
                    delete(tipH);
                end
            end
        end

    end
end


%ADAPTIVETHRESHOLD  Applies an adaptive threshold to an array
%
%     dst = cv.adaptiveThreshold(src)
%     dst = cv.adaptiveThreshold(src, 'OptionName',optionValue, ...)
%
% ## Input
% * __src__ Source 8-bit single-channel `uint8` image.
%
% ## Output
% * __dst__ Destination image of the same size and the same type as `src`.
%
% ## Options
% * __MaxValue__ Non-zero value assigned to the pixels for which the condition
%   is satisfied. See the details below. default 255
% * __Method__ Adaptive thresholding algorithm to use, default 'Mean'. One of:
%   * __Mean__ the threshold value `T(x,y)` is a mean of the
%     `BlockSize x BlockSize` neighborhood of `(x,y)` minus `C`
%   * __Gaussian__ the threshold value `T(x,y)` is a weighted sum
%     (cross-correlation with a Gaussian window) of the `BlockSize x BlockSize`
%     neighborhood of `(x,y)` minus `C`. The default sigma (standard deviation)
%     is used for the specified `BlockSize`. See cv.getGaussianKernel
% * __Type__ Thresholding type, default 'Binary'. One of:
%   * __Binary__    `dst(x,y) = (src(x,y) > thresh) ? maxValue : 0`
%   * __BinaryInv__ `dst(x,y) = (src(x,y) > thresh) ? 0 : maxValue`
% * __BlockSize__ Size of a pixel neighborhood that is used to calculate a
%   threshold value for the pixel: 3, 5, 7, and so on. Default 3
% * __C__ Constant subtracted from the mean or weighted mean. Normally, it is
%   positive but may be zero or negative as well. Default 5
%
% The function transforms a grayscale image to a binary image according to
% the formulae:
%
% * __Binary__
%
%                  | maxValue, if src(x,y) > T(x,y)
%       dst(x,y) = |
%                  | 0, otherwise
%
% * __BinaryInv__
%
%                  | 0, if src(x,y) > T(x,y)
%       dst(x,y) = |
%                  | maxValue, otherwise
%
% where `T(x,y)` is a threshold calculated individually for each pixel (see
% `Method` parameter).
%
% Note: Internally the options `'BorderType','Replicate', 'Isolated',true` are
% used to process boundaries (see cv.copyMakeBorder).
%
% See also: cv.threshold, cv.blur, cv.GaussianBlur, adaptthresh, imbinarize
%