function detector = acfTrain2( varargin )
% Train aggregate channel features object detector.
%
% Train aggregate channel features (ACF) object detector as described in:
%  P. Doll?, R. Appel, S. Belongie and P. Perona
%   "Fast Feature Pyramids for Object Detection", PAMI 2014.
% The ACF detector is fast (30 fps on a single core) and achieves top
% accuracy on rigid object detection. Please see acfReadme.m for details.
%
% Takes a set of parameters opts (described in detail below) and trains a
% detector from start to finish including performing multiple rounds of
% bootstrapping if need be. The return is a struct 'detector' for use with
% acfDetect.m which fully defines a sliding window detector. Training is
% fast (on the INRIA pedestrian dataset training takes ~10 minutes on a
% single core or ~3m using four cores). Taking advantage of parallel
% training requires launching matlabpool (see help for matlabpool). The
% trained detector may be altered in certain ways via acfModify(). Calling
% opts=acfTrain() returns all default options.
%
% (1) Specifying features and model: The channel features are defined by
% 'pPyramid'. See chnsCompute.m and chnsPyramid.m for more details. The
% channels may be convolved by a set 'filters' to remove local correlations
% (see our NIPS14 paper on LDCF), improving accuracy but slowing detection.
% If 'filters'=[wFilter,nFilter] these are automatically computed. The
% model dimensions ('modelDs') define the window height and width. The
% padded dimensions ('modelDsPad') define the extended region around object
% candidates that are used for classification. For example, for 100 pixel
% tall pedestrians, typically a 128 pixel tall region is used to make a
% decision. 'pNms' controls non-maximal suppression (see bbNms.m), 'stride'
% controls the window stride, and 'cascThr' and 'cascCal' are the threshold
% and calibration used for the constant soft cascades. Typically, set
% 'cascThr' to -1 and adjust 'cascCal' until the desired recall is reached
% (setting 'cascCal' shifts the final scores output by the detector by the
% given amount). Training alternates between sampling (bootstrapping) and
% training an AdaBoost classifier (clf). 'nWeak' determines the number of
% training stages and number of trees after each stage, e.g. nWeak=[32 128
% 512 2048] defines four stages with the final clf having 2048 trees.
% 'pBoost' specifies parameters for AdaBoost, and 'pBoost.pTree' are the
% decision tree parameters, see adaBoostTrain.m for details. Finally,
% 'seed' is the random seed used and makes results reproducible and 'name'
% defines the location for storing the detector and log file.
%
% (2) Specifying training data location and amount: The training data can
% take on a number of different forms. The positives can be specified using
% either a dir of pre-cropped windows ('posWinDir') or dirs of full images
% ('posImgDir') and ground truth labels ('posGtDir'). The negatives can by
% specified using a dir of pre-cropped windows ('negWinDir'), a dir of full
% images without any positives and from which negatives can be sampled
% ('negImgDir'), and finally if neither 'negWinDir' or 'negImgDir' are
% given negatives are sampled from the images in 'posImgDir' (avoiding the
% positives). For the pre-cropped windows all images must have size at
% least modelDsPad and have the object (of size exactly modelDs) centered.
% 'imreadf' can be used to specify a custom function for loading an image,
% and 'imreadp' are custom additional parameters to imreadf. When sampling
% from full images, 'pLoad' determines how the ground truth is loaded and
% converted to a set of positive bbs (see bbGt>bbLoad). 'nPos' controls the
% total number of positives to sample for training (if nPos=inf the number
% of positives is limited by the training set). 'nNeg' controls the total
% number of negatives to sample and 'nPerNeg' limits the number of
% negatives to sample per image. 'nAccNeg' controls the maximum number of
% negatives that can accumulate over multiple stages of bootstrapping.
% Define 'pJitter' to jitter the positives (see jitterImage.m) and thus
% artificially increase the number of positive training windows. Finally if
% 'winsSave' is true cropped windows are saved to disk as a mat file.
%
% USAGE
%  detector = acfTrain( opts )
%  opts = acfTrain()
%
% INPUTS
%  opts       - parameters (struct or name/value pairs)
%   (1) features and model:
%   .pPyramid   - [{}] params for creating pyramid (see chnsPyramid)
%   .filters    - [] [wxwxnChnsxnFilter] filters or [wFilter,nFilter]
%   .modelDs    - [] model height+width without padding (eg [100 41])
%   .modelDsPad - [] model height+width with padding (eg [128 64])
%   .pNms       - [..] params for non-maximal suppression (see bbNms.m)
%   .stride     - [4] spatial stride between detection windows
%   .cascThr    - [-1] constant cascade threshold (affects speed/accuracy)
%   .cascCal    - [.005] cascade calibration (affects speed/accuracy)
%   .nWeak      - [128] vector defining number weak clfs per stage
%   .pBoost     - [..] parameters for boosting (see adaBoostTrain.m)
%   .seed       - [0] seed for random stream (for reproducibility)
%   .name       - [''] name to prepend to clf and log filenames
%   (2) training data location and amount:
%   .posGtDir   - [''] dir containing ground truth
%   .posImgDir  - [''] dir containing full positive images
%   .negImgDir  - [''] dir containing full negative images
%   .posWinDir  - [''] dir containing cropped positive windows
%   .negWinDir  - [''] dir containing cropped negative windows
%   .imreadf    - [@imread] optional custom function for reading images
%   .imreadp    - [{}] optional custom parameters for imreadf
%   .pLoad      - [..] params for bbGt>bbLoad (see bbGt)
%   .nPos       - [inf] max number of pos windows to sample
%   .nNeg       - [5000] max number of neg windows to sample
%   .nPerNeg    - [25]  max number of neg windows to sample per image
%   .nAccNeg    - [10000] max number of neg windows to accumulate
%   .pJitter    - [{}] params for jittering pos windows (see jitterImage)
%   .winsSave   - [0] if true save cropped windows at each stage to disk
%
% OUTPUTS
%  detector   - trained object detector (modify only via acfModify)
%   .opts       - input parameters used for model training
%   .clf        - learned boosted tree classifier (see adaBoostTrain)
%   .info       - info about channels (see chnsCompute.m)
%
% EXAMPLE
%
% See also acfReadme, acfDetect, acfDemoInria, acfModify, acfTest,
% chnsCompute, chnsPyramid, adaBoostTrain, bbGt, bbNms, jitterImage
%
% Piotr's Computer Vision Matlab Toolbox      Version 3.40
% Copyright 2014 Piotr Dollar.  [pdollar-at-gmail.com]
% Licensed under the Simplified BSD License [see external/bsd.txt]

% initialize opts struct
opts = initializeOpts( varargin{:} );
if(nargin==0), detector=opts; return; end

% load or initialize detector and begin logging
nm=[opts.name 'Detector.mat']; t=exist(nm,'file');
if(t), if(nargout), t=load(nm); detector=t.detector; end; return; end
t=fileparts(nm); if(~isempty(t) && ~exist(t,'dir')), mkdir(t); end
detector = struct( 'opts',opts, 'clf',[], 'info',[] );
startTrain=clock; nm=[opts.name 'Log.txt'];
if(exist(nm,'file')), diary(nm); diary('off'); delete(nm); end; diary(nm);
RandStream.setGlobalStream(RandStream('mrg32k3a','Seed',opts.seed));

% iterate bootstraping and training
for stage = 0:numel(opts.nWeak)-1
  diary('on'); fprintf([repmat('-',[1 75]) '\n']);
  fprintf('Training stage %i\n',stage); startStage=clock;

  % sample positives and compute info about channels
  if( stage==0 )
    [Is1,IsOrig1] = sampleWins( detector, stage, 1 );
    t=ndims(Is1); if(t==3), t=Is1(:,:,1); else t=Is1(:,:,:,1); end
    t=chnsCompute2(t,opts.pPyramid.pChns); detector.info=t.info;
  end

  % compute local decorrelation filters
  if( stage==0 && length(opts.filters)==2 )
    fs = opts.filters; opts.filters = [];
    X1 = chnsCompute1( IsOrig1, opts );
    fs = chnsCorrelation( X1, fs(1), fs(2) );
    opts.filters = fs; detector.opts.filters = fs;
  end

  % compute lambdas
  if( stage==0 && isempty(opts.pPyramid.lambdas) )
    fprintf('Computing lambdas... '); start=clock;
    ds=size(IsOrig1); ds(1:end-1)=1;
    IsOrig1=IsOrig1(:,:,1:3,:);
    IsOrig1=mat2cell2(IsOrig1,ds);
    ls=chnsScaling(opts.pPyramid.pChns,IsOrig1,0);
    ls=round(ls*10^5)/10^5; detector.opts.pPyramid.lambdas=ls;
    fprintf('done (time=%.0fs).\n',etime(clock,start));
  end

  % compute features for positives
  if( stage==0 )
    X1 = chnsCompute1( Is1, opts );
    X1 = reshape(X1,[],size(X1,4))';
    clear Is1 IsOrig1 ls fs ds t;
  end

  % sample negatives and compute features
  Is0 = sampleWins( detector, stage, 0 );
  X0 = chnsCompute1( Is0, opts ); clear Is0;
  X0 = reshape(X0,[],size(X0,4))';

  % accumulate negatives from previous stages
  if( stage>0 )
    n0=size(X0p,1); n1=max(opts.nNeg,opts.nAccNeg)-size(X0,1);
    if(n0>n1 && n1>0), X0p=X0p(randSample(n0,n1),:); end
    if(n0>0 && n1>0), X0=[X0p; X0]; end %#ok<AGROW>
  end; X0p=X0;

  % train boosted clf
  detector.opts.pBoost.nWeak = opts.nWeak(stage+1);
  detector.clf = adaBoostTrain(X0,X1,detector.opts.pBoost);
  detector.clf.hs = detector.clf.hs + opts.cascCal;

  % update log
  fprintf('Done training stage %i (time=%.0fs).\n',...
    stage,etime(clock,startStage)); diary('off');
end

% save detector
save([opts.name 'Detector.mat'],'detector');

% finalize logging
diary('on'); fprintf([repmat('-',[1 75]) '\n']);
fprintf('Done training (time=%.0fs).\n',...
  etime(clock,startTrain)); diary('off');

end

