function varargout = gsynsq(varargin)
% function hgui = gsynsq()
%
% Runs the Synchrosqueezing toolbox GUI.
% Does not require input parameters.  Returns a handle to the GUI.
%
% This GUI tool can be used for speedy/rough Synchrosqueezing
% analysis, filtering, component extraction of signals.
%
%
%---------------------------------------------------------------------------------
%    Synchrosqueezing Toolbox
%    Authors: Eugene Brevdo (http://www.math.princeton.edu/~ebrevdo/)
%---------------------------------------------------------------------------------

% Begin initialization code - DO NOT EDIT
gui_Singleton = 0;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @gsynsq_OpeningFcn, ...
                   'gui_OutputFcn',  @gsynsq_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 gsynsq is made visible.
function gsynsq_OpeningFcn(hObj, eventdata, hs, varargin)
% This function has no output args, see OutputFcn.
% hObj    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
% varargin   command line arguments to gsynsq (see VARARGIN)

% Choose default command line output for gsynsq
hs.output = hObj;

% This sets up the initial plot - only do when we are invisible
% so window can get raised using gsynsq.
if strcmp(get(hObj,'Visible'),'off')
    set(hs.axesx, 'Visible', 'off');
    set(hs.axesTx, 'Visible', 'off');
    set(hs.axesrx, 'Visible', 'off');

    % Set up zoom
    zh = zoom;

    setAllowAxesZoom(zh, hs.axesx, true);
    setAllowAxesZoom(zh, hs.axesTx, true);
    setAllowAxesZoom(zh, hs.axesrx, true);

    % Link the axes w.r.t. zoom (just the t-axis component)
    linkaxes([hs.axesx hs.axesTx hs.axesrx], 'x');

    % Menu bar with "reset axes" does not work with linked axes
    set(zh, 'RightClickAction', 'InverseZoom');

    % Append any past ActionPostCallBack to run first
    %set(zh, 'ActionPostCallback', @postZoomUpdate);

    % Allow zoom
    set(zh, 'Enable', 'on');

    hs.zh = zh;

    % Initialize internal data structure placeholders
    hs = clear_data(hs);
end

% Update hs structure
guidata(hObj, hs);

% UIWAIT makes gsynsq wait for user response (see UIRESUME)
% uiwait(hs.figure1);

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

% Get default command line output from hs structure
varargout{1} = hs.output;

% --------------------------------------------------------------------
function FileMenu_Callback(hObj, eventdata, hs)
% hObj    handle to FileMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% --------------------------------------------------------------------
function OpenMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to OpenMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
file = uigetfile('*.mat');
if ~isequal(file, 0)
    try
        tx = load(file);
        assert(isfield(tx, 't'));
        assert(isfield(tx, 'x'));
        assert(length(tx.t) == length(tx.x));
    catch ME
        errordlg(sprintf('File load error: %s', ME.message), ...
                 'File Load Error');
        return;
    end

    load_data(hObj, eventdata, hs, file, tx);
end

function load_data(hObj, eventdata, hs, file, tx)
% Do something here about old data, if any?
if (hs.active && ~hs.saved)
end

% Load up the data.  First clear old data
hs = clear_data(hs);
hs.data.t = tx.t;
hs.data.x = tx.x - mean(tx.x);

[fpath, fname] = fileparts(file);
hs.prefix = fullfile(fpath, fname);

% Plot axes
plot(hs.axesx, hs.data.t, hs.data.x);
set(hs.axesx, 'Visible', 'on');
grid(hs.axesx, 'on');
axis(hs.axesx,'tight');
setAxesZoomMotion(hs.zh, hs.axesx, 'horizontal');
set(hs.axesx,'FontSize',10);

% Hide other axes and delete colorbars, if any
axes(hs.axesTx);
colorbar('delete');

cla(hs.axesTx);
set(hs.axesTx, 'Visible', 'off');
cla(hs.axesrx);
set(hs.axesrx, 'Visible', 'off');

% Save app data
guidata(hObj, hs);

% --------------------------------------------------------------------
function PrintMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to PrintMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
printdlg(hs.figure1)

% --------------------------------------------------------------------
function ExitMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to CloseMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
figure1_CloseRequestFcn(hObj, eventdata, hs);

% --- Executes when user attempts to close figure1.
function figure1_CloseRequestFcn(hObj, eventdata, hs)
% hObj    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% If the analysis has been modified but not been saved
if (hs.active && ~hs.saved)
    selection = questdlg(['Unsaved Analysis.  Close ' get(hs.figure1,'Name') '?'],...
        ['Close ' get(hs.figure1,'Name') '...'],...
        'Yes','No','Yes');
    if strcmp(selection,'No')
        return;
    end
end

% Closes the figure
delete(hs.output);

% Initializes / clears data from last job from hs
function hs = clear_data(hs)
% Is there an analysis active?
hs.active = 0;
% Has it been saved since the last change?
hs.saved = 0;

% What is the file prefix of the current analysis?
hs.prefix = '';

% Default parameters for the transforms
hs.padtype = 'symmetric';
% Number of voices
hs.nv = 32;
% Wavelet type and other options
hs.opt = struct('disp', 1, 'bd', 1, 'gamma', sqrt(eps), 'type', 'morlet');

% Placeholders for the input data
hs.data = [];

% Placeholders for the results of the analysis part
hs.data.as = [];
hs.data.fs = [];
hs.data.Wx = [];
hs.data.Tx = [];

% Placeholders for the results of the synthesis part
hs.data.rx = [];

% Placeholder for the results of synthesis after removing padding
%hs.data.rx0 = [];

% Placeholder for the extracted curves
hs.data.Cs = [];
hs.data.xrs = [];

% Attenuation filters
if isfield(hs, 'filters')
    for hi = 1:length(hs.filters)
        delete(hs.filters(hi).f);
    end
end
hs.filters = [];

% --------------------------------------------------------------------
function openpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to openpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
OpenMenuItem_Callback(hObj, eventdata, hs);

% --------------------------------------------------------------------
function savepush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to savepush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
SaveMenuItem_Callback(hObj, eventdata, hs);

% --------------------------------------------------------------------
function zoomtoggle_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to zoomtoggle (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% ZOOM

% --------------------------------------------------------------------
function zoomtoggle_OffCallback(hObj, eventdata, hs)
% hObj    handle to zoomtoggle (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Disable zoom
zoom_disable(hs);

function zoom_disable(hs)
set(hs.zoomtoggle, 'State', 'off');
set(hs.ZoomMenuItem, 'Checked', 'off');
set(hs.zh, 'Enable', 'off');

% --------------------------------------------------------------------
function zoomtoggle_OnCallback(hObj, eventdata, hs)
% hObj    handle to zoomtoggle (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Allow zoom
%if strcmp(get(hObj,'Visible'),'off')
zoom_enable(hs);
%end

function zoom_enable(hs)
set(hs.zoomtoggle, 'State', 'on');
set(hs.zh, 'Enable', 'on');
set(hs.ZoomMenuItem, 'Checked', 'on');

%%% NOT USED %%%
function postZoomUpdate(hObj, eventdata)
% Get the new horizontal values of the updated axes
XLim = get(eventdata.Axes, 'XLim');

% Update the values for each of the axes
hs = guidata(hObj);

XLim(1) = max(XLim(1), min(hs.data.t));
XLim(2) = min(XLim(2), max(hs.data.t));
set(eventdata.Axes, 'XLim', XLim);

if (eventdata.Axes ~= hs.axesTx), set(hs.axesTx, 'XLim', XLim); end
if (eventdata.Axes ~= hs.axesx), set(hs.axesx, 'XLim', XLim); end
if (eventdata.Axes ~= hs.axesrx), set(hs.axesrx, 'XLim', XLim); end

% --------------------------------------------------------------------
function SaveMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to SaveMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
if (~hs.active || hs.saved)
    return;
end

file = uiputfile(sprintf('%s_r.mat', hs.prefix));
if ~isequal(file, 0)
    try
        data = hs.data;
        save(file, '-struct', 'data');
    catch ME
        errordlg(sprintf('File save error: %s', ME.message), ...
            'File Save Error');
        return;
    end

    hs.saved = 1;
    guidata(hObj, hs);
end

% --------------------------------------------------------------------
function ExportMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to ExportMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
if (~hs.active), return; end
labels = {'Export t?', 'Export x(t)?', 'Export as?', 'Export Wx(a,t)?', ...
          'Export fs?', 'Export Tx(f,t)?', 'Export rx(t) (reconstructed)?', ...
          'Export filters?', 'Export identified contours?', ...
          'Export extracted components?'};
varnames = {'t', 'x', 'as', 'Wx', 'fs', 'Tx', 'rx', ...
            'filters', 'Cs', 'xrs'};
filters = hs.filters;
for fi=1:length(filters)
    filters(fi).bbox = filters(fi).f.getPosition();
    filters(fi).mask = filters(fi).f.createMask();
    % bbox = [tmin tmax fmin fmax]
    % filters(fi).bbox = [filters(fi).bbox([1 3])];
    tmin = filters(fi).bbox(1);
    tmax = tmin + filters(fi).bbox(3);
    flogmin = filters(fi).bbox(2);
    flogmax = flogmin + filters(fi).bbox(4);
    filters(fi).bbox = [tmin tmax 2^flogmin 2^flogmax];
end
if length(filters)>0, filters = rmfield(filters, 'f'); end
vars = {hs.data.t, hs.data.x, hs.data.as, ...
        hs.data.Wx, hs.data.fs, hs.data.Tx, hs.data.rx, ...
        filters, hs.data.Cs, hs.data.xrs};
selected = logical([0 0 0 0 0 0 0 0 0 0]);
export2wsdlg(labels, varnames, vars, 'Export to Workspace', selected);

% TODO - export to a variable with proper prefix and name of choice

% --------------------------------------------------------------------
function AnalysisMenu_Callback(hObj, eventdata, hs)
% hObj    handle to AnalysisMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% --------------------------------------------------------------------
function AnalysisMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to AnalysisMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_analysis(hObj, eventdata, hs);

function run_analysis(hObj, eventdata, hs)
if (isempty(hs.data))
    errordlg('Load a data file first', 'Error');
    return;
end

prompt = {'Wavelet [morlet,bump,hhhat,...]', ...
          'Number of Voices (nv)', ...
          'Pad type [symmetric, circular, replicate]', ...
          'Gamma (\gamma)'};
name = 'Synchrosqueezing Analysis Parameters';
answers = {hs.opt.type, num2str(hs.nv), hs.padtype, num2str(hs.opt.gamma)};
idata = inputdlg(prompt, name, 1, answers, struct('WindowStyle', 'normal'));
if isempty(idata)
    return
end

hs.opt.type = idata{1};
hs.nv = str2num(idata{2});
hs.padtype = idata{3};
hs.opt.gamma = str2num(idata{4});

[hs.data.Tx, hs.data.fs, hs.data.Wx, hs.data.as] = ...
    synsq_cwt_fw(hs.data.t, hs.data.x, hs.nv, hs.opt);

axes(hs.axesTx);
cla(hs.axesTx);
tplot(hs.data.Tx, hs.data.t, hs.data.fs, hs.opt);
set(hs.axesTx, 'Visible', 'on');
grid(hs.axesTx, 'on');
setAxesZoomMotion(hs.zh, hs.axesTx, 'vertical');
set(hs.axesTx,'FontSize',10);
hs.filters = [];

hs.active = 1;

% Store application parameters
guidata(hObj, hs);

% --------------------------------------------------------------------
function ReconMenuItem_Callback(hObj, eventdata, hs)
% hObj    handle to ReconMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_recon(hObj, eventdata, hs);

function run_recon(hObj, eventdata, hs);

if (~hs.active)
    errordlg('Run Analysis First', 'Error');
    return;
end

Tx = filter_Tx(hs);

hs.data.rx = synsq_cwt_iw(Tx, hs.data.fs, hs.opt);

plot(hs.axesrx, hs.data.t, hs.data.rx);
grid(hs.axesrx,'on');
set(hs.axesrx, 'Visible', 'on');
set(hs.axesrx,'FontSize',10);
axis(hs.axesrx, [min(hs.data.t) max(hs.data.t) min(hs.data.x) max(hs.data.x)]);

XLim = get(hs.axesx, 'XLim'); set(hs.axesrx, 'XLim', XLim);
setAxesZoomMotion(hs.zh, hs.axesrx, 'horizontal');
%zoom_enable(hs);

guidata(hObj, hs);

function Tx = filter_Tx(hs)
% Logic to see if any attenuation and/or passband filters are active
Tx = hs.data.Tx;
if ~isempty(hs.filters)
    % Step 1: combine all pass-band filters
    maskp = zeros(size(hs.data.Tx));
    maska = ones(size(hs.data.Tx));
    nmp = 0;
    for fi=1:length(hs.filters)
        mask = hs.filters(fi).f.createMask();
        if strcmpi(hs.filters(fi).type, 'atten')
            maska = maska & ~mask;
        else
            maskp = maskp | mask;
            nmp = nmp + 1;
        end
    end

    % Mask via the filters
    if (nmp > 0)
        Tx = Tx .* maskp .* maska;
    else
        Tx = Tx .* maska;
    end
end

% --------------------------------------------------------------------
function FiltersMenu_Callback(hObj, eventdata, hs)
% hObj    handle to FiltersMenu (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% --------------------------------------------------------------------
function RectAtten_Callback(hObj, eventdata, hs)
% hObj    handle to RectAtten (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'recta')

% --------------------------------------------------------------------
function filter_enable(hObj, hs, type)

% Check to make sure analysis is active
if (~hs.active)
    return;
end

% Disable zoom icon
set(hs.zoomtoggle, 'State', 'off');

% Start filter selection - this disabled zoom
fhs = filter_construct(hs, type);

% Make sure that it's within the proper constraints at first
for fhi=1:length(fhs)
    fh = fhs(fhi);
    fh.setConstrainedPosition(fh.getPosition());

    if strcmpi(type(end), 'a')
        hs.filters = [hs.filters struct('type', 'atten', 'f', fh)];
    else
        hs.filters = [hs.filters struct('type', 'pass', 'f', fh)];
    end
end

% Save
guidata(hObj, hs);

function h = filter_construct(hs, type)
switch type
  case 'recta',
    h = imrect(hs.axesTx);
  case 'ellipsea',
    h = imellipse(hs.axesTx);
  case 'polya',
    h = impoly(hs.axesTx);
  case 'rectp',
    h = imrect(hs.axesTx);
  case 'bda',
    t = hs.data.t(:);
    fs = [hs.data.fs(1), hs.data.fs(2:hs.nv:end-1), hs.data.fs(end)]; % Subsample
    [Lbdi, Rbdi] = synsq_bd_loc(t, fs);
    fsbd = [fs(1); fs(:); fs(end)];
    Lbdi = [1; Lbdi(:); 1];
    Rbdi = [length(t); Rbdi(:); length(t)];

    hL = impoly(hs.axesTx, [t(Lbdi) log2(fsbd)]);
    hR = impoly(hs.axesTx, [t(Rbdi) log2(fsbd)]);
    h = [hL hR];
  otherwise,
    error('Unknown filter type');
end

for j=1:length(h)
    hj = h(j);

    cstrstr = class(hj);

    % Set constraints
    constrfcn = makeConstrainToRectFcn(cstrstr, ...
      [min(hs.data.t)-sqrt(eps) max(hs.data.t)], ...
      [min(log2(hs.data.fs))-sqrt(eps) max(log2(hs.data.fs))]);
    hj.setPositionConstraintFcn(constrfcn);

    % Set color
    if strcmpi(type(end), 'a')
        hj.setColor('r');
    else
        hj.setColor('g');
    end
end

% --- Executes on mouse press over axes background.
function axesTx_ButtonDownFcn(hObj, eventdata, hs)
% hObj    handle to axesTx (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% Is not called due to axis ownership by zoom object

% --------------------------------------------------------------------
function EllipseAtten_Callback(hObj, eventdata, hs)
% hObj    handle to EllipseAtten (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'ellipsea')

% --------------------------------------------------------------------
function PolyAtten_Callback(hObj, eventdata, hs)
% hObj    handle to PolyAtten (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'polya')

% --------------------------------------------------------------------
function filterselector_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to filterselector (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% FILTER SELECTOR

% --------------------------------------------------------------------
function filterselector_OffCallback(hObj, eventdata, hs)
% hObj    handle to filterselector (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% FILTER SELECTOR

% --------------------------------------------------------------------
function filterselector_OnCallback(hObj, eventdata, hs)
% hObj    handle to filterselector (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)

% FILTER SELECTOR

% --------------------------------------------------------------------
function rectattenpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to rectattenpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'recta')

% --------------------------------------------------------------------
function polyattenpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to polyattenpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'polya')

% --------------------------------------------------------------------
function ellipseattenpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to ellipseattenpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
filter_enable(hObj, hs, 'ellipsea')

% --------------------------------------------------------------------
function analysispush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to analysispush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_analysis(hObj, eventdata, hs);

% --------------------------------------------------------------------
function reconpush_ClickedCallback(hObj, eventdata, hs)
% hObj    handle to reconpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% hs    structure with hs and user data (see GUIDATA)
run_recon(hObj, eventdata, hs);

% --------------------------------------------------------------------
function ZoomMenuItem_Callback(hObject, eventdata, handles)
% hObject    handle to ZoomMenuItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if strcmpi(get(handles.zh, 'Enable'), 'on'),
    zoomtoggle_OffCallback(hObject, eventdata, handles);
else
    zoomtoggle_OnCallback(hObject, eventdata, handles);
end

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

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

prompts = {'Import t variable: ', 'Import x(t) variable: ', 'Prefix: '};
name = 'Import signal from workspace';
danswers = {'t', 'x', 'ws'};
idata = inputdlg(prompts, name, 1, danswers, struct('WindowStyle', 'normal'));

% Cancelled
if isempty(idata),
    return;
end

try
    tx = struct('t', evalin('base', idata{1}), ...
        'x', evalin('base', idata{2}));
    assert(length(tx.t) == length(tx.x));
catch ME
    errordlg(sprintf('Import error: %s', ME.message), ...
        'Import Error');
        return;
end

% Set some file prefix in the current directory
file = fullfile(pwd, idata{3});

load_data(hObject, eventdata, handles, file, tx);

% --------------------------------------------------------------------
function rectpasspush_ClickedCallback(hObj, eventdata, hs)
% hObject    handle to rectpasspush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'rectp');

% --------------------------------------------------------------------
function RectPass_Callback(hObj, eventdata, hs)
% hObject    handle to RectPass (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'rectp');

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

% Extract a contour
function extract_contour(hObj, hs)

if (~hs.active)
    errordlg('Run Analysis First', 'Error');
    return;
end

prompt = {'Number of Curves to Extract', ...
          'Lambda (smoothness penalty)', ...
         'Extraction window (n_w)'};
name = 'Contour Extraction Parameters';
numlines = 1;
answers = {'2', '1e4', num2str(hs.nv/2)};

idata = inputdlg(prompt, name, numlines, answers, struct('WindowStyle', 'normal'));

% User cancelled
if isempty(idata),
    return;
end

nc = str2num(idata{1});
lambda = str2num(idata{2});
nw = str2num(idata{3});

Tx = filter_Tx(hs);
[na, N] = size(Tx);

% Extract curves for plotting
[hs.data.Cs,Es] = curve_ext_multi(Tx, log2(hs.data.fs), nc, lambda, nw);

% Extract functions for export
hs.data.xrs = curve_ext_recon(Tx, hs.data.fs, hs.data.Cs, hs.opt, nw);

% Save
guidata(hObj, hs);

figure;
hc = plot_ext_curves(hs.data.t, hs.data.x, ...
                     Tx, hs.data.fs, hs.data.Cs, Es, hs.opt);

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

% --------------------------------------------------------------------
function AttenBd_Callback(hObj, eventdata, hs)
% hObject    handle to AttenBd (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'bda');

% --------------------------------------------------------------------
function attenbdpush_ClickedCallback(hObj, eventdata, hs)
% hObject    handle to attenbdpush (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
filter_enable(hObj, hs, 'bda');


function [ outp, sdp_mode ] = cvx_pushcnstr( x, y, op, sdp_mode )

global cvx___
try
    pstr = cvx___.problems(end);
catch
    cvx_throw( 'No CVX model is present.' );
end

%
% Check problem
%

y_orig = y;
if isa( x, 'cvxcnst' ),
    x = rhs( x );
end

%
% Check for a dual reference
%

dx = cvx_getdual( x );
dy = cvx_getdual( y );
if isempty( dx ),
    dx = dy;
elseif ~isempty( dy ),
    cvx_throw( 'Two dual variable references found: %s %s', dx, dy );
end
if ~isempty( dx ),
    duals = pstr.duals;
    try
        dual = builtin( 'subsref', duals, dx );
    catch
        nm = cvx_subs2str( dx );
        cvx_throw( 'Dual variable "%s" has not been declared.', nm(2:end) );
    end
    if ~isempty( dual ),
        nm = cvx_subs2str( dx );
        cvx_throw( 'Dual variable "%s" is already in use.', nm(2:end) );
    end
end

%
% Check arguments
%

cx = isnumeric( x ) | isa( x, 'cvx' );
cy = isnumeric( y ) | isa( y, 'cvx' );
if ~cx,
    x = cvx_collapse( x, false, true );
    cx = isnumeric( x ) | isa( x, 'cvx' );
end
if ~cy,
    y = cvx_collapse( y, false, true );
    cy = isnumeric( y ) | isa( y, 'cvx' );
end
if ~cx || ~cy,
    if cx || cy || op(1) ~= '=',
        cvx_throw( 'Invalid CVX constraint: {%s} %s {%s}', class( x ), op, class( y ) );
    end
    sx = size( x );
    if ~isequal( sx, size( y ) ),
        cvx_throw( 'The left- and right-hand sides have incompatible sizes.' );
    else
        if ~isempty( dx ),
            pstr.duals = builtin( 'subsasgn', pstr.duals, dx, cell(sx) );
            cvx___.problems( end ) = pstr;
        end
        for k = 1 : prod( sx ),
            cvx_pushcnstr( x{k}, y{k}, op );
        end
        if nargout,
            outp = cvxcnst( y_orig );
        end
        return
    end
end

persistent param_eq param_ge param_le
if isempty( param_eq ),
    param_eq.map = cvx_remap( ...
            { { 'affine',  } }, ...
            { { 'l_affine' } } );
    param_eq.funcs = { @minus_nc, @eq_2 };
    param_eq.name = '=';
    param_eq.constant = [];
    param_ge.map = cvx_remap( ...
            { { 'constant' } }, ...
            { { 'l_concave' }, { 'l_convex' } }, ...
            { { 'concave' }, { 'convex' } }, ...
            [ 1, 2, 1 ] );
    param_ge.funcs = { @minus_nc, @ge_2 };
    param_ge.name = '>=';
    param_ge.constant = false;
    param_le.map = param_ge.map';
    param_le.funcs = { @le_1, @le_2 };
    param_le.name = '<=';
    param_le.constant = false;
end

if nargin < 5,
    sdp_mode = pstr.sdp;
end
switch op(1),
case '=',
    P = param_eq;
    sdp_mode = false;
case '<',
    P = param_le;
case '>',
    P = param_ge;
end
P.name = op;
P.sdp = sdp_mode;
[ z, sdp_mode ] = cvx_binary_op( P, x, y );

%
% Handle LMIs
%

zR = [];
sz = size( z );
zr = isreal( z );
if sdp_mode,
    z  = cvx_basis( z );
    dg = false;
    if zr,
        n  = sz(1);
        zq = any( z, 1 );
        nq = nnz( zq );
        if nq <= n,
            qn = bsxfun( @plus, (1:n+1:n*n)', 0:n*n:prod(sz)-1 );
            dg = nq == nnz( zq( qn ) );
            if dg,
                [ tx, dummy ] = find( cvx_basis( nonnegative( nq ) ) ); %#ok
                zz = sparse( tx, qn(zq(qn)), 1, tx(end), prod(sz) );
            end
        end
    end
    if ~dg,
        zt = z(:,permute(reshape(1:prod(sz),sz),[2,1,3:length(sz)]));
        if ~zr, zt = conj( zt ); end
        err = z - zt;
        if nnz(err),
            z   = 0.5 * ( z + zt );
            err = max( sum(abs(err),1) ./ max(realmin,sum(abs(z),1)) );
            if err > 8 * eps,
                if isreal(z), str = 'symmetric'; else str = 'Hermitian'; end
                cvx_throw( [ ...
                    'SDP constraints are expected to be %s (deviation: %g).\n', ...
                    '--- If this number is small (<1e-6), it may simply be due to roundoff error.\n', ...
                    '    This can be corrected by applying the SYM(X) function.\n', ...
                    '--- Otherwise, this is likely due to a modeling error. Did you declare the\n', ...
                    '    relevant matrix variables to be "symmetric" or "hermitian"?' ], str, full(err) );
            end
        end
        zz = cvx_basis( semidefinite( sz, ~zr ) );
    end
    z(size(zz,1),:) = 0;
    z = z - zz;
    iseq = true;
    if ~dg,
        [ zR, z ] = cvx_bcompress( z, 'full' );
    end
else
    iseq = op(1) == '=';
    z = cvx_basis( z );
end

%
% Add the (in)equalities, create the dual variable if request
%

need_dual = ~isempty( dx );
zD = cvx_newcnstr( z, iseq, need_dual );
if need_dual
    if isempty( cvx_getdual( z ) ),
        if ~isempty( zR ), zD = zD * cvx_invert_structure( zR ); end
        pstr.duals = builtin( 'subsasgn', pstr.duals, dx, cvx( sz, zD ) );
        cvx___.problems( end ) = pstr;
    else
        warning( 'CVX:NoDualVariables', 'Dual variables are not availble for log-convex constraints.' )
    end
end

%
% Create the output object
%

if nargout,
    outp = cvxcnst( y_orig );
end

function z = eq_2( x, y )
z = cvx_setdual( minus_nc( log( x ), log( y ) ), 1 );

function z = ge_2( x, y )
z = cvx_setdual( minus_nc( log( x ), log( y ) ), 1 );

function z = le_1( x, y )
z = minus_nc( y, x );

function z = le_2( x, y )
z = cvx_setdual( minus_nc( log( y ), log( x ) ), 1 );

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


function [select] = parameterselection(param, data);

% PARAMETERSELECTION selects the parameters that are present as a volume in the data
% add that have a dimension that is compatible with the specified dimensions of the
% volume, i.e. either as a vector or as a 3D volume.
%
% Use as
%   [select] = parameterselection(param, data)
% where
%   param    cell-array, or single string, can be 'all'
%   data     structure with anatomical or functional data
%   select   returns the selected parameters as a cell-array

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

if ischar(param)
  param = {param};   % it should be a cell-array
elseif isempty(param)
  param = {};        % even being empty, it should be a cell-array
end

sel = find(strcmp(param, 'all'));
if ~isempty(sel)
  % the old default was a list of all known volume parameters
  % the new default is to try all fields present in the data
  allparam = fieldnames(data);
  % fields can be nested in source.avg
  if isfield(data, 'avg')
    tmp = fieldnames(data.avg);
    for i=1:length(tmp)
      tmp{i} = ['avg.' tmp{i}];
    end
    allparam = cat(1, allparam, tmp);
  end
  % fields can be nested in source.trial
  if isfield(data, 'trial')
    tmp = fieldnames(data.trial);
    for i=1:length(tmp)
      tmp{i} = ['trial.' tmp{i}];
    end
    allparam = cat(1, allparam, tmp);
  end
  param(sel) = [];                          % remove the 'all'
  param      = [param(:)' allparam(:)'];    % add the list of all possible parameters, these will be tested later
else
  % check all specified parameters and give support for some parameters like 'pow' and 'coh'
  % which most often will indicate 'avg.pow' and 'avg.coh'
  for i=1:length(param)
    if ~issubfield(data, param{i}) && issubfield(data, ['avg.' param{i}])
      % replace the parameter xxx by avg.xxx
      param{i} = ['avg.' param{i}];
    end
  end
end

% remove empty fields
param(find(cellfun('isempty', param))) = [];

% ensure that there are no double entries
param = unique(param);

select = {};
for i=1:length(param)
  if issubfield(data, param{i})
    % the field is present, check whether the dimension is correct
    dim = size(getsubfield(data, param{i}));
    if isfield(data, 'dim') && isequal(dim(:), data.dim(:))
      select{end+1} = param{i};
    elseif isfield(data, 'dim') && prod(dim)==prod(data.dim)
      select{end+1} = param{i};
    elseif isfield(data, 'pos') && prod(dim)==size(data.pos, 1)
      select{end+1} = param{i};
    elseif isfield(data, 'dimord') && (isfield(data, 'pos') || isfield(data, 'transform')),
      dimtok = tokenize(data.dimord, '_');
      nels   = 1;
      for k=1:numel(dimtok)
        if strcmp(dimtok{k}, 'rpt') || strcmp(dimtok{k}, 'rpttap')
	  nels = nels*dim(k);
	elseif strcmp(dimtok{k}, 'pos') && isfield(data, 'pos')
	  %for source structure
	  nels = nels*size(data.pos,1);
	elseif strcmp(dimtok{k}, 'pos')
	  %for volume structure FIXME 'pos' in dimord is not OK
	  nels = nels*prod(data.dim(1:3));
	else
          nels = nels*numel(getfield(data, dimtok{k}));
	end
      end
      if nels==prod(dim),
        select{end+1} = param{i};
      end
    end
  end
end


function res = ifft2shift(x)

warning('Deprecated! Consider using ifftnshift(x) instead');
res = ifftnshift(x,1:2);

end


%kvsamp 'Sample an Image Along an Arbitrary Line (K1)'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros vsamp.pane file
%
% Parameters:
% InputFile: i 'Input image ', required: 'input image'
% Integer: x1 'x ', default: 0: 'starting x coordinate for line'
% Integer: x2 'x ', default: 511: 'ending x coordinate for line'
% Integer: y1 'y ', default: 255: 'starting y coordinate for line'
% Integer: y2 'y ', default: 255: 'ending y coordinate for line'
% Integer: n 'Number of samples ', default: 1: 'number of samples'
% OutputFile: o 'Output image', required: 'output image'
%
% Example: o = kvsamp(i, {'i','';'x1',0;'x2',511;'y1',255;'y2',255;'n',1;'o',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% vsamp - Sample an Image Along an Arbitrary Line  (K1)
%
%  DESCRIPTION
% .B NOTE:
% Program will NOT execute!
% vsamp functionality is replaced by the DATAMANIP::ksampline operator.
% The following command line arguments have changed.
%
%      -x1 (starting x coordinate for line), is now -wc1
%      -x2 (ending x coordinate for line), is now -wc2
%      -y1 (starting y coordinate for line), is now -hc1
%      -y2 (ending y coordinate for line), is now -hc1
%
% the values may be be slightly different because vsamp used bilinear
% interpolation whereas ksampline uses inverse-distance weighting
% interpolation (which is mush easier to extend to 5D and mask'd data).
%
% Given an image of arbitrary size and a line through that image, sample the
% image along the line.  The sampling interval is independent of
% image size and line orientation. The value at each sample point is obtained
% by bilinear interpolation.
%
% The first sample point is precisely at the starting point and the final sample
% is precisely at the ending point.
%
%
%
%  EXAMPLES
% vsamp -i diffract.xv -o line_data.xv -x1 0 -y1 0 -x2 0 -y2 0 -n 400
%
% Will sample the image diffract.xv to obtain a one dimensional data array
% in line_data.xv.  The line goes from the top left corner to the bottom
% right. 400 evenly spaced samples will be obtained via bilinear interpolation.
%
%  "SEE ALSO"
% DATAMANIP::ksampline
%
%  RESTRICTIONS
% .I vsamp
% will only work on images of type FLOAT.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kvsamp(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kvsamp(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'x1', 0;'x2', 511;'y1', 255;'y2', 255;'n', 1;'o', '__output'};
maxval={0,1,1,1,1,2,0};
minval={0,1,1,1,1,2,0};
istoggle=[0,1,1,1,1,1,0];
was_set=istoggle * 0;
paramtype={'InputFile','Integer','Integer','Integer','Integer','Integer','OutputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; 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(1,1);
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
[varargout{:}]=callKhoros([w 'vsamp"  '],Inputs,narglist);


function response_structure_description

%RESPONSE_STRUCTURE_DESCRIPTION Describe response structure fields
%   The fields of a response structure are as follows:
%       scnl: scnlobject associated with the response
%       time: time at which the response is valid
%frequencies: Nx1 vector of frequencies (Hz)
%     values: Nx1 vector of complex responses at each frequency.
%               Values should be normalized such that the maximum amplitude
%               of the response (max(abs(values))) is near 1.
%      calib: Scaler alibration value used to scale the normalized responses
%               to their real value. NOTE that many waveform objects already
%               have this value applied.
%      units: Character string describing the units
% sampleRate: Sample rate in Hz
%   respFile: Character string of name (and path) of response file.
%     status: Open-ended character string describing any issues with file
%
%  NOTE ABOUT CALIBRATION VALUES AND ANTELOPE DATABASES: In many cases the
%  calib value and units will already have been applied to a waveform if it
%  was loaded with WAVEFORM from an Antelope database. WAVEFORM attempts to
%  read the CALIB from the wfdisc table. DB_GET_RESPONSE retrieves the
%  calib value from the calibration table. Its inclusion here allows the
%  user to verify that the two values are the same. If they are not, it may
%  be necessary to reconcile the two. The units variable (translated from
%  the segtype field) should match as well.


function bc = spm_cfg_eeg_bc
% configuration file for baseline correction
%__________________________________________________________________________
% Copyright (C) 2009-2012 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: spm_cfg_eeg_bc.m 5652 2013-09-25 09:36:22Z volkmar $

%--------------------------------------------------------------------------
% D
%--------------------------------------------------------------------------
D        = cfg_files;
D.tag    = 'D';
D.name   = 'File Name';
D.filter = 'mat';
D.num    = [1 1];
D.help   = {'Select the M/EEG mat file.'};

%--------------------------------------------------------------------------
% timewin
%--------------------------------------------------------------------------
timewin         = cfg_entry;
timewin.tag     = 'timewin';
timewin.name    = 'Baseline';
timewin.help    = {'Start and stop of baseline [ms].'};
timewin.strtype = 'r';
timewin.num     = [1 2];

%--------------------------------------------------------------------------
% prefix
%--------------------------------------------------------------------------
prefix         = cfg_entry;
prefix.tag     = 'prefix';
prefix.name    = 'Filename Prefix';
prefix.help    = {'Specify the string to be prepended to the filenames of the filtered dataset. Default prefix is ''b''.'};
prefix.strtype = 's';
prefix.num     = [1 Inf];
prefix.val     = {'b'};

%--------------------------------------------------------------------------
% bc
%--------------------------------------------------------------------------
bc          = cfg_exbranch;
bc.tag      = 'bc';
bc.name     = 'Baseline correction';
bc.val      = {D, timewin, prefix};
bc.help     = {'Baseline correction of M/EEG time data'}';
bc.prog     = @eeg_bc;
bc.vout     = @vout_eeg_bc;
bc.modality = {'EEG'};

%==========================================================================
function out = eeg_bc(job)
% construct the S struct
S = job;
S.D = S.D{1};

out.D          = spm_eeg_bc(S);
out.Dfname     = {fullfile(out.D.path,out.D.fname)};

%==========================================================================
function dep = vout_eeg_bc(job)
% return dependencies
dep(1)            = cfg_dep;
dep(1).sname      = 'Baseline corrected M/EEG data';
dep(1).src_output = substruct('.','D');
dep(1).tgt_spec   = cfg_findspec({{'strtype','e'}});

dep(2)            = cfg_dep;
dep(2).sname      = 'Baseline corrected M/EEG datafile';
dep(2).src_output = substruct('.','Dfname');
dep(2).tgt_spec   = cfg_findspec({{'filter','mat'}});


function m_grid(varargin)
% M_GRID make a grid on a map.
%        M_GRID('parameter','value',...) with any number (or no)
%        optional parameters is used to draw a lat/long grid for a
%        previously initialized map projection.
%
%        The optional parameters allow the user
%        to control the look of the grid. These parameters are listed
%        by M_GRID('get'), with default parameters in M_GRID('set');
%
%        see also M_PROJ

% Rich Pawlowicz (rich@ocgy.ubc.ca) 2/Apr/1997
%
% This software is provided "as is" without warranty of any kind. But
% it's mine, so you can't sell it.
%
%  19/6/97 - set visibility of titles and so forth to 'on' (they
%            default to 'off' when axes visibility is turned off)
%  2/11/97 - for M5.1, the old way of making the patch at the bottom (i.e.
%            by rearranging the axes children) instead causes matlab to lose
%            track of titles. Try a different fix.
% 11/01/98 - Added way of making longitude lines cut off to prevent crowding near poles (you have
%            to specify a vector for allowabale latitudes for this to work).
% 16/02/98 - Made a little fudge to allow the user to fully specify grid location
%            without getting the edge points. It doesn't quite work if only *one* edge
%            point is desired....but I hope it will be OK that way.
% 19/02/98 - PC-users complain about layers getting out of order! Their fault for using
%            such an awful OS...however (with help from Eric Firing) I think I have
%            a fix.
%  7/04/98 - Another fix to grid locations to not automatically add edge points
%            (as requested by EF)
%  7/05/98 - Added 'fancy' outline box.
% 14/11/98 - Changed tag names from m_* to m_grid_*.
% 11/07/99 - Apparently fontname changing didn't work (thanks to Dave McCollum)
% 28/04/04 - Changed m_grid_color code so it works right under unix; old
%            way retained for windows (ugh).
% 16/10/05 - Kirk Ireson discovered that the way to fix those annoying 'cut-throughs'
%            in fancy_box was to add a 'large' zdata...so I've adapted his fix in
%            fancybox and fancybox2.
% 21/11/06 - added 'backcolor'
% 16/4/07  - sorted ticklabels when user-specified (prevents an odd problem near in
%            azimuthal projections).
% 4/DEc/11 - isstr to ischar
% 7/Dec/11 - cd /ocean/rich/home/matlab 3.2.3 compatibility
% 8/Sep/13 - added 'tickstyle' parameter
% 27/Sep/13 - matlab 2013b out, includes graphic bug. Workaround provided by
%             Corinne Bassin.
% 10/Jul/14 - in 2014a BITMAX starts not to be used, changed to FLINTMAX...
% 13/Nov/14 - 2014b graphics changes are biting; a number of version-dependent
%              fixes implemented.
% 17/Nov/17 - All kinds of "new graphics" changes in the background patch
% 27/Nov/17 - made a specialized fix for xaxis tick directions in Robinson and
%             Sinusoidal projections. (problem noted by J. Tsoa, but I didn't like
%             the way he fixed it. Also improved consistency of "lake"
%             handling with non-white background colours, and more Octave
%             compatibility.
%  8/Feb/18  - changed ^o to char(176) in labels. (thanks Brian Dushaw)
%  21/06/2018 added axes option to specify the axes to draw the grid on (instead of
%              gca) - thanks to Yoann Ladroit.
%  23/12/2018 - aesthetic improvement to mollweide projection.

%
% Note that much of the work in generating line data
% is done by calls to the individual projections -
% most of M_GRID is concerned with the mechanics of plotting

% These structures are initialized by m_proj()

global MAP_PROJECTION MAP_VAR_LIST

% Have to have initialized a map first

if isempty(MAP_PROJECTION)
  disp('No Map Projection initialized - call M_PROJ first!');
  return;
end

% Otherwise we are drawing a grid!

% Set current projection to geographic
Currentmap=m_coord('set');
m_coord(MAP_PROJECTION.coordsystem.name);

% Default parameters for grid

ax_idx=find(strcmpi('axes',varargin));
if isempty(ax_idx)
    ax=gca;
else
    ax=varargin{ax_idx+1};
end

xtick=6;
ytick=6;
xlabels=NaN;
ylabels=NaN;
gcolor='k';
ggridcolor=[];
gbackcolor='w'; %%get(gcf,'color');
glinestyle=':';
glinewidth=get(ax,'linewidth');
gbox='on';
gfontsize=get(ax,'fontsize');
gfontname=get(ax,'fontname');
gxaxisloc=get(ax,'xaxislocation');
gyaxisloc=get(ax,'yaxislocation');
gtickdir=get(ax,'tickdir');
gticklen=get(ax,'ticklength'); gticklen=gticklen(1);
gxticklabeldir='middle';
gyticklabeldir='end';
gtickstyle='dm';

dpatch=5; % interpolation factor for fancy grids

% Parse parameter list for options. I really should do some
% error checking here, but...

k=1;
while k<=length(varargin)
  switch lower(varargin{k}(1:3))
    case 'box'
      gbox=varargin{k+1};
    case 'xti'
      if length(varargin{k})==5
        xtick=sort(varargin{k+1});   % Added 'sort' here for people who put things in
      else                           % a random order near poles
        xlabels=varargin{k+1};
      end
    case 'yti'
      if length(varargin{k})==5
        ytick=sort(varargin{k+1});
      else
        ylabels=varargin{k+1};
      end
    case 'xla'
      gxticklabeldir=varargin{k+1};
    case 'yla'
      gyticklabeldir=varargin{k+1};
    case 'col'
      gcolor=varargin{k+1};
    case 'gri'
      ggridcolor=varargin{k+1};
    case 'bac'
      gbackcolor=varargin{k+1};
    case 'lin'
      switch lower(varargin{k}(1:5))
         case 'linew'
           glinewidth=varargin{k+1};
         case 'lines'
           glinestyle=varargin{k+1};
      end
    case 'fon'
       switch lower(varargin{k}(1:5))
         case 'fonts'
           gfontsize=varargin{k+1};
         case 'fontn'
           gfontname=varargin{k+1};
       end
    case 'xax'
      gxaxisloc=varargin{k+1};
    case 'yax'
      gyaxisloc=varargin{k+1};
    case 'tic'
      switch lower(varargin{k}(1:5))
        case 'tickl'
           gticklen=varargin{k+1};
        case 'tickd'
           gtickdir=varargin{k+1};
	    case 'ticks'
	       gtickstyle=varargin{k+1};
      end
    case {'get','usa'}
      disp('      ''axes'',( gca | axis handle)');
      disp('      ''box'',( ''on'' | ''fancy'' | ''off'' )');
      disp('      ''xtick'',( num | [value1 value2 ...])');
      disp('      ''ytick'',( num | [value1 value2 ...])');
      disp('      ''xticklabels'',[label1;label2 ...]');
      disp('      ''yticklabels'',[label1;label2 ...]');
      disp('      ''xlabeldir'', ( ''middle'' | ''end'' )');
      disp('      ''ylabeldir'', ( ''end'' | ''middle'' )');
      disp('      ''ticklength'',value');
      disp('      ''tickdir'',( ''in'' | ''out'' )');
      disp('      ''tickstyle'',(''dm'' | ''da'' | ''dd''  )');  % deg-min or abbreviated deg-min or decimal-deg
      disp('      ''color'',colorspec');
      disp('      ''gridcolor'',colorspec');
      disp('      ''backgroundcolor'',colorspec');
      disp('      ''linewidth'', value');
      disp('      ''linestyle'', ( linespec | ''none'' )');
      disp('      ''fontsize'',value');
      disp('      ''fontname'',name');
      disp('      ''XaxisLocation'',( ''bottom'' | ''middle'' | ''top'' ) ');
      disp('      ''YaxisLocation'',( ''left'' | ''middle'' | ''right'' ) ');
      return;
    case 'set'
      disp(['      axes = ' ax]);
      disp(['      box = ' gbox]);
      disp(['      xtick = ' num2str(xtick)]);
      disp(['      ytick = ' num2str(ytick)]);
      disp(['      ticklength = ' num2str(gticklen)]);
      disp(['      tickdir = ' gtickdir]);
      disp(['      tickstyle = ' gtickstyle]);
      disp(['      xlabeldir = ' gxticklabeldir]);
      disp(['      ylabeldir = ' gyticklabeldir]);
      disp(['      color = ' gcolor]);
      disp(['      gridcolor = ' gcolor]);
      disp(['      backgroundcolor - ' gbackcolor]);
      disp(['      linewidth = ' num2str(glinewidth)]);
      disp(['      linestyle = ' glinestyle]);
      disp(['      fontsize = ' num2str(gfontsize)]);
      disp(['      fontname = ' gfontname]);
      disp(['      XaxisLocation = ' gxaxisloc]);
      disp(['      YaxisLocation = ' gyaxisloc]);
      return;
  end
  k=k+2;
end

if isempty(ggridcolor)
    ggridcolor=gcolor;
end

if strcmp(gbox,'fancy')
  if strcmp(MAP_VAR_LIST.rectbox,'on') || strcmp(MAP_VAR_LIST.rectbox,'circle')
   gbox='on';
   warning([' No fancy outline with ''rectbox'' set to ''' MAP_VAR_LIST.rectbox '''']);
  end
end

[X,Y]=feval(MAP_PROJECTION.routine,'box');

% These color parts make Octave fail so I moved them here, otherwise
% they should be before this if block

% If there are lakes around, make them the backgroundcolor..
hh=get(ax,'children');
hh_tags=get(hh,'tag');
if length(hh)==1, hh_tags={hh_tags}; end
for i=1:length(hh_tags)
   if ~isempty(strfind(hh_tags{i},'_lake')) && strcmp(get(hh(i),'type'),'patch')
     set(hh(i),'facecolor',gbackcolor);
   end
end

% Set the axes colour so lakes get coloured right (if a coastline added
% next)

set(ax,'color',gbackcolor');

if MAP_PROJECTION.IsOctave

  %  Octave handling here all provided by Jamie Tsao

  % Note that both gnuplot and fltk doesn't really work well with the z-axis,
  % so here we simply ignore it.

  % The tag is important for M_COAST to catch the correct underlying color.
  % As a result, though, M_COAST should be called after M_GRID.

  % IN 2018, this workaround no longer needed.
  if 0,%strcmp(graphics_toolkit(), 'gnuplot')
    % Unfortunately, gnuplot also has a bug with using patch, where a linestyle
    % of 'none' would cause it to barf. This bug has been reported since 2013,
    % but apparently it is still not fixed. It may have something to do with
    % how Octave interacts with the underlying gnuplot backend.

    % On the other hand, this means I can set the linewidth to 0 and it should
    % work exactly as intended. If there is no problems, then I can refactor
    % this part, assuming ftlk doesn't barf on a zero linewidth.
    patch('xdata', X(:), 'ydata', Y(:), 'facecolor', gbackcolor, ...
          'edgecolor', 'k', 'linewidth', 0, 'tag', 'm_grid_color','parent',ax);
  else

    patch('xdata', X(:), 'ydata', Y(:), 'facecolor', gbackcolor, ...
          'edgecolor', 'k', 'linestyle', 'none', 'tag', 'm_grid_color','parent',ax);
  end

  % Now I set it at the bottom of the children list so it gets drawn first
  % (i.e. doesn't cover anything)

  % A bug in Octave 3.8.1 makes set(ax, 'children', -) insert the new
  % permutation before the old one instead of replacing it. This only occurs
  % when the ShowHiddenHandles flag is set to on instead of off. Although fltk
  % is not affected, gnuplot still is. Combined with fltk's lack of support for
  % unicode and hence the degree sign, this is still important. Furthermore,
  % this bug causes delete() to function incorrectly.

  show = get(0, 'ShowHiddenHandles');
  set(0, 'ShowHiddenHandles', 'on');

  hh = get(ax,'children');
  htags = get(hh,'tag');
  k = strcmp('m_grid_color', htags);
  hht = hh;
  hh(k) = [];
  hh = [hh;hht(k)];

  % Continuing on the bug, there is no particular need for why this needs to be
  % placed under even hidden handles. Even so, as a workaround, we can grab all
  % hidden handles, set them to be visible, turn off flag, reorder, then set
  % them back as hidden, as noted below.

  hht_inv = strcmp('off', get(hht, 'HandleVisibility'));
  set(hht(hht_inv), 'HandleVisibility', 'on');
  set(0, 'ShowHiddenHandles', 'off');
  set(ax,'children',hh);
  set(hht(hht_inv), 'HandleVisibility', 'off');

  set(0, 'ShowHiddenHandles', show);

  % Patch clipping does not work correctly in Octave, so I have to
  % manually set the Z-limits to that fancy boxes (with 3D patches)
  % works correctly.
  zlim([-1 MAP_PROJECTION.LARGVAL]);
else   % For MATLAB

   % This is a very problematic part of the code. It turns out the the interaction between
   % PATCH objects and CONTOURF objects does not work correctly in the Painters renderer -
   % this is true in all versions up to 7.7 at least. Patches with large negative Z just
   % don't get drawn under contourgroup patches.
   %
   % There are several possible workarounds:
   %
   %  1) Make sure you use the 'v6' option in contourf calls (see m_contourf.m to see
   %     how I have tried to do that for some versions of matlab)
   %      - problem: the 'v6' option is going away soon, also you may want the
   %                contourgroup object that the v6 option destroys.
   %
   %  2) Change the renderer to something else:
   %        set(gcf,'renderer','opengl') or
   %        set(gcf,'renderer','zbuffer')
   %      - problem: These other renderers are not available on all systems, they may also
   %                give less-precise results.
   %
   %  3) Use the painters renderer, but reorder the children so that the patch is at the
   %     bottom of the list (painters cares about child order)
   %      - problem: sometimes the child order is rearranged if you click on the figure,
   %                 also (at least in some versions of matlab) this causes labels to
   %                 disappear.
   %
   % With version 7.4 onwards I have discovered that reordering the children apparently
   % is Mathworks-blessed (c.f. the UISTACK function).  So I am going to try to implement
   % the latter as a default.
   %
   % Now, putting in a white background works under linux (at least) and
   % NOT under windows...I don't know about macs.
   %
   % This broke with matlab 2014b - but eventually (I sent in a "bug
   % report" and found it was a "feature") I learned of the 'sortmethod'
   % tag, which made it work again - as long as I didn't use negative z
   % values in the patch command. I don't know WHY that is...

   if ~MAP_PROJECTION.newgraphics
        patch('xdata',X(:),'ydata',Y(:),'zdata',-MAP_PROJECTION.LARGVAL*ones(size(X(:))),'facecolor',gbackcolor,...
	   'edgecolor','k','linestyle','none','tag','m_grid_color','Parent',ax);
	  % Now I set it at the bottom of the children list so it gets drawn first (i.e. doesn't
	  % cover anything)
	 show=get(0, 'ShowHiddenHandles');
	 set(0, 'ShowHiddenHandles', 'on');
	 hh=get(ax,'children');
	 htags = get(hh,'tag');
	 k = strcmp('m_grid_color',htags);
	 hht = hh;
	 hh(k) = [];
	 hh = [hh;hht(k)];
	 set(ax,'children',hh);
	 set(0, 'ShowHiddenHandles', show);

   else  % after 2014b

     patch('xdata',X(:),'ydata',Y(:),'zdata',-MAP_PROJECTION.LARGVAL*ones(size(X(:))),'facecolor',gbackcolor,...
             'edgecolor','k','linestyle','none','tag','m_grid_color','parent',ax);
%     patch('xdata',X(:),'ydata',Y(:),'facecolor',gbackcolor,...
%             'edgecolor','k','linestyle','none','tag','m_grid_color');
	 set(ax,'sortmethod','childorder');
     uistack(findobj(ax,'tag','m_grid_color'),'bottom');

   end

end

% X-axis labels and grid

if ~isempty(xtick)

 % Tricky thing - if we are drawing a map with the poles, its nasty when the lines get too close
 % together. So we can sort of fudge this by altering MAP_VAR_LIST.lats to be slightly smaller,
 % and then changing it back again later.
 fudge_north='n';fudge_south='n';
 if ~isempty(ytick) && length(ytick)>1
  if MAP_VAR_LIST.lats(2)==90
    fudge_north='y';
    MAP_VAR_LIST.lats(2)=ytick(end);
  end
  if MAP_VAR_LIST.lats(1)==-90
    fudge_south='y';
    MAP_VAR_LIST.lats(1)=ytick(1);
  end
 end

 [X,Y,lg,lgI]=feval(MAP_PROJECTION.routine,'xgrid',xtick,gxaxisloc,gtickstyle);
 [labs,scl]=m_labels('lon',lg,xlabels,gtickstyle);

 % Draw the grid. Every time we draw something, I first reshape the matrices into a long
 % row so that a) it plots faster, and b) all lines are given the same handle (which cuts
 % down on the number of children hanging onto the axes).

 [n,m]=size(X);
 line(reshape([X;NaN+ones(1,m)],(n+1)*m,1),reshape([Y;NaN+ones(1,m)],(n+1)*m,1),...
      'linestyle',glinestyle,'color',ggridcolor,'linewidth',0.1,'tag','m_grid_xgrid','parent',ax);

 % Get the tick data
 [ltx,lty,utx,uty]=maketicks(X,Y,gticklen,gtickdir);

 % Draw ticks if labels are on top or bottom (not if they are in the middle)

 if strcmp(gxticklabeldir,'middle')
  if lgI==size(X,1) && strcmp(gxaxisloc,'top')  % Check to see if the projection supports this option.
   vert='bottom';horiz='center';drawticks=1;
   xx=utx(1,:);yy=uty(1,:);rotang=atan2(diff(uty),diff(utx))*180/pi+90;
  elseif lgI==1 && strcmp(gxaxisloc,'bottom')
   vert='top';horiz='center';drawticks=1;
   xx=ltx(1,:);yy=lty(1,:);rotang=atan2(diff(lty),diff(ltx))*180/pi-90;
  else
   vert='middle';horiz='center';lgIp1=lgI+1;drawticks=0;
   xx=X(lgI,:); yy=Y(lgI,:);rotang=atan2(Y(lgIp1,:)-Y(lgI,:),X(lgIp1,:)-X(lgI,:))*180/pi-90;
  end
 else
  if lgI==size(X,1) && strcmp(gxaxisloc,'top')  % Check to see if the projection supports this option.
   vert='middle';horiz='left';drawticks=1;
   xx=utx(1,:);yy=uty(1,:);rotang=atan2(diff(uty),diff(utx))*180/pi+180;
  elseif lgI==1 && strcmp(gxaxisloc,'bottom')
   vert='middle';horiz='right';drawticks=1;
   xx=ltx(1,:);yy=lty(1,:);rotang=atan2(diff(lty),diff(ltx))*180/pi;
  else
   vert='top';horiz='center';lgIp1=lgI+1;drawticks=0;
   xx=X(lgI,:); yy=Y(lgI,:);rotang=atan2(Y(lgIp1,:)-Y(lgI,:),X(lgIp1,:)-X(lgI,:))*180/pi;
  end
 end

 % For some projections it is better if the x-grid labels are horizontal or
 % vertical

 if any(strcmp(MAP_PROJECTION.name,{'Robinson','Sinusoidal'}) )
     if strcmp(gxaxisloc,'bottom') || strcmp(gxaxisloc,'top')
         if strcmp(gxticklabeldir,'middle')
            rotang = zeros(size(rotang));    % flat
            yy=repmat(mean(yy),size(yy));    % Put them all on the same y-value.
         else
            rotang = zeros(size(rotang))+90;  % upright
         end
     end
 end

 if strcmp(gbox,'fancy')
    if gtickdir(1)=='i'
      fancybox(lg,MAP_VAR_LIST.longs,'xgrid','bottom',dpatch,gticklen,gtickstyle,ax);
      drawticks=0;
    else
      fancybox2(lg,MAP_VAR_LIST.longs,'xgrid','bottom',dpatch,gticklen,gtickstyle,ax);
    end
 end
 if drawticks
   [n,m]=size(ltx);
   line(reshape([ltx;NaN+ones(1,m)],(n+1)*m,1),reshape([lty;NaN+ones(1,m)],(n+1)*m,1),...
        'linestyle','-','color',gcolor,'linewidth',glinewidth,'tag','m_grid_xticks-lower','clipping','off','parent',ax);
   line(reshape([utx;NaN+ones(1,m)],(n+1)*m,1),reshape([uty;NaN+ones(1,m)],(n+1)*m,1),...
        'linestyle','-','color',gcolor,'linewidth',glinewidth,'tag','m_grid_xticks-upper','clipping','off','parent',ax);
 end

 % Add the labels! (whew)

 ik=1:size(X,2);

 for k=ik
   [rotang(k), horizk, vertk] = upright(rotang(k), horiz, vert);
   text(xx(k),yy(k),labs{k},'horizontalalignment',horizk,'verticalalignment',vertk, ...
        'rotation',rotang(k),'fontsize',gfontsize*scl(k),'color',gcolor,...
        'tag','m_grid_xticklabel','fontname',gfontname,'parent',ax);
 end

 if fudge_north=='y'
   MAP_VAR_LIST.lats(2)=90;
 end
 if fudge_south=='y'
   MAP_VAR_LIST.lats(1)=-90;
 end

end

if ~isempty(ytick)
 % Y-axis labels and grid

 [X,Y,lt,ltI]=feval(MAP_PROJECTION.routine,'ygrid',ytick,gyaxisloc,gtickstyle);
 [labs,scl]=m_labels('lat',lt,ylabels,gtickstyle);

 % Draw the grid
 [n,m]=size(X);
 line(reshape([X;NaN+ones(1,m)],(n+1)*m,1),reshape([Y;NaN+ones(1,m)],(n+1)*m,1),...
      'linestyle',glinestyle,'color',ggridcolor,'linewidth',0.1,'tag','m_grid_ygrid','parent',ax);

 % Get the tick data
 [ltx,lty,utx,uty]=maketicks(X,Y,gticklen,gtickdir);

 % Draw ticks if labels are on left or right (not if they are in the middle)
 if strcmp(gyticklabeldir,'end')
  if ltI==size(X,1) && strcmp(gyaxisloc,'right')  % Check to see if the projection supports this option.
   horiz='left';vert='middle';drawticks=1;
   xx=utx(1,:);yy=uty(1,:);rotang=atan2(diff(uty),diff(utx))*180/pi+180;
  elseif ltI==1 && strcmp(gyaxisloc,'left')
   horiz='right';vert='middle';drawticks=1;
   xx=ltx(1,:);yy=lty(1,:);rotang=atan2(diff(lty),diff(ltx))*180/pi;
  else
   horiz='center';vert='top';ltIp1=ltI+1;drawticks=0;
   xx=X(ltI,:); yy=Y(ltI,:);rotang=atan2(Y(ltIp1,:)-Y(ltI,:),X(ltIp1,:)-X(ltI,:))*180/pi;
  end
 else
  if ltI==size(X,1) && strcmp(gyaxisloc,'right')  % Check to see if the projection supports this option.
   horiz='center';vert='top';drawticks=1;
   xx=utx(1,:);yy=uty(1,:);rotang=atan2(diff(uty),diff(utx))*180/pi+270;
  elseif ltI==1 && strcmp(gyaxisloc,'left')
   horiz='center';vert='bottom';drawticks=1;
   xx=ltx(1,:);yy=lty(1,:);rotang=atan2(diff(lty),diff(ltx))*180/pi+90;
  else
   horiz='left';vert='middle';ltIp1=ltI+1;drawticks=0;
   xx=X(ltI,:); yy=Y(ltI,:);rotang=atan2(Y(ltIp1,:)-Y(ltI,:),X(ltIp1,:)-X(ltI,:))*180/pi+90;
  end
 end

 % Added special case for latitudes. Dec/2018
 if any(strcmp(MAP_PROJECTION.name,{'Mollweide'}) )
     if strcmp(gyaxisloc,'left')
         rotang=-lt;
     elseif strcmp(gyaxisloc,'right')
         rotang=lt;
     end
 end

 if strcmp(gbox,'fancy')
    if gtickdir(1)=='i'
      fancybox(lt,MAP_VAR_LIST.lats,'ygrid','left',dpatch,gticklen,gtickstyle,ax);
      drawticks=0;
    else
      fancybox2(lt,MAP_VAR_LIST.lats,'ygrid','left',dpatch,gticklen,gtickstyle,ax);
    end
 end
 if drawticks
   [n,m]=size(ltx);
   line(reshape([ltx;NaN+ones(1,m)],(n+1)*m,1),reshape([lty;NaN+ones(1,m)],(n+1)*m,1),...
        'linestyle','-','color',gcolor,'linewidth',glinewidth,'tag','m_grid_yticks-left','clipping','off','parent',ax);
   line(reshape([utx;NaN+ones(1,m)],(n+1)*m,1),reshape([uty;NaN+ones(1,m)],(n+1)*m,1),...
        'linestyle','-','color',gcolor,'linewidth',glinewidth,'tag','m_grid_yticks-right','clipping','off','parent',ax);
 end

 % Finally - the labels!
 ik=1:size(X,2);

 for k=ik
   [rotang(k), horizk, vertk] = upright(rotang(k), horiz, vert);
   text(xx(k),yy(k),labs{k},'horizontalalignment',horizk,'verticalalignment',vertk,...
        'rotation',rotang(k),'fontsize',gfontsize*scl(k),'color',gcolor,...
	'tag','m_grid_yticklabel','fontname',gfontname,'parent',ax);
 end

end

% Draw the plot box

if strcmp(gbox,'on')
  [X,Y]=feval(MAP_PROJECTION.routine,'box');
  line(X(:),Y(:),'linestyle','-','linewidth',glinewidth,'color',gcolor,'tag','m_grid_box','clipping','off','parent',ax);
end

% Give a 1-1 aspect ratio and get rid of the matlab-provided axes stuff.

if isempty(strfind(version,'R2013b'))  % 27/Sept/13 - Handling for 2013b provided by CB.
    set(ax,'visible','off',...
            'dataaspectratio',[1 1 1],...
            'xlim',MAP_VAR_LIST.xlims,...
            'ylim',MAP_VAR_LIST.ylims);
else
    set(ax,'visible','off',...
            'dataaspectratio',[1 1 1e16],...
            'xlim',MAP_VAR_LIST.xlims,...
            'ylim',MAP_VAR_LIST.ylims);
end

set(ax,'tag',['m_grid_' MAP_PROJECTION.name]);

set(get(ax,'title'),'visible','on');
set(get(ax,'xlabel'),'visible','on');
set(get(ax,'ylabel'),'visible','on');

% Set coordinate system back

m_coord(Currentmap.name);

end

%-------------------------------------------------------------
% upright simply turns tick labels right-side up while leaving
% their positions unchanged.
% Sat  98/02/21 Eric Firing
%
function   [rotang, horiz, vert] = upright(rotang, horiz, vert);
if isnan(rotang), rotang=0; end   % Added in 2014!
if rotang > 180, rotang = rotang - 360; end
if rotang < -180, rotang = rotang + 360; end
if rotang > 90
   rotang = rotang - 180;
elseif rotang < -90
   rotang = 180 + rotang;
else
   return    % no change needed.
end
switch horiz(1)
   case 'l'
      horiz = 'right';
   case 'r'
      horiz = 'left';
end
switch vert(1)
   case 't'
      vert = 'bottom';
   case 'b'
      vert = 'top';
end

end

%--------------------------------------------------------------------------
function [L,fs]=m_labels(dir,vals,uservals,tickstyle)
% M_LONLABEL creates longitude labels
%         Default values are calculated automatically when the grid is
%         generated. However, the user may wish to specify the labels
%         as either numeric values or as strings (in the usual way
%         for axes).
%
%         If auto-labelling occurs, minutes are labelled in a different
%         (smaller) fontsize than even degrees.

% Rich Pawlowicz (rich@ocgy.ubc.ca) 2/Apr/1997

% If the user has specified [] (i.e. no labels), we return blanks.

if isempty(uservals)
  L=cellstr(char(' '*ones(length(vals),1)));
  fs=1.0*ones(length(L),1);
  return;
end

% If the user has specified strings, we merely need to make
% sure that there are enough to cover all ticks.

if any(ischar(uservals))
  L=cellstr( uservals((rem([0:length(vals)-1],length(uservals))+1),:) );
  fs=1.0*ones(length(L),1);
  return;
end

% Otherwise we are going to have to generate labels from numeric
% data.

if length(uservals)==1 && isnan(uservals)  % use default values
  vals=vals(:)'; % make it a row.
else                                       % or ones provided
  lv=length(vals);
  vals=uservals(:)';
  while length(vals)<lv
    vals=[vals uservals(:)'];
  end
end

% longitudes and latitudes have some differences....
% and keep a label for 0 or 180 where we don't want a letter
if strfind(dir,'lat')
  labname=['S';'N';' '];
else
  labname=['W';'E';' '];
  vals=rem(vals+540,360)-180;
end

%i=[vals<0;vals>0;vals==0];  % get the 'names' (i.e. N/S or E/W)
%modified by Changyong He the label for 0 or +/-180 will not show the
%an E or W.
i=[-180~=vals & vals<0; 180~=vals & vals>0; vals==0 | abs(vals) == 180];

L=cell(length(vals),1);
fs=ones(length(vals),1);

if strcmp(tickstyle,'dm')
   vals=abs(vals);             % Convert to +ve values

   % For each label we have different options:
   %  1 - even degrees are just labelled as such.
   %  2 - ticks that fall on even minutes are just labelled as even minutes
   %      in a smaller fontsize.
   %  3 - fractional minutes are labelled to 2 decimal places in the
   %      smaller fontsize.
   for k=1:length(vals)
     if rem(vals(k),1)==0
       nam=find(i(:,k));
       L{k}=sprintf([' %3.0f' char(176) labname(nam) ' '],vals(k));
     elseif abs(vals*60-round(vals*60))<.01
       L{k}=sprintf([' %2.0f'' '],rem(vals(k),1)*60);
       fs(k)=0.75;
     else
       L{k}=sprintf([' %2.2f'' '],rem(vals(k),1)*60);
       fs(k)=0.75;
     end
   end

   % In most cases, the map will have at least one tick with an even degree label,
   % but for very small regions (<1 degree in size) this won't happen so we
   % want to force one label to show degrees *and* minutes.

   if ~any(fs==1)
    k=round(length(vals)/2);
    nam=find(i(:,k));
    L{k}={sprintf([' %3.0f' char(176) labname(nam) ' '],fix(vals(k))),...
	  sprintf([' %2.2f'' '],rem(vals(k),1)*60)};
    fs(k)=1;
   end

elseif strcmp(tickstyle,'da')
   % Abbreviated option suggested by Brooke Jones - Dec/2018
   % For each label we have different options:
   %  1 - even degrees are just labelled as such.
   %  2 - 2 decimal place intervals use 2 decimal places
   %  3 - the rest fo to 4
   for k=1:length(vals)
     if rem(vals(k),1)==0
       nam=find(i(:,k));
       L{k}=sprintf([' %4.0f '],vals(k));
     elseif abs(vals*100-round(vals*100))<0.01
       L{k}=sprintf([' %2.0f'' '],rem(abs(vals(k)),1)*60);
       fs(k)=0.6;
     else
       L{k}=sprintf([' %2.2f'' '],rem(abs(vals(k)),1)*60);
       fs(k)=0.6;
     end
   end

   % In most cases, the map will have at least one tick with an even degree label,
   % but for very small regions (<1 degree in size) this won't happen so we
   % want to force one label to show degrees *and* minutes.

   if ~any(fs==1)
    k=round(length(vals)/2);
    nam=find(i(:,k));
    L{k}={sprintf([' %4.0f    '],fix(vals(k))),...
	  sprintf([' %2.2f'' '],abs(rem(vals(k),1))*60)};
    fs(k)=1;
   end

elseif strcmp(tickstyle,'dd')
   vals=abs(vals);             % Convert to +ve values
   % For each label we have different options:
   %  1 - even degrees are just labelled as such.
   %  2 - 2 decimal place intervals use 2 decimal places
   %  3 - the rest fo to 4
   for k=1:length(vals)
     if rem(vals(k),1)==0
       nam=find(i(:,k));
       L{k}=sprintf([' %3.0f' char(176) labname(nam) ' '],vals(k));
     elseif abs(vals*100-round(vals*100))<0.01
       L{k}=sprintf([' %2.2f'],vals(k));
       fs(k)=0.75;
     else
       L{k}=sprintf([' %6.4f'],vals(k));
       fs(k)=0.75;
     end
   end

  % write code.
end
end

%---------------------------------------------------------
function [ltx,lty,utx,uty]=maketicks(X,Y,gticklen,gtickdir)
% MAKETICKS makes the axis ticks.
%           AXes ticks are based on making short lines at
%           the end of the grid lines X,Y.

% Rich Pawlowicz (rich@ocgy.ubc.ca) 2/Apr/1997

global MAP_VAR_LIST

tlen=gticklen*max( diff(MAP_VAR_LIST.xlims),diff(MAP_VAR_LIST.ylims));

lx=sqrt((X(2,:)-X(1,:)).^2+(Y(2,:)-Y(1,:)).^2);

if strcmp(gtickdir,'out')
  ltx=[X(1,:)-tlen*(X(2,:)-X(1,:))./lx;X(1,:)];
  lty=[Y(1,:)-tlen*(Y(2,:)-Y(1,:))./lx;Y(1,:)];
else
  ltx=[X(1,:);X(1,:)+tlen*(X(2,:)-X(1,:))./lx];
  lty=[Y(1,:);Y(1,:)+tlen*(Y(2,:)-Y(1,:))./lx];
end

lx=sqrt((X(end,:)-X(end-1,:)).^2+(Y(end,:)-Y(end-1,:)).^2);

if strcmp(gtickdir,'out')
  utx=[X(end,:)-tlen*(X(end-1,:)-X(end,:))./lx;X(end,:)];
  uty=[Y(end,:)-tlen*(Y(end-1,:)-Y(end,:))./lx;Y(end,:)];
else
  utx=[X(end,:);X(end,:)+tlen*(X(end-1,:)-X(end,:))./lx];
  uty=[Y(end,:);Y(end,:)+tlen*(Y(end-1,:)-Y(end,:))./lx];
end
end

%---------------------------------------------------------
function fancybox(vals,lims,gridarg1,gridarg2,dpatch,gticklen,gridarg3,ax);
%
%  FANCYBOX  - draws fancy outlines for either top/bottom or left/right sides,
%              depending on calling parameters.

global MAP_PROJECTION

% Get xlocations including endpoints
xval=sort([lims(1) vals(vals>lims(1) & vals<lims(2)) lims(2)]);
% Add all half-way points as well.
xval=sort([xval,xval(1:end-1)+diff(xval)/2]);

% Interpolate extra points to handle curved boundary conditions.

xval=(xval(1:end-1)'*ones(1,dpatch)+diff(xval)'*[0:dpatch-1]/dpatch)';
xval=[xval(:);lims(2)];

% Get lat/long positions for everything

[X2,Y2,lg2,lgI2]=feval(MAP_PROJECTION.routine,gridarg1,xval,gridarg2,gridarg3);
[l2x,l2y,u2x,u2y]=maketicks(X2,Y2,gticklen,'in');

if gridarg1(1)=='x', sig=1; else sig=-1; end

id=[1:dpatch size(l2x,2)+[-dpatch+1:0]];
dx=diff(l2x(:,id));
l2x(2,id)=l2x(2,id)+diff(l2y(:,id)).*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;
l2y(2,id)=l2y(2,id)-dx.*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;
dx=diff(u2x(:,id));
u2x(2,id)=u2x(2,id)-diff(u2y(:,id)).*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;
u2y(2,id)=u2y(2,id)+dx.*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;

% Now actually draw the patches.

% Added the z-values 16/Oct/05.

px=prod(size(l2x));
kk=[0:(dpatch*4):px-3]'*ones(1,dpatch*2+2);
kk=kk+ones(size(kk,1),1)*[1 2:2:(dpatch*2+2) (dpatch*2+1):-2:3];
patch(reshape(u2x(kk),size(kk,1),size(kk,2))',...
      reshape(u2y(kk),size(kk,1),size(kk,2))',...
      repmat(MAP_PROJECTION.LARGVAL  ,size(kk,2),size(kk,1)),'w','edgecolor','k','clipping','off','tag','m_grid_fancybox1','parent',ax);
patch(reshape(l2x(kk),size(kk,1),size(kk,2))',...
      reshape(l2y(kk),size(kk,1),size(kk,2))',...
      repmat(MAP_PROJECTION.LARGVAL-1,size(kk,2),size(kk,1)),'k','clipping','off','tag','m_grid_fancybox1','parent',ax);

kk=[dpatch*2:(dpatch*4):px-3]'*ones(1,dpatch*2+2);
kk=kk+ones(size(kk,1),1)*[1 2:2:(dpatch*2+2) (dpatch*2+1):-2:3];
patch(reshape(l2x(kk),size(kk,1),size(kk,2))',...
      reshape(l2y(kk),size(kk,1),size(kk,2))',...
      repmat(MAP_PROJECTION.LARGVAL  ,size(kk,2),size(kk,1)),'w','edgecolor','k','clipping','off','tag','m_grid_fancybox1','parent',ax);
patch(reshape(u2x(kk),size(kk,1),size(kk,2))',...
      reshape(u2y(kk),size(kk,1),size(kk,2))',...
      repmat(MAP_PROJECTION.LARGVAL-1,size(kk,2),size(kk,1)),'k','clipping','off','tag','m_grid_fancybox1','parent',ax);
end

%---------------------------------------------------------
function fancybox2(vals,lims,gridarg1,gridarg2,dpatch,gticklen,gridarg3,ax)
%
%  FANCYBOX  - draws fancy outlines for either top/bottom or left/right sides,
%              depending on calling parameters.

global MAP_PROJECTION

% Get xlocations including endpoints
xval=sort([lims(1) vals(vals>lims(1) & vals<lims(2)) lims(2)]);
% Add all half-way points as well.
xval=sort([xval,xval(1:end-1)+diff(xval)/2]);

% Interpolate extra points to handle curved boundary conditions.

xval=(xval(1:end-1)'*ones(1,dpatch)+diff(xval)'*[0:dpatch-1]/dpatch)';
xval=[xval(:);lims(2)];

% Get lat/long positions for everything

[X2,Y2,lg2,lgI2]=feval(MAP_PROJECTION.routine,gridarg1,xval,gridarg2,gridarg3);
[l2x,l2y,u2x,u2y]=maketicks(X2,Y2,gticklen,'in');

if gridarg1(1)=='x'
    sig=1;
else
    sig=-1;
end

id=[1:dpatch size(l2x,2)+[-dpatch+1:0]];
dx=diff(l2x(:,id));
l2x(2,id)=l2x(2,id)+diff(l2y(:,id)).*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;
l2y(2,id)=l2y(2,id)-dx.*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;
dx=diff(u2x(:,id));
u2x(2,id)=u2x(2,id)-diff(u2y(:,id)).*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;
u2y(2,id)=u2y(2,id)+dx.*([dpatch:-1:1 -1:-1:-dpatch]/(dpatch))*sig;

% Now actually draw the patches.

% Added large z-values 16/Oct/05

px=prod(size(l2x));
kk=[0:(dpatch*2):px-3]'*ones(1,dpatch*2+2);
kk=kk+ones(size(kk,1),1)*[1 2:2:(dpatch*2+2) (dpatch*2+1):-2:3];
 patch(reshape(l2x(kk),size(kk,1),size(kk,2))',...
       reshape(l2y(kk),size(kk,1),size(kk,2))',...
       repmat(MAP_PROJECTION.LARGVAL-1,size(kk,2),size(kk,1)),...
       'w','edgecolor','k','clipping','off','linewidth',.2,'tag','m_grid_fancybox2','parent',ax);
 patch(reshape(u2x(kk),size(kk,1),size(kk,2))',...
       reshape(u2y(kk),size(kk,1),size(kk,2))',...
       repmat(MAP_PROJECTION.LARGVAL-1,size(kk,2),size(kk,1)),...
       'w','edgecolor','k','clipping','off','linewidth',.2,'tag','m_grid_fancybox2','parent',ax);
kk=[0:(dpatch*2):size(l2x,2)-dpatch-1]'*ones(1,dpatch+1);
kk=(kk+ones(size(kk,1),1)*[1:dpatch+1])';
[k1,k2]=size(kk);
line(reshape(mean(l2x(:,kk)),k1,k2),reshape(mean(l2y(:,kk))',k1,k2),...
     repmat(MAP_PROJECTION.LARGVAL,k1,k2),'color','k','clipping','off','tag','m_grid_fancybox2','parent',ax);

kk=[dpatch:(dpatch*2):size(l2x,2)-dpatch-1]'*ones(1,dpatch+1);
kk=(kk+ones(size(kk,1),1)*[1:dpatch+1])';
[k1,k2]=size(kk);
line(reshape(mean(u2x(:,kk))',k1,k2),reshape(mean(u2y(:,kk))',k1,k2),...
     repmat(MAP_PROJECTION.LARGVAL,k1,k2),'color','k','clipping','off','tag','m_grid_fancybox2','parent',ax);

end


function [sp, reg2sp] = read_sprep(spfile, reg2spfile)
reg2sp=imread(reg2spfile);
B=textread(spfile, '%d');

sz=B(1:2);
sp=reshape(B(3:end),sz(:)');


function [ ival1, ival2,  val1, val2 ] = p08_hx ( option )

%*****************************************************************************80
%
%% P08_HX reports the constraint equation data.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OPTION, the option index.
%
%    Output, integer VAL1, IVAL2, the indices of the two constrained
%    variables.
%
%    Output, real VAL1, VAL2, the values to which the two constrained
%    variables are to be set.
%
  ival1 = 4;
  val1 = 0.0;
  ival2 = 5;
  val2 = 0.0;

  return
end


function export_mesh(fileName,meshStruct)

% function export_mesh(fileName,meshStruct)
% ------------------------------------------------------------------------
% This function exports a .MESH file based on the meshStruct which should
% contain the vertices and elements.
%
% Elements can be specified as either:
% meshStruct.elements or meshStruct.E
%
% Vertices can be specified as either:
% meshStruct.vertices or meshStruct.V or meshStruct.nodes
%
% This function currently only supporte tetrahedral and hexahedral element
% exporting.
%
% The element type is either inferred from the size of the element matrix
% or can be set using:
% meshStruct.elementType
% Use meshStruct.elementType='Hexahedral', for 8-noded hexahedral elements
% Use meshStruct.elementType='Tetrahedral', for 4-noded tetrahedral elements
%
% This implementation was created using examples of .MESH files contained
% in: https://github.com/cnr-isti-vclab/HexaLab
%
%
% Change log:
% 2021/07/20 Kevin M. Moerman Created for use with https://www.hexalab.net/
%
% To do:
% - Proper treatment of vertex/element "attributes"
% - Support more .MESH options (surface meshes, FEA types,....)
% - Test for tetrahedral meshes
% - Expand and test for surface meshes
% ------------------------------------------------------------------------

%% Parse input

%Get elements
if isfield(meshStruct,'E')
    E=meshStruct.E;
elseif isfield(meshStruct,'elements')
    E=meshStruct.elements;
end

%Get vertices
if isfield(meshStruct,'V')
    V=meshStruct.V;
elseif isfield(meshStruct,'nodes')
    V=meshStruct.nodes;
elseif isfield(meshStruct,'vertices')
    V=meshStruct.vertices;
end

%Get/set element type
if isfield(meshStruct,'elementType')
    elementType=meshStruct.elementType;
else
    switch size(E,2)
        case 4 % 4 noded tetrahedral elements
            elementType='Tetrahedra';
        case 8 % 8 noded tetrahedral elements
            elementType='Hexahedra';
    end
end

%Replace "FEBio-style" element type designations by .MESH types
if strcmp(elementType,'tet4')
    elementType='Tetrahedra';
elseif strcmp(elementType,'hex8')
    elementType='Hexahedra';
end

%% Open file for writing

fileID = fopen(fileName,'w'); %Open file for writing

%% Write .MESH file version line
% Example:
%
% MeshVersionFormatted 1

fprintf(fileID,'%s \n','MeshVersionFormatted 1');

%% Write dimensionality line
% Example:
%
% Dimension 3

fprintf(fileID,'Dimension %d \n',size(V,2));

%% Write line specifying number of vertices
% Example:
%
% Vertices 12

fprintf(fileID,'Vertices %d \n',size(V,1));

%% Write vertex data field
% Example:
%
% 0 0 0 1
% -1.0000000000000000e+00 -1.0000000000000000e+00 -1.0000000000000000e+00 1

%Prepare data
V_id=ones(size(V,1),1);
V_field=[V V_id(:)];

%Write field
tForm=repmat('%0.16e ',1,size(V,2)); %Replicated text format
tForm=[tForm(1:end-1),' %i \n']; %Text format with attribute and end of line
fprintf(fileID,tForm,V_field');

%% Write line specifying number of elements and element type
% Example:
%
% Hexahedra 50

fprintf(fileID,[elementType,' %d \n'],size(E,1));

%% Write element data field

%Prepare data
E_id=ones(size(E,1),1); %1:1:size(E,1);
E_field=[E E_id(:)];

%Write field
tForm=repmat('%d ',1,size(E,2)); %Replicated text format
tForm=[tForm(1:end-1),' %i \n']; %Text format with attribute and end of line
fprintf(fileID,tForm,E_field');

%% Write "End" line
fprintf(fileID,'%s \n','End');

%% Close file for writing
fclose(fileID);

end

%%
% _*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/>.


function [ fx, fy ] = f13_f1 ( n, x, y )

%*****************************************************************************80
%
%% F13_F1 returns first derivatives of function f13.
%
%  Discussion:
%
%    Currently, the derivative information is of no interest to me.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    01 August 2012
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of evaluation points.
%
%    Input, real X(N,1), Y(N,1), the evalution points.
%
%    Output, real FX(N,1), FY(N,1), the derivative values.
%
  fx(1:n,1) = zeros ( n, 1 );
  fy(1:n,1) = zeros ( n, 1 );

  return
end


function t = tsurf(F,V,varargin)
  % TSURF trisurf wrapper, easily plot triangle meshes with(out) face or vertex
  % indices. Attaches callbacks so that click-and-holding on the mesh and then
  % pressing 'm' launches meshplot (if available)
  %
  % t = tsurf(F,V);
  % t = tsurf(F,V,'ParameterName',ParameterValue, ...)
  %
  % Inputs:
  %   F  list of faces #F x 3
  %   V  vertex positiosn #V x 3 or #V x 2
  %   Optional:
  %     'VertexIndices' followed by
  %     'FaceIndices' followed by
  %                   0 -> off
  %                   1 -> text and grey background
  %                  -1 -> text and grey background (decremented by 1)
  %                  >1 -> text
  %     ... additional options passed on to trisurf
  % Outputs:
  %  t  handle to trisurf object
  %
  % Copyright 2011, Alec Jacobson (jacobson@inf.ethz.ch)
  %
  % Example:
  %   % Compose with set function to set trisurf parameters to achieve
  %   % sharp isointervals
  %   tsurf(F,V, ...
  %     'FaceColor','interp', ...
  %     'FaceLighting','phong', ...
  %     'EdgeColor',[0.2 0.2 0.2]);
  %   colormap(flag(8))
  %
  % See also: trisurf
  %

  if issparse(V)
      V = full(V);
  end

  vertex_indices = 0;
  face_indices = 0;
  tets = [];
  buttondownfcn = 'default';

  v = 1;
  while v<=numel(varargin) && ischar(varargin{v})
    switch varargin{v}
    case 'VertexIndices'
      v = v+1;
      assert(v<=numel(varargin));
      vertex_indices = varargin{v};
    case 'FaceIndices'
      v = v+1;
      assert(v<=numel(varargin));
      face_indices = varargin{v};
    case 'Tets'
      v = v+1;
      assert(v<=numel(varargin));
      tets = varargin{v};
    case 'ButtonDownFcn'
      v = v+1;
      assert(v<=numel(varargin));
      buttondownfcn = varargin{v};
    otherwise
      break;
    end
    v = v+1;
  end

  % number of vertices
  n = size(V,1);

  % nuymber of dimensions
  dim = size(V,2);

  if(dim==2 || (dim ==3 && sum(abs(V(:,3))) == 0))
    V = [V(:,1) V(:,2) 0*V(:,1)];
    dim = 2;
  elseif ( (dim>3 || dim<2 ))
      if dim==4 && all(V(:,4)==1.0)
          V = V(:,1:3);
      else
    error('V must be #V x 3 or #V x 2');
    return;
      end
  end

  %tets = size(F,2) ==4 && (size(F,1)*4 > 1.01*size(boundary_faces(F),1));
  if isempty(tets)
    tets = false;
  if size(F,2) == 4
    VV = bsxfun(@minus,V,min(V))*max(max(V)-min(V));
    tets = sum(volume(VV,F))>1e-10;
    if ~tets
      Ftri = [F(:,[1 2 3]);F(:,[1 3 4])];
      Itri = repmat(1:size(F,1),1,2);
    end
  else
    tets = false;
    Ftri = F;
    Itri = 1:size(F,1);
  end
  end

  FI = 1:size(F,1);
  if face_indices<0
    FI = FI-1;
  end
  if tets
    t_copy = tetramesh(F,V,'FaceAlpha',0.5);
    FC = barycenter(V,F);
    if(abs(face_indices)==1)
      text(FC(:,1),FC(:,2),FC(:,3),num2str((FI)'),'BackgroundColor',[.7 .7 .7]);
    elseif(face_indices)
      text(FC(:,1),FC(:,2),FC(:,3),num2str((FI)'));
    end
    set(gcf,'Renderer','OpenGL');
  else
    t_copy = trisurf(F,V(:,1),V(:,2),V(:,3));
    FC = barycenter(V,F);
    if(abs(face_indices)==1)
      text(FC(:,1),FC(:,2),FC(:,3),num2str((FI)'),'BackgroundColor',[.7 .7 .7]);
    elseif(face_indices)
      text(FC(:,1),FC(:,2),FC(:,3),num2str((FI)'));
    end
  end

  % if 2d then set to view (x,y) plane
  if( dim == 2)
    view(2);
  end

  if vertex_indices
    visible = reshape(unique(F),[],1);
    VI = visible - (vertex_indices<0);
    if(abs(vertex_indices)==1)
      text(V(visible,1),V(visible,2),V(visible,3),num2str(VI),'BackgroundColor',[.8 .8 .8]);
    elseif(vertex_indices)
      text(V(visible,1),V(visible,2),V(visible,3),num2str(VI));
    end
  end
  % uncomment these to switch to a better 3d surface viewing mode
  %axis equal; axis vis3d;
  %axis(reshape([min(V(:,1:dim));max(V(:,1:dim))],1,dim*2));
  if ...
    strcmp(get(gca,'XLimMode'),'auto') && ...
    strcmp(get(gca,'YLimMode'),'auto') && ...
    strcmp(get(gca,'ZLimMode'),'auto')
    axis tight;
  end

  % surgery on varargin
  cdata_v = find(cellfun(@(c) ischar(c) && strcmpi(c,'cdata'),varargin),1,'last');
  if ~isempty(cdata_v) && (cdata_v+1)<=numel(varargin)
    varargin{cdata_v+1} = double(varargin{cdata_v+1});
  end

  if v<=numel(varargin)
    set(t_copy,varargin{v:end});
  end

  % Only output if called with output variable, prevents ans = ... output on
  % terminal
  if nargout>=1
    t = t_copy;
  end

  % subversively set up the callbacks so that if the user clicks and holds on
  % the mesh then hits m, meshplot will open with this mesh
  switch buttondownfcn
  case 'default'
    set(t_copy,'buttondownfcn',@ondown);
  case 'none'
  otherwise
  end

  function ondown(src,ev)
    if exist('point_mesh_squared_distance','file')==3
      [~,ci,C] = point_mesh_squared_distance(ev.IntersectionPoint,V,Ftri);
      warning off;
        B = barycentric_coordinates(C,V(Ftri(ci,1),:),V(Ftri(ci,2),:),V(Ftri(ci,3),:));
      warning on;
      on_vertex = sum(B<0.15)==2;
      color = [.7 .5 .5];
      if on_vertex
        ci = Ftri(ci,max(B)==B);
        color = [.5 .5 .7];
        C = V(ci,:);
      else
        ci = Itri(ci);
        C = FC(ci,:);
      end
      text_h = text(C(:,1),C(:,2),C(:,3),num2str(ci),'BackgroundColor',color);
    end
    set(gcf,'windowbuttonupfcn',@onup);
    set(gcf,'keypressfcn',@onkeypress);

    function onkeypress(src,ev)
      switch ev.Character
      case 'm'
        V3 = t_copy.Vertices;
        if size(V3,2) == 2
          V3(:,3) = 0*V(:,1);
        end
        fprintf('Opening mesh in viewmesh...\n');
        writeOBJ('.vm.obj',V3,F);
        system('/usr/local/bin/viewmesh .vm.obj');
      otherwise
        warning(['Unknown key: ' ev.Character]);
      end
    end

    function onup(src,ev)
      if exist('text_h','var') && ishandle(text_h)
        delete(text_h);
      end
      set(gcf,'windowbuttonupfcn','');
      set(gcf,'keypressfcn',      '');
      set(gcf,'windowbuttonmotionfcn','');
    end
  end

end


function hout=suptitle(str, fs)
%SUPTITLE Puts a title above all subplots.
%	SUPTITLE('text') adds text to the top of the figure
%	above all subplots (a "super title"). Use this function
%	after all subplot commands.

% Drea Thomas 6/15/95 drea@mathworks.com

% Warning: If the figure or axis units are non-default, this
% will break.

% Parameters used to position the supertitle.

% Amount of the figure window devoted to subplots
plotregion = .92;

% Y position of title in normalized coordinates
titleypos  = .95;

% Fontsize for supertitle
if nargin < 2
  fs = get(gcf,'defaultaxesfontsize')+4;
end

% Fudge factor to adjust y spacing between subplots
fudge=1;

haold = gca;
figunits = get(gcf,'units');

% Get the (approximate) difference between full height (plot + title
% + xlabel) and bounding rectangle.

	if (~strcmp(figunits,'pixels')),
		set(gcf,'units','pixels');
		pos = get(gcf,'position');
		set(gcf,'units',figunits);
	else,
		pos = get(gcf,'position');
	end
	ff = (fs-4)*1.27*5/pos(4)*fudge;

        % The 5 here reflects about 3 characters of height below
        % an axis and 2 above. 1.27 is pixels per point.

% Determine the bounding rectange for all the plots

% h = findobj('Type','axes');

% findobj is a 4.2 thing.. if you don't have 4.2 comment out
% the next line and uncomment the following block.

h = findobj(gcf,'Type','axes');  % Change suggested by Stacy J. Hills

% If you don't have 4.2, use this code instead
%ch = get(gcf,'children');
%h=[];
%for i=1:length(ch),
%  if strcmp(get(ch(i),'type'),'axes'),
%    h=[h,ch(i)];
%  end
%end

max_y=0;
min_y=1;

oldtitle =0;
for i=1:length(h),
	if (~strcmp(get(h(i),'Tag'),'suptitle')),
		pos=get(h(i),'pos');
		if (pos(2) < min_y), min_y=pos(2)-ff/5*3;end;
		if (pos(4)+pos(2) > max_y), max_y=pos(4)+pos(2)+ff/5*2;end;
	else,
		oldtitle = h(i);
	end
end

if max_y > plotregion,
	scale = (plotregion-min_y)/(max_y-min_y);
	for i=1:length(h),
		pos = get(h(i),'position');
		pos(2) = (pos(2)-min_y)*scale+min_y;
		pos(4) = pos(4)*scale-(1-scale)*ff/5*3;
		set(h(i),'position',pos);
	end
end

np = get(gcf,'nextplot');
set(gcf,'nextplot','add');
if (oldtitle),
	delete(oldtitle);
end
ha=axes('pos',[0 1 1 1],'visible','off','Tag','suptitle');
ht=text(.5,titleypos-1,str);set(ht,'horizontalalignment','center','fontsize',fs);
set(gcf,'nextplot',np);
axes(haold);
if nargout,
	hout=ht;
end


function map = preset_colormap(N,varargin)
% Wrapper for any COLORMAP function, to provide preset parameter values.
%
% (c) 2020-2022 Stephen Cobeldick
%
%%% Syntax:
% preset_colormap(@fun,p1,p2,...,pN) % store function and any parameters
% map = preset_colormap(N)           % generate colormap
%
%%% Examples %%%
%
% >> preset_colormap(@cubehelix,0.25,-0.67,1.5,1)
% >> colormap(preset_colormap)
% or
% >> preset_colormap(5)
% ans =
%          0         0         0
%     0.1055    0.2788    0.4895
%     0.1660    0.6705    0.4961
%     0.6463    0.8479    0.5076
%     1.0000    1.0000    1.0000
%
% >> preset_colormap(@brewermap,'PuOr')
% >> load topo
% >> load coast
% >> figure
% >> worldmap(topo, topolegend)
% >> contourfm(topo, topolegend);
% >> contourcmap('preset_colormap', 'Colorbar','on', 'Location','horizontal','TitleString','Contour Intervals in Meters');
% >> plotm(lat, long, 'k')
%
% See Also BREWERMAP CUBEHELIX COLORMAP CONTOURCMAP

persistent fnh arg
%
if nargin==0 % Default N same as MATLAB colormaps.
	N = cmDefaultN();
elseif nargin==1 && isnumeric(N)
	assert(isscalar(N) && isreal(N) && fix(N)==N,...
		'SC:preset_colormap:N:NotRealScalarNumeric',...
		'First input <N> must be a real scalar numeric of the colormap size.')
	N = double(N);
else % Store function handle and parameter values.
	assert(isa(N,'function_handle'),...
		'SC:preset_colormap:N:NotFunctionHandle',...
		'First input <N> must be a function handle to a colormap function.')
	fnh = N;
	arg = varargin;
	return
end
%
map = fnh(N,arg{:});
%
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%preset_colormap
function N = cmDefaultN()
% Get the colormap size from the current figure or default colormap.
try
	F = get(groot,'CurrentFigure');
catch %#ok<CTCH> pre HG2
	N = size(get(gcf,'colormap'),1);
	return
end
if isempty(F)
	N = size(get(groot,'DefaultFigureColormap'),1);
else
	N = size(F.Colormap,1);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%cmDefaultN