function allen_ccf_npx_4shank_spherical(tv,av,st)
% allen_ccf_npx(tv,av,st)
% Andy Peters (peters.andrew.j@gmail.com)
% bunch of 4-shank modifications: Flora

% GUI for planning Neuropixels trajectories with the Allen CCF
% Part of repository: https://github.com/cortex-lab/allenCCF
% - directions for installing atlas in repository readme
% - some dependent functions from that repository
%
% (optional inputs - if CCF path written in Line 22, loaded automatically)
% tv, av, st = CCF template volume, annotated volume, structure tree

% Initialize gui_data structure
gui_data = struct;

% Allen CCF-bregma transform (estimated from eyeballing Paxinos->CCF)
% [AP,DV,ML]
bregma = [540,0,570];

% If not already loaded in, load in atlas
if nargin < 3
    allen_atlas_path = 'C:\Users\Andrew\OneDrive for Business\Documents\Atlases\AllenCCF';
    if isempty(allen_atlas_path)
        error('Enter path where Allen CCF is stored at Line 23');
    end
    tv = readNPY([allen_atlas_path filesep 'template_volume_10um.npy']); % grey-scale "background signal intensity"
    av = readNPY([allen_atlas_path filesep 'annotation_volume_10um_by_index.npy']); % the number at each pixel labels the area, see note below
    st = loadStructureTree([allen_atlas_path filesep 'structure_tree_safe_2017.csv']); % a table of what all the labels mean
end

% Load the colormap (located in the repository, find by associated fcn)
allenCCF_path = fileparts(which('allenCCFbregma'));
cmap_filename = [allenCCF_path filesep 'allen_ccf_colormap_2017.mat'];
load(cmap_filename);

% Set up the gui
probe_atlas_gui = figure('Toolbar','none','Menubar','none','color','w', ...
    'Name','Atlas-probe viewer','Units','normalized','Position',[0.2,0.2,0.7,0.7]);

% Set up the atlas axes
axes_atlas = subplot(1,5,1);
[~, brain_outline] = plotBrainGrid([],axes_atlas);
hold(axes_atlas,'on');
axis vis3d equal off manual
view([-30,25]);
caxis([0 300]);
[ap_max,dv_max,ml_max] = size(tv);
xlim([-10,ap_max+10])
ylim([-10,ml_max+10])
zlim([-10,dv_max+10])

% 4 shank parameters
shank_spacing=0:20:60;
shank_no=4;

for i=1:shank_no
% Set up the probe area axes                    % add 4 of these
    axes_probe_areas(i) = subplot(1,5,i+1);
    axes_probe_areas(i).ActivePositionProperty = 'position';
    set(axes_probe_areas(i),'FontSize',11);
    yyaxis(axes_probe_areas(i),'left');
    probe_areas_plot(i) = image(0);
    set(axes_probe_areas(i),'XTick','','YLim',[0,2880],'YColor','k','YDir','reverse');
    ylabel(axes_probe_areas(i),'');

    colormap(axes_probe_areas(i),cmap);
    caxis([1,size(cmap,1)])
    yyaxis(axes_probe_areas(i),'right');
    set(axes_probe_areas(i),'XTick','','YLim',[0,2880],'YColor','k','YDir','reverse');
    set(axes_probe_areas(i),'YTicklabels',[])
    title(axes_probe_areas(i),sprintf('shank %d',i));
end
yyaxis(axes_probe_areas(1),'left');
ylabel(axes_probe_areas(1),'Depth (\mum)');

% Position the axes              % change positioning so that there are 4
set(axes_atlas,'Position',[-0.15,-0.1,.8,1.2]);
set(axes_probe_areas(1),'Position',[.55,0.05,0.01,0.8]);
set(axes_probe_areas(2),'Position',[.67,0.05,0.01,0.8]);
set(axes_probe_areas(3),'Position',[.79,0.05,0.01,0.8]);
set(axes_probe_areas(4),'Position',[.91,0.05,0.01,0.8]);

% Set the current axes to the atlas (dirty, but some gca requirements)
axes(axes_atlas);

% Set up the probe reference/actual % again set up the reference so that 4
% shans are plotted
probe_ref_top = [bregma(1),bregma(3),0];
probe_ref_bottom = [bregma(1),bregma(3),size(tv,2)];
probe_ref_vector = [probe_ref_top',probe_ref_bottom'];
shank_spacing_vector=repmat(shank_spacing,2,1)';
allshanks_ref_vector=repmat(probe_ref_vector(2,:),shank_no,1)+shank_spacing_vector;

probe_ref_line = line(probe_ref_vector(1,:),allshanks_ref_vector,probe_ref_vector(3,:), ...
    'linewidth',1.5,'color','r','linestyle','--');

% probe_ref_line = line(probe_ref_vector(1,:),probe_ref_vector(2,:),probe_ref_vector(3,:), ...
%     'linewidth',1.5,'color','r','linestyle','--');

probe_length = 288.8; % IMEC phase 2.0 (in 10 ums)
probe_vector = [probe_ref_vector(:,1),diff(probe_ref_vector,[],2)./ ...
    norm(diff(probe_ref_vector,[],2))*probe_length + probe_ref_vector(:,1)];

allshanks_vector=repmat(probe_vector(2,:),shank_no,1)+shank_spacing_vector;
probe_line = line(probe_vector(1,:),allshanks_vector,probe_vector(3,:),'linewidth',3,'color','b','linestyle','-');
set(probe_line(1),'color','g'); % set shank 0 to be a different color

%probe_line = line(probe_vector(1,:),probe_vector(2,:),probe_vector(3,:),'linewidth',3,'color','b','linestyle','-');

% Set up the text to display coordinates
probe_coordinates_text = uicontrol('Style','text','String','', ...
    'Units','normalized','Position',[0,0.95,1,0.05], ...
    'BackgroundColor','w','HorizontalAlignment','left','FontSize',12);

% Store data
gui_data.tv = tv; % Intensity atlas
gui_data.av = av; % Annotated atlas
gui_data.st = st; % Labels table
gui_data.cmap = cmap; % Atlas colormap
gui_data.bregma = bregma; % Bregma for external referencing
gui_data.probe_length = probe_length; % Length of probe
gui_data.structure_plot_idx = []; % Plotted structures
gui_data.probe_angle = [0;0]; % Probe angles in azimuth/elevation
gui_data.shank_no=shank_no;
gui_data.spin=0;
gui_data.shank_spacing=shank_spacing;

%Store handles
gui_data.handles.cortex_outline = brain_outline;
gui_data.handles.structure_patch = []; % Plotted structures
gui_data.handles.axes_atlas = axes_atlas; % Axes with 3D atlas
gui_data.handles.axes_probe_areas = axes_probe_areas; % Axes with probe areas
gui_data.handles.slice_plot = surface('EdgeColor','none'); % Slice on 3D atlas
gui_data.handles.slice_volume = 'tv'; % The volume shown in the slice
gui_data.handles.probe_ref_line = probe_ref_line; % Probe reference line on 3D atlas
gui_data.handles.probe_line = probe_line; % Probe reference line on 3D atlas
gui_data.handles.probe_areas_plot = probe_areas_plot; % Color-coded probe regions
gui_data.probe_coordinates_text = probe_coordinates_text; % Probe coordinates text

% Make 3D rotation the default state (toggle on/off with 'r')
h = rotate3d(axes_atlas);
h.Enable = 'on';
% Update the slice whenever a rotation is completed
h.ActionPostCallback = @update_slice;

% Set functions for key presses
hManager = uigetmodemanager(probe_atlas_gui);
[hManager.WindowListenerHandles.Enabled] = deal(false);
set(probe_atlas_gui,'KeyPressFcn',@key_press);
set(probe_atlas_gui,'KeyReleaseFcn',@key_release);

% Upload gui_data
guidata(probe_atlas_gui, gui_data);

% Display the first slice and update the probe position
update_slice(probe_atlas_gui);
update_probe_coordinates(probe_atlas_gui);

% Display controls
display_controls;

end

function key_press(probe_atlas_gui,eventdata)

% Get guidata
gui_data = guidata(probe_atlas_gui);