function opts = initializeOpts( varargin )
% Initialize opts struct.
dfs= { 'pPyramid',{}, 'filters',[], ...
  'modelDs',[100 41], 'modelDsPad',[128 64], ...
  'pNms',struct(), 'stride',4, 'cascThr',-1, 'cascCal',.005, ...
  'nWeak',128, 'pBoost', {}, 'seed',0, 'name','', 'posGtDir','', ...
  'posImgDir','', 'negImgDir','', 'posWinDir','', 'negWinDir','', ...
  'imreadf',@imread, 'imreadp',{}, 'pLoad',{}, 'nPos',inf, 'nNeg',5000, ...
  'nPerNeg',25, 'nAccNeg',10000, 'pJitter',{}, 'winsSave',0, 'trainSet', [] };
opts = getPrmDflt(varargin,dfs,1);
% fill in remaining parameters
% p=chnsPyramid([],opts.pPyramid); p=p.pPyramid;
p=chnsPyramid2([],opts.pPyramid); p=p.pPyramid;
p.minDs=opts.modelDs; shrink=p.pChns.shrink;
opts.modelDsPad=ceil(opts.modelDsPad/shrink)*shrink;
p.pad=ceil((opts.modelDsPad-opts.modelDs)/shrink/2)*shrink;
p=chnsPyramid([],p); p=p.pPyramid; p.complete=1;
p.pChns.complete=1; opts.pPyramid=p;
% initialize pNms, pBoost, pBoost.pTree, and pLoad
dfs={ 'type','maxg', 'overlap',.65, 'ovrDnm','min' };
opts.pNms=getPrmDflt(opts.pNms,dfs,-1);
dfs={ 'pTree',{}, 'nWeak',0, 'discrete',1, 'verbose',16 };
opts.pBoost=getPrmDflt(opts.pBoost,dfs,1);
dfs={'nBins',256,'maxDepth',2,'minWeight',.01,'fracFtrs',1,'nThreads',16};
opts.pBoost.pTree=getPrmDflt(opts.pBoost.pTree,dfs,1);
opts.pLoad=getPrmDflt(opts.pLoad,{'squarify',{0,1}},-1);
opts.pLoad.squarify{2}=opts.modelDs(2)/opts.modelDs(1);
end

function [Is,IsOrig] = sampleWins( detector, stage, positive )
% Load or sample windows for training detector.
opts=detector.opts; start=clock;
if( positive ), n=opts.nPos; else n=opts.nNeg; end
if( positive ), crDir=opts.posWinDir; else crDir=opts.negWinDir; end
if( exist(crDir,'dir') && stage==0 )
  % if window directory is specified simply load windows
  fs=bbGt2('getFiles',{crDir}); nImg=length(fs); assert(nImg>0);
  if(nImg>n), fs=fs(:,randSample(nImg,n)); end; n=nImg;
  for i=1:n, fs{i}=[{opts.imreadf},fs(i),opts.imreadp]; end
  Is=cell(1,n); parfor i=1:n, Is{i}=feval(fs{i}{:}); end
else
  % sample windows from full images using sampleWins1()
  hasGt=positive||isempty(opts.negImgDir); fs={opts.negImgDir};
  if(hasGt), fs={opts.posImgDir,opts.posImgDir,opts.posGtDir}; end
  % fs=bbGt('getFiles',fs);
  fs=bbGt2('getSubsetFiles',fs, opts.trainSet);
%   fp=fopen(opts.trainset, 'r'); info=textscan(fp, '%s%s%s', 'Delimiter', '/');  fclose(fp);
%   rgbFile=cellfun(@(x,y,z) fullfile(opts.posImgDir, x, y, 'visible', [z '.jpg']), info{1}, info{2}, info{3}, 'uniformoutput', false );
%   lwirFile=cellfun(@(x,y,z) fullfile(opts.posImgDir, x, y, 'lwir', [z '.jpg']), info{1}, info{2}, info{3}, 'uniformoutput', false );
%   gtFile=cellfun(@(x,y,z) fullfile(opts.posGtDir, x, y, [z '.txt']), info{1}, info{2}, info{3}, 'uniformoutput', false );
%   fs = cat(2, rgbFile, lwirFile, gtFile)';

  nImg=size(fs,2); assert(nImg>0);
  if(~isinf(n)), fs=fs(:,randperm(nImg)); end; Is=cell(nImg*1000,1);
  diary('off'); tid=ticStatus('Sampling windows',1,30); k=0; i=0; batch=64;
  while( i<nImg && k<n )
    batch=min(batch,nImg-i); Is1=cell(1,batch);
    parfor j=1:batch, ij=i+j;
%     for j=1:batch, ij=i+j;
      I = feval(opts.imreadf,fs{1,ij}, fs{2,ij},opts.imreadp{:}); %#ok<PFBNS>
      gt=[]; if(hasGt), [~,gt]=bbGt2('bbLoad',fs{3,ij},opts.pLoad); end
      Is1{j} = sampleWins1( I, gt, detector, stage, positive );
    end
    Is1=[Is1{:}]; k1=length(Is1); Is(k+1:k+k1)=Is1; k=k+k1;
    if(k>n), Is=Is(randSample(k,n)); k=n; end
    i=i+batch; tocStatus(tid,max(i/nImg,k/n));
  end
  Is=Is(1:k); diary('on');
  fprintf('Sampled %i windows from %i images.\n',k,i);
end
% optionally jitter positive windows
if(length(Is)<2), Is={}; return; end
nd=ndims(Is{1})+1; Is=cat(nd,Is{:}); IsOrig=Is;
if( positive && isstruct(opts.pJitter) )
  opts.pJitter.hasChn=(nd==4); Is=jitterImage(Is,opts.pJitter);
  ds=size(Is); ds(nd)=ds(nd)*ds(nd+1); Is=reshape(Is,ds(1:nd));
end
% make sure dims are divisible by shrink and not smaller than modelDsPad
ds=size(Is); cr=rem(ds(1:2),opts.pPyramid.pChns.shrink); s=floor(cr/2)+1;
e=ceil(cr/2); Is=Is(s(1):end-e(1),s(2):end-e(2),:,:); ds=size(Is);
if(any(ds(1:2)<opts.modelDsPad)), error('Windows too small.'); end
% optionally save windows to disk and update log
nm=[opts.name 'Is' int2str(positive) 'Stage' int2str(stage)];
if( opts.winsSave ), save(nm,'Is','-v7.3'); end
fprintf('Done sampling windows (time=%.0fs).\n',etime(clock,start));
diary('off'); diary('on');
end

function Is = sampleWins1( I, gt, detector, stage, positive )
% Sample windows from I given its ground truth gt.
opts=detector.opts; shrink=opts.pPyramid.pChns.shrink;
modelDs=opts.modelDs; modelDsPad=opts.modelDsPad;
if( positive ), bbs=gt; bbs=bbs(bbs(:,5)==0,:); else
  if( stage==0 )
    % generate candidate bounding boxes in a grid
    [h,w,~]=size(I); h1=modelDs(1); w1=modelDs(2);
    n=opts.nPerNeg; ny=sqrt(n*h/w); nx=n/ny; ny=ceil(ny); nx=ceil(nx);
    [xs,ys]=meshgrid(linspace(1,w-w1,nx),linspace(1,h-h1,ny));
    bbs=[xs(:) ys(:)]; bbs(:,3)=w1; bbs(:,4)=h1; bbs=bbs(1:n,:);
  else
    % run detector to generate candidate bounding boxes
    bbs=acfDetect2(I,detector); [~,ord]=sort(bbs(:,5),'descend');
    bbs=bbs(ord(1:min(end,opts.nPerNeg)),1:4);
  end
  if( ~isempty(gt) )
    % discard any candidate negative bb that matches the gt
    n=size(bbs,1); keep=false(1,n);
    for i=1:n, keep(i)=all(bbGt2('compOas',bbs(i,:),gt,gt(:,5))<.1); end
    bbs=bbs(keep,:);
  end
end
% grow bbs to a large padded size and finally crop windows
modelDsBig=max(8*shrink,modelDsPad)+max(2,ceil(64/shrink))*shrink;
r=modelDs(2)/modelDs(1); assert(all(abs(bbs(:,3)./bbs(:,4)-r)<1e-5));
r=modelDsBig./modelDs; bbs=bbApply('resize',bbs,r(1),r(2));
Is=bbApply('crop',I,bbs,'replicate',modelDsBig([2 1]));
end

function chns = chnsCompute1( Is, opts )
% Compute single scale channels of dimensions modelDsPad.
if(isempty(Is)), chns=[]; return; end
fprintf('Extracting features... '); start=clock; fs=opts.filters;
pChns=opts.pPyramid.pChns; smooth=opts.pPyramid.smooth;
dsTar=opts.modelDsPad/pChns.shrink; ds=size(Is); ds(1:end-1)=1;
Is=squeeze(mat2cell2(Is,ds)); n=length(Is); chns=cell(1,n);
% parfor i=1:n
for i=1:n
  C=chnsCompute2(Is{i},pChns); C=convTri(cat(3,C.data{:}),smooth);
  if(~isempty(fs)), C=repmat(C,[1 1 size(fs,4)]);
    for j=1:size(C,3), C(:,:,j)=conv2(C(:,:,j),fs(:,:,j),'same'); end; end
  if(~isempty(fs)), C=imResample(C,.5); shr=2; else shr=1; end
  ds=size(C); cr=ds(1:2)-dsTar/shr; s=floor(cr/2)+1; e=ceil(cr/2);
  C=C(s(1):end-e(1),s(2):end-e(2),:); chns{i}=C;
end; chns=cat(4,chns{:});
fprintf('done (time=%.0fs).\n',etime(clock,start));
end

