% runDataExport.m
%   Exports simulation data in JSON format
%
% Author: Ruby Lee
% Author: Jonathan Karr, jkarr@stanford.edu
% Affiliation: Covert Lab, Department of Bioengineering, Stanford University
% Last Updated: 8/22/2012
function runDataExport(simBatch, simIdx)
setWarnings();
setPath();
setPreferences();
warning('off', 'MATLAB:MKDIR:DirectoryExists');

%import classes
import edu.stanford.covert.cell.sim.util.DiskLogger;
import edu.stanford.covert.cell.sim.util.SimulationDiskUtil;
import edu.stanford.covert.cell.sim.util.SimulationEnsemble;
import edu.stanford.covert.util.SparseMat;

%options
verbosity = 1;
exportProps = {
    'Chromosome', 'abasicSites'
    'Chromosome', 'complexBoundSites'
    'Chromosome', 'damagedBases'
    'Chromosome', 'damagedSugarPhosphates'
    'Chromosome', 'doubleStrandedRegions'
    'Chromosome', 'gapSites'
    'Chromosome', 'hollidayJunctions'
    'Chromosome', 'intrastrandCrossLinks'
    'Chromosome', 'linkingNumbers'
    'Chromosome', 'monomerBoundSites'
    'Chromosome', 'ploidy'
    'Chromosome', 'polymerizedRegions'
    'Chromosome', 'segregated'
    'Chromosome', 'singleStrandedRegions'
    'Chromosome', 'strandBreaks'
    'Chromosome', 'superhelicalDensity'
    'FtsZRing', 'numEdges'
    'FtsZRing', 'numEdgesOneStraight'
    'FtsZRing', 'numEdgesTwoBent'
    'FtsZRing', 'numEdgesTwoStraight'
    'FtsZRing', 'numResidualBent'
    'Geometry', 'chamberVolume'
    'Geometry', 'cylindricalLength'
    'Geometry', 'pinchedDiameter'
    'Geometry', 'surfaceArea'
    'Geometry', 'totalLength'
    'Geometry', 'volume'
    'Geometry', 'width'
    'Host', 'isBacteriumAdherent'
    'Host', 'isInflammatoryResponseActivated'
    'Host', 'isNFkBActivated'
    'Host', 'isTLRActivated'
    'Mass', 'cell'
    'Mass', 'cellDry'
    'Mass', 'dnaWt'
    'Mass', 'media'
    'Mass', 'metaboliteWt'
    'Mass', 'proteinWt'
    'Mass', 'rnaWt'
    'Mass', 'total'
    'Mass', 'waterWt'
    'MetabolicReaction', 'doublingTime'
    'MetabolicReaction', 'fluxs'
    'MetabolicReaction', 'growth'
    'Metabolite', 'counts'
    'ProteinComplex', 'counts'
    'ProteinMonomer', 'counts'
    'Ribosome', 'boundMRNAs'
    'Ribosome', 'mRNAPositions'
    'Ribosome', 'nActive'
    'Ribosome', 'nNotExist'
    'Ribosome', 'nStalled'
    'Ribosome', 'stateOccupancies'
    'Ribosome', 'states'
    'Ribosome', 'tmRNAPositions'
    'Rna', 'counts'
    'RNAPolymerase', 'nActive'
    'RNAPolymerase', 'nFree'
    'RNAPolymerase', 'nNonSpecificallyBound'
    'RNAPolymerase', 'nSpecificallyBound'
    'RNAPolymerase', 'positionStrands'
    'RNAPolymerase', 'stateOccupancies'
    'RNAPolymerase', 'states'
    'RNAPolymerase', 'supercoilingBindingProbFoldChange'
    'RNAPolymerase', 'transcriptionFactorBindingProbFoldChange'
    'Stimulus', 'values'
    'Transcript', 'abortedTranscripts'
    'Transcript', 'boundTranscriptChromosome'
    'Transcript', 'boundTranscriptionUnits'
    'Transcript', 'boundTranscriptProgress'
    'Transcript', 'rnaBoundRNAPolymerases'
    };

%sanitize arguments
if ischar(simIdx)
    simIdx = str2double(simIdx);
end

%load simulation object which contains meta data
[~, ~, sim] = SimulationDiskUtil.getSimulation([simBatch filesep num2str(simIdx)]);

%export data to JSON
errorLog = [];
outDir = [SimulationDiskUtil.getBaseDir() filesep simBatch filesep num2str(simIdx) filesep 'json'];

%create output directory
mkdir(outDir);

%summary data
if verbosity > 0, fprintf('Exporting summary ...'), end
summary = load(sprintf('%s%s%s%s%d%ssummary.mat', SimulationDiskUtil.getBaseDir(), filesep, simBatch, filesep, simIdx, filesep));
mkdir([outDir filesep 'Summary']);
fields = fieldnames(summary);
for i = 1:numel(fields)
    data = summary.(fields{i});
    for j = 1:size(data, 1)
        tmp = struct('label', [], 'data', []);
        tmp.label = [fields{i} '_' num2str(j)];
        tmp.data = compressData(data(j, :), 1, 1000);

        writeJSON(tmp, [outDir filesep 'Summary' filesep fields{i} '_' num2str(j) '.json']);
    end

    clear data;
end
clear summary;
if verbosity > 0, fprintf(' done\n'), end