switch eventdata.Key

    case 'uparrow'
        if isempty(eventdata.Modifier)
            % Up: move probe anterior
            ap_offset = -10;

            for myshank=1:numel(gui_data.handles.probe_ref_line)
            set(gui_data.handles.probe_ref_line(myshank,1),'XData',get(gui_data.handles.probe_ref_line(myshank,1),'XData') + ap_offset);
            set(gui_data.handles.probe_line(myshank,1),'XData',get(gui_data.handles.probe_line(myshank,1),'XData') + ap_offset);
            end

        elseif any(strcmp(eventdata.Modifier,'shift'))
            % Ctrl-up: increase DV angle
            angle_change=-1;
            update_rotation(probe_atlas_gui,angle_change,'Elevation');
            % update gui value
            new_angle = gui_data.probe_angle(2) + angle_change;
            gui_data.probe_angle(2) = new_angle;

        elseif any(strcmp(eventdata.Modifier,'alt'))
            % Alt-up: raise probe
            probe_offset = -10;

            for myshank=1:numel(gui_data.handles.probe_line)
            old_probe_vector = cell2mat(get(gui_data.handles.probe_line(myshank,1),{'XData','YData','ZData'})');

            move_probe_vector = diff(old_probe_vector,[],2)./ ...
                norm(diff(old_probe_vector,[],2))*probe_offset;

            new_probe_vector = bsxfun(@plus,old_probe_vector,move_probe_vector);

            set(gui_data.handles.probe_line(myshank,1),'XData',new_probe_vector(1,:), ...
                'YData',new_probe_vector(2,:),'ZData',new_probe_vector(3,:));
            end
        end

    case 'downarrow'
        if isempty(eventdata.Modifier)
            % Down: move probe posterior
            ap_offset = 10;
            for myshank=1:numel(gui_data.handles.probe_ref_line)
            set(gui_data.handles.probe_ref_line(myshank,1),'XData',get(gui_data.handles.probe_ref_line(myshank,1),'XData') + ap_offset);
            set(gui_data.handles.probe_line(myshank,1),'XData',get(gui_data.handles.probe_line(myshank,1),'XData') + ap_offset);
            end

        elseif any(strcmp(eventdata.Modifier,'shift'))
            % Ctrl-down: decrease DV angle

            angle_change=1;
            update_rotation(probe_atlas_gui,angle_change,'Elevation');
            new_angle = gui_data.probe_angle(2) + angle_change;
            gui_data.probe_angle(2) = new_angle;

        elseif any(strcmp(eventdata.Modifier,'alt'))
            % Alt-down: lower probe
            probe_offset = 10;
            for myshank=1:numel(gui_data.handles.probe_line)  % this could be yoked together
            old_probe_vector = cell2mat(get(gui_data.handles.probe_line(myshank,1),{'XData','YData','ZData'})');

            move_probe_vector = diff(old_probe_vector,[],2)./ ...
                norm(diff(old_probe_vector,[],2))*probe_offset;

            new_probe_vector = bsxfun(@plus,old_probe_vector,move_probe_vector);

            set(gui_data.handles.probe_line(myshank,1),'XData',new_probe_vector(1,:), ...
                'YData',new_probe_vector(2,:),'ZData',new_probe_vector(3,:));
            end
        end

    case 'rightarrow'
        if isempty(eventdata.Modifier)
            % Right: move probe right
            ml_offset = 10;
            for myshank=1:numel(gui_data.handles.probe_ref_line)
            set(gui_data.handles.probe_ref_line(myshank,1),'YData',get(gui_data.handles.probe_ref_line(myshank,1),'YData') + ml_offset);
            set(gui_data.handles.probe_line(myshank,1),'YData',get(gui_data.handles.probe_line(myshank,1),'YData') + ml_offset);
            end

        elseif any(strcmp(eventdata.Modifier,'shift'))
            % Ctrl-right: increase vertical angle
%             angle_change = [1;0];
%             new_angle = gui_data.probe_angle + angle_change;
%             gui_data.probe_angle = new_angle;
%             update_probe_angle(probe_atlas_gui);
            angle_change=-1;
            update_rotation(probe_atlas_gui,angle_change,'Azimuth');
            new_angle = gui_data.probe_angle(1) + angle_change;
            gui_data.probe_angle(1) = new_angle;

        elseif any(strcmp(eventdata.Modifier,'alt'))
            % alt-right : rotate clockwise
            angle_change=-1;
            update_rotation(probe_atlas_gui,angle_change,'spin');
            new_angle=gui_data.spin+angle_change;
            gui_data.spin=new_angle;
        end

    case 'leftarrow'
        if isempty(eventdata.Modifier)
            % Left: move probe left
            ml_offset = -10;
            for myshank=1:numel(gui_data.handles.probe_ref_line)
            set(gui_data.handles.probe_ref_line(myshank,1),'YData',get(gui_data.handles.probe_ref_line(myshank,1),'YData') + ml_offset);
            set(gui_data.handles.probe_line(myshank,1),'YData',get(gui_data.handles.probe_line(myshank,1),'YData') + ml_offset);
            end

        elseif any(strcmp(eventdata.Modifier,'shift'))
            % Ctrl-right: increase vertical angle
%             angle_change = [-1;0];
%             new_angle = gui_data.probe_angle + angle_change;
%             gui_data.probe_angle = new_angle;
%             update_probe_angle(probe_atlas_gui);
            angle_change=1;
            update_rotation(probe_atlas_gui,angle_change,'Azimuth');
            new_angle = gui_data.probe_angle(1) + angle_change;
            gui_data.probe_angle(1) = new_angle;

        elseif any(strcmp(eventdata.Modifier,'alt'))
            % alt-left : rotate anticlockwise
            angle_change=1;
            update_rotation(probe_atlas_gui,angle_change,'spin');
            new_angle=gui_data.spin+angle_change;
            gui_data.spin=new_angle;
        end

    case 'c'
        % Bring up controls again
        display_controls;

    case 'b'
        % Toggle brain outline visibility
        current_visibility = gui_data.handles.cortex_outline.Visible;
        switch current_visibility; case 'on'; new_visibility = 'off'; case 'off'; new_visibility = 'on'; end;
        set(gui_data.handles.cortex_outline,'Visible',new_visibility);

    case 'a'
        % Toggle plotted structure visibility
        if ~isempty(gui_data.structure_plot_idx)
            current_visibility = get(gui_data.handles.structure_patch(1),'Visible');
            switch current_visibility; case 'on'; new_visibility = 'off'; case 'off'; new_visibility = 'on'; end;
            set(gui_data.handles.structure_patch,'Visible',new_visibility);
        end

    case 's'
        % Toggle slice volume/visibility
        slice_volumes = {'tv','av','none'};
        new_slice_volume = slice_volumes{circshift( ...
            strcmp(gui_data.handles.slice_volume,slice_volumes),[0,1])};

        if strcmp(new_slice_volume,'none')
            set(gui_data.handles.slice_plot,'Visible','off');
        else
            set(gui_data.handles.slice_plot,'Visible','on');
        end

        gui_data.handles.slice_volume = new_slice_volume;
        guidata(probe_atlas_gui, gui_data);

        update_slice(probe_atlas_gui);

    case 'p'
        % Toggle probe visibility
        current_visibility = gui_data.handles.probe_ref_line.Visible;
        switch current_visibility; case 'on'; new_visibility = 'off'; case 'off'; new_visibility = 'on'; end;
        set(gui_data.handles.probe_ref_line,'Visible',new_visibility);
        set(gui_data.handles.probe_line,'Visible',new_visibility);

    case 'r'
        % Toggle 3D rotation
        h = rotate3d(gui_data.handles.axes_atlas);
        switch h.Enable
            case 'off'
                h.Enable = 'on';
                % Update the slice whenever a rotation is completed
                h.ActionPostCallback = @update_slice;
                %(need to restore key-press functionality with rotation)
                hManager = uigetmodemanager(probe_atlas_gui);
                [hManager.WindowListenerHandles.Enabled] = deal(false);
                set(probe_atlas_gui,'KeyPressFcn',@key_press);
            case 'on'
                h.Enable = 'off';
        end

    case 'm'
        % Set probe angle
        set_probe_position(probe_atlas_gui);
        % Get updated guidata
        gui_data = guidata(probe_atlas_gui);

    case {'equal','add'}
        % Add structure(s) to display
        slice_spacing = 10;

        % Prompt for which structures to show (only structures which are
        % labelled in the slice-spacing downsampled annotated volume)

        if ~any(strcmp(eventdata.Modifier,'shift'))
            % (no shift: list in native CCF order)
            parsed_structures = unique(reshape(gui_data.av(1:slice_spacing:end, ...
                1:slice_spacing:end,1:slice_spacing:end),[],1));
            plot_structures_parsed = listdlg('PromptString','Select a structure to plot:', ...
                'ListString',gui_data.st.safe_name(parsed_structures),'ListSize',[520,500]);
            plot_structures = parsed_structures(plot_structures_parsed);

            if ~isempty(plot_structures)
                for curr_plot_structure = reshape(plot_structures,1,[])
                    % If this label isn't used, don't plot
                    if ~any(reshape(gui_data.av( ...
                            1:slice_spacing:end,1:slice_spacing:end,1:slice_spacing:end),[],1) == curr_plot_structure)
                        disp(['"' gui_data.st.safe_name{curr_plot_structure} '" is not parsed in the atlas'])
                        continue
                    end

                    gui_data.structure_plot_idx(end+1) = curr_plot_structure;

                    plot_structure_color = hex2dec(reshape(gui_data.st.color_hex_triplet{curr_plot_structure},2,[])')./255;
                    structure_3d = isosurface(permute(gui_data.av(1:slice_spacing:end, ...
                        1:slice_spacing:end,1:slice_spacing:end) == curr_plot_structure,[3,1,2]),0);

                    structure_alpha = 0.2;
                    gui_data.handles.structure_patch(end+1) = patch('Vertices',structure_3d.vertices*slice_spacing, ...
                        'Faces',structure_3d.faces, ...
                        'FaceColor',plot_structure_color,'EdgeColor','none','FaceAlpha',structure_alpha);
                end
            end

        elseif any(strcmp(eventdata.Modifier,'shift'))
            % (shift: use hierarchy search)
            plot_structures = hierarchicalSelect(gui_data.st);

            if ~isempty(plot_structures) % will be empty if dialog was cancelled
                % get all children of this one
                thisID = gui_data.st.id(plot_structures);
                idStr = sprintf('/%d/', thisID);
                theseCh = find(cellfun(@(x)contains(x,idStr), gui_data.st.structure_id_path));

                % plot the structure
                slice_spacing = 5;
                plot_structure_color = hex2dec(reshape(gui_data.st.color_hex_triplet{plot_structures},3,[]))./255;
                structure_3d = isosurface(permute(ismember(gui_data.av(1:slice_spacing:end, ...
                    1:slice_spacing:end,1:slice_spacing:end),theseCh),[3,1,2]),0);

                structure_alpha = 0.2;
                gui_data.structure_plot_idx(end+1) = plot_structures;
                gui_data.handles.structure_patch(end+1) = patch('Vertices',structure_3d.vertices*slice_spacing, ...
                    'Faces',structure_3d.faces, ...
                    'FaceColor',plot_structure_color,'EdgeColor','none','FaceAlpha',structure_alpha);

            end

        end

    case {'hyphen','subtract'}
        % Remove structure(s) already plotted
        if ~isempty(gui_data.structure_plot_idx)
            remove_structures = listdlg('PromptString','Select a structure to remove:', ...
                'ListString',gui_data.st.safe_name(gui_data.structure_plot_idx));
            delete(gui_data.handles.structure_patch(remove_structures))
            gui_data.structure_plot_idx(remove_structures) = [];
            gui_data.handles.structure_patch(remove_structures) = [];
        end

    case 'x'
        % Export the probe coordinates in Allen CCF to the workspace
        probe_vector = cell2mat(get(gui_data.handles.probe_line,{'XData','YData','ZData'})');
        probe_vector_ccf = round(probe_vector([1,3,2],:))';
        assignin('base','probe_vector_ccf',probe_vector_ccf)
        disp('Copied probe vector coordinates to workspace');

    case 'h'
        % Load probe histology points, plot line of best fit
        probe_histology(probe_atlas_gui);
end

% Upload gui_data
guidata(probe_atlas_gui, gui_data);

end

function key_release(probe_atlas_gui,eventdata)

% Get guidata
gui_data = guidata(probe_atlas_gui);

switch eventdata.Key
    case {'rightarrow','leftarrow','uparrow','downarrow'}
        % Update the probe info/slice on arrow release
        update_probe_coordinates(probe_atlas_gui);
        update_slice(probe_atlas_gui);
end

% Upload gui_data
guidata(probe_atlas_gui, gui_data);

end

function update_slice(probe_atlas_gui,varargin)

% Get guidata
gui_data = guidata(probe_atlas_gui);

% Only update the slice if it's visible
if strcmp(gui_data.handles.slice_plot(1).Visible,'on')

    % Get current position of camera
    curr_campos = campos;

    % Get probe vector
    probe_ref_top = [gui_data.handles.probe_ref_line(1,1).XData(1), ...
        gui_data.handles.probe_ref_line(1,1).YData(1),gui_data.handles.probe_ref_line(1,1).ZData(1)];
    probe_ref_bottom = [gui_data.handles.probe_ref_line(1).XData(2), ...
        gui_data.handles.probe_ref_line(1,1).YData(2),gui_data.handles.probe_ref_line(1,1).ZData(2)];
    probe_vector = probe_ref_top - probe_ref_bottom;

    % Get probe-camera vector
    probe_camera_vector = probe_ref_top - curr_campos;

    % Get the vector to plot the plane in (along with probe vector)
    plot_vector = cross(probe_camera_vector,probe_vector);

    % Get the normal vector of the plane
    normal_vector = cross(plot_vector,probe_vector);

    % Get the plane offset through the probe
    plane_offset = -(normal_vector*probe_ref_top');

    % Define a plane of points to index
    % (the plane grid is defined based on the which cardinal plan is most
    % orthogonal to the plotted plane. this is janky but it works)
    slice_px_space = 3;
    %[~,cam_plane] = max(abs((campos - camtarget)./norm(campos - camtarget)));

    [~,cam_plane] = max(abs(normal_vector./norm(normal_vector)));

    switch cam_plane

        case 1
            [plane_y,plane_z] = meshgrid(1:slice_px_space:size(gui_data.tv,3),1:slice_px_space:size(gui_data.tv,2));
            plane_x = ...
                (normal_vector(2)*plane_y+normal_vector(3)*plane_z + plane_offset)/ ...
                -normal_vector(1);

        case 2
            [plane_x,plane_z] = meshgrid(1:slice_px_space:size(gui_data.tv,1),1:slice_px_space:size(gui_data.tv,2));
            plane_y = ...
                (normal_vector(1)*plane_x+normal_vector(3)*plane_z + plane_offset)/ ...
                -normal_vector(2);

        case 3
            [plane_x,plane_y] = meshgrid(1:slice_px_space:size(gui_data.tv,1),1:slice_px_space:size(gui_data.tv,3));
            plane_z = ...
                (normal_vector(1)*plane_x+normal_vector(2)*plane_y + plane_offset)/ ...
                -normal_vector(3);

    end

    % Get the coordiates on the plane
    x_idx = round(plane_x);
    y_idx = round(plane_y);
    z_idx = round(plane_z);

    % Find plane coordinates in bounds with the volume
    use_xd = x_idx > 0 & x_idx < size(gui_data.tv,1);
    use_yd = y_idx > 0 & y_idx < size(gui_data.tv,3);
    use_zd = z_idx > 0 & z_idx < size(gui_data.tv,2);
    use_idx = use_xd & use_yd & use_zd;

    curr_slice_idx = sub2ind(size(gui_data.tv),x_idx(use_idx),z_idx(use_idx),y_idx(use_idx));

    % Find plane coordinates that contain brain
    curr_slice_isbrain = false(size(use_idx));
    curr_slice_isbrain(use_idx) = gui_data.av(curr_slice_idx) > 1;

    % Index coordinates in bounds + with brain
    grab_pix_idx = sub2ind(size(gui_data.tv),x_idx(curr_slice_isbrain),z_idx(curr_slice_isbrain),y_idx(curr_slice_isbrain));

    % Grab pixels from (selected) volume
    curr_slice = nan(size(use_idx));
    switch gui_data.handles.slice_volume
        case 'tv'
            curr_slice(curr_slice_isbrain) = gui_data.tv(grab_pix_idx);
            colormap(gui_data.handles.axes_atlas,'gray');
            caxis([0,255]);
        case 'av'
            curr_slice(curr_slice_isbrain) = gui_data.av(grab_pix_idx);
            colormap(gui_data.handles.axes_atlas,gui_data.cmap);
            caxis([1,size(gui_data.cmap,1)]);
    end

    % Update the slice display
    set(gui_data.handles.slice_plot,'XData',plane_x,'YData',plane_y,'ZData',plane_z,'CData',curr_slice);

    % Upload gui_data
    guidata(probe_atlas_gui, gui_data);

end

end

function set_probe_position(probe_atlas_gui,varargin)

% Get guidata
gui_data = guidata(probe_atlas_gui);

% Prompt for angles
prompt_text = { ...
    'AP position (mm from bregma)', ...
    'ML position (mm from bregma)', ...
    'ML angle (relative to midline)', ....
    'DV angle (relative to coronal cut)',...
    'probe spin'};

new_probe_position = cellfun(@str2num,inputdlg(prompt_text,'Set probe position',1));

% Convert probe position: mm->CCF and degrees->radians
probe_ccf_coordinates = round(gui_data.bregma([1,3])' - new_probe_position(1:2)*100);
%probe_angle_rad = (new_probe_position(3:4)/360)*2*pi;

% Update the probe and trajectory reference
[ap_max,dv_max,ml_max] = size(gui_data.tv);

max_ref_length = sqrt(sum(([ap_max,dv_max,ml_max].^2)));

%[~,y,z] = sph2cart(probe_angle_rad(1),probe_angle_rad(2),max_ref_length);

%[~,x,~] = sph2cart(probe_angle_rad(2),probe_angle_rad(1),max_ref_length);

probe_ref_top = [probe_ccf_coordinates(1),probe_ccf_coordinates(2),0];
probe_ref_bottom = [probe_ccf_coordinates(1),probe_ccf_coordinates(2),max_ref_length];
probe_ref_vector = [probe_ref_top;probe_ref_bottom]';
probe_vector = [probe_ref_vector(:,1),diff(probe_ref_vector,[],2)./ ...
    norm(diff(probe_ref_vector,[],2))*gui_data.probe_length + probe_ref_vector(:,1)];

shank_spacing_vector=reshape([gui_data.shank_spacing;gui_data.shank_spacing],[],1)';

probe_ref_vector=repmat(probe_ref_vector,1,gui_data.shank_no);
probe_ref_vector(2,:)=probe_ref_vector(2,:)+shank_spacing_vector;

probe_vector=repmat(probe_vector,1,gui_data.shank_no);
probe_vector(2,:)=probe_vector(2,:)+shank_spacing_vector;

gui_data.probe_angle(1) =new_probe_position(3);
gui_data.probe_angle(2) =new_probe_position(4);
gui_data.spin=new_probe_position(5);
% % spin
% rotation_matrix=get_rotation_matrix(gui_data.spin,'spin');
% [probe_ref_vector,probe_vector]=please_rotate(probe_ref_vector,probe_vector,rotation_matrix);
%
% % rotate ML
% rotation_matrix=get_rotation_matrix(gui_data.probe_angle(1),'ML');
% [probe_ref_vector,probe_vector]=please_rotate(probe_ref_vector,probe_vector,rotation_matrix);
%
% % rotate AP
% rotation_matrix=get_rotation_matrix(gui_data.probe_angle(2),'AP');
% [probe_ref_vector,probe_vector]=please_rotate(probe_ref_vector,probe_vector,rotation_matrix);

for myshank=1:gui_data.shank_no
    st_ix=2*myshank-1;
    set(gui_data.handles.probe_ref_line(myshank),'XData',probe_ref_vector(1,st_ix:(st_ix+1)), ...
            'YData',probe_ref_vector(2,st_ix:(st_ix+1)), ...
            'ZData',probe_ref_vector(3,st_ix:(st_ix+1)));

    set(gui_data.handles.probe_line(myshank),'XData',probe_vector(1,st_ix:(st_ix+1)), ...
        'YData',probe_vector(2,st_ix:(st_ix+1)), ...
        'ZData',probe_vector(3,st_ix:(st_ix+1)));
end

% Upload gui_data
guidata(probe_atlas_gui, gui_data);

% Update the slice and probe coordinates
update_slice(probe_atlas_gui);
update_probe_coordinates(probe_atlas_gui);

end

function update_probe_angle(probe_atlas_gui,varargin)
% not used right now
% Get guidata
gui_data = guidata(probe_atlas_gui);

% Get the positions of the probe and trajectory reference
for myshank=1:numel(gui_data.handles.probe_ref_line)
    probe_ref_vector = cell2mat(get(gui_data.handles.probe_ref_line(myshank,1),{'XData','YData','ZData'})');
    probe_vector = cell2mat(get(gui_data.handles.probe_line(myshank,1),{'XData','YData','ZData'})');

    % Update the probe trajectory reference (rotate about trajectory top)
    [ap_max,dv_max,ml_max] = size(gui_data.tv);

    max_ref_length = sqrt(sum(([ap_max,dv_max,ml_max].^2)));

    probe_angle_rad = (gui_data.probe_angle./360)*2*pi;
    [x,y,z] = sph2cart(pi-probe_angle_rad(1),probe_angle_rad(2),max_ref_length);

    new_probe_ref_top = [probe_ref_vector(1,1),probe_ref_vector(2,1),0];
    new_probe_ref_bottom = new_probe_ref_top + [x,y,z];
    new_probe_ref_vector = [new_probe_ref_top;new_probe_ref_bottom]';

    set(gui_data.handles.probe_ref_line(myshank,1),'XData',new_probe_ref_vector(1,:), ...
        'YData',new_probe_ref_vector(2,:), ...
        'ZData',new_probe_ref_vector(3,:));

    % Update probe (retain depth)
    new_probe_vector = [new_probe_ref_vector(:,1),diff(new_probe_ref_vector,[],2)./ ...
        norm(diff(new_probe_ref_vector,[],2))*gui_data.probe_length + new_probe_ref_vector(:,1)];

    probe_depth = sqrt(sum((probe_ref_vector(:,1) - probe_vector(:,1)).^2));
    new_probe_vector_depth = (diff(new_probe_vector,[],2)./ ...
        norm(diff(new_probe_vector,[],2))*probe_depth) + new_probe_vector;

    set(gui_data.handles.probe_line(myshank,1),'XData',new_probe_vector_depth(1,:), ...
        'YData',new_probe_vector_depth(2,:),'ZData',new_probe_vector_depth(3,:));
end
% Upload gui_data
guidata(probe_atlas_gui, gui_data);

end

function update_probe_coordinates(probe_atlas_gui,varargin)

% Get guidata
gui_data = guidata(probe_atlas_gui);

for myshank=1:numel(gui_data.handles.probe_ref_line)
    % Get the positions of the probe and trajectory reference
    probe_ref_vector = cell2mat(get(gui_data.handles.probe_ref_line(myshank,1),{'XData','YData','ZData'})');
    probe_vector = cell2mat(get(gui_data.handles.probe_line(myshank,1),{'XData','YData','ZData'})');

    trajectory_n_coords = max(abs(diff(probe_ref_vector,[],2)));
    [trajectory_xcoords,trajectory_ycoords,trajectory_zcoords] = deal( ...
        linspace(probe_ref_vector(1,1),probe_ref_vector(1,2),trajectory_n_coords), ...
        linspace(probe_ref_vector(2,1),probe_ref_vector(2,2),trajectory_n_coords), ...
        linspace(probe_ref_vector(3,1),probe_ref_vector(3,2),trajectory_n_coords));

    probe_n_coords = sqrt(sum(diff(probe_vector,[],2).^2));
    [probe_xcoords,probe_ycoords,probe_zcoords] = deal( ...
        linspace(probe_vector(1,1),probe_vector(1,2),probe_n_coords), ...
        linspace(probe_vector(2,1),probe_vector(2,2),probe_n_coords), ...
        linspace(probe_vector(3,1),probe_vector(3,2),probe_n_coords));

    % Get brain labels across the probe and trajectory, and intersection with brain
    pixel_space = 5;
    trajectory_areas = interp3(single(gui_data.av(1:pixel_space:end,1:pixel_space:end,1:pixel_space:end)), ...
        round(trajectory_zcoords/pixel_space),round(trajectory_xcoords/pixel_space),round(trajectory_ycoords/pixel_space),'nearest');
    trajectory_brain_idx = find(trajectory_areas > 1,1);
    trajectory_brain_intersect = ...
        [trajectory_xcoords(trajectory_brain_idx),trajectory_ycoords(trajectory_brain_idx),trajectory_zcoords(trajectory_brain_idx)]';

    probe_areas = interp3(single(gui_data.av(1:pixel_space:end,1:pixel_space:end,1:pixel_space:end)), ...
        round(probe_zcoords/pixel_space),round(probe_xcoords/pixel_space),round(probe_ycoords/pixel_space),'nearest')';
    probe_area_boundaries = intersect(unique([find(~isnan(probe_areas),1,'first'); ...
        find(diff(probe_areas) ~= 0);find(~isnan(probe_areas),1,'last')]),find(~isnan(probe_areas)));
    probe_area_centers = probe_area_boundaries(1:end-1) + diff(probe_area_boundaries)/2;
    probe_area_labels = gui_data.st.safe_name(probe_areas(round(probe_area_centers)));

    % Get position of brain intersect relative to bregma
    probe_bregma_coordinate = round((gui_data.bregma([1,3])' - trajectory_brain_intersect(1:2))*10);

    % Get the depth of the bottom of the probe (sign: hack by z offset)
    probe_depth = round(sqrt(sum((trajectory_brain_intersect - probe_vector(:,2)).^2))*10)* ...
        sign(probe_vector(3,2)-trajectory_brain_intersect(3));

    % Update the text
    if myshank==1
        probe_text = ['Shank 1 insertion: ' ....
            num2str(probe_bregma_coordinate(1)) ' AP, ', ...
            num2str(-probe_bregma_coordinate(2)) ' ML, ', ...
            num2str(probe_depth) ' Probe-axis, ' ...
            num2str(gui_data.probe_angle(1)+90) char(176) ' from midline, ' ...
            num2str(-(gui_data.probe_angle(2)+90)) char(176) ' from horizontal,' ...
            num2str(gui_data.spin) char(176) ' spin '];
        set(gui_data.probe_coordinates_text,'String',probe_text);
    end

    % Update the probe areas
    yyaxis(gui_data.handles.axes_probe_areas(myshank),'right');
    set(gui_data.handles.probe_areas_plot(myshank),'YData',[1:length(probe_areas)]*10,'CData',probe_areas);
    set(gui_data.handles.axes_probe_areas(myshank),'YTick',probe_area_centers*10,'YTickLabels',probe_area_labels','YTickLabelRotation',45);

end
% Upload gui_data
guidata(probe_atlas_gui, gui_data);

end

function probe_histology(probe_atlas_gui)
% Load histology points
% UNDER CONSTRUCTION
% (used to use SHARP-Track, now using mine)

% Get guidata
gui_data = guidata(probe_atlas_gui);

[probe_file,probe_path] = uigetfile('*.mat','Choose probe coordinate file');
load([probe_path,probe_file]);

if exist('pointList','var')
    histology_points = pointList.pointList{1};
elseif exist('probe_ccf','var')
    histology_points = probe_ccf(1).points; % only use first probe
end

r0 = mean(histology_points,1);
xyz = bsxfun(@minus,histology_points,r0);
[~,~,V] = svd(xyz,0);
histology_probe_direction = V(:,1);

probe_eval_points = [-1000,1000];
probe_line_endpoints = bsxfun(@plus,bsxfun(@times,probe_eval_points',histology_probe_direction'),r0);

% Philip's GUI: not saved in native CCF order?
% plot3(histology_points(:,3),histology_points(:,1),histology_points(:,2),'.b','MarkerSize',20);
% line(P(:,3),P(:,1),P(:,2),'color','k','linewidth',2)

% % Mine: saved in native CCF order [AP,DV,ML]
plot3(histology_points(:,1),histology_points(:,3),histology_points(:,2),'.b','MarkerSize',20);
line(probe_line_endpoints(:,1),probe_line_endpoints(:,3),probe_line_endpoints(:,2),'color','k','linewidth',2)

% Place the probe on the histology best-fit axis
[ap_max,dv_max,ml_max] = size(gui_data.tv);

probe_ref_top = probe_line_endpoints(1,[1,3,2]);
probe_ref_bottom = probe_line_endpoints(2,[1,3,2]);
probe_ref_vector = [probe_ref_top;probe_ref_bottom]';

set(gui_data.handles.probe_ref_line,'XData',probe_ref_vector(1,:), ...
    'YData',probe_ref_vector(2,:), ...
    'ZData',probe_ref_vector(3,:));

probe_vector = [probe_ref_vector(:,1),diff(probe_ref_vector,[],2)./ ...
    norm(diff(probe_ref_vector,[],2))*gui_data.probe_length + probe_ref_vector(:,1)];
set(gui_data.handles.probe_line,'XData',probe_vector(1,:), ...
    'YData',probe_vector(2,:),'ZData',probe_vector(3,:));

% Upload gui_data
[theta,phi] = cart2sph(diff(probe_ref_vector(1,:)),diff(probe_ref_vector(2,:)),diff(probe_ref_vector(3,:)));
gui_data.probe_angle = ([theta,phi]/(2*pi))*360;
guidata(probe_atlas_gui, gui_data);

% Update the slice and probe coordinates
update_slice(probe_atlas_gui);
update_probe_coordinates(probe_atlas_gui);
end

function display_controls

% Print controls
CreateStruct.Interpreter = 'tex';
CreateStruct.WindowStyle = 'non-modal';
msgbox( ...
    {'\fontsize{12}' ...
    '\bf Probe: \rm' ...
    'Arrow keys : translate probe' ...
    'Alt/Option up/down : raise/lower probe' ...
    'Alt/Option left/right : spin probe' ...
    'Shift arrow keys : rotate probe (around top)' ...
    'm : set probe location manually', ...
    '\bf 3D brain areas: \rm' ...
    ' =/+ : add (list selector)' ...
    ' Shift =/+ : add (hierarchy selector)' ...
    ' - : remove', ...
    '\bf Visibility: \rm' ...
    's : atlas slice (toggle tv/av/off)' ...
    'b : brain outline' ...
    'p : probe' ...
    'a : 3D brain areas' ...
    '\bf Other: \rm' ...
    'r : toggle clickable rotation' ...
    'x : export probe coordinates to workspace' ...
    'h : load and plot histology-defined trajectory', ...
    'c : bring up controls box'}, ...
    'Controls',CreateStruct);

end

function [rotation_matrix]=get_rotation_matrix(probe_vector,change_angle,current_angle,direction)
% Zhiwen modified
% Convert Axis-Angle Rotation to Rotation Matrix
angle_rad=deg2rad(change_angle);
current_angle_rad = deg2rad(current_angle);
probeV = probe_vector(:,2)-probe_vector(:,1);
probehV = probe_vector(:,3)-probe_vector(:,1);
    switch direction
        case 'Azimuth'
            % Azimuth
            % Convert Axis-Angle Rotation to Rotation Matrix
            % DV line as rotation axis
            axang = [0 0 288.8 angle_rad];
        case 'Elevation'
            % Elevation
            % Convert Axis-Angle Rotation to Rotation Matrix
            % plane of 4-shank probe is the plane to rotate on;
            % which means it rotates around vector orthogonal to this
            % plane;
            axang1 = [0 0 1 current_angle_rad];
            rotation_matrix1 = axang2rotm1(axang1);
            rotated_azimuth = rotation_matrix1*[1; 0; 0];
            %%
            othorg = cross(probeV,rotated_azimuth);
            axang = [othorg(1) othorg(2) othorg(3) angle_rad];
        case 'spin'
            % spin
            % Convert Axis-Angle Rotation to Rotation Matrix
            % spin around reference vector;
            axang = [probeV(1,1) probeV(2,1) probeV(3,1) angle_rad];
    end
    rotation_matrix = axang2rotm1(axang);
end

function [rotated_ref_bregma,rotated_probe_bregma]=please_rotate(probe_ref_vector,probe_vector,rotation_matrix)
    zeropoint=probe_vector(:,1);
    zerocentered_ref=probe_ref_vector-zeropoint;

    zerocentered_probe=probe_vector-zeropoint;

    rotated_ref=rotation_matrix*zerocentered_ref;
    rotated_ref_bregma=rotated_ref+zeropoint;

    rotated_probe=rotation_matrix*zerocentered_probe;
    rotated_probe_bregma=rotated_probe+zeropoint;
end

function update_rotation(probe_atlas_gui,angle_change,mydir)
% this is the function when we update the rotation of the probe either in
% ML or AP
    gui_data = guidata(probe_atlas_gui);
    probe_ref_vector = cell2mat(get(gui_data.handles.probe_ref_line,{'XData','YData','ZData'})');
    probe_vector = cell2mat(get(gui_data.handles.probe_line,{'XData','YData','ZData'})');
    current_angle = gui_data.probe_angle(1)+90;

    rotation_matrix=get_rotation_matrix(probe_vector,angle_change,current_angle,mydir);
    [rotated_ref_bregma,rotated_probe_bregma]=please_rotate(probe_ref_vector,probe_vector,rotation_matrix);

    for myshank=1:gui_data.shank_no
        st_ix=2*myshank-1;
        set(gui_data.handles.probe_ref_line(myshank),'XData',rotated_ref_bregma(1,st_ix:(st_ix+1)), ...
                'YData',rotated_ref_bregma(2,st_ix:(st_ix+1)), ...
                'ZData',rotated_ref_bregma(3,st_ix:(st_ix+1)));

        set(gui_data.handles.probe_line(myshank),'XData',rotated_probe_bregma(1,st_ix:(st_ix+1)), ...
            'YData',rotated_probe_bregma(2,st_ix:(st_ix+1)), ...
            'ZData',rotated_probe_bregma(3,st_ix:(st_ix+1)));
    end
end

function R = axang2rotm1( axang )
%AXANG2ROTM Convert axis-angle rotation representation to rotation matrix
%   R = AXANG2ROTM(AXANG) converts a 3D rotation given in axis-angle form,
%   AXANG, to an orthonormal rotation matrix, R. AXANG is an N-by-4
%   matrix of N axis-angle rotations. The first three elements of every
%   row specify the rotation axis and the last element defines the rotation
%   angle (in radians).
%   The output, R, is an 3-by-3-by-N matrix containing N rotation matrices.
%   Each rotation matrix has a size of 3-by-3 and is orthonormal.
%
%   Example:
%      % Convert a rotation from axis-angle to rotation matrix
%      axang = [0 1 0 pi/2];
%      R = axang2rotm(axang)

% For a single axis-angle vector [ax ay az theta] the output rotation
% matrix R can be computed as follows:
% R =  [t*x*x + c	  t*x*y - z*s	   t*x*z + y*s
%       t*x*y + z*s	  t*y*y + c	       t*y*z - x*s
%       t*x*z - y*s	  t*y*z + x*s	   t*z*z + c]
% where,
% c = cos(theta)
% s = sin(theta)
% t = 1 - c
% x = normalized axis ax coordinate
% y = normalized axis ay coordinate
% z = normalized axis az coordinate

% Normalize the axis
v = bsxfun(@times, axang(:,1:3), 1./sqrt(sum(axang(:,1:3).^2,2)));
% Extract the rotation angles and shape them in depth dimension
numInputs = size(axang,1);
theta = zeros(1,1,numInputs);
theta(1,1,:) = axang(:,4);

% Compute rotation matrices
cth = cos(theta);
sth = sin(theta);
vth = (1 - cth);

% Preallocate input vectors
vx = zeros(1,1,numInputs,'like',axang);
vy = vx;
vz = vx;

% Shape input vectors in depth dimension
vx(1,1,:) = v(:,1);
vy(1,1,:) = v(:,2);
vz(1,1,:) = v(:,3);

% Explicitly specify concatenation dimension
tempR = cat(1, vx.*vx.*vth+cth,     vy.*vx.*vth-vz.*sth, vz.*vx.*vth+vy.*sth, ...
               vx.*vy.*vth+vz.*sth, vy.*vy.*vth+cth,     vz.*vy.*vth-vx.*sth, ...
               vx.*vz.*vth-vy.*sth, vy.*vz.*vth+vx.*sth, vz.*vz.*vth+cth);

R = reshape(tempR, [3, 3, length(vx)]);
R = permute(R, [2 1 3]);

end


function c = getColor(cidxs)

% http://colorbrewer2.org/

color = cell(8,4);

% qualitative
color{1}{1} = [55,126,184];
color{2}{1} = [255,127,0];
color{3}{1} = [77,175,74];
color{4}{1} = [228,26,28];
color{5}{1} = [255,255,51];%[178,223,138];
color{6}{1} = [0,0,0];
% color{6}{1} = [255,255,51];
color{7}{1} = [152,78,163];
color{8}{1} = [247,129,191];

% sequential
color{1}{2} = [253,141,60];
color{1}{3} = [254,204,92];
color{1}{4} = [255,255,178];
color{1}{5} = [254,240,217];

color{2}{2} = [107,174,214];
color{2}{3} = [189,215,231];
color{2}{4} = [117,107,177];

color{3}{2} = [194,230,153];

color{4}{2} = [140,150,198];
color{4}{3} = [179,205,227];
color{4}{4} = [237,248,251];

c = color{cidxs(1)}{cidxs(2)};

end


function varargout=savebnm(fname,varargin);
% a compact binary format for saving a matrix. (using float)
%
%
% savebnm(fname,M1[,M2,M3,...]);
%
% Can save multidimensional matrices containing: Nans,Imaginary numbers and Inf
%
% see loadbnm
%
% Aslak Grinsted 2004 april
%

% -------------------------------------------------------------------------
%   Copyright (C) 2002-2004, Aslak Grinsted
%   This software may be used, copied, or redistributed as long as it is not
%   sold and this copyright notice is reproduced on each copy made.  This
%   routine is provided as is without any express or implied warranties
%   whatsoever.

% TODO: save inputnames also

if (length(strfind(fname,'.'))==0)
    fname=[fname '.bnm'];
end

for ii=1:length(varargin) %validate arguments
    if ~isnumeric(varargin{ii})
        error('BNM format only supports numeric data!');
    end
end

[fid,message]=fopen(fname,'w','ieee-le');
if (fid<0)
    message=strrep(message,'Sorry. No help in figuring out the problem . . .','');
    error(['Could not open the specified file for writing. ' message ' file=' fname])
end

fwrite(fid,'BNM Matrix File');
version=2;

fwrite(fid,[version length(varargin)],'int16');

for ii=1:length(varargin)
    %[ii ftell(fid)]
    %M=varargin{ii};
    sz=size(varargin{ii});
    fwrite(fid,[length(sz) isreal(varargin{ii})],'int16');
    fwrite(fid,sz,'uint32');
    fwrite(fid,varargin{ii},'float');
    if ~isreal(varargin{ii})
        fwrite(fid,imag(varargin{ii}),'float');
    end
end
fclose(fid);

function savestring(fid,s);
if ~ischar(s)|(size(s,1)~=1)
    error('not a string')
end
fwrite(fid,length(s),'uint32');
fwrite(fid,s);


classdef DaeHandler < handle

  properties
    ode
    alg

    userdata_p
  end

  methods

    function self = DaeHandler(userdata)
      self.ode = struct;
      self.alg = [];
      self.userdata_p = userdata;
    end

    function r = userdata(self)
      r = self.userdata_p;
    end

     function setODE(self,id,eq)
      if isfield(self.ode, id)
        ocl.utils.exception(['Ode for var ', id, ' already defined']);
      end
      self.ode.(id) = ocl.Variable.getValueAsColumn(eq);
    end

    function setAlgEquation(self,eq)
      self.alg = [self.alg;ocl.Variable.getValueAsColumn(eq)];
    end

    function r = getOde(self, nx, statesOrder)

      r = cell(length(statesOrder),1);
      for k=1:length(statesOrder)
        id = statesOrder{k};
        if ~isfield(self.ode,id)
          ocl.utils.exception(['Ode for state ', id, ' not defined.']);
        end
        r{k} = self.ode.(id);
        self.ode = rmfield(self.ode, id);
      end
      r = vertcat(r{:});

      if length(r) ~= nx
        ocl.utils.exception(['Number of ode equations does not match ',...
                      'number of state variables.']);
      end

      if numel(fieldnames(self.ode)) > 0
        ocl.utils.exception('ODE for variables defined that do not exist.');
      end
    end

    function alg = getAlg(self, nz)
      alg = self.alg;
      if length(alg) ~= nz
        ocl.utils.exception(['Number of algebraic equations does not match ',...
                      'number of algebraic variables.']);
      end
    end
  end
end


function tf = evalbool(s)
%EVALBOOL Evaluate argument to a bool

if ~ischar(s) % S may not and cannot be empty
        tf = s;

else % Evaluation of string S
    if strncmpi(s, 'yes', 3) || strncmpi(s, 'on', 2) ...
        || strncmpi(s, 'true', 4) || strncmp(s, '1 ', 2)
            tf = 1;
    elseif strncmpi(s, 'no', 2) || strncmpi(s, 'off', 3) ...
        || strncmpi(s, 'false', 5) || strncmp(s, '0 ', 2)
            tf = 0;
    else
        try tf = evalin('caller', s); catch
            error(['String value "' s '" cannot be evaluated']);
        end
        try tf ~= 0; catch
            error(['String value "' s '" cannot be evaluated reasonably']);
        end
    end

end

end


function newmodel = addMultipleMetabolites(model, metIDs, varargin)
% Simultaneously add multiple metabolites. Allows the use of the field names of the model struct to specify additional properties of the metabolites added.
%
% USAGE:
%
%    newmodel = addMultipleMetabolites(model, metIDs, varargin)
%
% INPUTS:
%    model:         The model to add the Metabolite batch to.
%    metIDs:        The metabolite IDs to add. No duplicate IDs may be provided
%                   and no ID may be present in the supplied model.
%
% OPTIONAL INPUTS:
%    varargin:      fieldName, Value pairs with additional properties for the
%                   added metabolites. In addition, a 'printLevel' flag can be used (default: 0).
%                   The given values fields will be set according to the values.
%                   Only fields associated with mets defined in the COBRA definitions (except `S`)
%                   or fields already in the model may be used.
%                   Examples for this use would be:
%
%                     * 'metNames',{'Glucose';'Pyruvate'}
%                     * 'metCharges',[0;-2]
%
% OUTPUTS:
%
%    newmodel:     The model structure with the additional metabolites.
%
% EXAMPLE:
%
%    % To add metabolites, with charges, formulas and KEGG ids:
%    model = addMultipleMetabolites(model,{'A','b','c'},'metCharges', [ -1 1 0], 'metFormulas', {'C','CO2','H2OKOPF'}, 'metKEGGID',{'C000012','C000023','C000055'})
%

if numel(unique(metIDs)) < numel(metIDs)
    % check, if there are either duplicate metabolite IDS to be added OR if
    % any metabolite is already in the model.
    error('Duplicate Metabolite ID in the given IDs detected.');
end

if checkIDsForTypeExist(model,metIDs,'mets')
    % check, if there are either duplicate metabolite IDS to be added OR if
    % any metabolite is already in the model.
    [tf,dups] = checkIDsForTypeExist(model,metIDs,'mets');
    if any(ismember(model.mets,dups))
        pres = ismember(model.mets,dups);
        error('The following Metabolite ID(s) are already present in the model:\n%s',strjoin(model.mets(pres),'\n'));
    else
        error('The following Metabolite ID(s) are already present Ids of constraints in the model:\n%s',strjoin(dups,'\n'));
    end
end

nMets = numel(model.mets);

%We extract those fields which are either associated with the mets field
%(2nd or 3rd column contains 'mets' in the definitions), and we also look up which fields are in the model and
%associated with the mets field (from the sizes)
fieldDefs = getDefinedFieldProperties();
fieldDefs = fieldDefs(cellfun(@(x) strcmp(x,'mets'), fieldDefs(:,2)) | cellfun(@(x) strcmp(x,'mets'), fieldDefs(:,3)));
modelMetFields = getModelFieldsForType(model,'mets');

% extract additional parameters which are not fields (currently only
% printLevel
printLevel = 0;

for parameter = 1:2:numel(varargin)
    if strcmp(varargin{parameter},'printLevel')
        printLevel = varargin{parameter+1};
    end
end

%Then we add the ids.
model.mets = [model.mets;columnVector(metIDs)];
if printLevel > 0
    fprintf('Adding the following Metabolites to the model:\n%s\n',strjoin(metIDs,'\n'));
end

%Now, add the the data from the additional supplied fields, and check that
%they are in sync
for field = 1:2:numel(varargin)
    %If the field does not exist and is not defined, we will not add that
    %information, as we don't know how the field should look.
    cfield = varargin{field};
    if strcmp('S',cfield) || (~any(ismember(fieldDefs(:,1),cfield)) && ~any(ismember(modelMetFields,cfield)))
        if printLevel > 0 && ~strcmp('printLevel',cfield)
            warning('Field %s is excluded',cfield);
        end
        continue;
    end
    %Now add the field data.
    if ~isfield(model,cfield)
        %If its not yet in the model, create it
        %according to its defaults and replace the final elements.
        if printLevel > 2
            fprintf('Creating model field %s\n',cfield);
        end
        model = createEmptyFields(model,cfield);
        model.(cfield)((end-numel(varargin{field+1})+1):end) = columnVector(varargin{field+1});
    else
        %Or just extend it with the supplied data.0
        model.(cfield) = [model.(cfield);columnVector(varargin{field+1})];
    end

end

%Extend the remaining fields, to keep the fields in sync
newmodel = extendModelFieldsForType(model,'mets','originalSize',nMets);


function option_num = p11_option_num ( )

%*****************************************************************************80
%
%% P11_OPTION_NUM returns the number of options for problem 11.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, integer OPTION_NUM, the number of options.
%
  option_num = 2;

  return
end


function v = cat(dim,varargin)
% implements cat for S2Grid
%

v = cat(dim,vector3d(varargin{1}),varargin{2:end});


function print_eps(outfile)
%PRINT_EPS stores the figure as eps file
%
%   Usage: print_eps(outfile)
%
%   Input parameters:
%       outfile     - file name to store the figure
%
%   See also: plot_sound_field, print_png

%*****************************************************************************
% The MIT License (MIT)                                                      *
%                                                                            *
% Copyright (c) 2010-2019 SFS Toolbox Developers                             *
%                                                                            *
% Permission is hereby granted,  free of charge,  to any person  obtaining a *
% copy of this software and associated documentation files (the "Software"), *
% to deal in the Software without  restriction, including without limitation *
% the rights  to use, copy, modify, merge,  publish, distribute, sublicense, *
% and/or  sell copies of  the Software,  and to permit  persons to whom  the *
% Software is furnished to do so, subject to the following conditions:       *
%                                                                            *
% The above copyright notice and this permission notice shall be included in *
% all copies or substantial portions of the Software.                        *
%                                                                            *
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
% IMPLIED, INCLUDING BUT  NOT LIMITED TO THE  WARRANTIES OF MERCHANTABILITY, *
% FITNESS  FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL *
% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
% LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT, TORT  OR OTHERWISE, ARISING *
% FROM,  OUT OF  OR IN  CONNECTION  WITH THE  SOFTWARE OR  THE USE  OR OTHER *
% DEALINGS IN THE SOFTWARE.                                                  *
%                                                                            *
% The SFS Toolbox  allows to simulate and  investigate sound field synthesis *
% methods like wave field synthesis or higher order ambisonics.              *
%                                                                            *
% https://sfs.readthedocs.io                            sfstoolbox@gmail.com *
%*****************************************************************************

%% ===== Checking of input parameter =====================================
nargmin = 1;
nargmax = 1;
narginchk(nargmin,nargmax);

%% ===== Main ============================================================
% Font type
set_font_type('Arial');
print(outfile,'-depsc2','-r150');
close;


%  Y = typecast(X, type) converts a numeric value in X to the data type specified by type.
%*************************************************************************************
%
%  MATLAB (R) is a trademark of The Mathworks (R) Corporation
%
%  Function:    typecast
%  Filename:    typecast.m
%  Programmer:  James Tursa
%  Version:     3.00
%  Date:        March 17, 2011
%  Copyright:   (c) 2009, 2011 by James Tursa, All Rights Reserved
%
%  This code uses the BSD License:
%
%  Redistribution and use in source and binary forms, with or without
%  modification, are permitted provided that the following conditions are
%  met:
%
%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%
%  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
%  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
%  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
%  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
%  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
%  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
%  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
%  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
%  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
%  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%  POSSIBILITY OF SUCH DAMAGE.
%
% typecast is a mex function intended to mimic the MATLAB intrinsic typecast function
% for those users with older versions of MATLAB that do not have this intrinsic. Users
% of newer versions of MATLAB may be interested in this C-mex version to take advantage
% of the several extensions offered. This C-mex version of typecast differs from the
% intrinsic typecast in the following important aspects:
%
%                      Intrinsic typecast    C-mex typecast
%                      ------------------    --------------
% Type of copy:        Deep Data Copy        Shared Data Copy
% Allows complex:      No                    Yes
% Allows logical:      No                    Yes (cannot convert from complex)
% Allows char:         No                    Yes (cannot convert from complex)
% Allows non-vector:   No                    Yes
%
% Since this C-mex typecast produces a shared data copy of the original, it
% is more efficient than the MATLAB intrinsic typecast, which may be important
% if you are working with large variables. For non-vector inputs, the first
% non-singleton dimension must be compatible for the conversion.
%
% Building:
%
% typecast requires that a mex routine be built (one time only). This
% process is typically self-building the first time you call the function
% as long as you have the files typecast.m and typecast.c in the same
% directory somewhere on the MATLAB path. If you need to manually build
% the mex function, here are the commands:
%
% >> mex -setup
%   (then follow instructions to select a C or C++ compiler of your choice)
% >> mex typecast.c
%
% The usage is as follows (from The Mathworks website documentation):
%
% Syntax
%
% Y = typecast(X, type)
%
% Description
%
% Y = typecast(X, type) converts a value in X to the data type specified by type.
% Input X must be a full numeric or char or logical variable. The type input is a string
% set to one of the following: 'uint8', 'int8', 'uint16', 'int16', 'uint32', 'int32',
% 'uint64', 'int64', 'single', 'double', 'char' or 'logical'. typecast is different
% from the MATLAB cast function in that it does not alter the input data. typecast
% always returns the same number of bytes in the output Y as were in the input X.
% For example, casting the 16-bit integer 1000 to uint8 with typecast returns the full
% 16 bits in two 8-bit segments (3 and 232) thus keeping its original value
% (3*256 + 232 = 1000). The cast function, on the other hand, truncates the input value
% to 255.
%
% The output of typecast can be formatted differently depending on what system you use it on.
% Some computer systems store data starting with its most significant byte (an ordering
% called big-endian), while others start with the least significant byte (called little-endian).
%
% typecast issues an error if X contains fewer values than are needed to make an output value.
%
%*************************************************************************************

function varargout = typecast(varargin)
fname = 'typecast';
disp(' ');
disp(['Detected that the mex routine for ' fname ' is not yet built.']);
disp('Attempting to do so now ...');
disp(' ');
try
    mname = mfilename('fullpath');
catch
    mname = fname;
end
cname = [mname '.c'];
if( isempty(dir(cname)) )
    disp(['Cannot find the file ' fname '.c in the same directory as the']);
    disp(['file ' fname '.m. Please ensure that they are in the same']);
    disp('directory and try again. The following file was not found:');
    disp(' ');
    disp(cname);
    disp(' ');
    error(['Unable to compile ' fname '.c']);
else
    disp(['Found file ' fname '.c in ' cname]);
    disp(' ');
    disp('Now attempting to compile ...');
    disp('(If prompted, please press the Enter key and then select any C/C++');
    disp('compiler that is available, such as lcc.)');
    disp(' ');
    disp(['mex(''' cname ''',''-output'',''',mname,''')']);
    disp(' ');
    try
        mex(cname,'-output',mname);
        disp([ fname ' mex build completed ... you may now use ' fname '.']);
        disp(' ');
    catch
        disp(' ');
        error(['Unable to compile ' fname ' ... Contact author.']);
    end
    [varargout{1:nargout}] = typecast(varargin{:});
end
end


function data_list = read_data_list(base_path, classes, data_size, train_or_test, debug)
% read pre-computed volumetric filenames
% base_path: root data folder
% classes: categories to be fetched
% data_size: the size of the volumetric representation
% train_or_test: select training data or testing data('train', 'test')
% debug: true for debug.

if nargin < 5
    debug = 0;
end

if debug
    maxNum = 20;
elseif strcmp(train_or_test, 'train')
    maxNum = 80*12;
else
    maxNum = 20*12;
end

num_classes = length(classes);
data_list = cell(num_classes,1);
for c = 1 : num_classes
    fprintf('reading the %s category\n', classes{c});
    category_path = [base_path '/' classes{c} '/' num2str(data_size) '/' train_or_test];
    files = dir(category_path);

    cat_ind = 0;
    data_list{c} = [];
    for i = 1 : length(files)
        idx = i;
        if strcmp(files(idx).name, '.') || strcmp(files(idx).name, '..') || files(idx).isdir == 1 || ~strcmp(files(idx).name(end-2:end), 'mat')
            continue;
        end
        filename = [category_path '/' files(idx).name];

        cat_ind = cat_ind + 1;
        data_list{c}(cat_ind,1).filename = filename;
        data_list{c}(cat_ind,1).label = c;
        if cat_ind == maxNum
          break;
        end
    end
    fprintf('fetched %d number of instances\n', cat_ind);
end


function view=initLevelSlider(view,curLevel)
%
%       view=initLevelSlider(view,[curLevel])
%
% Installs the slider and text box that control the gray level.
% The slider is placed on the top right, near the slice control slider.
%
% The first time through, the callback and related textbox are installed
%
% It is also called repeatedly as dataTypes are switched to re-display
%   the slider that represents level information
%
% If you change this function make parallel changes in:
%    initSliceSlider
%
% bw,  01/16/01  Adjusted sliderstep
% bw,  12/25/00  Wrote it.
% ras, 09/30/04  adapted to initLevelSlider
if ieNotDefined('curLevel')
    curLevel = 1;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Add an edit field for the # of gray levels to show %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
color = get(gcf,'Color');

% text label
htmp = uicontrol('Style','text',...
                 'Units','Normalized',...
                 'Position',[0 0.75 0.12 0.03],...
                 'BackgroundColor',color,...
                 'HorizontalAlignment','left',...
                 'String','And next:');
view.ui.level.numLevelLabel = htmp;

% edit field
callBackStr = sprintf('%s = refreshScreen(%s);',view.name,view.name);
htmp = uicontrol('Style','edit',...
                 'Units','Normalized',...
                 'Position',[0.10 0.75 0.03 0.03],...
                 'BackgroundColor',color,...
                 'Callback',callBackStr,...
                 'String','0');
view.ui.level.numLevelEdit = htmp;

% also need to get text label for level slider
% (to turn off if looking across levels)
htmp = findobj('Parent',gcf,'Style','text','String','level:');
view.ui.level.levelLabel = htmp;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Initialize slider stuff                            %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% The sliderHandle always exists because it is created in openXXWindow
% Set the step size for clicking on the arrows and the trough
sliderHandle = view.ui.level.sliderHandle;

nScans=numScans(view);
if nScans < 2
    %   Can't have a slider value of 1.  Sorry.
    nScans=2;
    set(sliderHandle,'Enable','off');
else
    set(sliderHandle,'Enable','on');
end

hemi = findSelectedButton(view.ui.sliceButtons);
nLevels = view.numLevels(hemi); % update for diff. # levels

% By setting the max to a little more than nScans, we should
% never quite reach it by clicking the arrow or the trough
set(sliderHandle,'Min',1,'Max',nLevels);

% Clicking an arrow moves by an amount (max - min)*sliderstep(1)
% Cllicking in the trough moves by an amount (max - min)*sliderstep(2)
% setSlider traps any values == to the max value and queries the user
%    this is a cheap way of setting a value by hand.
sliderStep(1) = 1/(nLevels - 1);
sliderStep(2) = 2*sliderStep(1);
set(sliderHandle,'sliderStep',sliderStep);

% update the callback
callBackStr = ...
    ['val = get(',view.name,'.ui.level.sliderHandle,''value'');'...
        'val = round(val);'...
        'setSlider(',view.name,',',view.name,'.ui.level,val);'...
        view.name,'=refreshScreen(',view.name,');'];
set(sliderHandle,'CallBack',callBackStr);

% If we are first opening the window, create the text box.
if ~isfield(view.ui.level,'textHandle')

   % Position the text box to the right of the slider
   pos = get(sliderHandle,'Position');
   l2 = pos(1) + pos(3) + .01;
   w2 = pos(3)/4; b = pos(2); h = pos(4);
   position = [l2 b w2 h];

   view.ui.level.textHandle = ...
      uicontrol('Style','text',...
      'Units','normalized',...
      'Position',position);
end

% This text tells the user how many levels there are
% for the given gray mesh.
%
str = sprintf('%.0f',nLevels);
set(view.ui.level.textHandle,'String',str);

setSlider(view,view.ui.level,curLevel);

return


function newstr = untabify(str, n)
%UNTABIFY Convert tabs to spaces.
%
%   NEWSTR = UNTABIFY(STR) converts each tab character in STR to the
%   appropriate number of space characters.

%   Author:      Peter J. Acklam
%   Time-stamp:  2003-10-13 11:17:52 +0200
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   error(nargchk(1, 2, nargin));
   if ~ischar(str)
      error('First argument must be a string.');
   end
   if nargin < 2
      n = 8;
   else
      if any(size(n) ~= 1) | ~isnumeric(n) | n ~= round(n) | n < 0;
         error('Second argument must be a non-negative integer.');
      end
   end

   tab = sprintf('\t');                 % tab character

   strlen = length(str);                % length of input string

   newstr = '';
   if strlen == 0
      return
   end

   tabpos = find(str == tab);           % position of tabs in string
   tabpos = [ tabpos strlen+1 ];        % add "end of line mark"

   ntabs = length(tabpos);              % number of tabs

   newstr = str(1:tabpos(1)-1);
   newstrlen = length(newstr);

   for i = 1:ntabs-1
      nspc = n*ceil((newstrlen+1)/n) - newstrlen;
      newstr = [ newstr blanks(nspc) str(tabpos(i)+1:tabpos(i+1)-1) ];
      newstrlen = length(newstr);
   end


% pop_dipfit_nonlinear() - interactively do dipole fit of selected ICA components
%
% Usage:
%  >> EEGOUT = pop_dipfit_nonlinear( EEGIN )
%
% Inputs:
%   EEGIN       input dataset
%
% Outputs:
%   EEGOUT      output dataset
%
% Author: Robert Oostenveld, SMI/FCDC, Nijmegen 2003
%         Arnaud Delorme, SCCN, La Jolla 2003
%         Thanks to Nicolas Robitaille for his help on the CTF MEG
%         implementation

% SMI, University Aalborg, Denmark http://www.smi.auc.dk/
% FC Donders Centre, University Nijmegen, the Netherlands http://www.fcdonders.kun.nl/

% Copyright (C) 2003 Robert Oostenveld, SMI/FCDC roberto@smi.auc.dk
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [EEGOUT, com] = pop_dipfit_nonlinear( EEG, subfunction, parent, dipnum )
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% the code for this interactive dialog has 4 major parts
% - draw the graphical user interface
% - synchronize the gui with the data
% - synchronize the data with the gui
% - execute the actual dipole analysis

% the subfunctions that perform handling of the gui are
% - dialog_selectcomponent
% - dialog_checkinput
% - dialog_setvalue
% - dialog_getvalue
% - dialog_plotmap
% - dialog_plotcomponent
% - dialog_flip
% the subfunctions that perform the fitting are
% - dipfit_position
% - dipfit_moment

if nargin<1
  help pop_dipfit_nonlinear;
  return
elseif nargin==1

  EEGOUT = EEG;
  com = '';

  if ~isfield(EEG, 'chanlocs')
    error('No electrodes present');
  end

  if ~isfield(EEG, 'icawinv')
    error('No ICA components to fit');
  end

  if ~isfield(EEG, 'dipfit')
    error('General dipolefit settings not specified');
  end

  if ~isfield(EEG.dipfit, 'vol') & ~isfield(EEG.dipfit, 'hdmfile')
    error('Dipolefit volume conductor model not specified');
  end

  % select all ICA components as 'fitable'
  select = 1:size(EEG.icawinv,2);
  if ~isfield(EEG.dipfit, 'current')
    % select the first component as the current component
    EEG.dipfit.current = 1;
  end

  % verify the presence of a dipole model
  if ~isfield(EEG.dipfit, 'model')
    % create empty dipole model for each component
    for i=select
      EEG.dipfit.model(i).posxyz = zeros(2,3);
      EEG.dipfit.model(i).momxyz = zeros(2,3);
      EEG.dipfit.model(i).rv     = 1;
      EEG.dipfit.model(i).select = [1];
    end
  end

  % verify the size of each dipole model
  for i=select
    if ~isfield(EEG.dipfit.model, 'posxyz') | length(EEG.dipfit.model) < i | isempty(EEG.dipfit.model(i).posxyz)
      % replace all empty dipole models with a two dipole model, of which one is active
      EEG.dipfit.model(i).select = [1];
      EEG.dipfit.model(i).rv = 1;
      EEG.dipfit.model(i).posxyz = zeros(2,3);
      EEG.dipfit.model(i).momxyz = zeros(2,3);
    elseif size(EEG.dipfit.model(i).posxyz,1)==1
      % replace all one dipole models with a two dipole model
      EEG.dipfit.model(i).select = [1];
      EEG.dipfit.model(i).posxyz = [EEG.dipfit.model(i).posxyz; [0 0 0]];
      EEG.dipfit.model(i).momxyz = [EEG.dipfit.model(i).momxyz; [0 0 0]];
    elseif size(EEG.dipfit.model(i).posxyz,1)>2
      % replace all more-than-two dipole models with a two dipole model
      warning('pruning dipole model to two dipoles');
      EEG.dipfit.model(i).select = [1];
      EEG.dipfit.model(i).posxyz = EEG.dipfit.model(i).posxyz(1:2,:);
      EEG.dipfit.model(i).momxyz = EEG.dipfit.model(i).momxyz(1:2,:);
    end
  end

  % default is not to use symmetry constraint
  constr = [];

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % construct the graphical user interface
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % define the callback functions for the interface elements
  cb_plotmap         = 'pop_dipfit_nonlinear(EEG, ''dialog_plotmap'', gcbf);';
  cb_selectcomponent = 'pop_dipfit_nonlinear(EEG, ''dialog_selectcomponent'', gcbf);';
  cb_checkinput      = 'pop_dipfit_nonlinear(EEG, ''dialog_checkinput'', gcbf);';
  cb_fitposition     = 'pop_dipfit_nonlinear(EEG, ''dialog_getvalue'', gcbf); pop_dipfit_nonlinear(EEG, ''dipfit_position'', gcbf); pop_dipfit_nonlinear(EEG, ''dialog_setvalue'', gcbf);';
  cb_fitmoment       = 'pop_dipfit_nonlinear(EEG, ''dialog_getvalue'', gcbf); pop_dipfit_nonlinear(EEG, ''dipfit_moment''  , gcbf); pop_dipfit_nonlinear(EEG, ''dialog_setvalue'', gcbf);';
  cb_close           = 'close(gcbf)';
  cb_help            = 'pophelp(''pop_dipfit_nonlinear'');';
  cb_ok              = 'uiresume(gcbf);';
  cb_plotdip         = 'pop_dipfit_nonlinear(EEG, ''dialog_plotcomponent'', gcbf);';
  cb_flip1           = 'pop_dipfit_nonlinear(EEG, ''dialog_flip'', gcbf, 1);';
  cb_flip2           = 'pop_dipfit_nonlinear(EEG, ''dialog_flip'', gcbf, 2);';
  cb_sym             = [ 'set(findobj(gcbf, ''tag'', ''dip2sel''), ''value'', 1);' cb_checkinput ];

  % vertical layout for each line
  geomvert  =   [1 1 1 1 1 1 1 1 1];

  % horizontal layout for each line
  geomhoriz = {
    [0.8 0.5 0.8 1 1]
    [1]
    [0.7  0.7     2 2 1]
    [0.7 0.5 0.2 2 2 1]
    [0.7 0.5 0.2 2 2 1]
    [1]
    [1 1 1]
    [1]
    [1 1 1]
  };

  % define each individual graphical user element
  elements  = { ...
      { 'style' 'text'       'string' 'Component to fit'    } ...
      { 'style' 'edit'       'string' 'dummy'    'tag' 'component' 'callback' cb_selectcomponent } ...
      { 'style' 'pushbutton' 'string' 'Plot map'                   'callback' cb_plotmap } ...
      { 'style' 'text'       'string' 'Residual variance = '                                             } ...
      { 'style' 'text'       'string' 'dummy'         'tag' 'relvar'                                  } ...
      { } ...
      { 'style' 'text'    'string' 'dipole'     } ...
      { 'style' 'text'    'string' 'fit'        } ...
      { 'style' 'text'    'string' 'position'   } ...
      { 'style' 'text'    'string' 'moment'     } ...
      { } ...
      ...
      { 'style' 'text'        'string' '#1' 'tag' 'dip1'                                 } ...
      { 'style' 'checkbox'    'string' ''   'tag' 'dip1sel'    'callback' cb_checkinput  } { } ...
      { 'style' 'edit'        'string' ''   'tag' 'dip1pos'    'callback' cb_checkinput  } ...
      { 'style' 'edit'        'string' ''   'tag' 'dip1mom'    'callback' cb_checkinput  } ...
      { 'style' 'pushbutton'  'string' 'Flip (in|out)'         'callback' cb_flip1       } ...
      ...
      { 'style' 'text'        'string' '#2' 'tag' 'dip2'                                 } ...
      { 'style' 'checkbox'    'string' ''   'tag' 'dip2sel'    'callback' cb_checkinput  } { } ...
      { 'style' 'edit'        'string' ''   'tag' 'dip2pos'    'callback' cb_checkinput  } ...
      { 'style' 'edit'        'string' ''   'tag' 'dip2mom'    'callback' cb_checkinput  } ...
      { 'style' 'pushbutton'  'string' 'Flip (in|out)'         'callback' cb_flip2       } ...
      ...
      { } { 'style' 'checkbox' 'string' 'Symmetry constrain for dipole #2' 'tag' 'dip2sym' 'callback' cb_sym  'value' 1 } ...
      { } { } { } ...
      { 'style' 'pushbutton'  'string' 'Fit dipole(s)'' position & moment' 'callback' cb_fitposition } ...
      { 'style' 'pushbutton'  'string' 'OR fit only dipole(s)'' moment'   'callback' cb_fitmoment   } ...
      { 'style' 'pushbutton'  'string' 'Plot dipole(s)'                  'callback' cb_plotdip     } ...
    };

  % add the cancel, help and ok buttons at the bottom

  geomvert  = [geomvert 1 1];

  geomhoriz = {geomhoriz{:} [1] [1 1 1]};

  elements  = { elements{:} ...
      { } ...
      { 'Style', 'pushbutton', 'string', 'Cancel', 'callback', cb_close } ...
      { 'Style', 'pushbutton', 'string', 'Help',   'callback', cb_help  } ...
      { 'Style', 'pushbutton', 'string', 'OK',     'callback', cb_ok    } ...
    };

  % activate the graphical interface
  supergui(0, geomhoriz, geomvert, elements{:});
  dlg = gcf;
  set(gcf, 'name', 'Manual dipole fit -- pop_dipfit_nonlinear()');
  set(gcf, 'userdata', EEG);
  pop_dipfit_nonlinear(EEG, 'dialog_setvalue', dlg);
  uiwait(dlg);
  if ishandle(dlg)
    pop_dipfit_nonlinear(EEG, 'dialog_getvalue', dlg);
    % FIXME, rv is undefined since the user may have changed dipole parameters
    % FIXME, see also dialog_getvalue subfucntion
    EEGOUT = get(dlg, 'userdata');
    close(dlg);
  end

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % implement all subfunctions through a switch-yard
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
elseif nargin>=3

  %disp(subfunction)
  EEG = get(parent, 'userdata');

  switch subfunction

    case 'dialog_selectcomponent'
      current = get(findobj(parent, 'tag', 'component'), 'string');
      current = str2num(current);
      current = current(1);
      current = min(current, size(EEG.icaweights,1));
      current = max(current, 1);
      set(findobj(parent, 'tag', 'component'), 'string', int2str(current));
      EEG.dipfit.current = current;
      % reassign the global EEG object back to the dialogs userdata
      set(parent, 'userdata', EEG);
      % redraw the dialog with the current model
      pop_dipfit_nonlinear(EEG, 'dialog_setvalue', parent);

    case 'dialog_plotmap'
      current = str2num(get(findobj(parent, 'tag', 'component'), 'string'));
      figure; pop_topoplot(EEG, 0, current, [ 'IC ' num2str(current) ], [1 1], 1);
      title([ 'IC ' int2str(current) ]);

    case 'dialog_plotcomponent'
      current = get(findobj(parent, 'tag', 'component'), 'string');
      EEG.dipfit.current = str2num(current);
      if ~isempty( EEG.dipfit.current )
        pop_dipplot(EEG, 'DIPFIT',  EEG.dipfit.current, 'normlen', 'on', 'mri', EEG.dipfit.mrifile);
      end;

    case 'dialog_checkinput'
      if get(findobj(parent, 'tag', 'dip1sel'), 'value') & ~get(findobj(parent, 'tag', 'dip1act'), 'value')
        set(findobj(parent, 'tag', 'dip1act'), 'value', 1);
      end
      if get(findobj(parent, 'tag', 'dip2sel'), 'value') & ~get(findobj(parent, 'tag', 'dip2act'), 'value')
        set(findobj(parent, 'tag', 'dip2act'), 'value', 1);
      end
      if ~all(size(str2num(get(findobj(parent, 'tag', 'dip1pos'), 'string')))==[1 3])
        set(findobj(parent, 'tag', 'dip1pos'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).posxyz(1,:)));
      else
        EEG.dipfit.model(EEG.dipfit.current).posxyz(1,:) = str2num(get(findobj(parent, 'tag', 'dip1pos'), 'string'));
      end
      if ~all(size(str2num(get(findobj(parent, 'tag', 'dip2pos'), 'string')))==[1 3])
        set(findobj(parent, 'tag', 'dip2pos'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).posxyz(2,:)));
      else
        EEG.dipfit.model(EEG.dipfit.current).posxyz(2,:) = str2num(get(findobj(parent, 'tag', 'dip2pos'), 'string'));
      end
      if ~all(size(str2num(get(findobj(parent, 'tag', 'dip1mom'), 'string')))==[1 3])
        set(findobj(parent, 'tag', 'dip1mom'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).momxyz(1,:)));
      else
        EEG.dipfit.model(EEG.dipfit.current).momxyz(1,:) = str2num(get(findobj(parent, 'tag', 'dip1mom'), 'string'));
      end
      if ~all(size(str2num(get(findobj(parent, 'tag', 'dip2mom'), 'string')))==[1 3])
        set(findobj(parent, 'tag', 'dip2mom'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).momxyz(2,:)));
      else
        EEG.dipfit.model(EEG.dipfit.current).momxyz(2,:) = str2num(get(findobj(parent, 'tag', 'dip2mom'), 'string'));
      end
      if get(findobj(parent, 'tag', 'dip2sel'), 'value') & get(findobj(parent, 'tag', 'dip2sym'), 'value') & ~get(findobj(parent, 'tag', 'dip1sel'), 'value')
        set(findobj(parent, 'tag', 'dip2sel'), 'value', 0);
      end
      set(parent, 'userdata', EEG);

    case 'dialog_setvalue'
      % synchronize the gui with the data
      set(findobj(parent, 'tag', 'component'), 'string', EEG.dipfit.current);
      set(findobj(parent, 'tag', 'relvar' ), 'string', sprintf('%0.2f%%', EEG.dipfit.model(EEG.dipfit.current).rv * 100));
      set(findobj(parent, 'tag', 'dip1sel'), 'value', ismember(1, EEG.dipfit.model(EEG.dipfit.current).select));
      set(findobj(parent, 'tag', 'dip2sel'), 'value', ismember(2, EEG.dipfit.model(EEG.dipfit.current).select));
      set(findobj(parent, 'tag', 'dip1pos'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).posxyz(1,:)));
      if strcmpi(EEG.dipfit.coordformat, 'CTF')
           set(findobj(parent, 'tag', 'dip1mom'), 'string', sprintf('%f %f %f', EEG.dipfit.model(EEG.dipfit.current).momxyz(1,:)));
      else set(findobj(parent, 'tag', 'dip1mom'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).momxyz(1,:)));
      end;
      Ndipoles = size(EEG.dipfit.model(EEG.dipfit.current).posxyz, 1);
      if Ndipoles>=2
          set(findobj(parent, 'tag', 'dip2pos'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).posxyz(2,:)));
          if strcmpi(EEG.dipfit.coordformat, 'CTF')
               set(findobj(parent, 'tag', 'dip2mom'), 'string', sprintf('%f %f %f', EEG.dipfit.model(EEG.dipfit.current).momxyz(2,:)));
          else set(findobj(parent, 'tag', 'dip2mom'), 'string', sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(EEG.dipfit.current).momxyz(2,:)));
          end;
      end

    case 'dialog_getvalue'
      % synchronize the data with the gui
      if get(findobj(parent, 'tag', 'dip1sel'), 'value'); select = [1]; else select = []; end;
      if get(findobj(parent, 'tag', 'dip2sel'), 'value'); select = [select 2]; end;
      posxyz(1,:) = str2num(get(findobj(parent, 'tag', 'dip1pos'), 'string'));
      posxyz(2,:) = str2num(get(findobj(parent, 'tag', 'dip2pos'), 'string'));
      momxyz(1,:) = str2num(get(findobj(parent, 'tag', 'dip1mom'), 'string'));
      momxyz(2,:) = str2num(get(findobj(parent, 'tag', 'dip2mom'), 'string'));
      % assign the local values to the global EEG object
      EEG.dipfit.model(EEG.dipfit.current).posxyz = posxyz;
      EEG.dipfit.model(EEG.dipfit.current).momxyz = momxyz;
      EEG.dipfit.model(EEG.dipfit.current).select = select;
      % FIXME, rv is undefined after a manual change of parameters
      % FIXME, this should either be undated continuously or upon OK buttonpress
      % EEG.dipfit.model(EEG.dipfit.current).rv = nan;

      % reassign the global EEG object back to the dialogs userdata
      set(parent, 'userdata', EEG);

    case 'dialog_flip'
      % flip the orientation of the dipole
      current = EEG.dipfit.current;
      moment  = EEG.dipfit.model(current).momxyz;
      EEG.dipfit.model(current).momxyz(dipnum,:) = [ -moment(dipnum,1) -moment(dipnum,2) -moment(dipnum,3)];
      set(findobj(parent, 'tag', ['dip' int2str(dipnum) 'mom']), 'string', ...
        sprintf('%0.3f %0.3f %0.3f', EEG.dipfit.model(current).momxyz(dipnum,:)));
      set(parent, 'userdata', EEG);

    case {'dipfit_moment', 'dipfit_position'}
      % determine the selected dipoles and components
      current  = EEG.dipfit.current;
      select   = find([get(findobj(parent, 'tag', 'dip1sel'), 'value') get(findobj(parent, 'tag', 'dip2sel'), 'value')]);
      if isempty(select)
        warning('no dipoles selected for fitting');
        return
      end
      % remove the dipoles from the model that are not selected, but keep
      % the original dipole model (to keep the GUI consistent)
      model_before_fitting = EEG.dipfit.model(current);
      EEG.dipfit.model(current).posxyz = EEG.dipfit.model(current).posxyz(select,:);
      EEG.dipfit.model(current).momxyz = EEG.dipfit.model(current).momxyz(select,:);
      if strcmp(subfunction, 'dipfit_moment')
        % the default is 'yes' which should only be overruled for fitting dipole moment
        cfg.nonlinear  = 'no';
      end
      dipfitdefs;
      if get(findobj(parent, 'tag', 'dip2sym'), 'value') & get(findobj(parent, 'tag', 'dip2sel'), 'value')
          if strcmpi(EEG.dipfit.coordformat,'MNI')
              cfg.symmetry = 'x';
          else
              cfg.symmetry = 'y';
          end;
      else
          cfg.symmetry = [];
      end

      cfg.component  = current;
      % convert structure into list of input arguments
      arg = [fieldnames(cfg)' ; struct2cell(cfg)'];
      arg = arg(:)';

      % make a dialog to interrupt the fitting procedure
      fig = figure('visible', 'off');
      supergui( fig, {1 1}, [], ...
        {'style' 'text' 'string' 'Press button below to stop fitting' }, ...
        {'style' 'pushbutton' 'string' 'Interupt' 'callback' 'figure(gcbf); set(gcbf, ''tag'', ''stop'');' } );
      drawnow;
      % start the dipole fitting
      try
          warning backtrace off;
          EEG = dipfit_nonlinear(EEG, arg{:});
          warning backtrace on;
      catch,
          disp('Dipole localization failed');
      end;

      % should the following string be put into com? ->NOT SUPPORTED
      % --------------------------------------------------------
      com = sprintf('%s = dipfit_nonlinear(%s,%s)\n', inputname(1), inputname(1), vararg2str(arg));

      % this GUI always requires two sources in the dipole model
      % first put the original model back in and then replace the dipole parameters that have been fitted
      model_after_fitting = EEG.dipfit.model(current);
      newfields = fieldnames( EEG.dipfit.model );
      for index = 1:length(newfields)
      	  eval( ['EEG.dipfit.model(' int2str(current) ').' newfields{index} ' = model_after_fitting.' newfields{index} ';' ]);
      end;
      EEG.dipfit.model(current).posxyz(select,:) = model_after_fitting.posxyz;
      EEG.dipfit.model(current).momxyz(select,:) = model_after_fitting.momxyz;
      EEG.dipfit.model(current).rv               = model_after_fitting.rv;
      %EEG.dipfit.model(current).diffmap          = model_after_fitting.diffmap;

      % reassign the global EEG object back to the dialogs userdata
      set(parent, 'userdata', EEG);
      % close the interrupt dialog
      if ishandle(fig)
        close(fig);
      end

    otherwise
      error('unknown subfunction for pop_dipfit_nonlinear');
    end		% switch subfunction

  end		% if nargin


function mrGrayConvertClassToNifti(leftClassFile, rightClassFile, vAnatFile, t1NiftiFile)
% Convert mrGray class files to the NIFTI format used by ITKGray
%
%   mrGrayConvertClassToNifti(leftClassFile, rightClassFile, vAnatFile, t1NiftiFile)
%
% The vAnatFile and the t1NiftiFile may not align.  The NIFTI data are
% always in AC-PC coordinate frame and the old vAnatomy.dat files were not
% placed in this frame.  Hence, we find the transformation between the
% vAnatomy.dat and the new T1-NIFTI and apply that to the classifiction
% data.
%
% The left and right classification data are stored in a single file.
%
% See also:
%   analyze2mrGray, mrGray2Analyze
%
% Example:
%   mrGrayConvertClassToNifti - User is queried for files
%
% HISTORY:
% 2007.09.19 RFD wrote it.

labels = mrGrayGetLabels;

% false here will do 6-parameter rigid body alignment, true will do
% 9-parameter aligninment to account for scale differences.
adjustScales = true;

%% Get input file names
if(~exist('leftClassFile','var'))
    opts = {'*.class;*.Class;*.CLASS', 'Class files (*.class)'; '*.*','All Files (*.*)'};
    [f, p]=uigetfile(opts, 'Pick a mrGray class file for the left hemisphere');
    if(isequal(f,0)|| isequal(p,0))
        leftClassFile = '';
    else
        if(isempty(p)), p = pwd; end
        leftClassFile = fullFile(p,f);
    end
end
if(~exist('rightClassFile','var'))
    default = fullfile(fileparts(leftClassFile),'right.class');
    opts = {'*.class;*.Class;*.CLASS', 'Class files (*.class)'; '*.*','All Files (*.*)'};
    [f, p]=uigetfile(opts, 'Pick a mrGray class file for the right hemisphere',default);
    if(isequal(f,0)|| isequal(p,0))
        rightClassFile = '';
    else
        if(isempty(p)), p = pwd; end
        rightClassFile = fullFile(p,f);
    end
end
if(~exist('vAnatFile','var')||isempty(vAnatFile))
    default = fullfile(fileparts(rightClassFile),'vAnatomy.dat');
    opts = {'*.dat;*.Dat;*.DAT', 'MrGray dat files (*.dat)'; '*.nii;*.nii.gz', 'NIFTI files (*.nii,*.nii.gz)'; '*.*','All Files (*.*)'};
    [f, p]=uigetfile(opts, 'Pick a mrGray vAnatomy file',default);
    if(isequal(f,0)|| isequal(p,0)), disp('user canceled.'); return; end
    vAnatFile = fullFile(p,f);
end

if(~exist('t1NiftiFile','var')||isempty(t1NiftiFile))
    default = fullfile(fileparts(vAnatFile),'vAnatomy.nii.gz');
    opts = {'*.nii;*.nii.gz', 'NIFTI files (*.nii,*.nii.gz)'; '*.*','All Files (*.*)'};
    [f, p]=uigetfile(opts, 'Pick a NIFTI anatomy file',default);
    if(isequal(f,0)|| isequal(p,0)), disp('user canceled.'); return; end
    t1NiftiFile = fullFile(p,f);
end

[p,f,e] = fileparts(t1NiftiFile);
if(isempty(p)), p = pwd; end
if(strcmpi(e,'.gz')), [junk,f] = fileparts(f); end
outFileBaseName = fullfile(p, f);
outFileName = [outFileBaseName '_class.nii.gz'];
if(exist(outFileName,'file'))
    [f,p] = uiputfile('*.nii.gz','Save new NIFTI class file as...',outFileName);
    if(isequal(f,0)|| isequal(p,0)), disp('user canceled.'); return; end
    outFileName = fullfile(p,f);
    [p,f,e] = fileparts(outFileName);
    if(strcmpi(e,'.gz')), [junk,f] = fileparts(f); end
    outFileBaseName = fullfile(p, f);
end
disp(['NIFTI classification will be saved in ' outFileName]);

%% Read the data
% Allow t1NiftiFile and mrGrayClassFile to be passed as either filenames or
% structs that have already been loaded from file.
if(~isstruct(t1NiftiFile))
    t1NiftiFile = niftiRead(t1NiftiFile);
end

[vAnat,vAnatMm] = readVolAnat(vAnatFile);
if(all(vAnatMm==0))
    warning('vAnat voxel size is indeterminate - using [1 1 1]...'); %#ok<WNTAG>
    vAnatMm = [1 1 1];
end

%% Find the xForm between the vAnatomy data and the T1 NIFTI file data
[xformVAnatToAcpc] = dtiXformVanatCompute(t1NiftiFile.data, t1NiftiFile.qto_xyz, vAnat, vAnatMm, [], adjustScales);

% Create a bounding box identical to the nifti file bb.
bb = mrAnatXformCoords(t1NiftiFile.qto_xyz, [1 1 1; t1NiftiFile.dim]);
xform    = inv(xformVAnatToAcpc);
mm       = t1NiftiFile.pixdim;
interp   = [1 1 1 0 0 0];
outXform = t1NiftiFile.qto_xyz;
sz       = t1NiftiFile.dim;
vAnatSize = size(vAnat); %#ok<NASGU>
save([outFileBaseName '_xformToVanat.mat'],'sz','bb','mm','xform','vAnatSize');

% If you are concerned and want to check the alignments at this point use:
% newAnat = mrAnatResliceSpm(vAnat, xform, bb, mm, interp);
% showMontage(mrAnatHistogramClip(double(newAnat),0.4,0.99)-mrAnatHistogramClip(double(t1NiftiFile.data),0.4,0.99));
clear t1NiftiFile vAnat;
newClass = zeros(sz,'uint8');

% Get the left class file
if(~isempty(leftClassFile))
    if(~isstruct(leftClassFile))
        leftClassFile = readClassFile(leftClassFile, 0, 0);
    end
    disp('Resampling the left classification volumes...');
    cd = permute(leftClassFile.data,[2 1 3]);
    clear leftClassFile;
    types = unique(cd(:));
    types(types==0) = [];
    newTypes = mrGrayConvertClassTypeToLabel(types, 'l', labels);
    for(ii=1:numel(types))
    	c = double(cd==types(ii));
        c = mrAnatResliceSpm(c, xform, bb, mm, interp, false);
        newClass(c>=0.5) = newTypes(ii);
    end
    clear cd type newTypes;
end

% Get the right class file
if(~isempty(rightClassFile))
    if(~isstruct(rightClassFile))
        rightClassFile = readClassFile(rightClassFile, 0, 0);
    end
    disp('Resampling the right classification volumes...');
    cd = permute(rightClassFile.data,[2 1 3]);
    clear rightClassFile;
    types = unique(cd(:));
    types(types==0) = [];
    newTypes = mrGrayConvertClassTypeToLabel(types, 'r', labels);
    for(ii=1:numel(types))
    	c = double(cd==types(ii));
        c = mrAnatResliceSpm(c, xform, bb, mm, interp, false);
        newClass(c>=0.5) = newTypes(ii);
    end
    clear cd type;
end

% Both the right and left classification data are saved in a single file.
% They are transformed into the T1 NIFTI format.
disp(['Saving ' outFileName '...']);
dtiWriteNiftiWrapper(newClass, outXform, outFileName, 1, 'mrGray2 segmentation');

return;


function fg = dtiClipFiberGroup(fg, rlClip, apClip, siClip,saveFlag)
%
% [fg fgNot] = dtiClipFiberGroup(fg, rlClip, apClip, siClip,[saveFlag])
%
% Clips a MrD fiber group. Adapted from dtiFiberUI (RFD)
% If saveFlag == 1 then the fiber group will be written to the working
% directory with the fg.name'_clip'.
%
% HISTORY:
% 03.26.2009 - LMP wrote the thing.
% 08.2009 ER added proper treatment of "subgroup" field.

if notDefined('saveFlag'), saveFlag = 0; end
if(nargin<4) siClip = []; end
if(nargin<3) apClip = []; end
newName = [fg.name '_clip'];
if(nargin==1)
    prompt = {'Left (-80) Right (+80) clip (blank for none):',...
          'Posterior (-120) Anterior (+80) clip (blank for none):',...
          'Inferior (-50) Superior (+90) clip (blank for none):',...
          'New Fiber Group name:'};
    defAns = {'','','',newName};
    resp = inputdlg(prompt,'Clip Current FG',1,defAns);
    if(isempty(resp))
        disp('User cancelled clip.');
        return;
    end
    rlClip = str2num(resp{1});
    apClip = str2num(resp{2});
    siClip = str2num(resp{3});
    fg.name = resp{4};
end
empty = zeros(size(fg.fibers));
for ii=1:length(fg.fibers)
    keep = ones(size(fg.fibers{ii}(1,:)));
    if(~isempty(rlClip))
        keep = keep & (fg.fibers{ii}(1,:)<rlClip(1) | fg.fibers{ii}(1,:)>rlClip(2));
    end
    if(~isempty(apClip))
        keep = keep & (fg.fibers{ii}(2,:)<apClip(1) | fg.fibers{ii}(2,:)>apClip(2));
    end
    if(~isempty(siClip))
        keep = keep & (fg.fibers{ii}(3,:)<siClip(1) | fg.fibers{ii}(3,:)>siClip(2));
    end
    fg.fibers{ii} = fg.fibers{ii}(:,keep);
    empty(ii) = isempty(fg.fibers{ii})|size(fg.fibers{ii},2)==1;
end
fg.fibers = fg.fibers(~empty);
if isfield(fg, 'subgroup')
fg.subgroup = fg.subgroup(~empty);
end
fg.name = newName;

if saveFlag == 1
   dtiWriteFiberGroup(fg,fg.name);
end
return;


function setup(subnet, snum, enum, setupfile, chanmatch)
debug.printfunctionstack('>');
    [paths,PARAMS,subnets]=pf2PARAMS(setupfile);
    if ~exist('enum','var')
        enum = snum;
    end
    subnets(1).sites = get_closest_sites(subnets(1).source.longitude, subnets(1).source.latitude, subnets(1).radius, paths.DBMASTER, Inf, snum, enum+1, chanmatch);
    sites = subnets(1).sites;
	show_sites(sites);
    save sites.mat sites

%     %gismo_datasource = datasource('antelope', dbwfdata);
%     % Add response data (calib only?) for each scnl
%     for k=1:numel(subnets(1).sites)
%         %try
%                 subnets(1).sites(k).response = response_get_from_db(subnets(1).sites(k).channeltag.station, subnets(1).sites(k).channeltag.channel, snum, PARAMS.f, paths.DBMASTER);
%                 % for Sakurajima I get:
%                     % Error using response_get_from_db (line 59)
%                     % Database does not contain a sensor table.
%         %catch
%         %        subnets(1).sites(k).response.calib = NaN;
%         %end
%     end
    %save2mat(sprintf('pf/%s_%s.mat',subnet,datestr(snum,'yyyymmdd')), subnets, paths, PARAMS)

    % Replace this whole complicated response thing above with a simple
    % grab for calib value. Not much good for short periods, but who cares?
    % - implemented in closest stations

    save2mat(sprintf('pf/%s.mat',subnet), subnets, paths, PARAMS)
debug.printfunctionstack('<');
end

function [paths,PARAMS,subnets]=pf2PARAMS(setupfile)
    debug.printfunctionstack('>')

    % create pointer to main parameter file
    [dirname, filename, ext] = fileparts(setupfile);
    if exist(setupfile, 'file')
        setuppf = dbpf(sprintf('%s/%s',dirname,filename));

        % subnets
        subnet_tbl = pfget_tbl(setuppf, 'subnets');
        for c=1:numel(subnet_tbl)
            fields = regexp(subnet_tbl{c}, '\s+', 'split');
            subnets(c).name = fields{1};
            subnets(c).source.latitude = str2double(fields{2});
            subnets(c).source.longitude = str2double(fields{3});
            subnets(c).radius = str2double(fields{4});
            subnets(c).use = str2double(fields{5});
        end

        % Maximum number of scnls to display in a spectrogram
        PARAMS.max_number_scnls = pfget_num(setuppf, 'max_number_scnls');

        % Select channels to use according to this channel mask
        PARAMS.channel_mask = pfget(setuppf, 'channel_mask');

        % paths (removed from setup.pf file 2013/04/22)
        paths.DBMASTER = pfget(setuppf, 'dbmaster');
        paths.PFS = 'pf';
        paths.spectrogram_plots = 'spectrograms';

        % datasource
        try
            datasources = pfget_tbl(setuppf, 'datasources');
            for c=1:numel(datasources)
                fields = regexp(datasources{c}, '\s+', 'split');
                PARAMS.datasource(c).type = fields{1};
                PARAMS.datasource(c).path = fields{2};
                if numel(fields)>2
                    PARAMS.datasource(c).port = fields{3};
                end
            end
        end

        % archive_datasource
        PARAMS.switch_to_archive_after_days = pfget(setuppf, 'switch_to_archive_after_days');
        try
            archive_datasources = pfget_tbl(setuppf, 'archive_datasources');
            for c=1:numel(archive_datasources)
                fields = regexp(archive_datasources{c}, '\s+', 'split');
                PARAMS.archive_datasource(c).type = fields{1};
                PARAMS.archive_datasource(c).path = fields{2};
                if numel(fields)>2
                    PARAMS.archive_datasource(c).port = fields{3};
                end
            end
        end

        % waveform processing
        lowcut	 = pfget_num(setuppf,'lowcut');
        highcut	 = pfget_num(setuppf,'highcut');
        npoles	 = pfget_num(setuppf,'npoles');
        PARAMS.filterObj = filterobject('b',[lowcut highcut],npoles);

        % Spectrograms
        PARAMS.spectralobject = spectralobject( ...
            pfget_num(setuppf,'nfft'), ...
            pfget_num(setuppf,'overlap'), ...
            pfget_num(setuppf,'max_freq'), ...
            [ pfget_num(setuppf,'blue') pfget_num(setuppf,'red')] ...
        );

        % Derived data
        PARAMS.surfaceWaveSpeed = pfget_num(setuppf,'surfaceWaveSpeed');
        PARAMS.df = pfget_num(setuppf, 'df');
        PARAMS.f = 0:PARAMS.df:50;

        % Alarm system
        PARAMS.triggersForAlarmFraction = pfget_num(setuppf,'triggersForAlarmFraction');

        debug.print_debug(1, 'PARAMS setup OK')
    else
        error(sprintf('%s: parameter file %s.pf does not exist',mfilename, setupfile));
    end
    debug.printfunctionstack('<')
end

function save2mat(RUNTIMEMATFILE, subnets, paths, PARAMS)
    % write pf/tremor_runtime.mat, preserve current version if it already exists
    if exist(RUNTIMEMATFILE, 'file')
        system(sprintf('mv %s %s.%s',RUNTIMEMATFILE,RUNTIMEMATFILE,datestr(now,30)));
    end

    save(RUNTIMEMATFILE, 'subnets', 'paths', 'PARAMS');
end