function filters = chnsCorrelation( chns, wFilter, nFilter )
% Compute filters capturing local correlations for each channel.
fprintf('Computing correlations... '); start=clock;
[~,~,m,n]=size(chns); w=wFilter; wp=w*2-1;
filters=zeros(w,w,m,nFilter,'single');
for i=1:m
  % compute local auto-scorrelation using Wiener-Khinchin theorem
  mus=squeeze(mean(mean(chns(:,:,i,:)))); sig=cell(1,n);
  parfor j=1:n
    T=fftshift(ifft2(abs(fft2(chns(:,:,i,j)-mean(mus))).^2));
    sig{j}=T(floor(end/2)+1-w+(1:wp),floor(end/2)+1-w+(1:wp));
  end
  sig=double(mean(cat(4,sig{mus>1/50}),4));
  sig=reshape(full(convmtx2(sig,w,w)),wp+w-1,wp+w-1,[]);
  sig=reshape(sig(w:wp,w:wp,:),w^2,w^2); sig=(sig+sig')/2;
  % compute filters for each channel from sig (sorted by eigenvalue)
  [fs,D]=eig(sig); fs=reshape(fs,w,w,[]);
  [~,ord]=sort(diag(D),'descend');
  fs=flipdim(flipdim(fs,1),2); %#ok<DFLIPDIM>
  filters(:,:,i,:)=fs(:,:,ord(1:nFilter));
end
fprintf('done (time=%.0fs).\n',etime(clock,start));
end


function buttonIcon = createButtonLabel(string,varargin)
% createButtonLabel: creates a tightly cropped RGB image (button-label) of a simple text string
%
%          This is very useful for labeling uicontrols (pushbuttons), with the syntax:
%
%                           set(hndl,'cdata',buttonIcon)
%
%          All valid Parameter-Value pairs, INCLUDING TEXT ROTATION, are
%          supported. Note that this function requires the Image Processing
%          Toolbox, and that it triggers the creation of a temporary
%          figure, which will be momentarily visible during
%          button-label creation.
%
%          Allows easy creation of multi-line text for labeling
%          pushbuttons.
%
% SYNTAX:  BUTTONICON = CREATEBUTTONLABEL(STRING,PVs,FIGOPT);
%
% USAGE: buttonIcon = createButtonLabel(string)
%              Create a tightly cropped RGB image of a text object of the
%              simple string indicated in input STRING. Uses default
%              properties (fontname, fontsize, fontweight, fontcolor,
%              bgcolor, rotation, etc.) for text object.
%
%         buttonIcon = createButtonLabel(string,PVs)
%              Provide additional Parameter-Value pairs, either in a cell
%              array of strings, or in a structure of parameters. All PV
%              pairs valid for text objects are supported.
%
%         buttonIcon = createButtonLabel(string,PVs,figOpt)
%              Provides optional argument for controlling the (requisite)
%              temporary figure. By default, the temporary figure will be
%              (visibly) created and destroyed within this function.
%              However, if you will be calling CREATEBUTTONLABEL multiply (labeling
%              tabs in a tabPanel, for instance), it might be less
%              distracting to reuse the same figure, and to destroy the
%              figure after the last call. This parameter will allow you to
%              do so.
%
% ARGUMENTS:
%
%          BUTTONICON: Output RGB image
%
%          STRING: The string to be captured as a tightly-cropped RGB
%              Image.
%
%          PVs (OPTIONAL): Cell array or structure of any parameter-value pairs
%                 valid for TEXT object. (See 'Text Properties' for details.)
%                 Particularly useful: {'rotation',90} or {'rotation',270}.
%
%          FIGOPT (OPTIONAL): Control parameter for temporary figure. Valid options:
%              1: REUSE/DESTROY (DEFAULT): reuse existing temporary figure, or create
%                 one if it doesn't exist. Destroy figure on completion.
%              2: REUSE existing temporary figure, or create one if it
%                 doesn't exist. Do not destroy figure.
%
% EXAMPLES:
%
%         1) Create cdata to label a vertically-oriented pushbutton, apply
%              structure of text parameters, destroy figure.
%
%         txt.rotation = 270;
%         txt.color = 'r';
%         txt.backgroundcolor = 'y';
%         buttonIcon = createButtonLabel('PRESS',txt);
%
%         2) Create cdata, apply PV pairs, leave figure open for re-use.
%
%         buttonIcon = createButtonLabel('PUSH',{'fontname','Helvetica','fontweight','bold'},2);
%
%         3) Multi-line text on a pushbutton:
%         buttonIcon =  createButtonLabel({'PUSH', 'ME!'},...
%                    {'fontname','Helvetica','fontweight',...
%                     'bold','horizontalAlignment','center',...
%                     'color','r'});
%
% SO: Why is this useful? Try this (and then try it without createButtonLabel!):
%
%         4)
%             x = createButtonLabel('PRESS',...
%                 {'rotation',270,'fontsize',18,'fontweight','b',...
%                 'color','c','backgroundcolor','y'});
%             figure;uicontrol('style','pushbutton','units','pixels',...
%                 'pos',[10 10 60 100],'callback','disp(''I was pushed!'')',...
%                 'backgroundcolor','y','cdata',x);

% Created by Brett Shoelson
% brett.shoelson@mathworks.com
% 05/14/07
%
% Modifications:
% 02/29/08 Force figure into 'normal' (not docked) state
% 06/22/09 Modified at the suggestion of Dave Forstot to allow
%    the creation of multi-line text buttons. (Thanks, Dave.)

% Validate and parse inputs
string = parseValidateInputs(string,varargin{:});

% Depending on figOpt parameter, branch to use/reuse/destroy temporary button-label figure
ExistFig = findobj('tag','tmpcreateButtonLabelfig');
if ~isempty(ExistFig)
    figure(ExistFig);
    clf;
else
    figure('units','pixels','position',[100 100 500 500],'tag','tmpcreateButtonLabelfig','windowstyle','normal');
end

ax = axes('units','pixels','position',[0 0 500 500],'visible','on','xtick',[],'ytick',[],'color','w');
tmp = text(250,250,string,'units','pixels','fontunits','pixels');

% Apply any input Parameter-Value pairs
applyPVs(tmp,PVs);
% Temporarily change fontcolor to black for detection, then return it to
% intended color
currColor = get(tmp,'color');
set(tmp,'color','k');
% Create button-label
F = getframe(ax);
im = im2double(F.cdata(:,:,1));
im = im < 1;
[y,x] = find(im);
set(tmp,'color',currColor);
set(ax,'color',get(tmp,'backgroundcolor'));
buttonIcon = getframe(ax,[min(x(:))-1 500-max(y(:))-1 max(x(:))-min(x(:))+2 max(y(:))-min(y(:))+2]);
buttonIcon = buttonIcon.cdata;

if figOpt == 1
    delete(findobj('tag','tmpcreateButtonLabelfig'));
end

% NESTED FUNCTIONS
    function string = parseValidateInputs(string,varargin)
        error(nargchk(1,3,nargin))
        error(nargoutchk(0,1,nargout))
        if ~(ischar(string) || iscellstr(string))
            try
                string = num2str(string);
            catch
                error('CREATEBUTTONLABEL: Requires at a minimum a single input, which must be a string.')
            end
        end
        % Defaults
        figOpt = 1;
        PVs = [];
        if ~isempty(varargin)
            if ~isempty(varargin{1})
                PVs = varargin{1};
            end
            if nargin == 3
                figOpt = varargin{2};
            end
            if ~ismember(figOpt,1:2)
                error('CREATEBUTTONLABEL: Invalid option for figOpt parameter.')
            end
        end
    end

    function applyPVs(obj,pvarray)
        if isempty(pvarray)
            return;
        end
        if isstruct(pvarray)
            set(obj,pvarray);
        else %Cell
            for ii = 1:2:numel(pvarray)
                set(obj,pvarray{ii},pvarray{ii+1});
            end
        end
    end

end


function gui_LoadResult
% function pf_load
%   Opens a open dialog to load result data
%   and invokes the display dialog
%
% Danijel Schorlemmer
% August 22, 2002

global bDebug;
if bDebug
  report_this_filefun(mfilename('fullpath'));
end

% Invoke the open dialog
[newfile, newpath] = uigetfile('*.mat', 'Load calculated data');
% Cancel pressed?
if newfile == 0
  return;
end
% Everything ok?
newfile = [newpath newfile];
if length(newfile) > 1
  % Load the data
  load(newfile, 'vResults');
  % Open dialog
  hResultFig = gui_result2(vResults);
end


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

%%
clear; close all; clc;

