function store = getStore(problem, x, storedb)
% Extracts a store struct. pertaining to a point from the storedb database.
%
% function store = getStore(problem, x, storedb)
%
% Queries the storedb database of structures (itself a structure) and
% returns the store structure corresponding to the point x. If there is no
% record for the point x, returns an empty structure.
%
% See also: setStore purgeStoredb

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Dec. 30, 2012.
% Contributors:
% Change log:

    % Construct the fieldname (key) associated to the queried point x.
    key = problem.M.hash(x);

    % If there is a value stored for this key, return it.
    % Otherwise, return an empty structure.
    if isfield(storedb, key)
        store = storedb.(key);
    else
        store = struct();
    end

end


classdef AbstractGeometricPlane < matlab.mixin.SetGet & matlab.mixin.Heterogeneous
    %AbstractGeometricPlane Summary of this class goes here
    %   Detailed explanation goes here

    properties

    end

    methods
        normvect = getPlaneNormVectAtTime(obj, time, vehElemSet, inFrame)

        originPt = getPlaneOriginPtAtTime(obj, time, vehElemSet, inFrame)

        name = getName(obj)

        setName(obj, name)

        listboxStr = getListboxStr(obj)

        useTf = openEditDialog(obj)

        tf = isVehDependent(obj)

        tf = usesGroundObj(obj, groundObj)

        tf = usesGeometricPoint(obj, point)

        tf = usesGeometricVector(obj, vector)

        tf = usesGeometricCoordSys(obj, coordSys)

        tf = usesGeometricRefFrame(obj, refFrame)

        tf = usesGeometricAngle(obj, angle)

        tf = usesGeometricPlane(~, ~)

        tf = isInUse(obj, lvdData)
    end

    methods(Sealed)
        function tf = eq(a,b)
            tf = eq@handle(a,b);
        end

        function tf = ne(a,b)
            tf = ne@handle(a,b);
        end
    end
end


function r = eq(p,q)
% this function compares the equality condition of x, y, and z values of the structures that I use in
% the FVtool.
%
% SYNOPSIS:
%
%
% PARAMETERS:
%
%
% RETURNS:
%
%
% EXAMPLE:
%
% SEE ALSO:
%

% Copyright (c) 2012-2016 Ali Akbar Eftekhari
% See the license file

if (isa(p, 'CellVector')&&isa(q, 'CellVector'))
    r=p;
    r.xvalue = p.xvalue==q.xvalue;
    r.yvalue = p.yvalue==q.yvalue;
    r.zvalue = p.zvalue==q.zvalue;
elseif isa(p, 'CellVector')
    r=p;
    r.xvalue = p.xvalue==q;
    r.yvalue = p.yvalue==q;
    r.zvalue = p.zvalue==q;
else
    r=q;
    r.xvalue = p==q.xvalue;
    r.yvalue = p==q.yvalue;
    r.zvalue = p==q.zvalue;
end


function [hFig, iDS, iFig] = view_timefreq(TimefreqFile, DisplayMode, RowName, isNewFigure, Function)
% VIEW_TIMEFREQ: Display times frequency maps in a new figure.
%
% USAGE: [hFig, iDS, iFig] = view_timefreq(TimefreqFile, DisplayMode='SingleSensor', RowName=[], isNewFigure=0, Function=[])
%
% INPUT:
%     - TimefreqFile : Path to time-frequency file to visualize
%     - DisplayMode  : {'SingleSensor', 'AllSensors', '2DLayout', '2DLayoutOpt'}
%     - RowName      : Name of the row to display from the input timefreq file
%     - isNewFigure  : If 1, force the creation of a new figure
%
% OUTPUT :
%     - hFig : Matlab handle to the figure that was created or updated
%     - iDS  : DataSet index in the GlobalData variable
%     - iFig : Indice of returned figure in the GlobalData(iDS).Figure array

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2010-2014

%% ===== INITIALIZATION =====
% GlobalData : create if not existing yet
global GlobalData;
% Parse inputs
if (nargin < 2)
    DisplayMode = 'SingleSensor';
end
if (nargin < 3) || isempty(RowName)
    RowName = [];
elseif iscell(RowName)
    RowName = RowName{1};
end
if (nargin < 4) || isempty(isNewFigure) || (isNewFigure == 0)
    CreateMode = '';
else
    CreateMode = 'AlwaysCreate';
end
if (nargin < 5) || isempty(Function)
    Function = [];
end

if ~isempty(strfind(lower(TimefreqFile), 'spike_field_coherence')) ...
        || ~isempty(strfind(lower(TimefreqFile), 'noise_correlation')) ...
        || ~isempty(strfind(lower(TimefreqFile), 'rasterplot'))...
        || ~isempty(strfind(lower(TimefreqFile), 'spiking_phase_locking'))
    isEphysFile = 1;
    GlobalData.UserFrequencies.HideFreqPanel = 1;
else
    isEphysFile = 0;
end

%% ===== GET ALL ACCESSIBLE DATA =====
% Get study
[sStudy, iStudy, iItem, DataType, sTimefreq] = bst_get('AnyFile', TimefreqFile);
if isempty(sStudy)
    error('File is not registered in database.');
end

%% ===== LOAD TIME-FREQUENCY FILE =====
bst_progress('start', 'View time-frequency map', 'Loading data...');
% Load file
[iDS, iTimefreq] = bst_memory('LoadTimefreqFile', TimefreqFile);
if isempty(iDS)
    % error('Cannot load timefreq file.');
    hFig = [];
    iFig = [];
    return
end
% If there is only one row available: for DisplayMode to 'SingleSensor'
if (length(GlobalData.DataSet(iDS).Timefreq(iTimefreq).RowNames) == 1)
    DisplayMode = 'SingleSensor';
end
% Detect modality
Modality = GlobalData.DataSet(iDS).Timefreq(iTimefreq).Modality;
AllModalities = GlobalData.DataSet(iDS).Timefreq(iTimefreq).AllModalities;
LayoutRows = [];
% Get the all row names for this file
AllRows = figure_timefreq('GetRowNames', GlobalData.DataSet(iDS).Timefreq(iTimefreq).RefRowNames, GlobalData.DataSet(iDS).Timefreq(iTimefreq).RowNames);
% Check display type
if any(strcmpi(DisplayMode, {'2DLayout', '2DLayoutOpt'}))
    % Sensor types that cannot be displayed
    if ~isempty(Modality) && ~ismember(Modality, {'MEG','MEG GRAD','MEG MAG','EEG','NIRS'})
        disp(['TIMEFREQ> Error: Cannot display 2DLayout for modality "' Modality '", using AllSensors instead.']);
        DisplayMode = 'AllSensors';
    % If there are multiple modalities available in the file
    elseif isempty(Modality) && ~isempty(AllModalities)
        % Check for main sensor categories
        DispMod = intersect(AllModalities, {'MEG','MEG GRAD','MEG MAG','EEG','ECOG','NIRS'});
        % No available display types
        if isempty(DispMod)
            Modality = [];
        % If there are multiple modalities but one only that can be displayed as a topography
        elseif (length(DispMod) == 1)
            Modality = DispMod{1};
        % Else: ask user what to display
        elseif ~isempty(DispMod)
            res = java_dialog('question', ['This file contains multiple sensor types.' 10 'Which modality would you like to display?'], 'Select sensor type', [], DispMod);
            if isempty(res) || strcmpi(res, 'Cancel')
                return;
            else
                Modality = res;
            end
        end
        % Get the displayed rows only
        if ~isempty(Modality)
            iChan = channel_find(GlobalData.DataSet(iDS).Channel, Modality);
            LayoutRows = intersect(AllRows, {GlobalData.DataSet(iDS).Channel(iChan).Name});
        end
    end
    % No solution found
    if isempty(Modality)
        disp('TIMEFREQ> Error: Cannot display 2DLayout for this file, using AllSensors instead.');
        DisplayMode = 'AllSensors';
    end
end
% Make sure that the required row names are available
if ~isempty(RowName) && ischar(RowName) && ~ismember(RowName, AllRows)
    error(['There is no entry "' RowName '" in this file.']);
end

%% ===== CREATE A NEW FIGURE =====
% Prepare FigureId structure
FigureId.Type     = 'Timefreq';
FigureId.SubType  = DisplayMode;
FigureId.Modality = Modality;
% Create TimeSeries figure
[hFig, iFig] = bst_figures('CreateFigure', iDS, FigureId, CreateMode, sTimefreq.FileName);
if isempty(hFig)
    error('Cannot create figure');
end

%% ===== INITIALIZE FIGURE =====
% Configure app data
setappdata(hFig, 'DataFile',     GlobalData.DataSet(iDS).DataFile);
setappdata(hFig, 'StudyFile',    GlobalData.DataSet(iDS).StudyFile);
setappdata(hFig, 'SubjectFile',  GlobalData.DataSet(iDS).SubjectFile);
% Static dataset
setappdata(hFig, 'isStatic',     (GlobalData.DataSet(iDS).Timefreq(iTimefreq).NumberOfSamples <= 2));
%setappdata(hFig, 'isStaticFreq', (size(GlobalData.DataSet(iDS).Timefreq(iTimefreq).TF,3) <= 1));
setappdata(hFig, 'isStaticFreq', 0);
% Get figure data
TfInfo = getappdata(hFig, 'Timefreq');
% Create options structure
TfInfo.FileName    = sTimefreq.FileName;
TfInfo.Comment     = sTimefreq.Comment;
TfInfo.DisplayMode = DisplayMode;
TfInfo.iFreqs      = [];
if ismember(TfInfo.DisplayMode, {'2DLayout', '2DLayoutOpt', 'AllSensors'})
    TfInfo.RowName = LayoutRows;