%complete data
for i = 1:numel(sim.states)
    stateID = sim.states{i}.wholeCellModelID(7:end);
    propIDs = [sim.states{i}.stateNames; sim.states{i}.dependentStateNames];
    if verbosity > 0, fprintf('Exporting %s ...\n', stateID), end
    mkdir([outDir filesep stateID]);
    for j = 1:numel(propIDs)
        if ~any(strcmp(exportProps(:, 1), stateID) & strcmp(exportProps(:, 2), propIDs{j}))
            continue;
        end

        if verbosity > 0, fprintf('\t%s ...', propIDs{j}), end
        %try
        %retrieve data
        result = SimulationEnsemble.load(simBatch, {stateID propIDs{j}}, ...
            [], [], 1, 'extract', simIdx);
        result = result.(stateID).(propIDs{j});

        %output data in JSON format
        if isa(sim.states{i}, 'edu.stanford.covert.cell.sim.state.Chromosome') && isa(result, 'SparseMat')
            %chromosome properties
            [subs, vals] = find(result);
            tfs1 = subs(:, 3) < size(result, 3);
            tfs2 = subs(:, 3) > 1;

            result1 = SparseMat(subs(tfs1, :), vals(tfs1, :), size(result) - [0 0 1]);
            result2 = SparseMat([subs(tfs2, 1:2) subs(tfs2, 3)-1], vals(tfs2, :), size(result) - [0 0 1]);

            times = downsampleData([1; 1 + find(full(permute(any(any(result1 ~= result2, 1), 2), [3 1 2]))); size(result, 3)], 1000);
            if numel(times) > 1000
                times = downsampleData((1:size(result, 3))', 1000);
            end

            tmp = struct('time', [], 'strand', [], 'pos', [], 'val', []);
            tmp = tmp(ones(numel(times), 1), 1);
            for k = 1:numel(times)
                [tmpsubs, tmpvals] = find(result(:, :, times(k)));

                tmp(k).time = times(k);
                tmp(k).pos = tmpsubs(:, 1);
                tmp(k).strand = tmpsubs(:, 2);
                tmp(k).val = tmpvals;
            end

            writeJSON(tmp, [outDir filesep stateID filesep propIDs{j} '.json']);
        else
            %generic output

            %sum over compartments
            if ...
                    (isa(sim.states{i}, 'edu.stanford.covert.cell.sim.MoleculeCountState') && strcmp(propIDs{j}, 'counts')) || ...
                    (isa(sim.states{i}, 'edu.stanford.covert.cell.sim.state.Stimulus') && strcmp(propIDs{j}, 'values'))
                result = sum(result, 2);
            end

            %output
            if size(result, 2) == 1
                %save rows separately
                for k = 1:size(result, 1)
                    tmp = struct('label', [], 'data', []);
                    tmp.label = [stateID '_' propIDs{j} '_' num2str(k)];
                    tmp.data = compressData(result(k, :, :), 0, 1000);

                    writeJSON(tmp, [outDir filesep stateID filesep propIDs{j} '_' num2str(k) '.json']);
                end
            else
                %save rows and columns separately
                for k = 1:size(result, 1)
                    for l = 1:size(result, 2)
                        tmp = struct('label', [], 'data', []);
                        tmp.label = [stateID '_' propIDs{j} '_' num2str(k) '_' num2str(l)];
                        tmp.data = compressData(result(k, l, :), 0, 1000);

                        writeJSON(tmp, [outDir filesep stateID filesep propIDs{j} '_' num2str(k) '_' num2str(l) '.json']);
                    end
                end
            end
        end

        clear result tmp;
        %catch exception
        %    errorLog = [errorLog sprintf('Error in exporting %s in simulation %s/%s\n%s', ...
        %        propIDs{j}, simBatch, simIdx, exception.getReport())]; %#ok<AGROW>
        %end
        if verbosity > 0, fprintf(' done\n'), end
    end
    if verbosity > 0, fprintf('done\n'), end
end

%print error log
if ~isempty(errorLog)
    throw(MException('runAnalysis:error', errorLog));
end

function result = compressData(data, timeOffset, maxSamples)
data = full(double(data));
data = data(:);

%get time points where value changes
idxs = unique([1; find(diff(data(1:end-1))); 1 + find(diff(data(1:end-1))); numel(data)]);

if numel(idxs) > maxSamples
    %downsample
    idxs = downsampleData((1:numel(data))', maxSamples);
end

%return as time, value pairs
result = [idxs-timeOffset  data(idxs)];

function idxs = downsampleData(idxs, maxSamples)
if numel(idxs) > maxSamples
    tmp = idxs(1:floor(numel(idxs)/maxSamples):end);
    if tmp(end) ~= idxs(end)
        tmp = [tmp; idxs(end)];
    end
    idxs = tmp;
end

function writeJSON(data, fileName)
fid = fopen(fileName, 'w+');
writeJSON_data(fid, data);
fclose(fid);

function writeJSON_data(fid, data)
if ischar(data)
    writeJSON_char(fid, data)
elseif isnumeric(data)
    writeJSON_numeric(fid, data)
elseif isstruct(data)
    writeJSON_struct(fid, data)
else
    throw(MException('writeJSON_data:error', 'Unsupported data type %s', class(data)))
end

function writeJSON_struct(fid, data)
if numel(data) == 1
    fprintf(fid, '{');
    fields = fieldnames(data);
    for i = 1:numel(fields)
        fprintf(fid, '"%s": ', fields{i});
        writeJSON_data(fid, data.(fields{i}));
        if i < numel(fields)
            fprintf(fid, ', ');
        end
    end
    fprintf(fid, '}');
else
    fprintf(fid, '[');
    for i = 1:numel(data)
        writeJSON_struct(fid, data(i));
        if i < numel(data)
            fprintf(fid, ',');
        end
    end
    fprintf(fid, ']');
end

function writeJSON_char(fid, data)
fprintf(fid, '"%s"', data);

function writeJSON_numeric(fid, data)
fprintf(fid, '[');

if ndims(data) > 2
    throw(MException('writeJSON:error', 'Data with more than 2 dimensions is not supported'))
end

if isempty(data)
elseif size(data, 2) == 1
    if all(abs(data - round(data)) < 1e-10)
        fprintf(fid, '%d', round(data(1)));
        if numel(data) > 1
            fprintf(fid, ',%d', round(data(2:end)));
        end
    else
        fprintf(fid, '%e', data(1));
        if numel(data) > 1
            fprintf(fid, ',%e', data(2:end));
        end
    end
elseif size(data, 2) == 2 && all(abs(data(:, 1) - round(data(:, 1))) < 1e-10)
    for j = 1:size(data, 1)
        fprintf(fid, '[');
        fprintf(fid, '%d', round(data(j, 1)));
        fprintf(fid, ',%e', data(j, 2:end));
        fprintf(fid, ']');
        if j < size(data, 1)
            fprintf(fid, ',');
        end
    end
else
    for j = 1:size(data, 1)
        fprintf(fid, '[');
        fprintf(fid, '%e', data(j, 1));
        fprintf(fid, ',%e', data(j, 2:end));
        fprintf(fid, ']');
        if j < size(data, 1)
            fprintf(fid, ',');
        end
    end
end

fprintf(fid, ']');


function h=editpm1(fig,x,y,dx,dy,str,clb)

clr=[0.8313725490196079   0.8156862745098039   0.7843137254901961]; % background color

h=uicontrol('parent',fig,...
    'Style','edit','units','normalized','fontunits','normalized',...
    'BackgroundColor',[1 1 1],...
    'HorizontalAlignment','center',...
    'fontsize',0.8,...
    'position',[x y-dy/2 dx dy],'string',str,...
    'callback',clb);

st=0.1;

p=uicontrol('parent',fig,...
    'Style','pushbutton','units','normalized','fontunits','normalized',...
    'BackgroundColor',clr,...
    'HorizontalAlignment','center',...
    'fontsize',1,...
    'position',[x+dx y-dy/2+dy/2 dy/2 dy/2],'string','+',...
    'callback',['stp1(' num2str(h,'%20.20e') ',' num2str(st,'%20.20e') ',''' clb ''',' num2str(fig,'%20.20e') ')' ]);

m=uicontrol('parent',fig,...
    'Style','pushbutton','units','normalized','fontunits','normalized',...
    'BackgroundColor',clr,...
    'HorizontalAlignment','center',...
    'fontsize',1,...
    'position',[x+dx y-dy/2 dy/2 dy/2],'string','-',...
    'callback',['stp1(' num2str(h,'%20.20e') ',' num2str(-st,'%20.20e') ',''' clb ''',' num2str(fig,'%20.20e') ')' ]);


function model = gpReadFromFID(FID, varargin)

% GPREADFROMFID Load from a FID produced by the C++ implementation.
% FORMAT
% DESC loads in from a file stream the data format produced by the
% C++ GP implementation.
% ARG FID : the file ID from where the data is loaded.
% RETURN model : the model loaded in from the file.
%
% COPYRIGHT : Neil D. Lawrence, 2005, 2006, 2008, 2009
%
% SEEALSO : gpReadFromFile

% GP

try
  version = readVersionFromFID(FID);
catch
  if strcmp(lasterr, 'Incorrect file format')
    version = readDoubleFromFID(FID, 'gplvmVersion');
  else
    error(lasterr)
  end
end

if version > 0.2
  error('Incorrect file version.')
end

if version>0.11
  baseType = readStringFromFID(FID, 'baseType')
    if ~strcmp(baseType, 'dataModel') && ~strcmp(baseType, 'mapmodel')
    error('Incorrect base type in file.')
  end
  type = readStringFromFID(FID, 'type')
  if ~strcmp(type, 'gp')
    error('Incorrect type in file.')
  end
end
numData = readIntFromFID(FID, 'numData');
dataDim = readIntFromFID(FID, 'outputDim');
inputDim = readIntFromFID(FID, 'inputDim');
sparseApprox = readIntFromFID(FID, 'sparseApproximation');
numActive = readIntFromFID(FID, 'numActive');
if sparseApprox
  beta = modelReadFromFID(FID);
  beta = beta(1, 1);
end

learnScale = readBoolFromFID(FID, 'learnScale');
learnBias = readBoolFromFID(FID, 'learnBias');
scale = modelReadFromFID(FID);
bias = modelReadFromFID(FID);

kern = modelReadFromFID(FID);
noise = modelReadFromFID(FID);

if sparseApprox
  fixInducing = readBoolFromFID(FID, 'fixInducing');
  X_u = modelReadFromFID(FID);
end

X = varargin{1};
y = varargin{2};
% X = zeros(numData, inputDim);
% y = zeros(numData, dataDim);

warning('Noise model is ignored');

switch sparseApprox
 case 0
  approxType = 'ftc';
 case 1
  approxType = 'dtc';
 case 2
  approxType = 'fitc';
 case 3
  approxType = 'pitc';
 case 4
  approxType = 'dtcvar';
end
options = gpOptions(approxType);
options.numActive = numActive;
options.kern = kern;
if sparseApprox
  options.fixInducing = fixInducing;
end
model = gpCreate(inputDim, size(y, 2), X, y, options);
model.X = X;
if sparseApprox
  model.X_u = X_u;
  model.beta = beta;
end
model.scale = scale;
model.bias = bias;
model.m = gpComputeM(model);

% This forces kernel computation.
initParams = gpExtractParam(model);
model = gpExpandParam(model, initParams);


classdef AssessmentClassificationResultsPlotter < handle

    properties (Access = public, Constant)
        FontSize = 18;
        LineWidth = 2;
        MissedEventSymbolSize = 16;
        RectangleYPosToDataRatios = [1.0, 1.05];
        LabelYPosToRectangleRatios = [0.8,0.95];
        RectangleCurvature = 0.1;
    end

    properties (Access = public)
        yRange = [];
        missedEvents;
    end

    properties (Access = private)
        plotAxes;
        classNames;
        isUpperYPos = false;
    end

    methods (Access = public)
        function obj = AssessmentClassificationResultsPlotter(plotAxes,classNames)
            obj.plotAxes = plotAxes;
            obj.classNames = classNames;
        end

        function plotClassificationResults(obj, detailedClassificationResults, signal)

            nSegments = length(detailedClassificationResults.segments);
            for i = 1 : nSegments
                segment = detailedClassificationResults.segments(i);
                if ~isempty(segment.eventIdx)
                    y = signal(segment.eventIdx);
                else
                    y = [];
                end

                truthClass = detailedClassificationResults.classificationResult.truthClasses(i);
                predictedClass = detailedClassificationResults.classificationResult.predictedClasses(i);

                if truthClass ~= predictedClass || truthClass ~= Labeling.kNullClass
                    obj.plotClassificationResult(segment,y,truthClass,predictedClass);
                end
            end

            if ~isempty(obj.missedEvents)
                obj.plotMissedEventAnnotations(signal);
            end
        end
    end

    methods (Access = private)
        function plotHandle = plotClassificationResult(obj, segment, segmentEventY, truthClass, predictedClass)
            colorStr = obj.getColorForPrediction(truthClass,predictedClass);

            rectangleHeight = (obj.yRange(2) - obj.yRange(1)) * obj.RectangleYPosToDataRatios(obj.isUpperYPos+1);
            yOffset = (rectangleHeight - (obj.yRange(2) - obj.yRange(1))) / 2;
            rectangleWidth = single(segment.endSample - segment.startSample);
            rectanglePosition = [single(segment.startSample), obj.yRange(1) - yOffset, single(rectangleWidth), rectangleHeight];
            rectangleHandle = rectangle('Position',rectanglePosition,'Curvature',[obj.RectangleCurvature obj.RectangleCurvature],'LineWidth',obj.LineWidth, 'EdgeColor',colorStr);

            %plot text
            textStr = obj.getTextForPrediction(truthClass,predictedClass);
            xPos = (double(segment.startSample) + double(segment.endSample)) / 2;
            yPos = double(obj.yRange(2)) * obj.LabelYPosToRectangleRatios(obj.isUpperYPos+1);
            textHandle = text(obj.plotAxes,xPos,yPos, textStr,...
                'FontSize',obj.FontSize,'HorizontalAlignment','center','Color',colorStr);
            set(textHandle, 'Clipping', 'on');
            obj.isUpperYPos = ~obj.isUpperYPos;

            %plot symbol
            symbolHandle = [];
            if ~isempty(segmentEventY)
                x = segment.eventIdx;
                symbolHandle = plot(obj.plotAxes,x,segmentEventY,'*','Color',colorStr);
            end

            plotHandle = AssessmentClassificationResultPlotHandle(rectangleHandle, textHandle, symbolHandle);
        end

        function textStr = getTextForPrediction(obj,truthClass,predictedClass)
            predictedClassStr = Labeling.StringForClass(predictedClass,obj.classNames);

            if truthClass ~= predictedClass
                truthClassStr = Labeling.StringForClass(truthClass,obj.classNames);
                textStr = sprintf('%s\n (truth: %s)',predictedClassStr,truthClassStr);
            else
                textStr = predictedClassStr;
            end
        end

        function colorStr = getColorForPrediction(~,truthClass,predictedClass)
            if truthClass == predictedClass
                colorStr = Constants.kCorrectColor;
            else
                colorStr = Constants.kWrongColor;
            end
        end

        function plotMissedEventAnnotations(obj,signal)
            for i = 1 : length(obj.missedEvents)
                missedEvent = obj.missedEvents(i);
                if missedEvent.label ~= Labeling.kNullClass
                    obj.plotMissedEventAnnotation(missedEvent,signal);
                end
            end
        end

        function plotMissedEventAnnotation(obj,event,signal)
            eventX = event.sample;
            eventY = signal(eventX);
            class = event.label;

            classStr = Helper.StringForClass(class,obj.classNames);

            plot(obj.plotAxes,eventX,eventY,'*','Color',Constants.kMissedColor,'LineWidth',AssessmentClassificationResultsPlotter.MissedEventSymbolSize);
            textHandle = text(obj.plotAxes,double(eventX),double(eventY), classStr,'FontSize',AssessmentClassificationResultsPlotter.FontSize);
            set(textHandle, 'Clipping', 'on');
        end
    end
end


function [CnoCom, Com] = PurgeComments (C, ch)
%
%   [CnoCom, Com] = PurgeComments (C, [ch])
%
%Purpose:
%   purges all matlab coments from character array
%
%
%Input Parameters:
%   C : character array
%   ch : is the character indicating a comment line, default is '%'
%
%Output Parameters:
%   err : 0 No Problem
%       : 1 Mucho Problems
%   CnoCom : comment purged caharacter array
%   Com : Comment array
%
%More Info :
%
%  SkipMatlabHelp
%  FindChar
%	NextString
%
%   C = sprintf (...
%  '%%Hello \n%%Couci Couci \nAbc = 00\n%%Wald\njon\n%%ert\nMlk\n%%brt%%crt\nflp\n%%kr\n\nko=6\n%%bizz\n%%Bozz\n\nfly')
%   [CnoCom] = PurgeComments (C)
%
%
%     Author : Ziad Saad
%     Date : Sat Mar 27 15:42:11 CST 1999

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

if (nargin == 1), ch = '%'; end

%Debug Flag
DBG = 1;

%initailize return variables
err = 1;
CnoCom = [];
Com = '';

%skip the help lines (top comments) if there are any
if (C(1) == ch),
	[C] = SkipMatlabHelp (C);
end

[err, Loc_1] = FindChar (C, ch);

if (~isempty(Loc_1)),
	cnt = 1;
	CnoCom = C;
	nC = length(C);
	%fprintf ('C purged of heading is :\n');
	%fprintf ('%c', C);
	FirstChunk = C(1:Loc_1(1)-1);
	CnoCom(1:length(FirstChunk)) = FirstChunk;
	nCnoCom = length(FirstChunk);
	%fprintf ('CnoCom at first chunk : \n');
	%fprintf ('%c', CnoCom(1:nCnoCom));

	while (cnt < length(Loc_1)),
		%skip that line
		[err,Cnext, cend] = NextString (C, 'NewLine', Loc_1(cnt));
	   Com = [Com Cnext];
		%make sure next line is not a comment
		if (cend+1 == Loc_1(cnt+1)), lop = 1; else lop = 0; end
		while (lop), %looks like it is a comment, cycle through block
			% fprintf (1,'Looping, cnt = %g/%g\n', cnt, length(Loc_1));
			cnt = cnt+1;
			[err,Cnext, cend] = NextString (C, 'NewLine', Loc_1(cnt));
			Com = [Com Cnext];
         lop = 0;
			if (cnt < length(Loc_1))
				if (cend+1 == Loc_1(cnt+1)),
					lop = 1;
				end
			end
		end

		%copy the next good piece,
		%if you did not run out of cnt in the looping condition above
		if (cnt < length(Loc_1)),
			GoodChunk = C(cend+1:Loc_1(cnt+1)-1);
			CnoCom(1+nCnoCom:nCnoCom+length(GoodChunk)) = GoodChunk;
			nCnoCom = nCnoCom+length(GoodChunk);
			%fprintf ('CnoCom at middle chunk (cnt = %g) : \n', cnt);
			%fprintf ('%c', CnoCom(1:nCnoCom));

			%increment and go on
			cnt = cnt + 1;
		end
	end

		%append last piece if any
		if (Loc_1(cnt) < nC),
			[err,Cnext, cend] = NextString (C, 'NewLine', Loc_1(cnt));
			Com = [Com Cnext];
         LastChunk = C(cend+1:nC);
			CnoCom(1+nCnoCom:nCnoCom+length(LastChunk)) = LastChunk;
			nCnoCom = nCnoCom+length(LastChunk);
			%fprintf ('Last Chunk : \n');
			%fprintf ('%c',LastChunk);
		end

		CnoCom = CnoCom(1:nCnoCom);

		%fprintf ('\n***\nFinal CnoCom\n***\n');
		%fprintf ('%c', CnoCom(1:nCnoCom));
else %no comments
	CnoCom = C;
end
%Com
return;


classdef GemImageSliceParameters < CoreBaseClass
    % GemImageSliceParameters. Parameters for extracting a 2D slice from a
    % 3D volume
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2013.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    properties (SetObservable)
        Orientation = GemImageOrientation.XZ
        SliceNumber = [1, 1, 1] % The currently shown slice in 3 dimensions
        UpdateLock
    end
end


function notDefined = ieNotDefined( varString )
%
% notDefined = ieNotDefined( varString )
%
% Author: ImagEval
% Purpose:
%    Determine if a variable is defined in the calling function's workspace.
%  A variable is defined if (a) it exists and (b) it is not empty.
%
% notDefined: 1 (true) if the variable is not defined in the calling workspace
%             0 (false) if the variable is defined in the calling workspace
%
%  Defined means the variable exists and is not empty in the function that
%  called this function.
%
%  This routine should replace the many calls of the form
%    if ~exist('varname','var') | isempty(xxx), ...
%
%    with the call
%
%    if ieNotDefined('varname')
%
%

if (~ischar(varString)), error('Varible name must be a string'); end

notDefined = 0;  % Assume the variable is defined

str = sprintf('''%s''',varString);
cmd1 = ['~exist(' str ',''var'') == 1'];
cmd2 = ['isempty(',varString ') == 1'];
cmd = [cmd1, ' | ',cmd2];

% If either of these conditions holds, then not defined is true.
notDefined = evalin('caller',cmd1);     % Check that the variable exists in the caller space
if notDefined, return;                  % If it does not, return with a status of 0
else
    notDefined = evalin('caller',cmd2); % Check if the variable is empty in the caller space
    if notDefined return;
    end
end

return;


function options = scipset(varargin)
%SCIPSET  Create or alter the options for Optimization with SCIP
%
% options = scipset('param1',value1,'param2',value2,...) creates an SCIP
% options structure with the parameters 'param' set to their corresponding
% values in 'value'. Parameters not specified will be set to the SCIP
% default.
%
% options = scipset(oldopts,'param1',value1,...) creates a copy of the old
% options 'oldopts' and then fills in (or writes over) the parameters
% specified by 'param' and 'value'.
%
% options = scipset() creates an options structure with all fields set to
% SCIPSET defaults.
%
% scipset() prints a list of all possible fields and their function.
%
% See supplied SCIP Documentation for further details of these options.

%   Copyright (C) 2013 Jonathan Currie (I2C2)

% Print out possible values of properties.
if ((nargin == 0) && (nargout == 0))
    printfields();
    return
end
%Names and Defaults
Names = {'gamsfile','testmode'}';
Defaults = {[],0}';

%Enter and check user args
try
    options = opticheckset(Names,Defaults,@checkfield,varargin{:});
catch ME
    throw(ME);
end

function checkfield(field,value)
%Check a field contains correct data type
switch lower(field)
    %Scalar 0/1
    case 'testmode'
        err = opticheckval.checkScalar01(value,field);
    %char array
    case 'gamsfile'
        err = opticheckval.checkChar(value,field);
    otherwise
        err = MException('OPTI:SetFieldError','Unrecognized parameter name ''%s''.', field);
end
if(~isempty(err)), throw(err); end

function printfields()
%Print out fields with defaults
fprintf('         gamsfile: [ Write SCIP model to GAMS file (will skip solving): {[]}, ''filename'' ] \n');
fprintf('         testmode: [ Validate the nonlinear function generation (will skip solving): {0}, 1 ] \n');
fprintf('\n');


function create_all_menus(obj, force)
    % create menus for main zmap figure
    % create_all_menus() - will create all menus, if they don't exist
    % create_all_menus(force) - will delete and recreate menus if force is true
    import zmaptopo.pltopo

    h = findobj(obj.fig, 'Tag', 'mainmap_menu_divider');
    if ~exist('force', 'var')
        force = false;
    end
    if ~isempty(h) && force
        delete(h); h = [];
    end
    if isempty(h)
        add_menu_divider(obj.fig, 'mainmap_menu_divider');
    end
    create_overlay_menu();

    catmen = obj.catalog_menu(force);
    create_random_data_simulations_menu(catmen);

    create_decluster_menu(findobj(obj.fig, 'Label', 'Catalog', '-and', 'type', 'uimenu'));

    add_sampling_menu(obj);
    create_ztools_menu();

    uimenu('Label', '|>', 'Enable', 'off');%
    funcsymbol = char(402);
    create_time_analysis_menu();
    create_map_analysis_menu();
    create_xsec_analysis_menu();
    create_3d_analysis_menu();

    uimenu('Label', '<|>', 'Enable', 'off');
    create_results_menu();
    uimenu('Label', '<|', 'Enable', 'off');

    % modify the file menu to add ZMAP stuff
    hFileMenu = findall(obj.fig, 'tag', 'figMenuFile');
    if force
        if ~isempty(hFileMenu)
            delete(findobj(hFileMenu,'Label','Get/Load Catalog'))
        end
        copymenus(findobj(obj.fig, 'Label', 'Get/Load Catalog'), hFileMenu);
    elseif isempty(hFileMenu)
        copymenus(findobj(obj.fig, 'Label', 'Get/Load Catalog'), hFileMenu);
    end
    addPreferencesMenuItem();
    addQuitMenuItem();
    addAboutMenuItem();

    if ZmapGlobal.Data.debug
        mainhelp = findall(obj.fig, 'Tag', 'figMenuHelp');
        uimenu(mainhelp, 'Label', 'Export ZmapMainWindow to workspace',...
            'Separator', 'on', 'MenuSelectedFcn', @export_me);
    end

    function export_me(~,~)
        prompt = 'Variable name to use';
        name = 'Export ZmapMainWindow to desktop';
        numlines = 1;
        defaultanswer = {'zmw'};
        name = inputdlg(prompt, name, numlines, defaultanswer);
        if ~isempty(name)
            assignin('base', name{:}, obj);
        end
    end

    %% time analysis_menu
    function create_time_analysis_menu()
        submenu = uimenu('Label', [funcsymbol '(t)']);
        uimenu(submenu, 'Label', 'functions evaluated through time', 'enable', 'off');
        uimenu(submenu, 'Label', 'Analyze time series ...',...
            'Separator', 'on',...
            'MenuSelectedFcn', @analyze_time_series_cb);

        function analyze_time_series_cb(~,~)
            % pick which time series we are investigating
            if ~isempty(obj.shape)
                items = ["Selected Events (IN polygon)", "Unselected Events (OUTSIDE polygon)"];
                items_data = {@()obj.catalog, @()obj.rawcatalog.subset(~obj.shape.isinterior(obj.rawcatalog.X, obj.rawcatalog.Y))};
            else
                items = ["Selected Events"]; %#ok<NBRAK>
                items_data = {@()obj.catalog};
            end
            if ~isempty(obj.XSectionTitles)
                items(end+1 : end + numel(obj.XSectionTitles)) = strcat("XSEC: ", string(obj.XSectionTitles));
                data_start = numel(items_data);
                for i = numel(obj.XSectionTitles) : -1 : 1
                    items_data(data_start+i) = {@()obj.xscats(obj.XSectionTitles{i}) };
                end
            end
            items(end+1) = "FULL (raw) Catalog";
            items_data(end+1) = {@()obj.rawcatalog};
            [selection, ok] = listdlg('PromptString', 'Select catalog to analyze',...
                'SelectionMode', 'single',...
                'ListString', items);
            if ok
                % c = items_data{selection};
                ctp = CumTimePlot(items_data{selection}() );
                ctp.plot();
            end
        end
    end

    %% map-view analysis menu
    % analyze items according to spacing in a horizontal plane
    function create_map_analysis_menu()
        submenu = uimenu('Label', [funcsymbol '(x,y)']); % 'Map');

        uimenu(submenu, 'Label', 'functions evaluated over a planar grid', 'enable', 'off');

        import XYfun.* % the map functions exist in the XYfun package
        % AB menu
        bvalgrid.AddMenuItem(submenu, @()obj.map_zap, 'Separator', 'on');
        bvalmapt.AddMenuItem(submenu, @()obj.map_zap);
        bdepth_ratio.AddMenuItem(submenu, @()obj.map_zap);

        % P menu
        bpvalgrid.AddMenuItem(submenu, @()obj.map_zap, 'Separator', 'on');
        rcvalgrid_a2.AddMenuItem(submenu, @()obj.map_zap, 'Enable', 'off');

        % Rate Change menu
        comp2periodz.AddMenuItem(submenu, @()obj.map_zap, 'Separator', 'on');
        uimenu(submenu, 'Label', 'Calculate a z-value map', 'Enable', 'off', 'MenuSelectedFcn', @(~,~)inmakegr(obj.catalog));

        % Quarry menu : detect quarry contamination
        findquar.AddMenuItem(submenu, @()obj.map_zap, 'Separator', 'on');

        %uimenu(submenu, 'Label', 'Map stress tensor', 'MenuSelectedFcn', @(~,~)stressgrid());
        stressgrid.AddMenuItem(submenu, @()obj.map_zap, 'Separator', 'on');
        %{
            uimenu(tmp, 'Label', 'Load...', 'Enable', 'off', 'MenuSelectedFcn',  @(~,~)rcvalgrid_a2('lo'));
            uimenu(submenu, 'Label', 'Load a z-value movie (map-view)', 'MenuSelectedFcn', @(~,~)loadmovz());
        %}
    end

    %% xsec-view analysis menu
    % analyze items according to position & depth along strike.

    function create_xsec_analysis_menu()
        submenu = uimenu('Label', XSection.MainMenuLabel);
        uimenu(submenu, 'Label', 'functions evaluated along a cross-section', 'enable', 'off');
        uimenu(submenu, 'Separator', 'on',...
            'Label', 'Define a cross-section', 'MenuSelectedFcn', @obj.cb_xsection, 'Tag', 'CreateXsec');

        import XZfun.* % the cross-section functions exist in the XZfun package

        magrcros.AddMenuItem(submenu, @()obj.xsec_zap, 'Separator','on');
        rc_cross_a2.AddMenuItem(submenu, @()obj.xsec_zap);

        uimenu(submenu, 'enable', 'off', 'Label', 'Calc a b-value cross-section', 'MenuSelectedFcn', @(~,~)nlammap(@()obj.xsec_zap));

        bcross.AddMenuItem(submenu, @()obj.xsec_zap, 'Enable', 'off');
        bcrossVt2.AddMenuItem(submenu, @()obj.xsec_zap, 'Enable', 'off');
        calc_Omoricross.AddMenuItem(submenu, @()obj.xsec_zap, 'Enable', 'off');
        calc_across.AddMenuItem(submenu, @()obj.xsec_zap, 'Enable', 'off');
        cross_stress.AddMenuItem(submenu, @()obj.xsec_zap, 'Enable', 'off');
    end

    %% 3D-view analysis menu
    % analyze items in a volume

    function create_3d_analysis_menu()
        submenu = uimenu('Label', [funcsymbol '(x,y,z)']); %'3D-Vol');
        uimenu(submenu, 'Label', 'functions over a 3-D grid', 'enable', 'off');
        uimenu(submenu, 'Separator', 'on', 'Label', 'Nothing here yet', 'Enable', 'off');

        import XYZfun.* % the cross-section functions exist in the XYZfun package

        bgrid3dB.AddMenuItem(submenu, @()obj.map_zap, 'Enable', 'off');
        zgrid3d.AddMenuItem(submenu, @()obj.map_zap, 'Enable', 'off');

        %{
            uimenu(submenu, 'Label', 'Load a 3D b-value grid', 'MenuSelectedFcn', @(~,~)myslicer('load'));
        %}

    end

    function create_results_menu()
        ResultsDisplay.Hdisplay.add_menus();
        set(findobj(allchild(gcf), 'flat', 'Tag', 'lookmenu'), 'Enable', 'off');
    end

    %% individual menus to create

    function create_overlay_menu()
        h = findobj(obj.fig, 'Tag', 'mainmap_menu_overlay');
        axm = obj.map_axes;
        if ~isempty(h) && exist('force', 'var') && force
            delete(h); h = [];
        end
        if ~isempty(h)
            return
        end

        % Make the menu to change symbol size and type
        %
        mapoptionmenu = uimenu(obj.fig, 'Label', 'Display', 'Tag', 'mainmap_menu_overlay');

        uimenu(mapoptionmenu, 'Label', '3-D view',...
            'MenuSelectedFcn', @obj.set_3d_view); % callback was plot3d

        uimenu(mapoptionmenu, 'Label', 'Change Z aspect ratio', 'Tag', 'z_aspectratio_menu',...
            'MenuSelectedFcn', @obj.set_z_ratio, 'Enable', 'off'); % callback was plot3d

        if ~iscartesian(obj.refEllipsoid)
            uimenu(mapoptionmenu, 'Label', 'Set aspect ratio by latitude',...
                'MenuSelectedFcn', @set_aspects_cb, ...
                'Checked', char(ZmapGlobal.Data.lock_aspect));

            if ZmapGlobal.Data.lock_aspect
                daspect(obj.get_all_map_axes(), [1 cosd(mean(axm.YLim)) 10]);
            end
        end
        m = uimenu(mapoptionmenu, 'Label', 'Toggle Lat/Lon Grid',...
            'MenuSelectedFcn', @toggle_grid,...
            'checked', char(ZmapGlobal.Data.mainmap_grid));

        if iscartesian(obj.refEllipsoid)
             axis(obj.get_all_map_axes, 'equal');
             m.Label = 'Toggle X/Y Grid';
        end

        grid(axm, char(ZmapGlobal.Data.mainmap_grid));

        % choose what to plot by
        for j = 1:numel(obj.rawcatalog.FieldnamesForColorby)
            myfn = obj.rawcatalog.FieldnamesForColorby{j};
            um(j) = uimenu(mapoptionmenu, 'Label', ['Color by ' myfn], 'MenuSelectedFcn', @(s,v)set_colorby(s, v, myfn) ,...
                'Checked', tf2onoff(strcmp(obj.colorField, myfn)));
            if j == 1
                um(j).Separator = 'on';
            end
        end
        uimenu(mapoptionmenu, 'Label', 'Edit Main Map Symbols', 'MenuSelectedFcn', @manage_mainmap_symbols);
        uimenu(mapoptionmenu, 'Label', 'Edit Current Map Symbols', 'MenuSelectedFcn', @manage_symbols_for_current_map);
        % add_symbol_menu(axm, mapoptionmenu, 'Map Symbols');
        ovmenu = uimenu(mapoptionmenu, 'Label', 'Layers');
        uimenu(ovmenu, 'Label', 'Show unselected events', 'MenuSelectedFcn', @toggle_unselected_events,...
            'Checked', "on");

        try
            MapFeature.foreach(obj.Features, 'addToggleMenu', ovmenu, axm)
        catch ME
            warning(ME.message)
        end

        uimenu(ovmenu, 'Label', 'Plot stations + station names',...
            'Separator', 'on',...
            'MenuSelectedFcn', @(~,~)plotstations(axm));

        lemenu = uimenu(mapoptionmenu, 'Label', 'Legend by ...  ', 'Enable', 'off');

        uimenu(lemenu, 'Label', 'Change legend breakpoints',...
            'MenuSelectedFcn', @change_legend_breakpoints);
        legend_types = {'Legend by time', 'tim';...
            'Legend by depth', 'depth';...
            'Legend by magnitudes', 'mag';...
            'Mag by size, Depth by color', 'mad' %;...
            % 'Symbol color by faulting type', 'fau'...
            };

        for i = 1:size(legend_types, 1)
            m = uimenu(lemenu, 'Label', legend_types{i, 1},...
                'MenuSelectedFcn', @(s,v)cb_plotby(s, v, legend_types{i, 2}) );
            if i == 1
                m.Separator = 'on';
            end
        end
        clear legend_types

        uimenu(mapoptionmenu, 'Label', 'Change font size ...', 'Enable', 'off',...
            'MenuSelectedFcn', @change_map_fonts);

        uimenu(mapoptionmenu, 'Label', 'Change background colors',...
            'MenuSelectedFcn', @(~,~)setcol, 'Enable', 'off'); %

        uimenu(mapoptionmenu,...
            'Label', ['Mark large event with M > ' num2str(ZmapGlobal.Data.CatalogOpts.BigEvents.MinMag)],...
            'MenuSelectedFcn', @cb_plot_large_quakes);

        uimenu(mapoptionmenu,...
            'Separator', 'on',...
            'Label', 'Close all result tabs', 'MenuSelectedFcn', @clear_result_tabs);

        uimenu(mapoptionmenu, 'label', 'Redraw',...
            'Separator', 'on',...
            'MenuSelectedFcn', @obj.cb_redraw);

        uimenu(mapoptionmenu, 'label', 'Open printable figure', ...
            'MenuSelectedFcn', @(~,~)make_printable_figure_copy(obj.fig));

        function set_aspects_cb(src, ev)
            callbacks.toggle_aspectratio(src, ev, obj.get_all_map_axes(), "SetGlobal");
        end

        function clear_result_tabs(~, ~)
            delete(obj.maingroup.Children(obj.maingroup.Children ~= obj.maintab))
        end
        function manage_symbols_for_current_map(src, ev)
            %

            % look for plots, but only direct children of the selected tab.
            % this way we don't access axes such as the x-section plots
            ax = findobj(obj.maingroup.SelectedTab.Children, 'flat', 'Type', 'axes');
            SymbolManager.cb(src, ev, ax);
        end

        function manage_mainmap_symbols(src, ev)
            obj.make_map_active('notify');
            SymbolManager.cb(src, ev, axm);
        end

        function toggle_unselected_events(src,~)
            h_events = findobj(obj.map_axes, 'Tag', 'all events');
            if h_events.Visible == "on"
                h_events.Visible = 'off';
                src.Checked = 'off';
            else
                h_events.Visible = 'on';
                src.Checked = 'on';
            end

        end

        function set_colorby(~, ~, val)
            obj.colorField = val;
            obj.mainEventProps.ColorBy = val;
            obj.make_map_active('notify')

            % update menus
            for jj = 1:numel(um)
                myfn = obj.rawcatalog.FieldnamesForColorby{jj};
                um(jj).Checked = tf2onoff(strcmp(obj.colorField, myfn));
            end
            h = findobj(obj.fig, 'Type', 'colorbar', '-and', 'Parent', obj.fig);

            delete(h)
            obj.plotmainmap();
        end

        function cb_plotby(~,~, s)
            ZG = ZmapGlobal.Data;
            ZG.mainmap_plotby = s;
            watchon;
            watchoff;
        end

        function cb_plot_large_quakes(src,~)
            ZG = ZmapGlobal.Data;
            [~, ~, ZG.CatalogOpts.BigEvents.MinMag] = smart_inputdlg('Choose magnitude threshold',...
                struct('prompt', 'Mark events with M > ? ', 'value', ZG.CatalogOpts.BigEvents.MinMag));
            src.Label = "Mark large event with M > " + ZG.CatalogOpts.BigEvents.MinMag;
            obj.bigEvents = obj.catalog.subset(obj.catalog.Magnitude > ZG.CatalogOpts.BigEvents.MinMag);
            obj.CatalogManager.ChangeFilter('big events', @(c) c.Magnitude > ZmapGlobal.Data.CatalogOpts.BigEvents.MinMag);
            set(findobj(obj.fig, 'Tag', 'big events'), 'DisplayName', src.Label);
        end

        function toggle_grid(src, ~)
            src.Checked = toggleOnOff(src.Checked);
            arrayfun(@(x)grid(x, src.Checked) , obj.get_all_map_axes())
            drawnow nocallbacks
        end

    end

    function create_ztools_menu()
        h = findobj(obj.fig, 'Tag', 'mainmap_menu_ztools');
        if ~isempty(h) && exist('force', 'var') && force
            delete(h); h = [];
        end
        if ~isempty(h)
            return
        end
        submenu = uimenu('Label', 'ZTools', 'Tag', 'mainmap_menu_ztools');

        uimenu(submenu, 'Label', 'Plot Current Map into Projection',...
            'MenuSelectedFcn', @(~,~)create_projectedmap_from_mainmap(obj.fig));

        create_topo_map_menu(submenu);
        uimenu(submenu,'Label', 'Random data simulations has moved to CATALOG menu', 'Enable', 'off');
        % create_random_data_simulations_menu(submenu);

        % create_histogram_menu(submenu);
        create_explore_catalog_menu(submenu);

        uimenu(submenu, 'Label', 'Misfit calculation', 'MenuSelectedFcn', @(~,~)inmisfit(),...
            'Enable', 'off'); %FIXME: misfitcalclulation poorly documented, not sure what it is comparing.

    end
    function create_topo_map_menu(parent)
        submenu   =  uimenu(parent, 'Label', 'Plot topographic map');
        uimenu(submenu, 'Label', 'Open a Web Map Display', 'MenuSelectedFcn', @(~,~)webmap_of_catalog(obj.catalog, true));
        uimenu(submenu, 'Label', 'Plot Topography on main map', 'MenuSelectedFcn', @add_topography_to_main_map);
        uimenu(submenu, 'Label', 'Plot Swiss Topography on main map', 'MenuSelectedFcn', @(s,v)add_topography_to_main_map(s, v, 'CH') );

        % FIXME the following need to be found and fixed
        %{
        uimenu(submenu, 'Label', 'Open DEM GUI', 'MenuSelectedFcn', @(~,~)zmaptopo.prepinp());
        uimenu(submenu, 'Label', '3 arc sec resolution (USGS DEM)', 'MenuSelectedFcn', @(~,~)pltopo('lo3'));
        uimenu(submenu, 'Label', '30 arc sec resolution (GLOBE DEM)', 'MenuSelectedFcn', @(~,~)pltopo('lo1'));
        uimenu(submenu, 'Label', '30 arc sec resolution (GTOPO30)', 'MenuSelectedFcn', @(~,~)pltopo('lo30'));
        uimenu(submenu, 'Label', '2 deg resolution (ETOPO 2)', 'MenuSelectedFcn', @(~,~)pltopo('lo2'));
        uimenu(submenu, 'Label', '5 deg resolution (ETOPO 5, Terrain Base)', 'MenuSelectedFcn', @(~,~)pltopo('lo5'));
        uimenu(submenu, 'Label', 'Your topography (mydem, mx, my must be defined)', 'MenuSelectedFcn', @(~,~)pltopo('yourdem'));
        uimenu(submenu, 'Label', 'Help on plotting topography', 'MenuSelectedFcn', @(~,~)pltopo('genhelp'));
        %}
    end

    function add_topography_to_main_map(~, ~, code)
        htopo = findobj(obj.map_axes, '-regexp', 'Tag', 'topographic_map.*');
        delete(htopo);

        if exist('code', 'var')
            zmaptopo.add_topo(obj.map_axes, 'locale', code);
            % zmaptopo.add_topo(obj.map_axes, 'locale', code, 'ShadedOnly', true);
        end
        % now plot the world topography too.
        zmaptopo.add_topo(obj.map_axes, 'locale', 'world');
        % zmaptopo.add_topo(obj.map_axes, 'locale', 'world', 'ShadedOnly', true);
    end

    function create_random_data_simulations_menu(parent)
        submenu  =   uimenu(parent, 'Label', 'Create Synthetic/Permuted catalog', 'Separator', 'on');
        uimenu(submenu, 'label', 'Create permutated catalog (also new b-value)...', 'MenuSelectedFcn', @cb_create_permutated);
        uimenu(submenu, 'label', 'Create synthetic catalog...', 'MenuSelectedFcn', @cb_create_synhthetic_cat);

        % uimenu(submenu, 'Label', 'Evaluate significance of b- and a-values', 'MenuSelectedFcn', @(~,~)brand());
        %  uimenu(submenu, 'Label', 'Calculate a random b map and compare to observed data', 'MenuSelectedFcn', @(~,~)brand2());
        uimenu(submenu, 'Label', 'Info on synthetic catalogs', 'MenuSelectedFcn', @(~,~)web(['file:' ZmapGlobal.Data.hodi '/zmapwww/syntcat.htm']));

        function cb_create_permutated(~,~)
            % will replace existing primary catalog
            [rand_catalog, ok] = syn_invoke_random_dialog(obj.catalog);

            if ok && ~isempty(rand_catalog)
                ZmapMainWindow(figure, rand_catalog);
            end
        end

        function cb_create_synhthetic_cat(~,~)
            % will replace existing primary catalog
            [syn_cat, ok] = syn_invoke_dialog(obj.catalog);
            if ok
                ZmapMainWindow(figure, syn_cat);
            end
        end

    end

    function create_explore_catalog_menu(parent)
        uimenu(parent, 'Separator', 'on',...
            'Label', 'Explore Catalog', 'MenuSelectedFcn', @explore_catalog);

        function explore_catalog(~,~)
            t = findOrCreateTab(obj.fig, obj.maingroup, obj.maingroup, 'Exploration', 'deleteable');
            ax = findobj(t.Children, 'Type', 'axes');
            if isempty(ax)
                ax = axes(t);
            end
            cep = CatalogExplorationPlot(ax, @get_catalog);
            cep.scatter('explore catalog')
            legend(ax, 'show', 'Location', 'northeast');
            title(ax, sprintf('Exploring : catalog: %s , with %d events',...
                strrep(obj.catalog.Name, '_', '\_'), obj.catalog.Count));
            bringToForeground(ax);
        end

        function c = get_catalog()
            c = obj.catalog;
        end

    end

    function create_decluster_menu(parent)
        submenu = parent;% uimenu(parent, 'Label', 'Decluster the catalog');

        uimenu(submenu, 'Label', 'Decluster (Reasenberg)', 'MenuSelectedFcn', @cb_reasen,...
            'Separator', 'on');
        uimenu(submenu, 'Label', 'Decluster (Gardner & Knopoff)',...
            ...'Enable', 'off',...
            'MenuSelectedFcn', @cb_declus_inp);
        % uimenu(submenu, 'Label', 'Decluster (Zaliapin)', 'MenuSelectedFcn', @cb_zaliapin);
    end
    function cb_declus_inp(~,~)
        [out, eMethod] = declus_inp(obj.catalog);
        if isempty(eMethod)
            return
        end
        % Return Decluster results to the main window
        disp('Summary of Gardiner-Knopoff clusters using window:' + string(eMethod))
        disp('Cluster #     Number of Events (inc. mainshock)')
        summary(categorical(out.allClusterIdx(out.allClusterIdx ~= 0)))

        assignin('base', 'gk_decluster_output', out);
        disp(out.description)

        choice = questdlg({'Replace Main Catalog with:','',...
            sprintf('\\bfDeclustered\\rm : background + mainshocks [%d evts]', out.declusteredCatalog.Count),...
            sprintf('\\bfMainshocks\\rm: only mainshocks [%d evts]', sum(out.mainshockClusterIdx ~= 0)),...
            sprintf('\\bfClusters\\rm: only fore- and aftershocks) [%d evts]',out.aftershockCatalog.Count),...
            '','\itClose dialog to keep original catalog\rm',...
            '','Decluster results have been written to workspace as \itgk\_decluster\_output\rm'},...
            'Declust: close to keep orig cat',... % dialog title
            'Declustered','Mainshocks', 'Clusters',... % up to 3 buttons
            struct('Default','Declustered','Interpreter','tex'));  % Default choice
        switch choice
            case 'Declustered'
                obj.rawcatalog = out.declusteredCatalog;
                %replaceMainCatalog(out.declusteredCatalog)
            case 'Mainshocks'
                obj.rawcatalog = obj.catalog.subset(out.mainshockClusterIdx ~=0);
                obj.rawcatalog.Name = "GK Declust ("+string(eMethod)+") " + obj.rawcatalog.Name
                %replaceMainCatalog(obj.catalog.subset(out.mainshockClusterIdx ~=0))
            case 'Clusters'
                obj.rawcatalog = out.aftershockCatalog;
                %replaceMainCatalog(out.aftershockCatalog)
        end
        figure(obj.fig)
        obj.replot_all()
    end

    function cb_reasen(~,~)
        rdc = ReasenbergDeclusterClass(obj.catalog, "CalcFinishedFcn", @update_window_with_declust);

        function update_window_with_declust()
            if ~isempty(rdc.declusteredCatalog)
                msg.dbdisp('replacing the catalog')
                obj.rawcatalog = rdc.declusteredCatalog;
                obj.CatalogManager.RawCatalog = obj.rawcatalog;
                obj.replot_all()
            else
                errordlg('Empty declustered catalog, Main window will not be updated')
            end
        end
    end

    function cb_zaliapin(~,~)
        rdc = ZaliapinDeclusterClass(obj.catalog, "CalcFinishedFcn", @update_window_with_declust);

        function update_window_with_declust()
            if ~isempty(rdc.declusteredCatalog)
                msg.dbdisp('replacing the catalog')
                obj.rawcatalog = rdc.declusteredCatalog;
                obj.CatalogManager.RawCatalog = obj.rawcatalog;
                obj.replot_all()
            else
                errordlg('Empty declustered catalog, Main window will not be updated')
            end
        end
    end
end


function [rootexpdirs,jabfiles,T0,T1] = ethogram_plot(rootexpdirs,jabfiles,nframesplot,varargin)
% ethogram_plot(rootexpdirs,jabfiles,nframesplot,varargin)
% Plots experiment ethograms.
%
% rootexpdirs: cellstr, root directories for experiments to include in
% plot. Eg:
% rootexpdirs = {
%   '/groups/branson/home/kabram/matlab/adamTracking/videos/20130628/Laser On'
%   '/groups/branson/home/kabram/matlab/adamTracking/videos/20130628/Laser Off'
%   };
% jabfiles: cellstr, jab files for classifiers of interest. Eg:
% jabfiles = {
%   '/groups/branson/home/kabram/matlab/adamTracking/data/reach.jab'
%   '/groups/branson/home/kabram/matlab/adamTracking/data/grab.jab'
%   };
% nframesplot: Show this many frames in ethogram.
% varargin: optional P-V pairs:
%  - 'xlsexport', scalar logical.
%  - 'xlsfile', string specifying exported filename for xlsexport.
%  - 'automarks', scalar logical. If true, include automatic predictions
%     for "first lift", etc. In this case, the specified jabfiles will need
%     to include Lift/Handopen/Grab/Sup/Atmouth/Chew.
%  - 'exptags', Either 'readfromjabs' OR (for explicit specification) cell
%     of row cellstrs. If 'readfromjabs', ethogram_plot will look in the
%     jabfiles to find experiment tags. In the case of explicit
%     specification, exptags must have same size as rootexpdirs; exptags{i}
%     is a cellstr specifying tags for all experiments under rootexpdirs{i}.
%
% Example: ethogram_plot(expdirs,jabfiles,nframesplot,'xlsexport',true)
%
% ethogram_plot will run classifyMovie on experiments that do not have the
% appropriate scorefiles, or whose scorefiles are out of date with respect
% to their classifiers.
%
% Double clicking: If you double click on any patch, the corresponding
% experiment and the behavior detector will be opened up in JAABA.
% Shift clicking: Shift-clicking zooms in on an experiment.

[xlsexport,xlsfile,doforce,automarks,smartorder,scoresnotjabs,dontreclassify,exptags,ethcoreargexport] =...
  myparse(varargin,...
  'xlsexport',false,...
  'xlsfile',[],...
  'doforce',false,...
  'automarks',false,...
  'smartorder',true,...
  'scoresnotjabs',false,...
  'dontreclassify',false,...
  'exptags',[],...
  'ethcoreargexport',false);

if nargin < 1,
  rootexpdirs = {};
end
if nargin < 2,
  jabfiles = {};
end
if nargin < 3
  nframesplot = [];
elseif ~isempty(nframesplot)
  assert(isnumeric(nframesplot));
  assert(isscalar(nframesplot) || numel(nframesplot)==2,'Invalid nframesplot');
end

if isempty(rootexpdirs),
  rootexpdirs = uipickfiles('Prompt','Select root directories containing experiments','DirsOnly',true);
  if ~iscell(rootexpdirs) || isempty(rootexpdirs),
    return;
  end
end

% ensure jabfiles
if isempty(jabfiles)
  [tfsuccess,jabfiles] = ExpPP.uiGetJabFiles;
  if ~tfsuccess
    return;
  end
end

% determine exptag status
if ischar(exptags) && strcmp(exptags,'readfromjabs')
  tfExpTagsJab = true;
elseif ~isempty(exptags)
  % It would be odd for a user to initially specified exptags, but not
  % initially specify rootexpdirs. Nonetheless, if this occurs and the user
  % select rootexpdirs (when prompted) that correspond to their exptags,
  % then everything will work and more power to them.
  ExperimentTags.verifyTags(exptags,numel(rootexpdirs));
  tfExpTagsJab = false;
else
  exptags = ExperimentTags.expTags(rootexpdirs);
  tfExpTagsJab = false;
end

% compile expdirs, expdirtags
expdirs = {};
expdirtags = {};
for ndx = 1:numel(rootexpdirs)
  dd = findAllSubDirs(rootexpdirs{ndx});
  nn = numel(dd);
  expdirs(end+1:end+nn) = dd;
  if ~tfExpTagsJab
    expdirtags(end+1:end+nn) = exptags(ndx);
  end
end
nexps = numel(expdirs);
if nexps == 0,
  fprintf('No experiments found.\n');
  T0 = nan;
  T1 = nan;
  return;
end

% compile expdirtags, read-from-jabfile case
if tfExpTagsJab
  % compile exp->tag map
  exp2tagmap = containers.Map();
  for iJab = 1:numel(jabfiles)
    Q = loadAnonymous(jabfiles{iJab});
    jabexpdirs = Q.expDirNames;
    jabexptags = Q.expDirTags;
    if isempty(jabexptags)
      % legacy Jab, no expDirTags
      jabexptags = ExperimentTags.expTags(jabexpdirs);
    else
      jabexptags = ExperimentTags.cleanLegacyNotTags(jabexptags);
    end
    for iExp = 1:numel(jabexpdirs)
      lowerexpname = lower(jabexpdirs{iExp});
      if exp2tagmap.isKey(lowerexpname)
        warning('ethogram_plot:expTags',...
          'Experiment ''%s'' is present in more than one Jabfile. Using tags from one Jab only.',...
          lowerexpname);
      end
      exp2tagmap(lowerexpname) = jabexptags{iExp};
    end
  end

  % look up tags for each element of expdir
  expdirtags = ExperimentTags.expTags(expdirs);
  for iExp = 1:nexps
    lowerexpname = lower(expdirs{iExp});
    if exp2tagmap.isKey(lowerexpname);
      expdirtags{iExp} = exp2tagmap(lowerexpname);
    else
      warning('ethogram_plot:expTags',...
          'Experiment ''%s'' not present in Jabfile(s). No tags will be associated.',...
          lowerexpname);
    end
  end
end

assert(numel(expdirtags)==nexps);

% Reclassify expdirs as appropriate
if ~dontreclassify && ~scoresnotjabs
  for iExp = 1:nexps
    for iJab = 1:numel(jabfiles)
      classifyMovie(expdirs{iExp},jabfiles{iJab},'doforce',doforce,'verbose',1);
    end
  end
end

[scorefns,behaviornames,jabsforscorefiles,trxfilename] = compileJabInfo(jabfiles,scoresnotjabs);
assert(isequal(numel(scorefns),numel(behaviornames),numel(jabsforscorefiles)));

if smartorder
  % order the scorefiles/behaviors/jabnames
  [behaviornames,idx] = orderbypats(behaviornames,ExpPP.BASICBEHAVIORS);
  scorefns = scorefns(idx);
  jabsforscorefiles = jabsforscorefiles(idx);
end

% some experiments may be missing scorefiles at this point
tfGoodExp = cellfun(@(x)ExpPP.isExpDir(x,scorefns),expdirs);
if ~all(tfGoodExp)
  tmpexp = expdirs(~tfGoodExp);
  tmpstr = sprintf('%s\n',tmpexp{:});
  warningNoTrace('ethogram_plot:expsDiscarded','Excluding %d experiments with missing scorefiles: %s',...
    nnz(~tfGoodExp),tmpstr);
end
expdirs = expdirs(tfGoodExp);
expdirtags = expdirtags(tfGoodExp);

[labels,T0,T1] = ...
  formLabelMatrix(expdirs,trxfilename,scorefns,behaviornames,nframesplot);

hfig = figure;

[tfbehspresent,missingbehs] = ExpPP.doNamesSpanAllBasicBehaviors(behaviornames);
reallydoautomarks = automarks && tfbehspresent;
if automarks && ~tfbehspresent
  % user said automarks, but basic behaviors missing
  warning('ethogram_plot:missingBehaviors',...
      'The following behaviors are missing: %s.',...
      civilizedStringFromCellArrayOfStrings(missingbehs));
end
if reallydoautomarks
  dauto = ExpPP.loadexps(expdirs,jabfiles,'includelabels',true);
  assert(numel(dauto)==numel(expdirs)); % ExpPP.loadexps does not guarantee this, but here all expdirs should be valid
  dauto = ExpPP.ensureCustomGroupInit(dauto);
  allUniqueTags = ExperimentTags.allUniqueTags(expdirtags);
  allUniqueTagsValid = matlab.lang.makeValidName(allUniqueTags);
  if numel(unique(allUniqueTagsValid))~=numel(allUniqueTagsValid)
    error('ethogram_plot:tag','Repeated tags.'); % very rare
  end
  Ntag = numel(allUniqueTags);
  for iTag = 1:Ntag
    tagRaw = allUniqueTags{iTag};
    cgrpname = allUniqueTagsValid{iTag};
    tfTag = ExperimentTags.findTag(expdirtags,tagRaw);
    dauto = ExpPP.createCustomGroup(dauto,tfTag,cgrpname);
  end
  ethplotcoreargs = {hfig,labels,T0,T1,expdirs,behaviornames,jabsforscorefiles,'doautomarks',true,'automarkdata',dauto,'scoresnotjabs',scoresnotjabs};
else
  ethplotcoreargs = {hfig,labels,T0,T1,expdirs,behaviornames,jabsforscorefiles,'scoresnotjabs',scoresnotjabs};
end
if ethcoreargexport
  fname = sprintf('ethcoreargs.%s.mat',datestr(now,'yyyymmddTHHMMSS'));
  save(fname,'ethplotcoreargs');
end
[boutmat,line_names] = ethogram_plot_core(ethplotcoreargs{:});

if xlsexport
  if isempty(xlsfile)
    [xlsfile,xlspath] = uiputfile('bouts.txt','Export to file');
    if isequal(xlsfile,0)
      xlsfile = [];
    else
      xlsfile = fullfile(xlspath,xlsfile);
    end
  elseif exist(xlsfile,'file')
    warning('ethogram_plot:fileExists','Overwriting file ''%s''.',xlsfile);
  end

  if ~isempty(xlsfile)
    Ethogram.boutExport(xlsfile,boutmat,line_names,behaviornames);
    fprintf(1,'Exported to tab-delimited file ''%s''.\n',xlsfile);
  end
end

function [scorefns,behaviornames,jabs,trxfilename] = compileJabInfo(jabfiles,scoresnotjabs)
% Compile scorefiles/behaviors from jabfiles.
%
% jabfiles: cellstr of jabfilenames, or cellstr of scorefilenames (archaic)
% if scoresnotjabs is true
%
% scorefns: cellstr of (short) score filenames
% behaviornames: cellstr of behaviornames, one per scorefn
% jabs: cellstr of jabfiles, one per scorefn. eg, jabs{i} is the jabfile
% containing scorefns{i} and behaviornames{i}. jabs may contain repeated
% entries when multi-classifier jabs are present.
% trxfilename: char, unique trxfilename in jabs (all jabs are expected to
% be consistent)

if scoresnotjabs
  scorefns = jabfiles;
  behaviornames = cell(size(scorefns));
  for i = 1:numel(scorefns)
    tok = regexp(scorefns{i},'scores_(.+).mat','tokens');
    assert(~isempty(tok),'Unable to parse score filename.');
    behaviornames{i} = tok{1}{1};
  end
  jabs = scorefns; % just use scorefilenames here; no jabs avail
  trxfilename = 'trx.mat';
else
  % jabfiles specified.
  Njab = numel(jabfiles);
  scorefns = cell(0,1);
  behaviornames = cell(0,1);
  jabs = cell(0,1);
  trxfilename = cell(Njab,1);
  for iJab = 1:Njab
    Q = loadAnonymous(jabfiles{iJab});

    sfns = Q.file.scorefilename;
    if ischar(sfns)
      sfns = {sfns};
    end
    behs = Labels.verifyBehaviorNames(Q.behaviors.names);
    assert(isequal(numel(sfns),numel(behs)),...
      'Inconsistent scores/behaviors in JAB file: %s',jabfiles{iJab});
    Nbeh = numel(behs);
    scorefns = cat(1,scorefns,sfns(:));
    behaviornames = cat(1,behaviornames,behs(:));
    jabs = cat(1,jabs,repmat(jabfiles(iJab),Nbeh,1));

    trxfilename{iJab} = Q.file.trxfilename;
  end

  trxfilename = unique(trxfilename);
  assert(isscalar(trxfilename),'Jabs contain inconsistent trxfilenames.');
  trxfilename = trxfilename{1};
end

function [labels,T0,T1] = formLabelMatrix(expdirs,trxfilename,scorefns,behaviornames,nframesplot)

% behaviornames just used to check against behaviornames in scorefiles

nexps = numel(expdirs);
nbehaviors = numel(behaviornames);
assert(numel(scorefns)==nbehaviors);
labels = false(nexps,0,nbehaviors);
tStarts = nan(1,nexps);
tEnds = nan(1,nexps);

for i = 1:nexps
  trxfile = fullfile(expdirs{i},trxfilename);
  load(trxfile,'trx');
  tStarts(i) = min([trx.firstframe]);
  tEnds(i) = max([trx.endframe]);

  % load score data
  allScores = cell(nbehaviors,1);
  for j = 1:nbehaviors,
    scorefile = fullfile(expdirs{i},scorefns{j});
    allScores{j} = load(scorefile);
    if isfield(allScores{j},'behaviorName') && ...
       ~strcmp(allScores{j}.behaviorName,behaviornames{j})
        warning('ethogram_plot:behaviorMismatch',...
          'Behavior label mismatch in experiment %s. Scorefile: %s. Expected: %s.',...
          expdirs{i},allScores{j}.behaviorName,behaviornames{j});
    end
    allScores{j} = allScores{j}.allScores;
    if iscell(allScores{j})
      assert(isscalar(allScores{j}));
      allScores{j} = allScores{j}{1};
    end
  end

  for j = 1:nbehaviors
    t0 = tStarts(i);
    t1 = min(tEnds(i),numel(allScores{j}.postprocessed{1}));
    if t1 < tEnds(i),
      fprintf('Number of frames for behavior idx%d = %d < number of frames in trajectory file %s = %d\n',...
        j,t1,trxfile,tEnds(i));
      tEnds(i) = t1;
    end

    if t1 > size(labels,2),
      labels = cat(2,labels,nan(nexps,t1-size(labels,2),nbehaviors));
    end

    labels(i,t0:t1,j) = allScores{j}.postprocessed{1}(t0:t1);
  end
end

T0 = min(tStarts);
T1 = max(tEnds)-1;

if ~isempty(nframesplot)
  if numel(nframesplot) == 1,
    T1 = min(T1,T0+nframesplot-1);
  else
    assert(numel(nframesplot)==2);
    T0 = nframesplot(1);
    T1 = min(T1,nframesplot(2));
  end
end
%nframesplot = T1-T0+1;

labels = labels(:,T0:T1,:);

function [cellstr,idx] = orderbypats(cellstr,pats)
% sort cellstr by pats (patterns) as best as possible

Npat = numel(pats);
loc = nan(size(cellstr));
for i = 1:Npat
  tf = regexpmatch(cellstr,pats{i},'caseinsens',true);
  loc(tf) = i;
end
loc(isnan(loc)) = inf; % elements of cellstr that didn't match go at the end

% loc(i) gives the desired relative position of cellstr{i}

[~,idx] = sort(loc);
cellstr = cellstr(idx);


function [D, Nrows, Ncols] = addimagesize(D, HOMEIMAGES)
%
% [D, nrows, ncols] = addimagesize(D, HOMEIMAGES)

n = length(D);
Nrows = zeros(n,1);
Ncols = zeros(n,1);

for i = 1:n
    if mod(i,10)==1; disp(n-i); end
    disp(fullfile(D(i).annotation.folder, D(i).annotation.filename))

    if ~isfield(D(i).annotation,'imagesize')
        try
            info = imfinfo(strrep(strrep(fullfile(HOMEIMAGES, D(i).annotation.folder, D(i).annotation.filename), '\', '/'), ' ', '%20'));
            nrows = info.Height;
            ncols = info.Width;
        catch
            [ncols, nrows] = getaproximagesize(D(i).annotation);
        end

        D(i).annotation.imagesize.nrows = (nrows);
        D(i).annotation.imagesize.ncols = (ncols);

    else
        if ischar(D(i).annotation.imagesize.nrows)
            D(i).annotation.imagesize.nrows = str2num(D(i).annotation.imagesize.nrows);
            D(i).annotation.imagesize.ncols = str2num(D(i).annotation.imagesize.ncols);
        end
    end

    Nrows(i) = D(i).annotation.imagesize.nrows;
    Ncols(i) = D(i).annotation.imagesize.ncols;
end


classdef ViewGridTypeEnum < matlab.mixin.SetGet
    %ViewGridTypeEnum Summary of this class goes here
    %   Detailed explanation goes here

    enumeration
        Major('Major Grid Lines','on')
        Minor('Major and Minor Grid Lines','minor')
        Off('No Grid Lines','off')
    end

    properties
        name(1,:) char = '';
        gridStr(1,:) char = '';
    end

    methods
        function obj = ViewGridTypeEnum(name, gridStr)
            obj.name = name;
            obj.gridStr = gridStr;
        end
    end

    methods(Static)
        function listBoxStr = getListBoxStr()
            m = enumeration('ViewGridTypeEnum');
            [~,I] = sort({m.name});
            listBoxStr = {m(I).name};
        end

        function [ind, enum] = getIndForName(name)
            m = enumeration('ViewGridTypeEnum');
            [~,I] = sort({m.name});
            m = m(I);
            ind = find(ismember({m.name},name),1,'first');
            enum = m(ind);
        end

        function [enum, ind] = getEnumForListboxStr(nameStr)
            m = enumeration('ViewGridTypeEnum');
            ind = find(ismember({m.name},nameStr),1,'first');
            enum = m(ind);
        end
    end
end


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

cd(getCERRPath)
cd compiled
mcc -B sgl CERR.m
delete *.h
delete *.c


function OUTPUT = swap_spaces(STRING, varargin)
%replaces spaces with underscores and vice versa
%SWAP_SPACES replaces spaces with underscores or underscores with spaces
% for desciptions and file names. If there are underscores AND spaces
% present in STRING, either give an option or only underscores will be replaced.
%
%Syntax
%    swap_spaces(textstring [, option])
%
%    Valid options are:
%     * '_' replaces all underscores with spaces
%     * ' ' replaces all spaces with underscores
%
%Example
%    f = swap_spaces('text_text')
%    f =
%        'text text'
%
%    g = swap_spaces('text text')
%    g =
%        'text_text'
%
%    h = swap_spaces('text text_text', '_')
%    h =
%        'text text text'
%See also
%    regexprep, strrep

p	=	inputParser;
validOptions = {'_', ' '};
p.addRequired('STRING',			@(x) all(ischar(x), ~isempty(x)) );
p.addOptional('OPT',	'',		@(x) any(strcmp(x, validOptions)) );
p.parse(STRING, varargin{:});

if isempty(p.Results.OPT)
	if strfind(STRING, '_')
		OUTPUT	=	strrep(STRING, '_', ' ');
	else
		OUTPUT	=	strrep(STRING, ' ', '_');
	end
elseif strcmp(p.Results.OPT, '_')
	OUTPUT	=	strrep(STRING, '_', ' ');
else
	OUTPUT	=	strrep(STRING, ' ', '_');
end

% Copyright 2009-2011 Alexandra Heidsieck <aheidsieck@tum.de>,
%                     IMETUM, Technische Universitaet Muenchen


function v = setLayerOutputs(obj, layer, outputs)
%SETLAYEROUTPUTS  Set or change the outputs of a layer
%   Example: NET.SETLAYEROUTPUTS('layerName', {'output1', 'output2', ...})

v = [] ;
l = obj.getLayerIndex(layer) ;
for output = outputs
  v(end+1) = obj.addVar(char(output)) ;
end
obj.layers(l).outputs = outputs ;
obj.rebuild() ;


function [Prefix, Ext] = Remove1DExtension (fname)
%
%   [Prefix, Ext] = Remove1DExtension (fname)
%
%Purpose:
%   removes known .1D filename extensions.
%   Look at code more more details.
%
%Input Parameters:
%   fname: filename
%
%
%Output Parameters:
%   Prefix: filename without extension
%   Ext: recognized extension of 1D filename, if any
%        if Ext is empty then no know extensions were found
%
%
%Key Terms:
%
%More Info :
%   RemoveExtension
%
%
%
%     Author : Ziad Saad
%     Date : Tue Nov 9 09:59:58 EST 2004
%     SSCC/NIMH/ National Institutes of Health, Bethesda Maryland

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

%Debug Flag
DBG = 1;

Prefix = ''; Ext = '';

if (isempty(fname)) return; end
[Prefix, Ext] = RemoveExtension(fname,...
            '.1D|.1D.dset|.1D.col|.1D.NodeList|.1D.FaceSetList|.1D.cmap');

return;


%% FILESYNC Synchronize data using a USB key.
%
%   PLEASE MAKE SURE YOU UNDERSTAND THE CODE COMPLETELY BECAUSE IT DOES OVERWRITE DATA!
%
%   This file is designed to synchronize data between two computers using a
%   USB key. It only updates files that have been modified since the last
%   update. Files with the more recent modified date overwrite existing
%   files.
%
%   The first time this function is executed, it initializes the USB key.
%   However, since it doesn't know when the last backup was performed, no
%   data is copied to the USB key. Each time after the initialization,
%   this function will update the USB key to keep data between the two
%   computer synchronized.
%
%   Variables that need to be set are:
%      <basedir>    - (1 x 2) CELLL ARRAY of STRINGs of full paths to the base directory
%                     on each computer to be synchronized.
%      <staticname> - (1 x 2) CELL ARRAY of STRINGs that include the computer name
%                     of each computer retruned by "getenv('computername')"
%      <USBdir>     - STRING of directory path on the USB key used for synchronization
%      <USBtmp>     - STRING of directory path on the USB key used for temporary
%                     purposes during synchronization
%
% This function was tested on Windows XP, SP2, running Matlab 2006b (ver 7.3).
% There is a GETENV function call that expects 'computername' to exist as an
% environmet variable. This will need to be modified for other operating
% systems.
%
% Please send comments/suggestions to:
%
% Jesse Norris
% jnorris@wfubmc.edu
% http://jesse.musculoskeletaldynamics.com
%
% Last modified: 15 April 2007
%

%% filesync
function filesync

   % Start stopwatch, clear command window and store current directory
   %
   tic; clc; currdir = cd;

%% ~~~~~ USER SPECIFIC VARIABLES ~~~~~

   % Set the static computer names and base directories on those computers
   % that will be synchronized. These static computer names should match those
   % returned by the GETENV function with 'computername' argument.
   %
   staticname{1} = '3390-99RGXAZ';  % Computer 1 <computername>
   basedir{1}    = 'C:\Documents and Settings\jnorris\My Documents'; % Base directory on Computer 1
   staticname{2} = '1612-8QDLG11';   % Computer 2 <computername>
   basedir{2}    = 'C:\Documents and Settings\jnorris\My Documents'; % Base directory on Computer 2

   % Set the directories on the USB key used for synchronization.
   %
   USBdir   = 'E:\File Transfer';                  % Directories for storing updated files
   USBtmp   = 'E:\Temp';                           % Temporary directory

%% ~~~~~ END USER SPECIFIC VARIABLES ~~~~~

   % Retrieve current computer name. NOTE: The <getenv> function assumes
   % 'computername' is an enviroment variable.
   %
   computername  = getenv('computername');
   if isempty(computername); error('''computername'' does not exist in this computer''s environment variables.'); end;

   % Set files for keeping track of synchronization times.
   %
   USBfile  = ['\' '0 Files updated.txt'];         % File where updated files are stored.
   USBts{1} = ['\1 ' staticname{1} ' to USB.txt']; % Files on the USB key that are used solely for time stamps.
   USBts{2} = ['\1 ' staticname{2} ' to USB.txt'];

   % Check if directories & file exist. If not, attempt to create. If create fails, return error.
   %
   chkmkdir(USBdir);
   chkmkfile([USBdir USBfile]);
   chkmkfile([USBdir USBts{1}]);
   chkmkfile([USBdir USBts{2}]);

   % Ensure that user is ready to begin synchronization.
   %
   button = questdlg('Begin synchronization?', 'USB Synchronization', 'Yes', 'No', 'No');

   if strcmp(button,'Yes');

       % Determine which computer was most recently synchronized to the USB key
       %
       comp1date = filemodified([USBdir USBts{1}]);
       comp2date = filemodified([USBdir USBts{2}]);
       if (comp1date > comp2date); USBupdatedfrom = staticname{1}; else USBupdatedfrom = staticname{2}; end;

       % Collapse <basedir> and <USBts> to just STRING variables based on the current computer.
       %
       if strcmp(computername,staticname{1});
           basedir = basedir{1};
           USBts   = USBts{1};
       elseif strcmp(computername,staticname{2});
           basedir = basedir{2};
           USBts   = USBts{2};
       else
           error('filesync:unknowncomp','<computername> ''%s'' does not match either name in the variable <staticname>',computername);
       end;

       % If the current computer was the most recent to be synchronized with the USB key,
       % it is only necessary to update the USB key with any new data.
       %
       if strcmp(computername,USBupdatedfrom);
           updateUSB(basedir,USBdir,USBfile,USBts); % Update the USB key

       % Otherwise, it is necessary to
       % (1) Update any changes that have occured on the current computer to <USBtmp> on the USB key.
       % (2) Then update the current computer with USB data.
       % (3) Lastly, copy from <USBtmp> to <USBdir> for updates back to the other computer.
       %
       else
           chkmkdir(USBtmp);                          % Check if <USBtmp> exists. If not, attempt to create. If create fails, return error.
           copyfile([USBdir USBts],[USBtmp USBfile]); % Copy the time stamp file to the update file in the temporary directory
           updateUSB(basedir,USBtmp,USBfile,USBts);   % Update <USBtmp>  with data on computer
           updatecomputer(basedir,USBdir,USBfile);    % Update computer with data in <USBdir> on USB key,
           movefile([USBtmp '\*'],USBdir);            % Move all files from the <USBtmp> to <USBdir>, and
           rmdir(USBtmp);                             % Remove <USBtmp> from the USB key
       end;

       % Return to original directory
       %
       try
           cd(currdir);
       catch
           cd(basedir);
           warning('filesync:cderror','Unable to change directory to: %s',currdir);
       end

       toc; % Elapsed time

       fprintf(' ~~~ \n\n');
   else
       fprintf('Synchronization Cancelled.\n\n');
   end;
end

%% chkmkdir
function message = chkmkdir(dirname)
   % Check if <dirname> is a directory. If not, attempt to create.
   % If create fails return error.
   %
   if ~exist(dirname,'dir')
       try
           mkdir(dirname); % Create directory
           message = 'Directory created.';
       catch
           error('Could not create path: ''%s''.\n~Check initialization value of variable: <%s>.',dirname,inputname(1));
       end;
   else
       message = 'Directory exists.';
   end;
end

%% chkmkfile
function message = chkmkfile(fname)
   % Check if <fname> is a file. If not, attempt to create.
   % If create fails return error.
   %
   if ~exist(fname,'file')
       try
           fid = fopen(fname,'w'); fclose(fid); % Create file
           message = 'File created.';
       catch
           error('Could not create file: ''%s''.\n~Check initialization value of variable: <%s>.',fname,inputname(1));
       end;
   else
       message = 'File exists.';
   end;
end

%% removeemptyfolders
function removeemptyfolders(rmdirname)
   if isdir(rmdirname);
       cd(rmdirname);          % CD to directory
       while length(dir) == 2; % Recurrisvely remove until an unempty folder is encountered
           rmdirname = cd;     % Current directory
           cd ..;              % CD to parent directory
           rmdir(rmdirname);   % Remove the subdirectory
       end;
   end;
end

%% filemodified
function modified = filemodified(fullpath)
   if exist(fullpath,'file')
       fileinfo = dir(fullpath);   % Determine last modification time
       modified = datenum(fileinfo.date,'dd-mmm-yyyy HH:MM:SS');
   else
       modified = [];
   end;
end

%% updatecomputer
function updatecomputer(basedir,USBdir,USBfile)
   fprintf('\n... Updating the computer with files on USB key ...\n\n');

   USBfile = [USBdir USBfile];                 % Update to full path
   fid     = fopen(USBfile,'r');                 % Open file that includes full directory paths and file names
   files   = textscan(fid,'%s','delimiter',';'); % Load all the files to update
   files   = files{1};
   fclose(fid);                                % Close the file

   % Step through each file
   % -> check that file exists on USB key
   % -> check that the date on the USB key is more recent than that on the computer
   % -> copy file from USB key to computer
   for fileind = 1:length(files)
       [subdir name ext] = fileparts(files{fileind});
       fprintf('%s...',[subdir '\' name ext]); % Update workspace

       if exist([USBdir subdir '\' name ext],'file')
           USBdate = filemodified([USBdir  subdir '\' name ext]);
           Cdate   = filemodified([basedir subdir '\' name ext]);
           if ~isempty(Cdate)
               if  USBdate > Cdate
                   movefile([USBdir subdir '\' name ext],[basedir subdir '\' name ext],'f');
                   fprintf('%s\n','Updated.');
               else
                   switch questdlg(['Confirm Overwrite of ' name ext],'Overwrite','Yes','No - Delete from USB','No - Leave on USB','No - Leave on USB')
                       case 'Yes'
                           movefile([USBdir subdir '\' name ext],[basedir subdir '\' name ext],'f');
                           fprintf('%s\n','Updated.');
                       case 'No - Delete from USB'
                           delete([USBdir subdir '\' name ext]);
                           fprintf('%s\n','Not updated - deleted from USB.');
                       otherwise
                           fprintf('%s\n','Not updated - left on USB.');
                   end;
               end;
           else
               if ~isdir([basedir subdir]); mkdir([basedir subdir]); end; % Create new directory
               movefile([USBdir subdir '\' name ext],[basedir subdir '\' name ext],'f');
               fprintf('%s\n','Updated.');
           end;
       else
           fprintf('%s\n','File not found on USB key.');
       end;
       removeemptyfolders([USBdir subdir]); % Recurrisvely remove until an unempty folder is encountered
   end;
end

%% updateUSB
function updateUSB(basedir,USBdir,USBfile,USBts)
   fprintf('\n... Updating the USB key with files from the computer ...\n\n');

   USBfile = [USBdir USBfile];      % Update to full path
   USBts   = [USBdir USBts];        % Update to full path
   USBdate = filemodified(USBfile); % Check when it was modified
   fid     = fopen(USBfile,'a');    % Open file to append the full path strings to

   % Load all the paths to scan. This expression is based on a line in
   % Matlab function PARSEDIRS.
   %
   paths = regexp(genpath(basedir), sprintf('[^\\s%s][^%s]*', pathsep, pathsep), 'match')';

   % Step through all paths
   % -> examine if files have been modified since <USBdate>
   % -> copy updated files to the USB key
   % -> append <fid> with directory where file was originally from
   %
   for pathind = 1:length(paths)
       cd(paths{pathind}); % Change to directory
       files = dir;        % Load file information

       % Step through all files
       %
       for fileind = 3:length(files)
           % NOTE: THIS EXPRESSIONS STOPS THIS FUNCTION FROM COPYING OUTLOOK MAILBOX FILES (*.pst)
           % The one on my computer is too large to be copied with just a USB key.
           %
           if ~files(fileind).isdir && (datenum(files(fileind).date,'dd-mmm-yyyy HH:MM:SS') > USBdate) && ~strcmpi(files(fileind).name(end-2:end),'pst')
               if (files(fileind).bytes > 10e6)
                   okay2copy = questdlg(['File ' files(fileind).name ' is ' sprintf('%.0f',files(fileind).bytes/1e6) ' Mb. Copy file to the USB key?'],'Large file.','Yes','No','No');
                   okay2copy = strcmp(okay2copy,'Yes');
               else
                   okay2copy = true;
               end;
               if okay2copy
                   try
                       if ~isdir([USBdir paths{pathind}(47:end)]); mkdir([USBdir paths{pathind}(47:end)]); end; % Create directory on USB key
                       copyfile(files(fileind).name,[USBdir paths{pathind}(47:end) '\' files(fileind).name]);   % Copy file to USB key
                       fprintf(fid,'%s\r\n',[paths{pathind}(47:end) '\' files(fileind).name '; ']);             % Update the txt file
                       fprintf(      '%s\n',[paths{pathind}(47:end) '\' files(fileind).name]);                  % Update workspace
                   catch
                       warning('updateUSB:copyfile','Error caught in function <updateUSB>');
                       handleerror(lasterror);
                   end
               else
                   fidnoupdate = fopen([USBdir '\0 Files NOT updated'],'a');
                   fprintf(fidnoupdate,'%s\r\n',[paths{pathind}(47:end) '\' files(fileind).name '; ']);     % Update the txt file
                   fclose(fidnoupdate);
               end;
           end;
       end;
   end;
   fclose(fid); % Close the file

   fid = fopen(USBts,'w'); fclose(fid); % Update successful, replace appropriate time stamp file

   cd(basedir); % Return to base directory
end

%% genpath
% p = genpath(d)
% OUTPUT arguments
%      <p>: a path string that includes all subdirectories of <d>
%
% INPUT arguments
%      <d>: returns a path string starting in D, plus, recursively, all
%           the subdirectories of D, including empty subdirectories.
%
% This function is based on Matlab's genpath function. It was written because
% the genpath function skips method directories (i.e. those beginning with @),
% and 'private' directories.
%
function p = genpath(d)
  p = '';                % Initialize path string
  files = dir(d);        % Generate path based on given root directory
  if isempty(files)
      return;
  end;
  p     = [p d pathsep]; % Add d to the path even if it is empty.
  isdir = [files.isdir]; % Create logical vector for subdirectory entries in d
  dirs  = files(isdir);  % Select only directory entries from the current list

  for i = 1:length(dirs)
      dirname = dirs(i).name;
      if ~strcmp(dirname,'.') && ~strcmp(dirname,'..')
          p = [p genpath(fullfile(d,dirname))]; % recursive calling of this function.
      end;
  end;
end

%% handleerror
% handleerror(errorin)
% INPUT arguments
%      <errorin>: a structure containing at least the following two fields:
%
%        message    : the text of the error message
%        identifier : the message identifier of the error message
%
%      Additionally, <errorin> may contain field <stack>.
%
function handleerror(errorin)
  try
      fprintf('Message:    %s\n',errorin.message);
      fprintf('Identifier: %s\n',errorin.identifier);
      fprintf('Stack:\n');
      if(isfield(errorin,'stack'));
          while(length(errorin.stack) >=1)
              %fprintf('      %s\n',errorin.stack(1).file);
              fprintf('       <%s> line: %d\n',errorin.stack(1).name,...
                                               errorin.stack(1).line);
              errorin.stack = errorin.stack(2:end);
          end;
      end;
  catch
      warning('handleerror:errorcaught','Error in function <handleerror>');
  end
end


function dockfig(fig)
% Copyright 2008 The MathWorks, Inc.
% Programmatically dock one or all open figures.
%
% SYNTAX:
% dockfig
%    Docks the current figure.
%
% dockfig(fig)
%    Docks figure with handle FIG, and brings it to the front.
%
% dockfig('all')
%    Docks all open figures
%
% Written by Brett Shoelson, PhD
% brett.shoelson@mathworks.com
% 01/01/08

% Revision 1
% 01/03/08: Providing default behavior when no input argument is provided
% (operates on current figure).

if nargin == 0
    fig = gcf;
end
if ischar(fig) && strcmpi(fig,'all')
    fig = findall(0,'type','figure');
end
for ii = 1:numel(fig)
    set(fig(ii),'windowstyle','docked');
end
if numel(fig) == 1
    figure(fig);
end


function c = medalConstants(query);
%  c = medalConstants(query);%--------------------------------------------------------------------------
% Helper function for MEDAL.
%--------------------------------------------------------------------------
% DES

rootDir = which('medalConstants.m');
tmp = fileparts(rootDir);
rootDir = fileparts(tmp);

cTmp = load(fullfile(rootDir,'.config.mat'),query);
f = fields(cTmp);
c = cTmp.(f{:});


%% vecPair2Rot
% Below is a demonstration of the features of the |vecPair2Rot| function

%%
clear; close all; clc;

%% Syntax
% |[R]=vecPair2Rot(varargin);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


classdef GridFlex < uix.GridFlex
    %uiextras.GridFlex  Container with contents arranged in a resizable grid
    %
    %   obj = uiextras.GridFlex() creates a new new grid layout with
    %   draggable dividers between elements. The number of rows and columns
    %   to use is determined from the number of elements in the RowSizes
    %   and ColumnSizes properties respectively. Child elements are
    %   arranged down column one first, then column two etc. If there are
    %   insufficient columns then a new one is added. The output is a new
    %   layout object that can be used as the parent for other
    %   user-interface components. The output is a new layout object that
    %   can be used as the parent for other user-interface components.
    %
    %   obj = uiextras.GridFlex(param,value,...) also sets one or more
    %   parameter values.
    %
    %   See the documentation for more detail and the list of properties.
    %
    %   Examples:
    %   >> f = figure();
    %   >> g = uiextras.GridFlex( 'Parent', f, 'Spacing', 5 );
    %   >> uicontrol( 'Parent', g, 'Background', 'r' )
    %   >> uicontrol( 'Parent', g, 'Background', 'b' )
    %   >> uicontrol( 'Parent', g, 'Background', 'g' )
    %   >> uiextras.Empty( 'Parent', g )
    %   >> uicontrol( 'Parent', g, 'Background', 'c' )
    %   >> uicontrol( 'Parent', g, 'Background', 'y' )
    %   >> set( g, 'ColumnSizes', [-1 100 -2], 'RowSizes', [-1 -2] );
    %
    %   See also: uiextras.Grid
    %             uiextras.HBoxFlex
    %             uiextras.VBoxFlex
    %             uiextras.Empty

    %  Copyright 2009-2014 The MathWorks, Inc.
    %  $Revision: 1062 $ $Date: 2014-10-30 13:30:17 +0000 (Thu, 30 Oct 2014) $

    properties( Hidden, Access = public, Dependent )
        Enable % deprecated
        RowSizes % heights of contents, in pixels and/or weights
        MinimumRowSizes % minimum heights of contents, in pixels
        ColumnSizes % widths of contents, in pixels and/or weights
        MinimumColumnSizes % minimum widths of contents, in pixels
        ShowMarkings
    end

    methods

        function obj = GridFlex( varargin )

            % Call uix constructor
            obj@uix.GridFlex( varargin{:} )

            % Auto-parent
            if ~ismember( 'Parent', varargin(1:2:end) )
                obj.Parent = gcf();
            end

        end % constructor

    end % structors

    methods

        function value = get.Enable( ~ )

            % Warn
            % warning( 'uiextras:Deprecated', ...
            %     'Property ''Enable'' will be removed in a future release.' )

            % Return
            value = 'on';

        end % get.Enable

        function set.Enable( ~, value )

            % Check
            assert( ischar( value ) && any( strcmp( value, {'on','off'} ) ), ...
                'uiextras:InvalidPropertyValue', ...
                'Property ''Enable'' must be ''on'' or ''off''.' )

            % Warn
            % warning( 'uiextras:Deprecated', ...
            %     'Property ''Enable'' will be removed in a future release.' )

        end % set.Enable

        function value = get.RowSizes( obj )

            % Get
            value = obj.Heights;

        end % get.RowSizes

        function set.RowSizes( obj, value )

            % Set
            obj.Heights = value;

        end % set.RowSizes

        function value = get.MinimumRowSizes( obj )

            % Get
            value = obj.MinimumHeights;

        end % get.MinimumRowSizes

        function set.MinimumRowSizes( obj, value )

            % Set
            obj.MinimumHeights = value;

        end % set.MinimumRowSizes

        function value = get.ColumnSizes( obj )

            % Get
            value = obj.Widths;

        end % get.ColumnSizes

        function set.ColumnSizes( obj, value )

            % Get
            obj.Widths = value;

        end % set.ColumnSizes

        function value = get.MinimumColumnSizes( obj )

            % Get
            value = obj.MinimumWidths;

        end % get.MinimumColumnSizes

        function set.MinimumColumnSizes( obj, value )

            % Get
            obj.MinimumWidths = value;

        end % set.MinimumColumnSizes

        function value = get.ShowMarkings( obj )

            % Get
            value = obj.DividerMarkings;

        end % get.ShowMarkings

        function set.ShowMarkings( obj, value )

            % Set
            obj.DividerMarkings = value;

        end % set.ShowMarkings

    end % accessors

end % classdef


function [uniqueEventTypes, boundaryEvents, hardEvents, hardFrames] = ...
                                            getBoundaryEvents(events)
% Returns the event types and boundary events for EEGLAB events structure
% (as extracted from EEG.event).
   uniqueEventTypes = [];
   boundaryEvents = [];
   hardEvents = [];
   hardFrames = [];
   if isempty(events)
       return;
   end
   types = cellfun(@num2str, {events.type}, 'UniformOutput', false);
   uniqueEventTypes = unique(types);
   boundaryMask = strcmpi(types, 'boundary');
   if sum(boundaryMask > 0) && isfield(events, 'duration')
        boundaryEvents = (1:length(events));
        boundaryEvents = boundaryEvents(boundaryMask);
        boundaryDurations = cell2mat({events(boundaryMask).duration}');
        hard = isnan(boundaryDurations);
        if sum(hard) > 0
            hardEvents = boundaryEvents(hard);
            hardFrames = round(cell2mat({events(hardEvents).latency}));
        end
   end
end


% read_eeglabheader() - import EEGLAB dataset files
%
% Usage:
%   >> header = read_eeglabheader(filename);
%
% Inputs:
%   filename - [string] file name
%
% Outputs:
%   header   - FILEIO toolbox type structure
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, 2008-

%123456789012345678901234567890123456789012345678901234567890123456789012

% Copyright (C) 2008 Arnaud Delorme, SCCN, INC, UCSD, arno@salk.edu
%
% 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function header = read_eeglabheader(filename)

if nargin < 1
  help read_eeglabheader
  return
end

if ~isstruct(filename)
  load('-mat', filename, 'EEG');
else
  EEG = filename;
end

header.Fs          =  EEG.srate;
header.nChans      =  EEG.nbchan;
header.nSamples    =  EEG.pnts;
header.nSamplesPre = -EEG.xmin*EEG.srate;
header.nTrials     =  EEG.trials;
try
  header.label     = { EEG.chanlocs.labels }';
catch
  ft_warning('creating default channel names');
  for i=1:header.nChans
    header.label{i} = sprintf('chan%03d', i);
  end
end
ind = 1;
for i = 1:length( EEG.chanlocs )
  if isfield(EEG.chanlocs(i), 'X') && ~isempty(EEG.chanlocs(i).X)
    header.elec.label{ind, 1} = EEG.chanlocs(i).labels;
    % this channel has a position
    header.elec.elecpos(ind,1) = EEG.chanlocs(i).X;
    header.elec.elecpos(ind,2) = EEG.chanlocs(i).Y;
    header.elec.elecpos(ind,3) = EEG.chanlocs(i).Z;
    ind = ind+1;
  end
end

% remove data
% -----------
%if isfield(EEG, 'datfile')
%    if ~isempty(EEG.datfile)
%        EEG.data = EEG.datfile;
%    end;
%else
%    EEG.data = 'in set file';
%end;
EEG.icaact = [];

header.orig = EEG;