%% Syntax
% |[varargout]=quiverTriad(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/>.


%kkgentbindex 'generate index for a toolbox manual'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros kgentbindex.pane file
%
% Parameters:
% Toggle: alltb 'Generate Index for All toolboxes', default: 0: 'Generates Index for all toolbox manuals'
% String: tb 'Generate Single Toolbox Index', default: ' ': 'Generate a manual index for a single toolbox'
%
% Example: kkgentbindex( {'alltb',0;'tb',' '})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% kgentbindex - generate index for a toolbox manual
%
%  DESCRIPTION
% .I kgentbindex
% is used to generate the index chapter of a manual, by combining the
% index information from each chapter within the manual.  Run the
% command from the directory <toolbox_dir>/manual/<Toolbox_dir>.
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
%
%  RESTRICTIONS
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kkgentbindex(varargin)
Inputs={};
if nargin ==0
  arglist={'',''};
elseif nargin ==1
  arglist=varargin{1};
else error('Usage: [out1,..] = kkgentbindex(arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'alltb', 0;'tb', ' '};
maxval={0,0};
minval={0,0};
istoggle=[1,1];
was_set=istoggle * 0;
paramtype={'Toggle','String'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=0; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(0);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
callKhoros([w 'kgentbindex"  '],Inputs,narglist);


function [selection,value] = listdlg2(varargin)
%LISTDLG  List selection dialog box.
%   [SELECTION,OK] = LISTDLG2('ListString',S) creates a modal dialog box
%   which allows you to select a string or multiple strings from a list.
%   SELECTION is a vector of indices of the selected strings (length 1 in
%   the single selection mode).  This will be [] when OK is 0.  OK is 1 if
%   you push the OK button, or 0 if you push the Cancel button or close the
%   figure.
%
%   Double-clicking on an item or pressing <CR> when multiple items are
%   selected has the same effect as clicking the OK button.  Pressing <CR>
%   is the same as clicking the OK button. Pressing <ESC> is the same as
%   clicking the Cancel button.
%
%   Inputs are in parameter,value pairs:
%
%   Parameter       Description
%   'ListString'    cell array of strings for the list box.
%   'SelectionMode' string; can be 'single' or 'multiple'; defaults to
%                   'multiple'.
%   'ListSize'      [width height] of listbox in pixels; defaults
%                   to [160 300].
%   'InitialValue'  vector of indices of which items of the list box
%                   are initially selected; defaults to the first item.
%   'Name'          String for the figure's title; defaults to ''.
%   'PromptString'  string matrix or cell array of strings which appears
%                   as text above the list box; defaults to {}.
%   'OKString'      string for the OK button; defaults to 'OK'.
%   'CancelString'  string for the Cancel button; defaults to 'Cancel'.
%
%   A 'Select all' button is provided in the multiple selection case.
%
%   Example:
%     d = dir;
%     str = {d.name};
%     [s,v] = listdlg('PromptString','Select a file:',...
%                     'SelectionMode','single',...
%                     'ListString',str)
 %
%  See also DIALOG, ERRORDLG, HELPDLG, INPUTDLG,
%    MSGBOX, QUESTDLG, WARNDLG.

%   Copyright 1984-2014 The MathWorks, Inc.

%   'uh'            uicontrol button height, in pixels; default = 22.
%   'fus'           frame/uicontrol spacing, in pixels; default = 8.
%   'ffs'           frame/figure spacing, in pixels; default = 8.

% simple test:
%
% d = dir; [s,v] = listdlg2('PromptString','Select a file:','ListString',{d.name});
%

% Generate a warning in -nodisplay and -noFigureWindows mode.
% warnfiguredialog('listdlg');
%  usedpath=strcat(matlabroot,'/toolbox/matlab/uitools/private');
%  addpath(genpath(usedpath));
narginchk(1,inf)

figname = '';
smode = 2;   % (multiple)
promptstring = {};
liststring = [];
listsize = [160 300];
initialvalue = [];
okstring = getString(message('MATLAB:uistring:popupdialogs:OK'));
cancelstring = getString(message('MATLAB:uistring:popupdialogs:Cancel'));
fus = 8;
ffs = 8;
uh = 22;

% custom change
pos = [];

if mod(length(varargin),2) ~= 0
    % input args have not com in pairs, woe is me
    error(message('MATLAB:listdlg:InvalidArgument'))
end
for i=1:2:length(varargin)
    switch lower(varargin{i})
     case 'name'
      figname = varargin{i+1};
     case 'promptstring'
      promptstring = varargin{i+1};
     case 'selectionmode'
      switch lower(varargin{i+1})
       case 'single'
        smode = 1;
       case 'multiple'
        smode = 2;
      end
     case 'listsize'
      listsize = varargin{i+1};
     case 'liststring'
      liststring = varargin{i+1};
     case 'initialvalue'
      initialvalue = varargin{i+1};
     case 'uh'
      uh = varargin{i+1};
     case 'fus'
      fus = varargin{i+1};
     case 'ffs'
      ffs = varargin{i+1};
     case 'okstring'
      okstring = varargin{i+1};
     case 'cancelstring'
      cancelstring = varargin{i+1};

	 % custom change
	 case 'position'
      pos = varargin{i+1};

	% original code
    otherwise
      error(message('MATLAB:listdlg:UnknownParameter', varargin{ i }))
    end
end

if ischar(promptstring)
    promptstring = cellstr(promptstring);
end

if isempty(initialvalue)
    initialvalue = 1;
end

if isempty(liststring)
    error(message('MATLAB:listdlg:NeedParameter'))
end

ex = get(0,'DefaultUicontrolFontSize')*1.7;  % height extent per line of uicontrol text (approx)

fp = get(0,'DefaultFigurePosition');
w = 2*(fus+ffs)+listsize(1);
h = 2*ffs+6*fus+ex*length(promptstring)+listsize(2)+uh+(smode==2)*(fus+uh);
fp = [fp(1) fp(2)+fp(4)-h w h];  % keep upper left corner fixed

fig_props = { ...
    'windowstyle'            'modal' ...
    'name'                   figname ...
    'color'                  get(0,'DefaultUicontrolBackgroundColor') ...
    'resize'                 'off' ...
    'numbertitle'            'off' ...
    'menubar'                'none' ...
    'visible'                'off' ...
    'createfcn'              ''    ...
    'position'               fp   ...
    'closerequestfcn'        'delete(gcbf)' ...
            };

liststring=cellstr(liststring);

fig = figure(fig_props{:});

if ~isempty(promptstring)
    prompt_text = uicontrol('Style','text','String',promptstring,...
        'HorizontalAlignment','left',...
        'Position',[ffs+fus fp(4)-(ffs+fus+ex*length(promptstring)) ...
        listsize(1) ex*length(promptstring)]); %#ok
end

btn_wid = (fp(3)-2*(ffs+fus)-fus)/2;

listbox = uicontrol('Style','listbox',...
                    'Position',[ffs+fus ffs+uh+4*fus+(smode==2)*(fus+uh) listsize],...
                    'String',liststring,...
                    'BackgroundColor','w',...
                    'Max',smode,...
                    'Tag','listbox',...
                    'Value',initialvalue, ...
                    'Callback', {@doListboxClick});

ok_btn = uicontrol('Style','pushbutton',...
                   'String',okstring,...
                   'Position',[ffs+fus ffs+fus btn_wid uh],...
                   'Tag','ok_btn',...
                   'Callback',{@doOK,listbox});

cancel_btn = uicontrol('Style','pushbutton',...
                       'String',cancelstring,...
                       'Position',[ffs+2*fus+btn_wid ffs+fus btn_wid uh],...
                       'Tag','cancel_btn',...
                       'Callback',{@doCancel,listbox});

if smode == 2
    selectall_btn = uicontrol('Style','pushbutton',...
                              'String',getString(message('MATLAB:uistring:popupdialogs:SelectAll')),...
                              'Position',[ffs+fus 4*fus+ffs+uh listsize(1) uh],...
                              'Tag','selectall_btn',...
                              'Callback',{@doSelectAll, listbox});

    if length(initialvalue) == length(liststring)
        set(selectall_btn,'Enable','off')
    end
    set(listbox,'Callback',{@doListboxClick, selectall_btn})
end

set([fig, ok_btn, cancel_btn, listbox], 'KeyPressFcn', {@doKeypress, listbox});

% custom change
if isempty(pos)
   set(fig,'Position',getnicedialoglocation(fp, get(fig,'Units'))); % only this line existed in original code
else
   set(fig, 'Position', pos); % set the figure position as desired using custom code
end

% Make ok_btn the default button.
setdefaultbutton(fig, ok_btn);

% make sure we are on screen
movegui(fig)
set(fig, 'Visible','on'); drawnow;

try
    % Give default focus to the listbox *after* the figure is made visible
    uicontrol(listbox);
    c = matlab.ui.internal.dialog.DialogUtils.disableAllWindowsSafely();
    uiwait(fig);
    delete(c);
catch
    if ishghandle(fig)
        delete(fig)
    end
end

if isappdata(0,'ListDialogAppData__')
    ad = getappdata(0,'ListDialogAppData__');
    selection = ad.selection;
    value = ad.value;
    rmappdata(0,'ListDialogAppData__')
else
    % figure was deleted
    selection = [];
    value = 0;
end
drawnow; % Update the view to remove the closed figure (g1031998)

%% figure, OK and Cancel KeyPressFcn
function doKeypress(src, evd, listbox) %#ok
switch evd.Key
 case 'escape'
  doCancel([],[],listbox);
end

%% OK callback
function doOK(ok_btn, evd, listbox) %#ok
if (~isappdata(0, 'ListDialogAppData__'))
    ad.value = 1;
    ad.selection = get(listbox,'Value');
    setappdata(0,'ListDialogAppData__',ad);
    delete(gcbf);
end

%% Cancel callback
function doCancel(cancel_btn, evd, listbox) %#ok
ad.value = 0;
ad.selection = [];
setappdata(0,'ListDialogAppData__',ad)
delete(gcbf);

%% SelectAll callback
function doSelectAll(selectall_btn, evd, listbox) %#ok
set(selectall_btn,'Enable','off')
set(listbox,'Value',1:length(get(listbox,'String')));

%% Listbox callback
function doListboxClick(listbox, evd, selectall_btn) %#ok
% if this is a doubleclick, doOK
if strcmp(get(gcbf,'SelectionType'),'open')
    doOK([],[],listbox);
elseif nargin == 3
    if length(get(listbox,'String'))==length(get(listbox,'Value'))
        set(selectall_btn,'Enable','off')
    else
        set(selectall_btn,'Enable','on')
    end
end


classdef dme_load_opf < mp.dme_load & mp.dme_shared_opf

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

%     properties
%     end     %% properties

%     methods
%     end     %% methods
end         %% classdef


function varargout = lvd_EditGroundObjectsGUI(varargin)
    % LVD_EDITGROUNDOBJECTSGUI MATLAB code for lvd_EditGroundObjectsGUI.fig
    %      LVD_EDITGROUNDOBJECTSGUI, by itself, creates a new LVD_EDITGROUNDOBJECTSGUI or raises the existing
    %      singleton*.
    %
    %      H = LVD_EDITGROUNDOBJECTSGUI returns the handle to a new LVD_EDITGROUNDOBJECTSGUI or the handle to
    %      the existing singleton*.
    %
    %      LVD_EDITGROUNDOBJECTSGUI('CALLBACK',hObject,eventData,handles,...) calls the local
    %      function named CALLBACK in LVD_EDITGROUNDOBJECTSGUI.M with the given input arguments.
    %
    %      LVD_EDITGROUNDOBJECTSGUI('Property','Value',...) creates a new LVD_EDITGROUNDOBJECTSGUI or raises the
    %      existing singleton*.  Starting from the left, property value pairs are
    %      applied to the GUI before lvd_EditGroundObjectsGUI_OpeningFcn gets called.  An
    %      unrecognized property name or invalid value makes property application
    %      stop.  All inputs are passed to lvd_EditGroundObjectsGUI_OpeningFcn via varargin.
    %
    %      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
    %      instance to run (singleton)".
    %
    % See also: GUIDE, GUIDATA, GUIHANDLES

    % Edit the above text to modify the response to help lvd_EditGroundObjectsGUI

    % Last Modified by GUIDE v2.5 19-Jul-2020 08:59:13

    % Begin initialization code - DO NOT EDIT
    gui_Singleton = 1;
    gui_State = struct('gui_Name',       mfilename, ...
        'gui_Singleton',  gui_Singleton, ...
        'gui_OpeningFcn', @lvd_EditGroundObjectsGUI_OpeningFcn, ...
        'gui_OutputFcn',  @lvd_EditGroundObjectsGUI_OutputFcn, ...
        'gui_LayoutFcn',  [] , ...
        'gui_Callback',   []);
    if nargin && ischar(varargin{1})
        gui_State.gui_Callback = str2func(varargin{1});
    end

    if nargout
        [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
    else
        gui_mainfcn(gui_State, varargin{:});
    end
    % End initialization code - DO NOT EDIT

    % --- Executes just before lvd_EditGroundObjectsGUI is made visible.
function lvd_EditGroundObjectsGUI_OpeningFcn(hObject, eventdata, handles, varargin)
    % This function has no output args, see OutputFcn.
    % hObject    handle to figure
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    % varargin   command line arguments to lvd_EditGroundObjectsGUI (see VARARGIN)

    % Choose default command line output for lvd_EditGroundObjectsGUI
    handles.output = hObject;

    grndObjs = varargin{1};
    setappdata(hObject,'grndObjs',grndObjs);

    handles = populateGUI(grndObjs, handles);

    % Update handles structure
    guidata(hObject, handles);

    % UIWAIT makes lvd_EditGroundObjectsGUI wait for user response (see UIRESUME)
    uiwait(handles.lvd_EditGroundObjectsGUI);

function handles = populateGUI(grndObjs, handles)
    listboxStr = grndObjs.getListboxStr();
    handles.groundObjListbox.String = listboxStr;

    if(length(listboxStr) >= 1)
        handles.groundObjListbox.Value = 1;
    end

    celBodyData = grndObjs.lvdData.celBodyData;
    populateBodiesCombo(celBodyData, handles.celestialBodyCombo);

    grndObj = getSelectedGroundObj(handles);
    updateGuiForGroundObj(grndObj, handles);

function updateGuiForGroundObj(grndObj, handles)
    if(not(isempty(grndObj)))
        grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');
        celBodyData = grndObjs.lvdData.celBodyData;

        handles.groundObjectNameText.String = grndObj.name;
        handles.groundObjDescText.String = grndObj.desc;

        [~, sortedBodyInfo] = ma_getSortedBodyNames(celBodyData);
        sortedBodyInfo = [sortedBodyInfo{:}];

        ind = find(grndObj.centralBodyInfo == sortedBodyInfo,1,'first');
        handles.celestialBodyCombo.Value = ind;

        handles.groundObjColorCombo.String = ColorSpecEnum.getListboxStr();
        handles.groundObjColorCombo.Value = ColorSpecEnum.getIndForName(grndObj.markerColor.name);

        handles.groundObjMarkerShapeCombo.String = MarkerStyleEnum.getListboxStr();
        handles.groundObjMarkerShapeCombo.Value = MarkerStyleEnum.getIndForName(grndObj.markerShape.name);

        handles.groundObjLineColorCombo.String = ColorSpecEnum.getListboxStr();
        handles.groundObjLineColorCombo.Value = ColorSpecEnum.getIndForName(grndObj.grdTrkLineColor.name);

        handles.groundObjLineSpecCombo.String = LineSpecEnum.getListboxStr();
        handles.groundObjLineSpecCombo.Value = LineSpecEnum.getIndForName(grndObj.grdTrkLineSpec.name);

        handles.initialTimeText.String = fullAccNum2Str(grndObj.initialTime);
        handles.extrapolateTimesCheckbox.Value = double(grndObj.extrapolateTimes);
        handles.loopGroundObjTrackCheckbox.Value = double(grndObj.loopWayPts);

        handles.waypointsListbox.String = grndObj.getWayListboxStr();

        wayPt = getSelectedWayPt(handles);
        updateGuiForWayPt(wayPt, grndObj, handles);

        toggleEnableForGuiElements('on', handles);
    else
        handles.groundObjectNameText.String = '';
        handles.groundObjDescText.String = '';
        handles.initialTimeText.String = '';

        updateGuiForWayPt(LaunchVehicleGroundObjectWayPt.empty(1,0), grndObj, handles);
        toggleEnableForGuiElements('off', handles);
    end

    setWayPtDeleteButtonEnable(handles);
    setGrndObjDeleteButtonEnable(handles);

function updateGuiForWayPt(wayPt, grndObj, handles)
    if(not(isempty(wayPt)))
        handles.waypointLatText.String = fullAccNum2Str(rad2deg(wayPt.getLatitude()));
        handles.waypointLongText.String = fullAccNum2Str(rad2deg(wayPt.getLongitude()));
        handles.waypointAltText.String = fullAccNum2Str(wayPt.getAltitude());
        handles.timeToNextWaypointText.String = fullAccNum2Str(wayPt.getTimesToNextWaypt());

        updateDistanceLabel(handles);
    else
        handles.waypointLatText.String = '';
        handles.waypointLongText.String = '';
        handles.waypointAltText.String = '';
        handles.timeToNextWaypointText.String = '';
        handles.distToNextWaypointLabel.String = '';

        handles.waypointsListbox.String = {};
    end

    setWayPtDeleteButtonEnable(handles);

function updateDistanceLabel(handles)
    grndObj = getSelectedGroundObj(handles);
    wayPt = getSelectedWayPt(handles);

    wayPt2 = grndObj.getNextWaypt(wayPt);
    if(not(isempty(wayPt2)))
        wayPtDistStr = fullAccNum2Str(grndObj.getDistanceBetweenWayPts(wayPt, wayPt2));
    else
        wayPtDistStr = '--';
    end

    handles.distToNextWaypointLabel.String = wayPtDistStr;

function wayPt = getSelectedWayPt(handles)
    grndObj = getSelectedGroundObj(handles);

    ind = handles.waypointsListbox.Value;
    wayPt = grndObj.getWayPointAtInd(ind);

function grndObj = getSelectedGroundObj(handles)
    grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');

    ind = handles.groundObjListbox.Value;
    grndObj = grndObjs.getGroundObjAtInd(ind);

function toggleEnableForGuiElements(enableStr, handles)
    handles.groundObjListbox.Enable = enableStr;
    handles.removeGroundObjButton.Enable = enableStr;

    handles.groundObjectNameText.Enable = enableStr;
    handles.groundObjDescText.Enable = enableStr;

    handles.celestialBodyCombo.Enable = enableStr;

    handles.groundObjColorCombo.Enable = enableStr;
    handles.groundObjMarkerShapeCombo.Enable = enableStr;
    handles.groundObjLineColorCombo.Enable = enableStr;
    handles.groundObjLineSpecCombo.Enable = enableStr;

    handles.initialTimeText.Enable = enableStr;
    handles.extrapolateTimesCheckbox.Enable = enableStr;
    handles.loopGroundObjTrackCheckbox.Enable = enableStr;

    handles.waypointsListbox.Enable = enableStr;
    handles.moveWaypointDownButton.Enable = enableStr;
    handles.moveWaypointUpButton.Enable = enableStr;
    handles.addWaypointButton.Enable = enableStr;
    handles.removeWaypointButton.Enable = enableStr;

    handles.waypointLatText.Enable = enableStr;
    handles.waypointLongText.Enable = enableStr;
    handles.waypointAltText.Enable = enableStr;
    handles.timeToNextWaypointText.Enable = enableStr;
    handles.distToNextWaypointLabel.Enable = enableStr;

function setGrndObjDeleteButtonEnable(handles)
    grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');
    numGrndObjs = grndObjs.getNumGroundObj();

    if(numGrndObjs > 0)
        handles.removeGroundObjButton.Enable = 'on';
    else
        handles.removeGroundObjButton.Enable = 'off';
    end

function setWayPtDeleteButtonEnable(handles)
    grndObj = getSelectedGroundObj(handles);

    if(not(isempty(grndObj)))
        numWayPts = grndObj.getNumWayPts();

        if(numWayPts > 1)
            handles.removeWaypointButton.Enable = 'on';
        else
            handles.removeWaypointButton.Enable = 'off';
        end
    else
        handles.removeWaypointButton.Enable = 'off';
    end

    % --- Outputs from this function are returned to the command line.
function varargout = lvd_EditGroundObjectsGUI_OutputFcn(hObject, eventdata, handles)
    % varargout  cell array for returning output args (see VARARGOUT);
    % hObject    handle to figure
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Get default command line output from handles structure
    if(isempty(handles))
        varargout{1} = false;
    else
        varargout{1} = true;
        close(handles.lvd_EditGroundObjectsGUI);
    end

    % --- Executes on selection change in groundObjListbox.
function groundObjListbox_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjListbox (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns groundObjListbox contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from groundObjListbox
    grndObj = getSelectedGroundObj(handles);
    updateGuiForGroundObj(grndObj, handles);

    % --- Executes during object creation, after setting all properties.
function groundObjListbox_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjListbox (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: listbox controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on button press in addGroundObjButton.
function addGroundObjButton_Callback(hObject, eventdata, handles)
    % hObject    handle to addGroundObjButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');
    celBodyData = grndObjs.lvdData.celBodyData;

    grndObj = LaunchVehicleGroundObject.getDefaultObj(celBodyData);
    grndObjs.addGroundObj(grndObj);

    handles.groundObjListbox.String = grndObjs.getListboxStr();
    handles.groundObjListbox.Value = grndObjs.getNumGroundObj();
    updateGuiForGroundObj(grndObj, handles);

    setGrndObjDeleteButtonEnable(handles);

    % --- Executes on button press in removeGroundObjButton.
function removeGroundObjButton_Callback(hObject, eventdata, handles)
    % hObject    handle to removeGroundObjButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');

    grndObj = getSelectedGroundObj(handles);

    tf = grndObj.isInUse();
    if(tf == false)
        grndObjs.removeGroundObj(grndObj);
        numGrndObjs = grndObjs.getNumGroundObj();

        grndObjs.lvdData.viewSettings.removeGrdObjFromViewProfiles(grndObj);

        handles.groundObjListbox.String = grndObjs.getListboxStr();

        if(handles.groundObjListbox.Value > numGrndObjs)
            handles.groundObjListbox.Value = numGrndObjs;
        end

        grndObj = getSelectedGroundObj(handles);
        updateGuiForGroundObj(grndObj, handles);
        setGrndObjDeleteButtonEnable(handles);
    else
        errordlg('The selected ground object could not be deleted.  It is in use as a constraint or geometric point.  Remove ground object dependecies first.','Could Not Delete Ground Object');
    end

function groundObjectNameText_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjectNameText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of groundObjectNameText as text
    %        str2double(get(hObject,'String')) returns contents of groundObjectNameText as a double
    grndObj = getSelectedGroundObj(handles);
    grndObj.name = get(hObject,'String');

    grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');
    handles.groundObjListbox.String = grndObjs.getListboxStr();

    % --- Executes during object creation, after setting all properties.
function groundObjectNameText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjectNameText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

function groundObjDescText_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjDescText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of groundObjDescText as text
    %        str2double(get(hObject,'String')) returns contents of groundObjDescText as a double
    grndObj = getSelectedGroundObj(handles);
    grndObj.desc = get(hObject,'String');

    % --- Executes during object creation, after setting all properties.
function groundObjDescText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjDescText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on selection change in groundObjColorCombo.
function groundObjColorCombo_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjColorCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns groundObjColorCombo contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from groundObjColorCombo
    grndObj = getSelectedGroundObj(handles);

    contents = cellstr(get(hObject,'String'));
    str = contents{get(hObject,'Value')};

    grndObj.markerColor = ColorSpecEnum.getEnumForListboxStr(str);

    % --- Executes during object creation, after setting all properties.
function groundObjColorCombo_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjColorCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: popupmenu controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on selection change in groundObjLineSpecCombo.
function groundObjLineSpecCombo_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjLineSpecCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns groundObjLineSpecCombo contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from groundObjLineSpecCombo
    grndObj = getSelectedGroundObj(handles);

    contents = cellstr(get(hObject,'String'));
    str = contents{get(hObject,'Value')};

    grndObj.grdTrkLineSpec = LineSpecEnum.getEnumForListboxStr(str);

    % --- Executes during object creation, after setting all properties.
function groundObjLineSpecCombo_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjLineSpecCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: popupmenu controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on selection change in waypointsListbox.
function waypointsListbox_Callback(hObject, eventdata, handles)
    % hObject    handle to waypointsListbox (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns waypointsListbox contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from waypointsListbox
    grndObj = getSelectedGroundObj(handles);
    wayPt = getSelectedWayPt(handles);
    updateGuiForWayPt(wayPt, grndObj, handles);

    % --- Executes during object creation, after setting all properties.
function waypointsListbox_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to waypointsListbox (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: listbox controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on button press in addWaypointButton.
function addWaypointButton_Callback(hObject, eventdata, handles)
    % hObject    handle to addWaypointButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    grndObj = getSelectedGroundObj(handles);

    initialTime = grndObj.initialTime;
    frameToUse = grndObj.centralBodyInfo.getBodyFixedFrame();
    wayPt = LaunchVehicleGroundObjectWayPt.getDefaultWayPt(initialTime, frameToUse);
    grndObj.addWaypoint(wayPt);

    handles.waypointsListbox.String = grndObj.getWayListboxStr();
    handles.waypointsListbox.Value = grndObj.getNumWayPts();
    updateGuiForWayPt(wayPt, grndObj, handles);

    setWayPtDeleteButtonEnable(handles);

    % --- Executes on button press in removeWaypointButton.
function removeWaypointButton_Callback(hObject, eventdata, handles)
    % hObject    handle to removeWaypointButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    grndObj = getSelectedGroundObj(handles);

    wayPt = getSelectedWayPt(handles);
    grndObj.removeWaypoint(wayPt);
    numWayPts = grndObj.getNumWayPts();

    handles.waypointsListbox.String = grndObj.getWayListboxStr();

    if(handles.waypointsListbox.Value > numWayPts)
        handles.waypointsListbox.Value = numWayPts;
    end

    wayPt = getSelectedWayPt(handles);
    updateGuiForWayPt(wayPt, grndObj, handles);
    setWayPtDeleteButtonEnable(handles);

    % --- Executes on button press in extrapolateTimesCheckbox.
function extrapolateTimesCheckbox_Callback(hObject, eventdata, handles)
    % hObject    handle to extrapolateTimesCheckbox (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hint: get(hObject,'Value') returns toggle state of extrapolateTimesCheckbox
    grndObj = getSelectedGroundObj(handles);
    grndObj.extrapolateTimes = logical(get(hObject,'Value'));

    % --- Executes on button press in loopGroundObjTrackCheckbox.
function loopGroundObjTrackCheckbox_Callback(hObject, eventdata, handles)
    % hObject    handle to loopGroundObjTrackCheckbox (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hint: get(hObject,'Value') returns toggle state of loopGroundObjTrackCheckbox
    grndObj = getSelectedGroundObj(handles);
    grndObj.loopWayPts = logical(get(hObject,'Value'));

    wayPt = getSelectedWayPt(handles);
    updateGuiForWayPt(wayPt, grndObj, handles);

function initialTimeText_Callback(hObject, eventdata, handles)
    % hObject    handle to initialTimeText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of initialTimeText as text
    %        str2double(get(hObject,'String')) returns contents of initialTimeText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

    grndObj = getSelectedGroundObj(handles);

    errMsg = {};

    value = str2double(get(hObject,'String'));
    enteredStr = get(hObject,'String');
    numberName = 'Initial Time';
    lb = 0;
    ub = Inf;
    isInt = false;
    errMsg = validateNumber(value, numberName, lb, ub, isInt, errMsg, enteredStr);

    if(isempty(errMsg))
        grndObj.initialTime = value;
    else
        hObject.String = fullAccNum2Str(grndObj.initialTime);

        msgbox(errMsg,'Invalid Initial Time Value','error');
    end

    % --- Executes during object creation, after setting all properties.
function initialTimeText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to initialTimeText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on button press in moveWaypointDownButton.
function moveWaypointDownButton_Callback(hObject, eventdata, handles)
    % hObject    handle to moveWaypointDownButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    grndObj = getSelectedGroundObj(handles);

    wayPtNum = get(handles.waypointsListbox,'Value');
    grndObj.moveWayPtAtIndexDown(wayPtNum);

    if(wayPtNum < grndObj.getNumWayPts())
        set(handles.waypointsListbox,'Value',wayPtNum+1);
    end

    handles.waypointsListbox.String = grndObj.getWayListboxStr();

    % --- Executes on button press in moveWaypointUpButton.
function moveWaypointUpButton_Callback(hObject, eventdata, handles)
    % hObject    handle to moveWaypointUpButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    grndObj = getSelectedGroundObj(handles);

    wayPtNum = get(handles.waypointsListbox,'Value');
    grndObj.moveWayPtAtIndexUp(wayPtNum);

    if(wayPtNum > 1)
        set(handles.waypointsListbox,'Value',wayPtNum-1);
    end

    handles.waypointsListbox.String = grndObj.getWayListboxStr();

function waypointLatText_Callback(hObject, eventdata, handles)
    % hObject    handle to waypointLatText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of waypointLatText as text
    %        str2double(get(hObject,'String')) returns contents of waypointLatText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

    wayPt = getSelectedWayPt(handles);

    errMsg = {};

    value = str2double(get(hObject,'String'));
    enteredStr = get(hObject,'String');
    numberName = 'Waypoint Latitude';
    lb = -90;
    ub = 90;
    isInt = false;
    errMsg = validateNumber(value, numberName, lb, ub, isInt, errMsg, enteredStr);

    if(isempty(errMsg))
        wayPt.setLatitude(deg2rad(value));

        grndObj = getSelectedGroundObj(handles);
        handles.waypointsListbox.String = grndObj.getWayListboxStr();
        updateDistanceLabel(handles);
    else
        hObject.String = fullAccNum2Str(rad2deg(wayPt.getLatitude()));

        msgbox(errMsg,'Invalid Waypoint Latitude Value','error');
    end

    % --- Executes during object creation, after setting all properties.
function waypointLatText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to waypointLatText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

function waypointLongText_Callback(hObject, eventdata, handles)
    % hObject    handle to waypointLongText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of waypointLongText as text
    %        str2double(get(hObject,'String')) returns contents of waypointLongText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

    wayPt = getSelectedWayPt(handles);

    errMsg = {};

    value = str2double(get(hObject,'String'));
    enteredStr = get(hObject,'String');
    numberName = 'Waypoint Longitude';
    lb = -360;
    ub = 360;
    isInt = false;
    errMsg = validateNumber(value, numberName, lb, ub, isInt, errMsg, enteredStr);

    if(isempty(errMsg))
        wayPt.setLongitude(deg2rad(value));

        grndObj = getSelectedGroundObj(handles);
        handles.waypointsListbox.String = grndObj.getWayListboxStr();
        updateDistanceLabel(handles);
    else
        hObject.String = fullAccNum2Str(rad2deg(wayPt.getLongitude()));

        msgbox(errMsg,'Invalid Waypoint Longitude Value','error');
    end

    % --- Executes during object creation, after setting all properties.
function waypointLongText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to waypointLongText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

function waypointAltText_Callback(hObject, eventdata, handles)
    % hObject    handle to waypointAltText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of waypointAltText as text
    %        str2double(get(hObject,'String')) returns contents of waypointAltText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

    wayPt = getSelectedWayPt(handles);

    errMsg = {};

    value = str2double(get(hObject,'String'));
    enteredStr = get(hObject,'String');
    numberName = 'Waypoint Altitude';
    lb = -Inf;
    ub = Inf;
    isInt = false;
    errMsg = validateNumber(value, numberName, lb, ub, isInt, errMsg, enteredStr);

    if(isempty(errMsg))
        wayPt.setAltitude(value);

        grndObj = getSelectedGroundObj(handles);
        handles.waypointsListbox.String = grndObj.getWayListboxStr();
        updateDistanceLabel(handles);
    else
        hObject.String = fullAccNum2Str(wayPt.getAltitude());

        msgbox(errMsg,'Invalid Waypoint Altitude Value','error');
    end

    % --- Executes during object creation, after setting all properties.
function waypointAltText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to waypointAltText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

function timeToNextWaypointText_Callback(hObject, eventdata, handles)
    % hObject    handle to timeToNextWaypointText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: get(hObject,'String') returns contents of timeToNextWaypointText as text
    %        str2double(get(hObject,'String')) returns contents of timeToNextWaypointText as a double
    newInput = get(hObject,'String');
    newInput = attemptStrEval(newInput);
    set(hObject,'String', newInput);

    wayPt = getSelectedWayPt(handles);

    errMsg = {};

    value = str2double(get(hObject,'String'));
    enteredStr = get(hObject,'String');
    numberName = 'Time to Next Waypoint';
    lb = 0.001;
    ub = Inf;
    isInt = false;
    errMsg = validateNumber(value, numberName, lb, ub, isInt, errMsg, enteredStr);

    if(isempty(errMsg))
        wayPt.timeToNextWayPt = value;

        grndObj = getSelectedGroundObj(handles);
        handles.waypointsListbox.String = grndObj.getWayListboxStr();
        updateDistanceLabel(handles);
    else
        hObject.String = fullAccNum2Str(wayPt.timeToNextWayPt);

        msgbox(errMsg,'Invalid Time to Next Waypoint Value','error');
    end

    % --- Executes during object creation, after setting all properties.
function timeToNextWaypointText_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to timeToNextWaypointText (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: edit controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on button press in saveAndCloseButton.
function saveAndCloseButton_Callback(hObject, eventdata, handles)
    % hObject    handle to saveAndCloseButton (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    errMsg = {};
    if(isempty(errMsg))
        uiresume(handles.lvd_EditGroundObjectsGUI);
    else
        msgbox(errMsg,'Errors were found while editing ground objects.','error');
    end

    % --- Executes on selection change in celestialBodyCombo.
function celestialBodyCombo_Callback(hObject, eventdata, handles)
    % hObject    handle to celestialBodyCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns celestialBodyCombo contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from celestialBodyCombo
    grndObjs = getappdata(handles.lvd_EditGroundObjectsGUI,'grndObjs');
    celBodyData = grndObjs.lvdData.celBodyData;
    [~, sortedBodyInfo] = ma_getSortedBodyNames(celBodyData);

    grndObj = getSelectedGroundObj(handles);

    ind = hObject.Value;
    grndObj.centralBodyInfo = sortedBodyInfo{ind};

    % --- Executes during object creation, after setting all properties.
function celestialBodyCombo_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to celestialBodyCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: popupmenu controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes during object creation, after setting all properties.
function distToNextWaypointLabel_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to distToNextWaypointLabel (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % --- Executes on selection change in groundObjLineColorCombo.
function groundObjLineColorCombo_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjLineColorCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns groundObjLineColorCombo contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from groundObjLineColorCombo
    grndObj = getSelectedGroundObj(handles);

    contents = cellstr(get(hObject,'String'));
    str = contents{get(hObject,'Value')};

    grndObj.grdTrkLineColor = ColorSpecEnum.getEnumForListboxStr(str);

    % --- Executes during object creation, after setting all properties.
function groundObjLineColorCombo_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjLineColorCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: popupmenu controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --- Executes on selection change in groundObjMarkerShapeCombo.
function groundObjMarkerShapeCombo_Callback(hObject, eventdata, handles)
    % hObject    handle to groundObjMarkerShapeCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)

    % Hints: contents = cellstr(get(hObject,'String')) returns groundObjMarkerShapeCombo contents as cell array
    %        contents{get(hObject,'Value')} returns selected item from groundObjMarkerShapeCombo
    grndObj = getSelectedGroundObj(handles);

    contents = cellstr(get(hObject,'String'));
    str = contents{get(hObject,'Value')};

    grndObj.markerShape = MarkerStyleEnum.getEnumForListboxStr(str);

    % --- Executes during object creation, after setting all properties.
function groundObjMarkerShapeCombo_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to groundObjMarkerShapeCombo (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: popupmenu controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end

    % --------------------------------------------------------------------
function enterUTAsDateTimeMenu_Callback(hObject, eventdata, handles)
    % hObject    handle to enterUTAsDateTimeMenu (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    secUT = enterUTAsDateTimeGUI(str2double(get(gco, 'String')));
    if(secUT >= 0)
        set(gco, 'String', fullAccNum2Str(secUT));
        initialTimeText_Callback(handles.initialTimeText, [], handles);
    end

    % --------------------------------------------------------------------
function getUTFromKSPMenu_Callback(hObject, eventdata, handles)
    % hObject    handle to getUTFromKSPMenu (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    secUT = readDoublesFromKSPTOTConnect('GetUT', '', true);
    if(secUT >= 0)
        set(gco, 'String', fullAccNum2Str(secUT));
        initialTimeText_Callback(handles.initialTimeText, [], handles);
    end

    % --------------------------------------------------------------------
function initialTimeContextMenu_Callback(hObject, eventdata, handles)
    % hObject    handle to initialTimeContextMenu (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)


function c = stringToCellArray(s)
%stringToCellArray Convert string with newlines to cell array of strings.
%   C = stringToCellArray(S) converts the input string S to a cell array of
%   strings, breaking up S at new lines.

%   Steven L. Eddins
%   Copyright 2009 The MathWorks, Inc.

if isempty(s)
    c = cell(0, 1);
else
    c = textscan(s, '%s', 'Delimiter', '\n', 'Whitespace', '');
    c = c{1};
end


function arg = reverse(arg)

% Copyright (C) Christian Kothe, SCCN, 2011, christian@sccn.ucsd.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

arg = arg([end:-1:1]);


function loadSession(baseDir)
% Loads the mrSESSION and SCANS structures from mrSESSION.mat.
%
%  loadSession([baseDir])
%
% baseDir: directory containing the mrSESSION.mat file
%             defaults to current directory (pwd)
%
% djh, 2/17/2001
% 7/16/02 djh, update mrSESSION to version3.01
% - inhomoCorrect can now be 0, 1, or 2
%   previous default of 0 is now option 1 (divide by mean)
%   0 now means do nothing
% - eliminate mrSESSION.vAnatomyPath
%   replace with global vANATOMYPATH
%
% ras, 04/05 -- got rid of all references to versions earlier
% than 3.0, on the grounds that none of those sessions would
% work anyomre anyway, and times have changed...

mrGlobals

if ~exist('HOMEDIR', 'var') || isempty(HOMEDIR), HOMEDIR = pwd; end
if ~exist('baseDir', 'var') || isempty(baseDir), baseDir = HOMEDIR; end

mrSessPath = fullfile(baseDir, 'mrSESSION.mat');

% Load mrSESSION & dataTYPES
if exist(mrSessPath,'file'), load(mrSessPath)
else  myErrorDlg(sprintf('No mrSESSION.mat file in %s \n', baseDir))
end

% Old mrSESSIONs don't seem to have this field.  Can we do better than
% setting it empty?  Maybe we should write and call a mrSESSION update
% routine that queries the user? -- BW
if(~isfield(mrSESSION,'sessionCode')), mrSESSION.sessionCode = 'sCode'; end
if(~isfield(mrSESSION,'description')), mrSESSION.description = 'sDesc'; end

return


%$Release 4.0
% Copyright (c) Copyright 2004 - 2006 - The General Hospital Corporation and
% President and Fellows of Harvard University.
%
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% *       Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% *       Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in the
% documentation and/or other materials provided with the distribution.
% *       Neither the name of The General Hospital Corporation and Harvard
% University nor the names of its contributors may be used to endorse or
% promote products derived from this software without specific prior written
% permission.
%
% The Software has been designed for research purposes only and has not been
% reviewed or approved by the Food and Drug Administration or by any other
% agency.  YOU ACKNOWLEDGE AND AGREE THAT CLINICAL APPLICATIONS ARE NEITHER
% RECOMMENDED NOR ADVISED.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

% --------------------------------------------------------------------
function DisplayAxesSDG(handles)
global maingui
global UNIT_TEST

tic;

if ~exist('handles','var')
    handles = [];
end

% This function plots the probe geometry
% Command line call:
% plotAxes_SDG(guidata(gcbo),bool);
%
if isempty(handles)
    maingui.axesSDG = InitAxesSDG(handles);
end
hAxes        = maingui.axesSDG.handles.axes;
iCh          = maingui.axesSDG.iCh;
iSrcDet      = maingui.axesSDG.iSrcDet;
SDPairColors = maingui.axesSDG.SDPairColors;
iWl_gui      = GetWl(handles);

SD          = maingui.dataTree.currElem.GetSDG('2D');

if isfield(maingui.axesSDG, 'xlim')
    xbox        = maingui.axesSDG.xlim;
    ybox        = maingui.axesSDG.ylim;
    bbox        = [xbox(1), xbox(2), ybox(1), ybox(2)];
else
    bbox        = maingui.dataTree.currElem.GetSdgBbox();
end

% Set axes handle properties and parameters
if ~ishandles(hAxes)
    return;
end
if length(bbox)<4
    return;
end

% Set gca to be SDG axes
axes(hAxes);

% Delete all channel lines drawn
if ishandles(maingui.axesSDG.handles.ch) && (maingui.axesSDG.handles.axes == hAxes)
    delete(maingui.axesSDG.handles.ch)
    delete(findobj(hAxes, 'Type', 'line'))  % Prevents previously drawn lines from piling up
end
axis(hAxes, [bbox(1), bbox(2), bbox(3), bbox(4)]);

%set(hAxes, 'xticklabel','', 'yticklabel','', 'xgrid','off, ygrid','off')
set(hAxes, 'xticklabel','')
bttndownfcn = get(hAxes,'ButtonDownFcn');
gridsize = get(hAxes, {'xlim', 'ylim', 'zlim'});
edgecol = 'none';
if ismac() || islinux()
	fs = 18;
else
	fs = 11;
end

% Go through all the data blocks and plot the channels in each block
nSrcs       = size(SD.SrcPos,1);
nDets       = size(SD.DetPos,1);

% get mlActAuto from procResult if it exists and replace ch.MeasListActMan
nDataBlks = maingui.dataTree.currElem.GetDataBlocksNum();
MeasList = [];
MeasListActMan = [];
MeasListActAuto = [];
MeasListVis = [];
for iBlk = 1:nDataBlks
    ch              = maingui.dataTree.currElem.GetMeasList(iBlk);
    chVis           = maingui.dataTree.currElem.GetMeasListVis(iBlk);
    MeasList        = [MeasList; ch.MeasList];
    MeasListActMan  = [MeasListActMan; ch.MeasListActMan];
    MeasListActAuto = [MeasListActAuto; ch.MeasListActAuto];
    MeasListVis     = [MeasListVis; chVis];
end
ml    = MeasList(MeasList(:,1)>0,:);
lstSDPairs = find(ml(:,4)==iWl_gui); %cw6info.displayLambda);
lstInvisible = find(MeasListVis(:,3)==0);
hCh = zeros(length(lstSDPairs),1);

LINESTYLE_MAN_EXCL = ':';
LINESTYLE_AUTO_EXCL = '--';
LINESTYLE_MAN_AND_AUTO_EXCL = '-.';

% Draw all channels
for ii = 1:length(lstSDPairs)
    hCh(ii) = line2(SD.SrcPos(ml(lstSDPairs(ii),1),:), SD.DetPos(ml(lstSDPairs(ii),2),:), [], gridsize, hAxes);

    col = [1.00 1.00 1.00] * 0.85;
    linestyle = '-';
    linewidth = 3;

    % Draw auto-excluded channel
    k1 = find( (MeasListActMan(:,1) == ml(lstSDPairs(ii),1))  &  (MeasListActMan(:,2) == ml(lstSDPairs(ii),2))  &  (MeasListActMan(:,4) == ml(lstSDPairs(ii),4)) );
    k2 = find( (MeasListActAuto(:,1) == ml(lstSDPairs(ii),1))  &  (MeasListActAuto(:,2) == ml(lstSDPairs(ii),2))  &  (MeasListActAuto(:,4) == ml(lstSDPairs(ii),4)) );

    if (~isempty(k1) && MeasListActMan(k1,3) == 0) && (~isempty(k2) && MeasListActAuto(k2,3) == 0)
        col = [1.00 0.6 0.6];
        linestyle = LINESTYLE_MAN_AND_AUTO_EXCL;
    elseif ~isempty(k1) &&  MeasListActMan(k1,3) == 0
        col = [1.00 1.00 1.00] * 0.85;
        linestyle = LINESTYLE_MAN_EXCL;
    elseif ~isempty(k2) && MeasListActAuto(k2,3) == 0
        col = [1.00 0.6 0.6];
        linestyle = LINESTYLE_AUTO_EXCL;
    end

    if ismember(ii, lstInvisible)
        linewidth = 1;
    end

    set(hCh(ii), 'color',col, 'linewidth',linewidth, 'ButtonDownFcn',bttndownfcn, 'linestyle',linestyle);
end

% Draw the user-selected channels
for idx = 1:size(iSrcDet,1)
    hCh(idx+ii) = line2(SD.SrcPos(iSrcDet(idx,1),:), SD.DetPos(iSrcDet(idx,2),:), [], gridsize, hAxes);

    % Attach toggle callback to the selected channels for function on
    % second click
    iSD = GetSelectedSDPairIndex( iSrcDet(idx,1), iSrcDet(idx,2) );
    set(hCh(idx+ii), 'color',SDPairColors(iSD,:), 'ButtonDownFcn',sprintf('toggleLinesAxesSDG_ButtonDownFcn(gcbo,[%d],guidata(gcbo))',idx), 'linewidth',2);

    linestyle = '-';
    linewidth = 3;
    if ~isempty(iCh)
        k1 = find( (MeasListActMan(:,1)==iSrcDet(idx,1))  &  (MeasListActMan(:,2)==iSrcDet(idx,2))  &  (MeasListActMan(:,4)==iWl_gui) );
        k2 = find( (MeasListActAuto(:,1)==iSrcDet(idx,1))  &  (MeasListActAuto(:,2)==iSrcDet(idx,2)) &  (MeasListActMan(:,4)==iWl_gui) );
        k3 = find( (MeasListVis(:,1) == iSrcDet(idx,1))  &  (MeasListVis(:,2) == iSrcDet(idx,2)));

        if (~isempty(k1) && MeasListActMan(k1,3) == 0) && (~isempty(k2) && MeasListActAuto(k2,3) == 0)
            linestyle = LINESTYLE_MAN_AND_AUTO_EXCL;
        elseif ~isempty(k1) &&  MeasListActMan(k1,3) == 0
            linestyle = LINESTYLE_MAN_EXCL;
        elseif ~isempty(k2) && MeasListActAuto(k2,3) == 0
            linestyle = LINESTYLE_AUTO_EXCL;
        end

        if ~isempty(k3) && MeasListVis(k3,3) == 0
            linewidth = 1;
        end
    end
    set(hCh(idx+ii),'linewidth', linewidth, 'linestyle',linestyle);
end

if (maingui.axesSDG.handles.axes == hAxes)
    maingui.axesSDG.handles.ch = hCh;
end

% ADD SOURCE AND DETECTOR LABELS
hSD = zeros(nSrcs+nDets,1);
if isempty(maingui.axesSDG.handles.SD) || (maingui.axesSDG.handles.axes ~= hAxes)
    for idx1 = 1:nSrcs
        if ~isempty(find(MeasList(:,1)==idx1)) %#ok<*EFIND>
            hSD(idx1) = text( SD.SrcPos(idx1,1), SD.SrcPos(idx1,2), sprintf('%d', idx1), 'fontsize',fs, 'fontweight','bold', 'color','r' );
            set(hSD(idx1), 'ButtonDownFcn',get(hAxes,'ButtonDownFcn'), 'horizontalalignment','center', 'edgecolor',edgecol, 'Clipping', 'on');
        end
    end
    for idx2 = 1:nDets
        if ~isempty(find(MeasList(:,2)==idx2))
            hSD(idx2+idx1) = text( SD.DetPos(idx2,1), SD.DetPos(idx2,2), sprintf('%d', idx2), 'fontsize',fs, 'fontweight','bold', 'color','b' );
            set(hSD(idx2+idx1), 'ButtonDownFcn',get(hAxes,'ButtonDownFcn'), 'horizontalalignment','center', 'edgecolor',edgecol, 'Clipping', 'on');
        end
    end
    if (maingui.axesSDG.handles.axes == hAxes)
        maingui.axesSDG.handles.SD = hSD;
    end
else
    uistack(nonzeros(maingui.axesSDG.handles.SD),'top')
end

% Turn off zoom but only for SDG axes
h = zoom(hAxes);
if isempty(UNIT_TEST) || ~UNIT_TEST
    setAllowAxesZoom(h, hAxes, 0);
end

% fprintf('DisplayAxesSDG: Elapsed Time - %0.3f\n', toc);


function [CP, ppf] = getControlPoints( input_A, input_B, maxppf, ransac)
    disp('Detecting and Matching SIFT features...');
    fileListA = dir(input_A);
    fileListA = fileListA(3:length(fileListA));
    fileListB = dir(input_B);
    fileListB = fileListB(3:length(fileListB));
    nFrames = min(length(fileListA), length(fileListB));
    CP = zeros(nFrames, maxppf, 4);
    ppf = zeros(nFrames, 1);
    trackerA = vision.PointTracker('MaxBidirectionalError', 1);
    trackerB = vision.PointTracker('MaxBidirectionalError', 1);
    for frameIndex = 1:nFrames
        fprintf('%5d', frameIndex);
        if mod(frameIndex, 20) == 0
            fprintf('\n') ;
        end

        fileNameA = fileListA(frameIndex).name;
        fileNameB = fileListB(frameIndex).name;
        IA = imread([input_A fileNameA]);
        IB = imread([input_B fileNameB]);

        [H, W, ~] = size(IA);

        if frameIndex > 1
            setPoints(trackerA, trackA);
            [trackAcont, validityA] = step(trackerA, IA);
            setPoints(trackerB, trackB);
            [trackBcont, validityB] = step(trackerB, IB);
            trackAcont = trackAcont(validityA & validityB, :);
            trackBcont = trackBcont(validityA & validityB, :);
        end
        if ransac
            [trackAsurf, trackBsurf] = SURF(IA, IB);
        else
            [trackAsurf, trackBsurf] = SURF2(IA, IB);
        end

        trackA = trackAsurf;
        trackB = trackBsurf;

        valid = trackA(:, 1) > 0 & trackA(:, 1) < W & trackA(:, 2) > 0 & trackA(:, 2) < H ...
            & trackB(:, 1) > 0 & trackB(:, 1) < W & trackB(:, 2) > 0 & trackB(:, 2) < H;

        trackA = trackA(valid, :);
        trackB = trackB(valid, :);

        valid = filtermask(IA, trackA);
        trackA = trackA(valid, :);
        trackB = trackB(valid, :);
        valid = filtermask(IB, trackB);
        trackA = trackA(valid, :);
        trackB = trackB(valid, :);

        if frameIndex == 1
            initialize(trackerA, trackA, IA);
            initialize(trackerB, trackB, IB);
        else
            if length(trackA) + length(trackAcont) > maxppf
                ordering = randperm(length(trackAcont));
                trackAcont = trackAcont(ordering(1:maxppf - length(trackA)), :);
                trackBcont = trackBcont(ordering(1:maxppf - length(trackA)), :);
            end
            trackA = [trackA ; trackAcont];
            trackB = [trackB ; trackBcont];
        end

        IA = insertMarker(IA, trackA, 'o', 'color', 'red');
        IB = insertMarker(IB, trackB, 's', 'color', 'yellow');
        figure(1);
        imshow(IA);
        figure(2);
        imshow(IB);

        if length(trackA) > maxppf
            ppf(frameIndex) = maxppf;
            CP(frameIndex, :, 1:2) = trackA(1:maxppf, :);
            CP(frameIndex, :, 3:4) = trackB(1:maxppf, :);
        else
            ppf(frameIndex) = length(trackA);
            CP(frameIndex, 1:ppf(frameIndex), 1:2) = trackA;
            CP(frameIndex, 1:ppf(frameIndex), 3:4) = trackB;
        end
%         CP(frameIndex, :, :) = [featuresA featuresB; zeros(maxppf - ppf(frameIndex), 4)];

    end
end

function valid = filtermask(frame, points_)
    [H, W, ~] = size(frame);
    valid = points_(:, 1) > 0 & points_(:, 1) < W & points_(:, 2) > 0 & points_(:, 2) < H;
    points_ = points_(valid, :);
    mask = frame(:, :, 1) < 20 & frame(:, :, 2) < 20 & frame(:, :, 3) < 20;
    mask = imgaussfilt(double(mask), 50);
    videoH = size(frame, 1);
    mask(mask > 0.2) = 1;
    mask(mask ~= 1) = 0;
    valid = mask(round(points_(:, 1) - 1) * videoH + round(points_(:, 2))) == 0;
end


function optFlagC = checkInputStructsROE(modelC)
% Identify optional input structures

optFlagC = cell(1,length(modelC));
for modelNum = 1:length(modelC)
    if isstruct(modelC{modelNum}.parameters.structures)
        structC = fieldnames(modelC{modelNum}.parameters.structures);
        flagV = false(1,length(structC));
        for strNum = 1:length(structC)
            strParS = modelC{modelNum}.parameters.structures.(structC{strNum});
            if isfield(strParS,'optional') && strcmpi(strParS.optional,'yes')
                flagV(strNum) = true;
            end
        end
        optFlagC{modelNum} = flagV;
    else
        optFlagC{modelNum} = false;
    end
end

end