elseif ~isempty(RowName)
    TfInfo.RowName = RowName;
elseif iscell(AllRows)
    TfInfo.RowName = AllRows{1};
else
    TfInfo.RowName = AllRows(1);
end
% Default function
if isEphysFile
    TfInfo.Function = 'power';
    TfInfo.DisplayMeasure = 0;
    TfInfo.DisableHideEdgeEffects = 1;
elseif ~isempty(Function)
    TfInfo.Function = Function;
else
    TfInfo.Function = process_tf_measure('GetDefaultFunction', GlobalData.DataSet(iDS).Timefreq(iTimefreq));
end
% EPhys specific fields
if ~isempty(strfind(lower(TimefreqFile), 'noise_correlation'))
    DataMat = in_bst_data(TimefreqFile, 'NeuronNames');
    TfInfo.NeuronNames = DataMat.NeuronNames;
elseif ~isempty(strfind(lower(TimefreqFile), 'rasterplot'))
    TfInfo.DisplayAsDots = 1;
    TfInfo.DisableSmoothDisplay = 1;
elseif ~isempty(strfind(lower(TimefreqFile), 'spiking_phase_locking'))
    TfInfo.DisplayAsPhase = 1;
end
% Set figure data
setappdata(hFig, 'Timefreq', TfInfo);
% Add colormap (stat or timefreq)
if ~isempty(GlobalData.DataSet(iDS).Timefreq(iTimefreq).ColormapType)
    ColormapType = GlobalData.DataSet(iDS).Timefreq(iTimefreq).ColormapType;
elseif strcmpi(file_gettype(TimefreqFile), 'ptimefreq') || ~isempty(strfind(lower(TimefreqFile), 'zscore')) || ~isempty(strfind(lower(TimefreqFile), 'ersd'))
    ColormapType = 'stat2';
else
    ColormapType = 'timefreq';
end
% Display units
DisplayUnits = GlobalData.DataSet(iDS).Timefreq(iTimefreq).DisplayUnits;
% Add colormap to figure
bst_colormaps('AddColormapToFigure', hFig, ColormapType, DisplayUnits);
% Display options panel
isDisplayTab = ~strcmpi(TfInfo.Function, 'other') || (~isempty(TfInfo.RowName) && (ischar(TfInfo.RowName) || iscell(TfInfo.RowName)));
if isDisplayTab
    gui_brainstorm('ShowToolTab', 'Display');
end

%% ===== PLOT DATA =====
% Plot time-freq map
figure_timefreq('UpdateFigurePlot', hFig);
% Update figure name
bst_figures('UpdateFigureName', hFig);

%% =====  DISPLAY FIGURE =====
% Reset selected figure, so that the following figure selection can update the timefreq panel
bst_figures('SetCurrentFigure', [], 'TF');
% Set selected figure
bst_figures('SetCurrentFigure', hFig, 'TF');
% Select display options
if isDisplayTab
    panel_display('UpdatePanel', hFig);
end
% Set figure visible
set(hFig, 'Visible', 'on');
bst_progress('stop');

end


filename='waferdata.bin';

%% Open
fid=fopen(filename,'rb');
NumRows=1e3;

%% Write Header
a=fread(fid,20*NumRows,'*uint8' );
a=reshape(a,20,NumRows);

%% Close
fclose(fid);
disp('Finished');


function [outputSignal, inputImages] = applyImagesToMovie(inputImages,inputMovie, varargin)
	% Applies images to a 3D movie matrix in order to get a signal based on a thresholded version of the image.
	% Biafra Ahanonu
	% started: 2013.10.11
	% inputs
		% inputImages - [x y signalNo] of images, signals will be calculated for each image from the movie.
		% inputMovie - [x y frame] or char string path to the movie.
	% outputs
		% outputSignal - [signalNo frame] matrix of each signal's activity trace extracted directly from the movie.
		% inputImages - [x y signalNo], same as input.

	% changelog
		% 2014.02.17 [11:37:35] updated to have single inputs, bring notation in line with other programs
		% 2014.08.11 - obtain traces using linear indexing and reshaping, much faster than using bsxfun since don't have to make a large intermediate matrix.
		% 2017.01.14 [20:06:04] - support switched from [nSignals x y] to [x y nSignals].
		% 2020.04.28 [17:10:37] - Output modified inputImages.
		% 2020.10.19 [11:27:33] - Supports inputMovie as a character path to the movie.
		% 2020.10.26 [17:08:16] - Finished updating to allow read from disk and binning.
		% 2020.10.27 [12:57:53] - Added support for weighted computation of signals based on pixel values in each image.
		% 2020.12.02 [00:21:28] - Remove parallelization across inputImages to reduce memory overhead and serialization memory issues (e.g. transferring a duplicate of the movie to all workers).
		% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
	% TODO
		% DONE: Change so that it accepts a movie and images, current implementation is too specific.
		% DONE: Add ability to use the values of the filter (multiple by indices).

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	%========================
	% Binary: 1 = input images already thresholded, 0 = not thresholded, applyImagesToMovie will threshold.
	options.alreadyThreshold = 0;
	% Binary: 1 = show the wait bar.
	options.waitbarOn = 1;
	% Float: value between 0 to 1, all values below this fraction of max value for each input image will be set to zero and not used for calculating output signal.
	options.threshold = 0.5;
	% Str: hierarchy name in hdf5 where movie data is located
	options.inputDatasetName = '/1';
	% Int vector: list of specific frames to load.
	options.frameList = [];
	% Binary: 1 = read movie from HDD, 0 = load into RAM
	options.readMovieChunks = 0;
	% Int: Number of frames
	options.nFramesPerChunk = 5e3;
	% Binary: 1 = weight the output trace by the value of the individual pixels, 0 = all image pixels above threshold are weighted evenly when calculating activity trace.
	options.weightSignalByImage = 0;

	% OBSOLETE Binary: 1 = load the images/movies
	% inputDir, inputID, fileRegExp, PCAsuffix
	options.manualLoadSave = 0;

	% get options
	options = getOptions(options,varargin);
	% display(options)
	% unpack options into current workspace
	% fn=fieldnames(options);
	% for i=1:length(fn)
	%   eval([fn{i} '=options.' fn{i} ';']);
	% end
	%========================

	% Check maximum number of cores available
	% maxCores = feature('numCores');
	% Open works = max core #, probably should do maxCores-1 for
	%     stability...
	% matlabpool('open',maxCores);

	if ischar(inputMovie)==1
		% options.readMovieChunks = 1;
		inputMoviePath = inputMovie;
		[movieInfo] = ciapkg.io.getMovieInfo(inputMoviePath,'frameList',options.frameList,'inputDatasetName',options.inputDatasetName);

		if options.readMovieChunks==0
			inputMovie = loadMovieList(inputMovie,'frameList',options.frameList,'inputDatasetName',options.inputDatasetName);
		end
		% if options.readMovieChunks==0
		% else

		% end
	else
		options.readMovieChunks = 0;
	end

	% get number of ICs and frames
	nImages = size(inputImages,3);
	nFrames = size(inputMovie,3);
	%
	nPts = nFrames;
	movieDims = size(inputMovie);

	% matrix multiple to get trace for each time-point
	reverseStr = '';
	if options.alreadyThreshold==0
		inputImages = thresholdImages(inputImages,'waitbarOn',1,'threshold',options.threshold);
	end
	disp(num2str([nanmin(inputMovie(:)) nanmax(inputMovie(:))]))

	% Only implement in Matlab 2017a and above
	if ~verLessThan('matlab', '9.2')
		D_updateParforProgress = parallel.pool.DataQueue;
		afterEach(D_updateParforProgress, @nUpdateParforProgress);
		p_updateParforProgress = 1;
		N_updateParforProgress = nImages;
		nInterval_updateParforProgress = round(nImages/30); %25
		options_waitbarOn = options.waitbarOn;
	end

	if options.readMovieChunks==1
		frameBins = 1:options.nFramesPerChunk:movieInfo.three;
		frameBins(end+1) = movieInfo.three;
		nBins = (length(frameBins)-1);
		outputSignalCell = cell([nBins 1]);
		% Pre-threshold the PCA-ICA filters to save time.
		inputImagesThres = thresholdImages(inputImages,'waitbarOn',1,'threshold',options.threshold);
		tmpOpts = options;
		for binNo = 1:nBins
			disp('+++++++++++++++++++++++++++++++++++++++++++++++++++++')
			if binNo==nBins
				framesToProcess = frameBins(binNo):frameBins(binNo+1);
			else
				framesToProcess = frameBins(binNo):(frameBins(binNo+1)-1);
			end
			fprintf('ROI signal extract %d to %d frames.\n',framesToProcess(1),framesToProcess(end));
			tmpOpts.frameList = framesToProcess;
			tmpOpts.weightSignalByImage = options.weightSignalByImage;
			tmpOpts.alreadyThreshold = 1;
			tmpOpts.readMovieChunks = 0; % Prevent recusion loop.
			[outputSignalCell{binNo}, ~] = applyImagesToMovie(inputImagesThres,inputMoviePath,'options',tmpOpts);
		end

		% Combine all the frame chunks into a single [signalNo frames] size matrix
		outputSignal = cat(2,outputSignalCell{:});

	else
		outputSignal = subfxn_runSignalExtraction();
	end
	function outputSignal = subfxn_runSignalExtraction()
		% pre-allocate traces
		outputSignal = zeros(nImages,nFrames);
		opts_weightSignalByImage = options.weightSignalByImage;
		% parfor(imageNo = 1:nImages,2)
		for imageNo = 1:nImages
			iImage = squeeze(inputImages(:,:,imageNo));

			% =======
			% get the linear indices, much faster that way
			% tmpThres = squeeze(inputImagesThres(:,:,i));
			tmpThres = iImage;
			nPts = size(inputMovie,3);
			movieDims = size(inputMovie);
			[x, y] = find(tmpThres~=0);
			nValid = length(x);
			xrepmat = repmat(x,[1 nPts])';
			yrepmat = repmat(y,[1 nPts])';
			framerepmat = repmat(1:nPts,[1 length(x)]);
			linearInd = sub2ind(movieDims, xrepmat(:),yrepmat(:), framerepmat(:));
			if isempty(linearInd)
				disp('empty linearInd!!!')
			end
			tmpTrace = inputMovie(linearInd);
			% tmpTrace
			tmpTrace = reshape(tmpTrace,[nPts nValid]);
			if opts_weightSignalByImage==1
                    tmpWeights = tmpThres(tmpThres~=0);
                    tmpWeights = tmpWeights/nanmax(tmpWeights(:));
                    tmpTrace = tmpTrace.*tmpWeights(:)';
			end
			% imagesc(tmpTrace); colorbar;pause
			% size(tmpTrace)
			tmpTrace = squeeze(nanmean(tmpTrace,2));
			% size(tmpTrace)
			% display(num2str([nanmin(tmpTrace(:)) nanmax(tmpTrace(:))]))
			% =======
			% use bsxfun to matrix multiple 2D image to 3D movie
			% tmpTrace = nansum(nansum(bsxfun(@times,iImage,inputMovie),1),2);
			% normalize trace
			% tmpTrace = tmpTrace/mean(tmpTrace)-1;
			% =======
			outputSignal(imageNo,:) = tmpTrace(:);
			if ~verLessThan('matlab', '9.2'); send(D_updateParforProgress, imageNo); end % Update progress bar

			% reverseStr = cmdWaitbar(imageNo,nImages,reverseStr,'inputStr','applying images to movie','displayEvery',5,'waitbarOn',options.waitbarOn);
		end
	end
	function nUpdateParforProgress(~)
		if ~verLessThan('matlab', '9.2')
			p_updateParforProgress = p_updateParforProgress + 1;
			pTmp = p_updateParforProgress;
			nTmp = N_updateParforProgress;
			if (mod(pTmp,nInterval_updateParforProgress)==0||pTmp==2||pTmp==nTmp)&&options_waitbarOn==1
				if pTmp==nTmp
					fprintf('%d\n',round(pTmp/nTmp*100))
				else
					fprintf('%d|',round(pTmp/nTmp*100))
				end
				% cmdWaitbar(p,nSignals,'','inputStr','','waitbarOn',1);
			end
			% [p mod(p,nInterval)==0 (mod(p,nInterval)==0||p==nSignals)&&options_waitbarOn==1]
		end
	end
end
	% normalize traces around zero
	% outputSignal = normalizeVector(outputSignal,'normRange','zeroCentered');

	% if options.manualLoadSave==1
	%     %For each day, load the downsampled DFOF movie
	%     files = getFileList(inputDir, fileRegExp);
	%     % load movies, automatically concatenate
	%     numMovies = length(files);
	%     for tifMovie=1:numMovies
	%         display(['loading ' num2str(tifMovie) '/' num2str(numMovies) ': ' files{tifMovie}])
	%         tmpDFOF = load_tif_movie(files{tifMovie},1);
	%         if(tifMovie==1)
	%             DFOF(:,:,:) = tmpDFOF.Movie;
	%         else
	%             DFOF(:,:,end+1:end+length(tmpDFOF.Movie)) = tmpDFOF.Movie;
	%         end
	%     end

	%     filesToLoad={};
	%     filesToLoad{1} = [inputDir filesep inputID '_ICfilters' PCAsuffix '.mat'];
	%     for i=1:length(filesToLoad)
	%         display(['loading: ' filesToLoad{i}]);
	%         load(filesToLoad{i})
	%     end
	% end

	% if options.manualLoadSave==1
	%     % save IC traces
	%     savestring = [inputDir filesep inputID '_ICtraces_applied' '.mat'];
	%     display(['saving: ' savestring])
	%     save(savestring,'IcaTraces');
	% end


function [mpc, warns] = psse_convert(warns, data, verbose)
%PSSE_CONVERT  Converts data read from PSS/E RAW file to MATPOWER case.
%   [MPC, WARNINGS] = PSSE_CONVERT(WARNINGS, DATA)
%   [MPC, WARNINGS] = PSSE_CONVERT(WARNINGS, DATA, VERBOSE)
%
%   Converts data read from a version RAW data file into a
%   MATPOWER case struct.
%
%   Input:
%       WARNINGS :  cell array of strings containing accumulated
%                   warning messages
%       DATA : struct read by PSSE_READ (see PSSE_READ for details).
%       VERBOSE :   1 to display progress info, 0 (default) otherwise
%
%   Output:
%       MPC : a MATPOWER case struct created from the PSS/E data
%       WARNINGS :  cell array of strings containing updated accumulated
%                   warning messages
%
%   See also PSSE_READ.

%   MATPOWER
%   Copyright (c) 2014-2016, Power Systems Engineering Research Center (PSERC)
%   by Yujia Zhu, PSERC ASU
%   and Ray Zimmerman, PSERC Cornell
%   Based on mpraw2mp.m, written by: Yujia Zhu, Jan 2014, yzhu54@asu.edu.
%
%   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.

%% define named indices into bus, gen, branch matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
    TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
    ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;

%% options
sort_buses = 1;

%% defaults
if nargin < 3
    verbose = 0;
end
haveVlims = 0;
Vmin = 0.9;
Vmax = 1.1;

%%-----  case identification data  -----
baseMVA = data.id.SBASE;
rev = data.id.REV;

%%-----  bus data  -----
numbus = data.bus.num;
[nb, ncols] = size(numbus); %% number of buses, number of cols
bus = zeros(nb, VMIN);      %% initialize bus matrix
if rev < 24
    bus_name_col = 10;
else
    bus_name_col = 2;
end
if sort_buses
    [numbus, i] = sortrows(numbus, 1);
    bus_name = data.bus.txt(i, bus_name_col);
else
    bus_name = data.bus.txt(:, bus_name_col);
end
if rev < 24     %% includes loads
    bus(:, [BUS_I BUS_TYPE PD QD GS BS BUS_AREA VM VA BASE_KV ZONE]) = ...
        numbus(:, [1:9 11:12]);
elseif rev < 31     %% includes GL, BL
    bus(:, [BUS_I BASE_KV BUS_TYPE GS BS BUS_AREA ZONE VM VA]) = ...
        numbus(:, [1 3 4 5 6 7 8 9 10]);
else                %% fixed shunts and loads are in their own tables
    bus(:, [BUS_I BASE_KV BUS_TYPE BUS_AREA ZONE VM VA]) = ...
        numbus(:, [1 3 4 5 6 8 9]);
    if ncols >= 11 && all(all(~isnan(numbus(:, [10 11]))))
        haveVlims = 1;
        bus(:, [VMAX VMIN]) = numbus(:, [10 11]);
    end
end
if ~haveVlims  %% add default voltage magnitude limits if not provided
    warns{end+1} = sprintf('Using default voltage magnitude limits: VMIN = %g p.u., VMAX = %g p.u.', Vmin, Vmax);
    if verbose
        fprintf('WARNING: No bus voltage magnitude limits provided.\n         Using defaults: VMIN = %g p.u., VMAX = %g p.u.\n', Vmin, Vmax);
    end
    bus(:, VMIN) = Vmin;
    bus(:, VMAX) = Vmax;
end

%% create map of external bus numbers to bus indices
i2e = bus(:, BUS_I);
e2i = sparse(i2e, ones(nb, 1), 1:nb, max(i2e), 1);

%%-----  load data  -----
if rev >= 24
    nld = size(data.load.num, 1);
    loadbus = e2i(data.load.num(:,1));
    %% PSS/E loads are divided into:
    %%  1. constant MVA, (I=1)
    %%  2. constant current (I=I)
    %%  3. constant reactance/resistance (I = I^2)
    %% NOTE: reactive power component of constant admittance load is negative
    %%       quantity for inductive load and positive for capacitive load
    Pd = data.load.num(:,6) + data.load.num(:,8) .* bus(loadbus, VM) ...
            + data.load.num(:,10) .* bus(loadbus, VM).^2;
    Qd = data.load.num(:,7) + data.load.num(:,9) .* bus(loadbus, VM) ...
            - data.load.num(:,11) .* bus(loadbus, VM).^2;
    Cld = sparse(1:nld, loadbus, data.load.num(:,3), nld, nb);    %% only in-service-loads
    bus(:, [PD QD]) = Cld' * [Pd Qd];
end

%%-----  fixed shunt data  -----
if isfield(data, 'shunt')   %% rev > 30
    nsh = size(data.shunt.num, 1);
    shuntbus = e2i(data.shunt.num(:,1));
    Csh = sparse(1:nsh, shuntbus, data.shunt.num(:,3), nsh, nb);  %% only in-service shunts
    bus(:, [GS BS]) = Csh' * data.shunt.num(:, 4:5);
end

%%-----  switched shunt data  -----
nswsh = size(data.swshunt.num, 1);
swshuntbus = e2i(data.swshunt.num(:,1));
Cswsh = sparse(1:nswsh, swshuntbus, 1, nswsh, nb);
if rev <= 27
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 6);
elseif rev <= 29
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 7);
elseif rev < 32
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 8);
else
    bus(:, BS) = bus(:, BS) + Cswsh' * data.swshunt.num(:, 10);
end

%%-----  branch data  -----
nbr = size(data.branch.num, 1);
branch = zeros(nbr, ANGMAX);
branch(:, ANGMIN) = -360;
branch(:, ANGMAX) = 360;
branch(:, [F_BUS BR_R BR_X BR_B RATE_A RATE_B RATE_C]) = ...
    data.branch.num(:, [1 4 5 6 7 8 9]);
branch(:, T_BUS) = abs(data.branch.num(:, 2));  %% can be negative to indicate metered end
if rev <= 27        %% includes transformer ratio, angle
    branch(:, BR_STATUS) = data.branch.num(:, 16);
    branch(~isnan(data.branch.num(:, 10)), TAP) = ...
        data.branch.num(~isnan(data.branch.num(:, 10)), 10);
    branch(~isnan(data.branch.num(:, 11)), SHIFT) = ...
        data.branch.num(~isnan(data.branch.num(:, 11)), 11);
else
    branch(:, BR_STATUS)    = data.branch.num(:, 14);
end
%% integrate branch shunts (explicit shunts, not line-charging)
ibr = (1:nbr)';
fbus = e2i(branch(:, F_BUS));
tbus = e2i(branch(:, T_BUS));
nzf = find(fbus);               %% ignore branches with bad bus numbers
nzt = find(tbus);
if length(nzf) < nbr
    warns{end+1} = sprintf('%d branches have bad ''from'' bus numbers', nbr-length(nzf));
    if verbose
        fprintf('WARNING: %d branches have bad ''from'' bus numbers\n', nbr-length(nzf));
    end
end
if length(nzt) < nbr
    warns{end+1} = sprintf('%d branches have bad ''to'' bus numbers', nbr-length(nzt));
    if verbose
        fprintf('WARNING: %d branches have bad ''to'' bus numbers\n', nbr-length(nzt));
    end
end
Cf = sparse(ibr(nzf), fbus(nzf), branch(nzf, BR_STATUS), nbr, nb);  %% only in-service branches
Ct = sparse(ibr(nzt), tbus(nzt), branch(nzt, BR_STATUS), nbr, nb);  %% only in-service branches
if rev <= 27
    bus(:, [GS BS]) = bus(:, [GS BS]) + ...
        Cf' * data.branch.num(:, 12:13)*baseMVA + ...
        Ct' * data.branch.num(:, 14:15)*baseMVA;
else
    bus(:, [GS BS]) = bus(:, [GS BS]) + ...
        Cf' * data.branch.num(:, 10:11)*baseMVA + ...
        Ct' * data.branch.num(:, 12:13)*baseMVA;
end

%%-----  generator data  -----
ng = size(data.gen.num, 1);
genbus = e2i(data.gen.num(:,1));
gen = zeros(ng, APF);
gen(:, [GEN_BUS PG QG QMAX QMIN VG MBASE GEN_STATUS PMAX PMIN]) = ...
    data.gen.num(:, [1 3 4 5 6 7 9 15 17 18]);

%%-----  transformer data  -----
if rev > 27
    [transformer, bus, warns, bus_name] = psse_convert_xfmr(warns, data.trans2.num, data.trans3.num, verbose, baseMVA, bus, bus_name);
    branch = [branch; transformer];
end

%%-----  two-terminal DC transmission line data  -----
dcline = psse_convert_hvdc(data.twodc.num, bus);

%% assemble MPC
mpc = struct( ...
    'baseMVA',  baseMVA, ...
    'bus', bus, ...
    'bus_name', {bus_name}, ...
    'branch', branch, ...
    'gen', gen ...
);
if ~isempty(dcline)
    mpc.dcline = dcline;
    mpc = toggle_dcline(mpc, 'on');
end


function [costs] = initCosts()
  costs.total = 0;
  costs.word = 0;
end


classdef MrDimInfo < MrCopyData
    % Holds all dimensionality info (r.g. range/dimLabels/units) of multidimensional data
    %
    %   See also demo_dim_info MrImage.select

    % Author:   Saskia Bollmann & Lars Kasper
    % Created:  2016-01-23
    % Copyright (C) 2016 Institute for Biomedical Engineering
    %                    University of Zurich and ETH Zurich
    %
    % This file is part of the TAPAS UniQC Toolbox, which is released
    % under the terms of the GNU General Public License (GPL), version 3.
    % You can redistribute it and/or modify it under the terms of the GPL
    % (either version 3 or, at your option, any later version).
    % For further details, see the file COPYING or
    %  <http://www.gnu.org/licenses/>.

    % the following properties can be fully derived from sampling points, ...
    % but are stored for convenience
    properties (Dependent)
        nDims;      % number of dimensions in dataset, default: 6

        nSamples;   % vector [1,nDims] of number of elements per dimension

        % vector [1,nDims] of resolutions for each dimension, ...
        % i.e. distance (in specified units) of adjacent elements,
        % NaN for non-equidistant spacing of elements
        % Example: TE = 2, 20, 35 ms => resolution = NaN
        resolutions;

        % vector [2,nDims] of [firstSample; lastSample] for each dimension ...
        ranges;

    end % properties (dependent)

    properties
        % cell(1,nDims) of string dimLabels for each dimension
        % default: {'x', 'y', 'z', 't', 'coil, 'echo'}
        dimLabels = {};

        % cell(1,nDims) of strings describing unit; '' for unit-free dims
        % default: {'mm', 'mm', 'mm', 's', '', 'ms'};
        units = {};

        % cell(1,nDims) of sampling position vectors for each dimension
        % Note: The entries for each dimension have to be numerical
        samplingPoints = {};

        % [1,nDims] vector of sampling width vectors for each dimension ...
        % Note: Typically, this will correspond to diff(samplingPoints);
        % However, if sampling does not cover the full interval between
        % consecutive points, it should be noted here
        % Example:  If z-coordinate has slice thickness and slice gap
        %           samplingWidths = sliceThickness
        %           resolutions = sliceThickness + sliceGap
        samplingWidths = [];

    end % properties

    methods

        function this = MrDimInfo(varargin)
            % Constructor of class, call via MrDimInfo('propertyName', propertyValue, ...
            % ...) syntax
            %               OR
            % MrDimInfo(fileName)
            %
            %               OR
            % MrDimInfo(fileArray) - additional dims are added via filename
            %
            %               OR
            % MrDimInfo(dimInfoStruct) - MrDimInfo object is costructed
            % from struct with same fields (especially for load and save)
            %
            % See also MrDimInfo.set_dims
            %
            % IN
            %   varargin    PropertyName/Value-pairs of MrDim-Info properties to be
            %               changed, e.g. resolutions, nSamples, units etc.
            %
            %   Properties:
            %
            %   'units'           cell(1,nDims) of strings describing unit; '' for unit-free dims
            %	'dimLabels'       cell(1,nDims) of string dimLabels for each changed dimension
            %
            %
            %   (1): 1st variant: explicit setting of sampling points for dimension(s)
            %
            %   'samplingPoints'  cell(1,nDims) of index vectors for each dimension
            %
            %
            %   (2)-(6): Other variants depend on setting some of the following parameters
            %
            %   'nSamples'              [1, nDims] number of samples per dimension
            %   'ranges'                [2, nDims] first and last sample per dimension
            %   'resolutions'           [1, nDims] resolution (in units), i.e. spacing
            %                           between sampling points per dimension
            %
            %   'arrayIndex'            index of samplingPoint to be set
            %   'samplingPoint'         value of sampling point at position arrayIndex
            %
            %   'firstSamplingPoint'    special case of samplingPoint, arrayIndex = 1 set
            %   'lastSamplingPoint'     special case of samplingPoint, arrayIndex = end set
            %
            %   Variants:
            %       (2) nSamples + ranges: sampling points computed automatically via
            %               samplingPoint(k) = ranges(1) + (ranges(2)-ranges(1))/nSamples*(k-1)
            %       (3) nSamples + resolutions + samplingPoint + arrayIndex:
            %               from samplingPoints(arrayIndex) = samplingPoints, others
            %               are constructed via
            %               [...    samplingPoint-resolution
            %                       samplingPoint
            %                       samplingPoint+resolution ...]
            %               until nSamples are created in total.
            %       (4) nSamples + resolutions + firstSamplingPoint:
            %               as (3), assuming arrayIndex = 1
            %       (5) nSamples + resolutions + lastSamplingPoint:
            %               as (3), assuming arrayIndex = end
            %       (6) nSamples Or resolution Or (samplingPoint+arrayIndex)
            %               missing input value taken from defaults
            %               nSamples        -> resolutions = 1, 1st sample 1
            %               resolutions     -> nSamples = 2, 1st sample 1
            %
            %           and always samplingWidths = resolution

            if nargin == 1 % single file, file array or struct is given
                if isstruct(varargin{1}) % is struct
                    this.update_properties_from(varargin{1});
                else % is file/file array
                    fileInput = varargin{1}; % extract input
                    isSingleFile = 0;
                    isFile = 1;
                    % check whether single filename or folder is given
                    if ischar(fileInput)
                        % determine whether file or folder
                        [~, ~, ext] = fileparts(fileInput);
                        if ~isempty(ext) % single file
                            fileName = fileInput;
                            isSingleFile = 1;
                        else
                            % or folder
                            fileInput = cellstr(spm_select('FPList',...
                                fileInput, '^*.*'));
                        end
                    elseif iscell(fileInput) && numel(fileInput) == 1
                        % fileArray with only one entry
                        fileName = fileInput{1}; % extract from cell array
                        isSingleFile = 1;
                    elseif isa(varargin{1}, 'MrAffineTransformation')
                        this.set_from_affine_geometry(varargin{1});
                        isFile = 0;
                    end

                    if isFile
                        if isSingleFile
                            % load from single file
                            this.load(fileName);
                        else
                            % fileArray is given
                            % load dimInfo from first file in file array
                            this.load(fileInput{1});
                            this.set_from_filenames(fileInput);
                        end
                    end
                end
            else % prop/value pairs given

                propertyNames = varargin(1:2:end);
                propertyValues = varargin(2:2:end);
                % Find nSamples property, and corresponding value to determine
                % dimension
                iArgNsamples = tapas_uniqc_find_string(propertyNames, 'nSamples');
                iArgSamplingPoints = tapas_uniqc_find_string(propertyNames, 'samplingPoints');
                iArgRanges = tapas_uniqc_find_string(propertyNames, 'ranges');

                hasNsamples = ~isempty(iArgNsamples);
                hasExplicitSamplingPoints = ~isempty(iArgSamplingPoints);
                hasRanges = ~isempty(iArgRanges);

                if hasExplicitSamplingPoints
                    nDims = numel(propertyValues{iArgSamplingPoints});
                elseif hasNsamples
                    % otherwise, allow empty constructor for copyobj-functionality
                    nDims = numel(propertyValues{iArgNsamples});
                elseif hasRanges
                    nDims = numel(propertyValues{iArgRanges})/2;
                else
                    % guessed number of update dimensions
                    % find shortest given input to dimInfo and determine
                    % dimensionality from that
                    nDims =[];
                    for p = 1:numel(propertyNames)
                        nDims(p) = numel(propertyValues{p});
                    end
                    nDims = min(nDims);

                    % populate nDims with non-samplingpoint parameters by adding dimensions
                    % , s.t. samplingPoints remains an empty cell for each
                    % dimension
                    % only samplingWidhts, units and dimLabels could be
                    % in varargin, so OK to add
                    this.add_dims(1:nDims, varargin{:});

                end

                % allows empty constructor for copyobj
                if ~isempty(nDims)
                    this.set_dims(1:nDims, varargin{:});
                end
            end

        end
        % NOTE: Most of the methods are saved in separate function.m-files in this folder;
        %       except: constructor, delete, set/get methods for properties.
        %

        function nDims = get.nDims(this)
            nDims = numel(this.nSamples);
        end

        function this = set.nDims(this, nDimsNew)
            % Sets number of dimensions of dimInfo, via add_dims or ...
            % remove_dims
            nDimsOld = this.nDims;

            if nDimsNew > nDimsOld
                this.add_dims((nDimsOld+1):nDimsNew);
            elseif nDimsNew < nDimsOld
                this.remove_dims((nDimsNew+1):nDimsOld);
            end
        end

        function this = set.nSamples(this, nSamplesNew)
            % Changes nSamples by keeping given resolution and adding samples ...
            % at end of samplingPoints-vectors
            % TODO: Can we do this via set_dims? Or is this a problem for
            % empty dimInfos which are just created?
            % No: more for when we don't have a defined resolution
            % (non-equidistant!)

            nSamplesOld = this.nSamples;
            if numel(nSamplesNew) ~= numel(nSamplesOld)
                error('tapas:uniqc:MrDimInfo:NDimsChanged', ...
                    'nDims cannot change via nSamples, use add_dims instead');
            end

            iChangedDims = find(nSamplesOld ~= nSamplesNew);

            for iDim = iChangedDims
                nOld = nSamplesOld(iDim);
                nNew = nSamplesNew(iDim);
                if nOld > nNew
                    this.samplingPoints{iDim}((nNew+1):end) = [];
                else
                    if nOld == 0 % no samples before, create all new
                        offset = 0;
                    else % start samples from last existing
                        offset = this.samplingPoints{iDim}(nOld);
                    end

                    % set default resolution to 1 for adding samples
                    res = this.resolutions(iDim);
                    if isnan(res)
                        res = 1;
                    end

                    this.samplingPoints{iDim}((nOld+1):nNew) = ...
                        offset + (1:(nNew-nOld))*res;
                end
            end

        end

        function nSamples = get.nSamples(this)
            if isempty(this.samplingPoints)
                nSamples = [];
            else
                nSamples = cell2mat(cellfun(@numel, this.samplingPoints, ...
                    'UniformOutput', false));
            end
        end

        function this = set.resolutions(this, resolutionsNew)
            % Changes resolutions by setting the new resolution via
            % changeDim, the first sample is preserved
            resolutionsOld = this.resolutions;

            if numel(resolutionsNew) ~= numel(resolutionsOld)
                error('tapas:uniqc:MrDimInfo:NDimsChanged', ...
                    'nDims cannot change via resolutions, use add_dims instead');
            end

            iChangedDims = find(~arrayfun(@isequaln, resolutionsOld, resolutionsNew));
            this.set_dims(iChangedDims, 'resolutions', resolutionsNew(iChangedDims));
        end

        function resolutions = get.resolutions(this)
            if isempty(this.samplingPoints)
                resolutions = [];
            else

                for iDim = 1:this.nDims
                    res = unique(diff(this.samplingPoints{iDim}));
                    switch numel(res)
                        case 0 % one element samplingPoints, take its value (?)
                            if ~isempty(this.samplingWidths) && ...
                                    numel(this.samplingWidths) >= iDim && ...
                                    ~isnan(this.samplingWidths(iDim))
                                resolutions(iDim) = this.samplingWidths(iDim);
                            else
                                resolutions(iDim) = NaN;
                            end
                            % resolutions(iDim) = this.samplingPoints{iDim};
                        case  1 % single resolution, return it
                            resolutions(iDim) = res;
                        otherwise % if no unique resolution,
                            % first check if within single floating precision,
                            % accept that as same!
                            if max(abs(diff(res))) < eps(single(1))
                                resolutions(iDim) = mean(res);
                            else
                                %  otherwise, really non-equidistant spacing, return NaN for this
                                %  dim
                                resolutions(iDim) = NaN;
                            end
                    end
                end
            end
        end

        function this = set.ranges(this, rangesNew)
            % Changes ranges by keeping given nSamples and adjusting ...
            % samplingPoints (i.e. spacing i.e. resolution)
            rangesOld = this.ranges;

            if numel(rangesNew) ~= numel(rangesOld)
                error('tapas:uniqc:MrDimInfo:NDimsChanged', ...
                    'nDims cannot change via ranges, use add_dims instead');
            end

            iChangedDims = union(find(rangesOld(1,:) ~= rangesNew(1,:)), ...
                find(rangesOld(2,:) ~= rangesNew(2,:)));

            this.set_dims(iChangedDims, 'ranges', rangesNew(:,iChangedDims), ...
                'nSamples', this.nSamples(iChangedDims));
        end

        function ranges = get.ranges(this)
            ranges = [first(this); last(this)];
        end

        function centerSamples = center(this, iDim)
            % return center sample for given dimension (i.e. index ceil(nSamples/2))
            if nargin < 2
                iDim = 1:this.nDims;
            end

            centerSamples = nan(1, numel(iDim));
            for d = iDim
                if ~isempty(this.samplingPoints{d})
                    centerSamples(find(d==iDim)) = this.samplingPoints{d}(...
                        ceil(this.nSamples(d)/2));
                end
            end

        end

        function firstSamples = first(this, iDim)
            if nargin < 2
                iDim = 1:this.nDims;
            end

            firstSamples = nan(1, numel(iDim));
            for d = iDim
                if ~isempty(this.samplingPoints{d})
                    firstSamples(find(d==iDim)) = this.samplingPoints{d}(1);
                end
            end

        end

        % return last sampling point for all or given dimensions
        function lastSamples = last(this, iDim)
            if nargin < 2
                iDim = 1:this.nDims;
            end

            lastSamples = nan(1,numel(iDim));
            for d = iDim
                if ~isempty(this.samplingPoints{d})
                    lastSamples(find(d==iDim)) = this.samplingPoints{d}(end);
                end
            end
        end

        function [iDim, isValidLabel] = get_dim_index(this, dimLabel, varargin)
            % return index of dimension(s) given by a dimLabel
            % IN
            %   dimLabel  dimension label string (or array of strings).
            %             or dimension number or cell of dim numbers (for
            %             compatibility)
            %   varargin
            %   'invert'    true or false (default)
            %               if true, all other indices not within dimLabel
            %               are returned
            %
            % OUT
            %   iDim            index of dimension with corresponding label
            %   isValidLabel    [nLabels,1] returns for each given label 1/0
            %                   i.e. whether it is indeed a label of dimInfo
            defaults.invert = false;
            args = tapas_uniqc_propval(varargin,defaults);
            if isnumeric(dimLabel) % (vector of) numbers
                iDim = dimLabel;
                % cell of numbers:
            elseif iscell(dimLabel) && isnumeric(dimLabel{1})
                iDim = cell2mat(dimLabel);
            else % string or cell of strings
                isExact = 1;
                iDim = tapas_uniqc_find_string(this.dimLabels, dimLabel, isExact);
                if iscell(iDim)
                    isValidLabel = ~cellfun(@isempty, iDim);
                    iDim = iDim(isValidLabel); % remove unfound dimensions;
                    iDim = cell2mat(iDim)';
                else
                    isValidLabel = ~isempty(iDim);
                end
            end
            if args.invert
                iDim = setdiff(1:this.nDims,iDim);
            end
        end

    end

end


function [surface_handles, colors] = isosurface(atlas_obj, varargin)
% Create a series of surfaces in different colors, one for each region
% - Options for single color
%
% surface_handles = isosurface(atlas_obj, [optional arguments])
%
% optional arguments:
% - Any optional inputs to imageCluster, e.g., 'alpha'
% - 'colors', followed by single color in { } or cell array of multiple colors
% - 'nomatchleftright', do not match colors across hemispheres (left/right)
%               Note: The default matches, and may override your colors.
%
% Examples:
% atlasfile = which('Morel_thalamus_atlas_object.mat');
% load(atlasfile)
%
% surface_handles = isosurface(atlas_obj);
% surface_handles = isosurface(atlas_obj, 'alpha', .5);
% surface_handles = isosurface(atlas_obj, 'alpha', .5, 'nomatchleftright');
%
% view(135, 30);
% lightRestoreSingle;
% lightFollowView;
%
% load(which('CIT168_MNI_subcortical_atlas_object.mat'));
%
% surface_handles = isosurface(atlas_obj, 'alpha', .5, 'color', {[.3 .6 .4] [.5 .4 .2]});
% surface_handles = isosurface(atlas_obj, 'alpha', .5, 'color', {[.3 .6 .4] [.5 .4 .2]}, 'nomatchleftright');
% p = addbrain('hires right');
% lightFollowView;

r = atlas2region(atlas_obj);

[surface_handles, colors] = isosurface(r, varargin{:});

end % function


function respStruct = mrMeshTcpParseResponseData(respByteStream)
%
% respStruct = mrMeshTcpParseResponseData(respByteStream)
%
% Parses the mrMesh-encoded byteStream into a matlab structure.
%
% HISTORY
% 2007.04.12 RFD wrote it.

respStruct = [];
if(isempty(respByteStream))
    return;
end

done = false;
while(~done)
    % max param name length is 200
    numBytesLeft = length(respByteStream);
    eq = strfind(respByteStream(1:min(204,numBytesLeft)),' = ');
    if(isempty(eq))
        done = true;
    else
        fn = char(respByteStream(1:eq(1)-1));
        if(fn(end)==']')
            % then it's an array
            sep = strfind(fn,'[');
            dims = str2num(fn(sep(1):end));
            fn = fn(1:sep(1)-1);
            % read all the returned bytes as doubles (8 bytes per element)
            dataEnd = eq+3+prod(dims)*8+1;
            data = respByteStream(eq+3:dataEnd);
            % the data block should begin and end with a ' (ascii=39)
            if(data(1)~=39||data(end)~=39)
                done = true;
                warning('response parsing teminated abnormally.');
            else
                data = typecast(data(2:end-1),'double');
                if(length(dims)>1)
                    data = reshape(data,dims);
                end
                respStruct = setfield(respStruct, fn, data);
                respByteStream = respByteStream(dataEnd(1)+2:end);
            end
        else
            % scalar
            % MaxDoubleAsString = 40
            dataEnd = strfind(respByteStream(1:min(245,numBytesLeft)),sprintf('\n'));
            data = char(respByteStream(eq+3:dataEnd(1)));
            respStruct = setfield(respStruct, fn, str2double(data));
            respByteStream = respByteStream(dataEnd(1)+1:end);
        end
    end
end
if isempty(respStruct)
    respStruct=respByteStream;
end


function p=getvarfrom(ws,name)
%
% p=getvarfrom(ws,name)
%
% get variable value by name from specified work-space
%
% author: Qianqian Fang, <q.fang at neu.edu>
%
% input:
%    ws: name of the work-space, for example, 'base'
%    name: name string of the variable
%
% output:
%    p: the value of the specified variable, if the variable does not
%       exist, return empty array
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

wsname=ws;
if(~iscell(ws))
   wsname=cell(1);
   wsname{1}=ws;
end

p=[];
for i=1:length(wsname)
    isdefined=evalin(wsname{i},['exist(''' name ''')']);
    if(isdefined==1)
        p=evalin(wsname{i},name);
        break;
    end
end


classdef NodesConnector < handle

    properties (Access = private)
        nodes
        coord
        div
        theta
    end

    properties (Access = public)
        connec
    end

    methods (Access = public)

        function obj = NodesConnector(cParams)
            obj.init(cParams);
        end

        function computeConnections(obj)
            obj.connec = delaunay(obj.coord);
            if obj.nodes.vert == 6
                obj.deleteExtraElementsCaseA();
            elseif obj.theta(2) ~= 90
                obj.deleteExtraElementsCaseB();
            end
        end

    end

    methods (Access = private)

        function init(obj,cParams)
            obj.nodes = cParams.nodes;
            obj.coord = cParams.coord;
            obj.theta = cParams.theta;
            obj.div = cParams.div;
        end

        function deleteExtraElementsCaseA(obj)
            cont = 1;
            rowsToDelete = [];
            for i = 1:size(obj.connec,1)
                found = zeros(1,3);
                elementNodes = obj.connec(i,:);
                for j = 1:size(obj.connec,2)
                    if elementNodes(j) < obj.nodes.bound+1
                        found(j) = found(j)+1;
                    end
                end
                if sum(found) == 3
                    rowsToDelete(cont,1) = i;
                    cont = cont+1;
                end
            end
            obj.connec(rowsToDelete,:) = [];
        end

        function deleteExtraElementsCaseB(obj)
            cont = 1;
            rowsToDelete = [];
            for i = 1:size(obj.connec,1)
                found = zeros(1,3);
                noDeletionA = zeros(1,3);
                noDeletionB = zeros(1,3);
                noDeletionC = zeros(1,3);
                noDeletionD = zeros(1,3);
                elementNodes = obj.connec(i,:);
                for j = 1:size(obj.connec,2)
                    if (elementNodes(j) < obj.nodes.bound+1)
                        found(j) = found(j)+1;
                        if (elementNodes(j) == 1) || (elementNodes(j) == obj.nodes.vert+1) || (elementNodes(j) == obj.nodes.bound)
                            noDeletionA(j) = noDeletionA(j)+1;
                        end
                        if (elementNodes(j) == 2) || (elementNodes(j) == obj.nodes.vert+obj.div(1)-1) || (elementNodes(j) == obj.nodes.vert+obj.div(1))
                            noDeletionB(j) = noDeletionB(j)+1;
                        end
                        if (elementNodes(j) == 3) || (elementNodes(j) == obj.nodes.vert+obj.div(1)+obj.div(2)-2) || (elementNodes(j) == obj.nodes.vert+obj.div(1)+obj.div(2)-1)
                            noDeletionC(j) = noDeletionC(j)+1;
                        end
                        if (elementNodes(j) == 4) || (elementNodes(j) == obj.nodes.vert+2*obj.div(1)+obj.div(2)-3) || (elementNodes(j) == obj.nodes.vert+2*obj.div(1)+obj.div(2)-2)
                            noDeletionD(j) = noDeletionD(j)+1;
                        end
                    end
                end
                if (sum(found) == 3) && (sum(noDeletionA) ~= 3) && (sum(noDeletionB) ~= 3) && (sum(noDeletionC) ~= 3) && (sum(noDeletionD) ~= 3)
                    rowsToDelete(cont,1) = i;
                    cont = cont+1;
                end
            end
            obj.connec(rowsToDelete,:) = [];
        end

    end

end


% Jiao Xianjun (putaoshu@msn.com; putaoshu@gmail.com)
% parameter setting via tcp to rtl_tcp
% A script of project: https://github.com/JiaoXianjun/multi-rtl-sdr-calibration

function tcp_obj = set_gain_tcp(tcp_obj, gain)

if gain
    fwrite(tcp_obj, 3, 'uint8');
    fwrite(tcp_obj, uint32(1), 'uint32');
    fwrite(tcp_obj, 4, 'uint8');
    fwrite(tcp_obj, uint32(gain), 'uint32');
else
    fwrite(tcp_obj, 3, 'uint8');
    fwrite(tcp_obj, uint32(0), 'uint32');
end


function count = hprintf (varargin)
%HPRINTF fprintf with hypertext links not highlighted in the command window.
% hprintf does this by replacing the string "href=" with "HREF=".  If the file
% descriptor is not present, the output defaults to the command window, just
% like fprintf.  Note that all browsers accept either href= or HREF=, so your
% hypertext links will not be affected except within the MATLAB Command Window.
% General usage is identical to fprintf:
%
%      hprintf (format, arg1, arg2, ...)
%      hprintf (fid, format, arg1, arg2, ...)
%      count = hprintf ( ... same as above ... )
%
% Example:
%      fprintf ('MathWorks\n') ;
%      fprintf ('%d\n', 42) ;
%      hprintf ('MathWorks\n') ;
%      hprintf ('%d\n', 42) ;
%
% For a discussion, see Kristin's blog and the comments there at
% http://blogs.mathworks.com/desktop/2007/07/09
% (Kristin's blog).
%
% NOTE: the examples above are modified by "help hprintf" so that you cannot
% see the HREF= text.  To see the examples properly (without hypertext
% highlighting) use:
%
%      edit hprintf
%
% To try the examples above, use hprintf with no inputs (note that this usage
% of hprintf also flags an error, to exactly mimic the fprintf behavior):
%
%      hprintf
%
% Here is a slightly more complex example that has the advantage of being
% printed properly by "help hprintf":
%
%      % a string template with hypertext contents:
%      str = 'MathWorks\n' ;
%      % made into an active hypertext, which will be underlined when
%      % displayed in the command window:
%      hstr = strrep (str, 'AREF', 'href') ;
%      fprintf (hstr) ;
%      %          displays: 'MathWorks'
%      hprintf (hstr) ;
%      %          displays: 'MathWorks'
%
% See also fprintf, strrep, sprintf.

% Copyright 2007, T. Davis, with thanks to 'us' (us at neurol dot unizh dot ch)
% for suggestions.

% Aug 25, 2007

if (nargin < 1)

    % try hprintf
    help hprintf
    fprintf ('\nhypertext highlighting with fprintf:\n\n') ;
    fprintf ('MathWorks\n') ;
    fprintf ('%d\n', 42) ;
    fprintf ('\nhypertext highlighting turned off with hprintf:\n\n') ;
    hprintf ('MathWorks\n') ;
    hprintf ('%d\n\n', 42) ;

    % flag an error, to mimic fprintf behavior
    error ('Not enough input arguments') ;

elseif (nargout > 1)

    % mimic fprintf
    error ('Too many output arguments') ;

else

    if (ischar (varargin {1}))
        % mimic fprintf ('hello world %d\n', 42), with no file ID
        cnt = fprintf (strrep (sprintf (varargin {:}), 'href=', 'HREF=')) ;
    else
        % mimic fprintf (fid, 'hello world %d\n', 42), with file ID given
        cnt = fprintf (varargin {1}, ...
            strrep (sprintf (varargin {2:end}), 'href=', 'HREF=')) ;
    end
    if (nargout > 0)
        % return the fprintf output
        count = cnt ;
    end
end


function hh = rotateXLabels( ax, angle, varargin )
%rotateXLabels: rotate any xticklabels
%
%   hh = rotateXLabels(ax,angle) rotates all XLabels on axes AX by an angle
%   ANGLE (in degrees). Handles to the resulting text objects are returned
%   in HH.
%
%   hh = rotateXLabels(ax,angle,param,value,...) also allows one or more
%   optional parameters to be specified. Possible parameters are:
%     'MaxStringLength'   The maximum length of label to show (default inf)
%
%   Examples:
%   >> bar( hsv(5)+0.05 )
%   >> days = {'Monday','Tuesday','Wednesday','Thursday','Friday'};
%   >> set( gca(), 'XTickLabel', days )
%   >> rotateXLabels( gca(), 45 )
%
%   See also: GCA, BAR

%   Copyright 2006-2013 The MathWorks Ltd.

error( nargchk( 2, inf, nargin ) );
if ~isnumeric( angle ) || ~isscalar( angle )
    error( 'RotateXLabels:BadAngle', 'Parameter ANGLE must be a scalar angle in degrees' )
end
angle = mod( angle, 360 );

% From R2014b, rotating labels is built-in using 'XTickLabelRotation'
if ~verLessThan('matlab','8.4.0')
    set(ax, 'XTickLabelRotation', angle)
    if nargout > 1
        hh = [];
    end
    return;
end

[maxStringLength] = parseInputs( varargin{:} );

% Get the existing label texts and clear them
[vals, labels] = findAndClearExistingLabels( ax, maxStringLength );

% Create the new label texts
h = createNewLabels( ax, vals, labels, angle );

% Reposition the axes itself to leave space for the new labels
repositionAxes( ax );

% If an X-label is present, move it too
repositionXLabel( ax );

% Store angle
setappdata( ax, 'RotateXLabelsAngle', angle );

% Only send outputs if requested
if nargout
    hh = h;
end

%-------------------------------------------------------------------------%
    function [maxStringLength] = parseInputs( varargin )
        % Parse optional inputs
        maxStringLength = inf;
        if nargin > 0
            params = varargin(1:2:end);
            values = varargin(2:2:end);
            if numel( params ) ~= numel( values )
                error( 'RotateXLabels:BadSyntax', 'Optional arguments must be specified as parameter-value pairs.' );
            end
            if any( ~cellfun( 'isclass', params, 'char' ) )
                error( 'RotateXLabels:BadSyntax', 'Optional argument names must be specified as strings.' );
            end
            for pp=1:numel( params )
                switch upper( params{pp} )
                    case 'MAXSTRINGLENGTH'
                        maxStringLength = values{pp};

                    otherwise
                        error( 'RotateXLabels:BadParam', 'Optional parameter ''%s'' not recognised.', params{pp} );
                end
            end
        end
    end % parseInputs
%-------------------------------------------------------------------------%
    function [vals,labels] = findAndClearExistingLabels( ax, maxStringLength )
        % Get the current tick positions so that we can place our new labels
        vals = get( ax, 'XTick' );

        % Now determine the labels. We look first at for previously rotated labels
        % since if there are some the actual labels will be empty.
        ex = findall( ax, 'Tag', 'RotatedXTickLabel' );
        if isempty( ex )
            % Store the positions and labels
            labels = get( ax, 'XTickLabel' );
            if isempty( labels )
                % No labels!
                return
            else
                if ~iscell(labels)
                    labels = cellstr(labels);
                end
            end
            % Clear existing labels so that xlabel is in the right position
            set( ax, 'XTickLabel', {}, 'XTickMode', 'Manual' );
            setappdata( ax, 'OriginalXTickLabels', labels );
        else
            % Labels have already been rotated, so capture them
            labels = getappdata( ax, 'OriginalXTickLabels' );
            set(ex, 'DeleteFcn', []);
            delete(ex);
        end
        % Limit the length, if requested
        if isfinite( maxStringLength )
            for ll=1:numel( labels )
                if length( labels{ll} ) > maxStringLength
                    labels{ll} = labels{ll}(1:maxStringLength);
                end
            end
        end

    end % findAndClearExistingLabels
%-------------------------------------------------------------------------%
    function restoreDefaultLabels( ax )
        % Restore the default axis behavior
        removeListeners( ax );

        % Try to restore the tick marks and labels
        set( ax, 'XTickMode', 'auto', 'XTickLabelMode', 'auto' );
        rmappdata( ax, 'OriginalXTickLabels' );

        % Try to restore the axes position
        if isappdata( ax, 'OriginalAxesPosition' )
            set( ax, 'Position', getappdata( ax, 'OriginalAxesPosition' ) );
            rmappdata( ax, 'OriginalAxesPosition' );
        end
    end
%-------------------------------------------------------------------------%
    function textLabels = createNewLabels( ax, vals, labels, angle )
        % Work out the ticklabel positions
        zlim = get(ax,'ZLim');
        z = zlim(1);

        % We want to work in normalised coords, but this doesn't print
        % correctly. Instead we have to work in data units even though it
        % makes positioning hard.
        y = getYPositionToUse( ax );

        % Now create new text objects in similar positions.
        textLabels = -1*ones( numel( vals ), 1 );
        for ll=1:numel(vals)
            textLabels(ll) = text( ...
                'Units', 'Data', ...
                'Position', [vals(ll), y, z], ...
                'String', labels{ll}, ...
                'Parent', ax, ...
                'Clipping', 'off', ...
                'Rotation', angle, ...
                'Tag', 'RotatedXTickLabel', ...
                'UserData', vals(ll));
        end
        % So that we can respond to CLA and CLOSE, attach a delete
        % callback. We only attach it to one label to save massive numbers
        % of callbacks during axes shut-down.
        set(textLabels(end), 'DeleteFcn', @onTextLabelDeleted);

        % Now copy font properties into the texts
        updateFont();
        % Update the alignment of the text
        updateAlignment();

    end % createNewLabels

%-------------------------------------------------------------------------%
    function repositionAxes( ax )
        % Reposition the axes so that there's room for the labels
        % Note that we only do this if the OuterPosition is the thing being
        % controlled
        if ~strcmpi( get( ax, 'ActivePositionProperty' ), 'OuterPosition' )
            return;
        end

        % Work out the maximum height required for the labels
        labelHeight = getLabelHeight(ax);

        % Remove listeners while we mess around with things, otherwise we'll
        % trigger redraws recursively
        removeListeners( ax );

        % Change to normalized units for the position calculation
        oldUnits = get( ax, 'Units' );
        set( ax, 'Units', 'Normalized' );

        % Not sure why, but the extent seems to be proportional to the height of the axes.
        % Correct that now.
        set( ax, 'ActivePositionProperty', 'Position' );
        pos = get( ax, 'Position' );
        axesHeight = pos(4);
        % Make sure we don't adjust away the axes entirely!
        heightAdjust = min( (axesHeight*0.9), labelHeight*axesHeight );

        % Move the axes
        if isappdata( ax, 'OriginalAxesPosition' )
            pos = getappdata( ax, 'OriginalAxesPosition' );
        else
            pos = get(ax,'Position');
            setappdata( ax, 'OriginalAxesPosition', pos );
        end
        if strcmpi( get( ax, 'XAxisLocation' ), 'Bottom' )
            % Move it up and reduce the height
            set( ax, 'Position', pos+[0 heightAdjust 0 -heightAdjust] )
        else
            % Just reduce the height
            set( ax, 'Position', pos+[0 0 0 -heightAdjust] )
        end
        set( ax, 'Units', oldUnits );
        set( ax, 'ActivePositionProperty', 'OuterPosition' );

        % Make sure we find out if axes properties are changed
        addListeners( ax );

    end % repositionAxes

%-------------------------------------------------------------------------%
    function repositionXLabel( ax )
        % Try to work out where to put the xlabel
        removeListeners( ax );
        labelHeight = getLabelHeight(ax);

        % Use the new max extent to move the xlabel. We may also need to
        % move the title
        xlab = get(ax,'XLabel');
        titleh = get( ax, 'Title' );
        set( [xlab,titleh], 'Units', 'Normalized' );
        if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
            titleExtent = get( xlab, 'Extent' );
            set( xlab, 'Position', [0.5 1+labelHeight-titleExtent(4) 0] );
            set( titleh, 'Position', [0.5 1+labelHeight 0] );
        else
            set( xlab, 'Position', [0.5 -labelHeight 0] );
            set( titleh, 'Position', [0.5 1 0] );
        end
        addListeners( ax );
    end % repositionXLabel

%-------------------------------------------------------------------------%
    function height = getLabelHeight(ax)
        height = 0;
        textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
        if isempty(textLabels)
            return;
        end
        oldUnits = get( textLabels(1), 'Units' );
        set( textLabels, 'Units', 'Normalized' );
        for ll=1:numel(vals)
            ext = get( textLabels(ll), 'Extent' );
            if ext(4) > height
                height = ext(4);
            end
        end
        set( textLabels, 'Units', oldUnits );
    end % getLabelExtent

%-------------------------------------------------------------------------%
    function updateFont()
        % Update the rotated text fonts when the axes font changes
        properties = {
            'FontName'
            'FontSize'
            'FontAngle'
            'FontWeight'
            'FontUnits'
            };
        propertyValues = get( ax, properties );
        textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
        set( textLabels, properties, propertyValues );
    end % updateFont

    function updateAlignment()
        textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
        angle = get( textLabels(1), 'Rotation' );
        % Depending on the angle, we may need to change the alignment. We change
        % alignments within 5 degrees of each 90 degree orientation.
        if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
            if 0 <= angle && angle < 5
                set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
            elseif 5 <= angle && angle < 85
                set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' );
            elseif 85 <= angle && angle < 95
                set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' );
            elseif 95 <= angle && angle < 175
                set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' );
            elseif 175 <= angle && angle < 185
                set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
            elseif 185 <= angle && angle < 265
                set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' );
            elseif 265 <= angle && angle < 275
                set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' );
            elseif 275 <= angle && angle < 355
                set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' );
            else % 355-360
                set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
            end
        else
            if 0 <= angle && angle < 5
                set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
            elseif 5 <= angle && angle < 85
                set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' );
            elseif 85 <= angle && angle < 95
                set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' );
            elseif 95 <= angle && angle < 175
                set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' );
            elseif 175 <= angle && angle < 185
                set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' );
            elseif 185 <= angle && angle < 265
                set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' );
            elseif 265 <= angle && angle < 275
                set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' );
            elseif 275 <= angle && angle < 355
                set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' );
            else % 355-360
                set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' );
            end
        end
    end

%-------------------------------------------------------------------------%
    function onAxesFontChanged( ~, ~ )
        updateFont();
        repositionAxes( ax );
        repositionXLabel( ax );
    end % onAxesFontChanged

%-------------------------------------------------------------------------%
    function onAxesPositionChanged( ~, ~ )
        % We need to accept the new position, so remove the appdata before
        % redrawing
        if isappdata( ax, 'OriginalAxesPosition' )
            rmappdata( ax, 'OriginalAxesPosition' );
        end
        if isappdata( ax, 'OriginalXLabelPosition' )
            rmappdata( ax, 'OriginalXLabelPosition' );
        end
        repositionAxes( ax );
        repositionXLabel( ax );
    end % onAxesPositionChanged

%-------------------------------------------------------------------------%
    function onXAxisLocationChanged( ~, ~ )
        updateAlignment();
        repositionAxes( ax );
        repositionXLabel( ax );
    end % onXAxisLocationChanged

%-------------------------------------------------------------------------%
    function onAxesLimitsChanged( ~, ~ )
        % The limits have moved, so make sure the labels are still ok
        textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' );
        xlim = get( ax, 'XLim' );
        pos = [0 getYPositionToUse( ax )];
        for tt=1:numel( textLabels )
            xval = get( textLabels(tt), 'UserData' );
            pos(1) = xval;
            % If the tick is off the edge, make it invisible
            if xval<xlim(1) || xval>xlim(2)
                set( textLabels(tt), 'Visible', 'off', 'Position', pos )
            elseif ~strcmpi( get( textLabels(tt), 'Visible' ), 'on' )
                set( textLabels(tt), 'Visible', 'on', 'Position', pos )
            else
                % Just set the position
                set( textLabels(tt), 'Position', pos );
            end
        end

        repositionXLabel( ax );
    end % onAxesPositionChanged

%-------------------------------------------------------------------------%
    function onTextLabelDeleted( ~, ~ )
        % The final text label has been deleted. This is likely from a
        % "cla" or "close" call, so we should remove all of our dirty
        % hacks.
        restoreDefaultLabels(ax);
    end

%-------------------------------------------------------------------------%
    function addListeners( ax )
        % Create listeners. We store the array of listeners in the axes to make
        % sure that they have the same life-span as the axes they are listening to.
        axh = handle( ax );
        listeners = [
            handle.listener( axh, findprop( axh, 'FontName' ), 'PropertyPostSet', @onAxesFontChanged )
            handle.listener( axh, findprop( axh, 'FontSize' ), 'PropertyPostSet', @onAxesFontChanged )
            handle.listener( axh, findprop( axh, 'FontWeight' ), 'PropertyPostSet', @onAxesFontChanged )
            handle.listener( axh, findprop( axh, 'FontAngle' ), 'PropertyPostSet', @onAxesFontChanged )
            handle.listener( axh, findprop( axh, 'FontUnits' ), 'PropertyPostSet', @onAxesFontChanged )
            handle.listener( axh, findprop( axh, 'OuterPosition' ), 'PropertyPostSet', @onAxesPositionChanged )
            handle.listener( axh, findprop( axh, 'XLim' ), 'PropertyPostSet', @onAxesLimitsChanged )
            handle.listener( axh, findprop( axh, 'YLim' ), 'PropertyPostSet', @onAxesLimitsChanged )
            handle.listener( axh, findprop( axh, 'XAxisLocation' ), 'PropertyPostSet', @onXAxisLocationChanged )
            ];
        setappdata( ax, 'RotateXLabelsListeners', listeners );
    end % addListeners

%-------------------------------------------------------------------------%
    function removeListeners( ax )
        % Rempove any property listeners whilst we are fiddling with the axes
        if isappdata( ax, 'RotateXLabelsListeners' )
            delete( getappdata( ax, 'RotateXLabelsListeners' ) );
            rmappdata( ax, 'RotateXLabelsListeners' );
        end
    end % removeListeners

%-------------------------------------------------------------------------%
    function y = getYPositionToUse( ax )
        % Use the direction and XAxisLocation properties to work out which
        % Y-value to draw the labels at.
        whichYLim = 1;
        % If YDir is reversed, switch where we position
        if strcmpi( get( ax, 'YDir' ), 'Reverse' )
            whichYLim = 3-whichYLim;
        end
        % If set to "top", then switch (again?)
        if strcmpi( get( ax, 'XAxisLocation' ), 'Top' )
            whichYLim = 3-whichYLim;
        end
        % Now get the value
        ylim = get( ax, 'YLim' );
        y = ylim(whichYLim);
    end % getYPositionToUse

end % EOF


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT 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.
%
% SPHARM-MAT 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 SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function SpharmMatExpandAlignPDM(confs, objs)
numSbj = length(objs);
if ~exist(confs.OutDirectory,'dir')
    mkdir(confs.OutDirectory);
end
if (~exist([confs.OutDirectory '/Logs'],'dir'))
    mkdir([confs.OutDirectory '/Logs']);
end

h = waitbar(0,'Please wait...');
for i = 1:numSbj
    file1 = objs{i};
    [pa, name, ext] = fileparts(file1);
    file2 = [pa '/' name(1:end-4) 'para' ext];
    diary(fullfile([confs.OutDirectory '/Logs'],[date, '_', num2str(round(cputime)), '_' name '_ExpPDM.log']));

    if ~exist(file2,'file')
        disp(sprintf('Corresponding parameter file to %s doesnot exist',name));
    else

        optionSTR = '';
        for j = 1:length(confs.vars)
            vals = eval(sprintf('confs.%s', confs.vars{j}));
            if ~isempty(vals) & (confs.args(j) < 10)
                vals = num2str(vals);
                optionSTR = [optionSTR ' ' sprintf('-%s ', confs.vars{j}) vals];
            elseif (confs.args(j) < 200) & (confs.args(j) >= 10) & ~isempty(deblank(vals)) & ~strcmp(confs.vars{j}, 'others')
                optionSTR = [optionSTR ' ' sprintf('-%s ', confs.vars{j}) vals];
            elseif (confs.args(j) < 200) & (confs.args(j) >= 10) & ~isempty(deblank(vals)) & strcmp(confs.vars{j}, 'others')
                optionSTR = [optionSTR ' ' vals];
            end
        end

        if ispc
            cSTR = sprintf('!%s %s %s %s', confs.command, file1, file2, optionSTR);
            disp('This function is not working on MS Windows. Please use Linux version.');
        elseif isunix
            preload = '/lib64/libgcc_s.so.1';
            if exist(preload, 'file')
                setenv('LD_PRELOAD', preload);
            else
                setenv('LD_PRELOAD', 'lib/libcc_s_so.1');
            end
            cSTR = sprintf('!./%s %s %s %s', confs.command, file1, file2, optionSTR);
            curPath = pwd;
            cd(confs.path);
                eval(cSTR);
            cd(curPath);
            movefile([pa '/' name 'SPHARM*.meta'],confs.OutDirectory);
            movefile([pa '/' name 'SPHARM*.coef'],confs.OutDirectory);
        elseif ismac
            disp('Not implemented yet.');
        end
    end
    diary('off');

    waitbar(i/numSbj)
end
close(h);

return;