function allen_ccf_npx_4shank(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 ML/DV
gui_data.spherical_angle=[0;0];
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,'AP');
            % update gui value
%             new_angle = gui_data.probe_angle(2) + angle_change;
%             new_angle=check_for360(new_angle);
%             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,'AP');

%             new_angle = gui_data.probe_angle(2) + angle_change;
%             new_angle=check_for360(new_angle);
%             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'))

            angle_change=-1;
            update_rotation(probe_atlas_gui,angle_change,'ML');

%             new_angle = gui_data.probe_angle(1) + angle_change;
%             new_angle=check_for360(new_angle);
%
%             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');
            %update_spin(probe_atlas_gui,angle_change);
            new_angle=gui_data.spin+angle_change;
            new_angle=check_for360(new_angle);
            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'))

            angle_change=1;
            update_rotation(probe_atlas_gui,angle_change,'ML');

%             new_angle = gui_data.probe_angle(1) + angle_change;
%             new_angle=check_for360(new_angle);
%             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');
            %update_spin(probe_atlas_gui,angle_change');
            new_angle=gui_data.spin+angle_change;
            new_angle=check_for360(new_angle);
            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 horizontal)',...
    '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)));

probe_angle_rad = (new_probe_position(3:4)/360)*2*pi;
[x,y,z] = sph2cart(probe_angle_rad(1),probe_angle_rad(2),max_ref_length);

% Get top of probe reference with user brain intersection point
% (get DV location of brain surface at point)
probe_brain_dv = find(gui_data.av(probe_ccf_coordinates(1),:, ...
    probe_ccf_coordinates(2)) > 1,1);
% (back up to 0 DV in CCF space)
probe_ref_top_ap = interp1(probe_brain_dv+[0,z],probe_ccf_coordinates(1)+[0,x],0,'linear','extrap');
probe_ref_top_ml = interp1(probe_brain_dv+[0,z],probe_ccf_coordinates(2)+[0,y],0,'linear','extrap');

% Set new probe position
probe_ref_top = [probe_ref_top_ap,probe_ref_top_ml,0];
probe_ref_bottom = probe_ref_top + [x,y,z];
probe_ref_vector = [probe_ref_top;probe_ref_bottom]';

%[~,,~] = 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);

probe_top=probe_vector(:,1);
probe_bottom=probe_vector(:,2);
u=(probe_bottom-probe_top)/norm(probe_bottom-probe_top);

rotation_matrix=get_rotation_matrix(gui_data.spin,'spin',u);
[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_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'})');

    % try reading the spherical coordinate here
    [theta,rho]=get_spherical_coords(probe_vector);

    %%
    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
        gui_data.probe_angle(1)=rho;
        gui_data.probe_angle(2)=theta;

        probe_text = ['Shank 1 insertion: ' ...
                num2str(probe_bregma_coordinate(1)) ' AP, ', ...
                num2str(-probe_bregma_coordinate(2)) ' ML, ', ...
                num2str(probe_depth) ' Probe-axis, ', ...
                num2str(round(theta)) char(176) ' from midline, ' ...
                num2str(round(rho)) char(176) ' from horizontal, '  ...
                num2str(gui_data.spin) char(176) ' spin '];

       %         num2str(gui_data.probe_angle(2)) char(176) ' from midline, ' ...
        %        num2str(gui_data.probe_angle(1)) char(176) ' from horizontal, ' ...

        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 : change probe angle' ...
    '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 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'})');

    probe_top=probe_vector(:,1);
    probe_bottom=probe_vector(:,2);
    u=(probe_bottom-probe_top)/norm(probe_bottom-probe_top);

    rotation_matrix=get_rotation_matrix(angle_change,mydir,u);
    [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 [rotation_matrix]=get_rotation_matrix(angle,direction,varargin)
angle_rad=deg2rad(angle);
    switch direction
        case 'ML'
            u=[1,0,0];
        case 'AP'
            u=[0,1,0];
        case 'spin' % that is any arbitrary unit vector that one requests
            u=varargin{1};
    end
    dotU=u*u';
    cross_M=[0,-u(3),u(2);u(3),0,-u(1);-u(2),u(1),0];
    rotation_matrix=cos(angle_rad)*eye(3)+sin(angle_rad)*cross_M+(1-cos(angle_rad))*dotU;
    rotation_matrix=round(rotation_matrix,3);
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(1:3,:)+zeropoint;

    rotated_probe=rotation_matrix*zerocentered_probe;
    rotated_probe_bregma=rotated_probe(1:3,:)+zeropoint;
end

function [angle]=check_for360(angle)
    if angle>360
        angle=angle-360;
    elseif angle<-360
        angle=angle+360;
    end
end

function [theta,rho]=get_spherical_coords(probe_ref_vector)

[probe_azimuth,probe_elevation] = cart2sph( ...
    diff(fliplr(probe_ref_vector(1,1:2))), ...
    diff(fliplr(probe_ref_vector(2,1:2))), ...
    diff(fliplr(probe_ref_vector(3,1:2))));

a = [probe_azimuth,probe_elevation]*(360/(2*pi));
theta=a(1);
rho=a(2);

end


function X = ctransposefactor(X)
if ~isempty(X.midfactors)
    for i = 1:length(X.midfactors)
        temp = X.leftfactors{i};
        X.leftfactors{i} = X.rightfactors{i}';
        X.rightfactors{i} = temp';
        X.midfactors{i} = X.midfactors{i}';
    end
end


function gpsimMapEcoliResults(model, type, expNo, scale, printResults)

% GPSIMMAPECOLIRESULTS Plot the results from the MAP script.
% FORMAT
% DESC plots results from the MAP script, printing them if the
% given flag is set to true.
% ARG model : the model for which the results are being plotted.
% ARG type : gives a descriptive label for the type of the
% experiment.
% ARG expNo : number identifier for the experiment.
% ARG scale : the scaling factor applied to normalise.
% ARG printResults : function prints the plots if set to true
% (default is false).
%
% COPYRIGHT : Pei Gao, Magnus Rattray and Neil D. Lawrence, 2008
%
% SEEALSO : demEcoliMap1

% SHEFFIELDML

type(1) = upper(type(1));
switch nargin
 case 3
  scale = 1;
  printResults = false;
 case 4
  printResults = false;
end

gene = {'dinF', 'dinI', 'lexA', 'recA', 'recN', 'ruvA', 'ruvB', ...
                  'sbmC', 'sulA', 'umuC', 'umuD', 'uvrB', 'yebG', 'yjiW' };
if model.comp{1}.numGenes == 8
  targetInd = [2 3 6 7 9 11 12 14];
  gene = gene(targetInd);
end

predt = model.comp{1}.mapt;
predExprsFull = [];
for i = 1:length(model.comp)
    predExprsFull = [predExprsFull; model.comp{i}.ypred];
end
meanPredExprs = ones(size(predExprsFull, 1), 1)*mean(predExprsFull);
scalePredExprs = sqrt(var(predExprsFull - meanPredExprs));
scaleMat = ones(size(predExprsFull, 1), 1)*scalePredExprs;
% predExprsFull = meanPredExprs + (predExprsFull-meanPredExprs)./scaleMat;
startVal = 1;
endVal = length(predt);
for i = 1:length(model.comp)
    predExprs{i} = predExprsFull(startVal:endVal, :);
    startVal = startVal + length(predt);
    endVal = endVal + length(predt);
end

modelB = model.comp{1}.B.*scale;
modelS = model.comp{1}.S.*scale;
modelAlpha = model.comp{1}.alpha.*scale;

for j = 1:length(model.comp)

  scalePred = sqrt(var(model.comp{j}.f));
  truef = [0.16 0 0.19 0.75 1];
%  truef = truef/sqrt(var(truef))*scalePred;

  % Figure 2(a) histograms;
%   B = trueParams.B./scale; % From Martino paper ... but don't know the
%                            % scale
%   B = B/mean(B)*mean(modelB); % do a rough rescaling so
%                                         % that the scales match.
%   S = trueParams.S./scale;
%   S = S/mean(S)*mean(modelS);
%   D = trueParams.D; % From Martino paper, again
%                                       % we know the scale because
%                                       % p21 is fixed to 0.8.

  % Martino f from Figure 2(b), again measured with a ruler.
  if model.comp{j}.ngParam
    nonLinearity = 'multi';
  else
    nonLinearity = model.comp{j}.nonLinearity;
  end
  switch nonLinearity
   case 'linear'
    figure, lin = plot(model.comp{j}.mapt,model.comp{j}.f, '-');
    hold on;
    bh = plot(model.comp{j}.mapt, model.comp{j}.f+2*sqrt(model.comp{j}.varF),'--');
    bh =[bh plot(model.comp{j}.mapt, model.comp{j}.f-2*sqrt(model.comp{j}.varF),'--')];
    lin = [lin plot(model.comp{j}.t(1:5), truef, 'rx')];
   case 'multi'
    figure,
    predF = exp(model.comp{j}.f);
    varFplus = exp(model.comp{j}.f+2*sqrt(model.comp{j}.varf));
    varFminus = exp(model.comp{j}.f-2*sqrt(model.comp{j}.varf));
    lin = plot(model.comp{j}.mapt, predF, '-');
    hold on;
    bh = plot(model.comp{j}.mapt, varFplus, '--');
    bh = [bh plot(model.comp{j}.mapt, varFminus, '--')];
%    truef = truef/sqrt(var(truef))*sqrt(var(predF));
    title('Inferred LexA Activity', 'fontsize', 20);
%    lin = [lin plot(model.comp{j}.t(1:5), truef, 'rx')];
   otherwise
     figure,
%
%     predF = exp(model.comp{j}.f);
%     varFplus = exp(model.comp{j}.f+2*sqrt(model.comp{j}.varf));
%     varFminus = exp(model.comp{j}.f-2*sqrt(model.comp{j}.varf));
%     lin = plot(model.comp{j}.mapt, predF-min(predF), '-');
%     hold on;
%     bh = plot(model.comp{j}.mapt, varFplus-min(predF), '--');
%     bh = [bh plot(model.comp{j}.mapt, varFminus-min(predF), '--')];
%     truef = truef/sqrt(var(truef))*sqrt(var(predF));
%

    subplot(2,1,1);
    lin = plot(model.comp{j}.mapt, model.comp{j}.f, '-');
    hold on;
    bh = plot(model.comp{j}.mapt, model.comp{j}.f+2*sqrt(model.comp{j}.varf),'--');
    bh = [bh plot(model.comp{j}.mapt, model.comp{j}.f-2* ...
                                           sqrt(model.comp{j}.varf),'--')];
    lin = [lin plot(model.comp{j}.t(1:5), truef, 'rx')];
    title('f(t) Profile');

    func = str2func(model.comp{j}.nonLinearity);
    subplot(2,1,2); lin = [lin plot(model.comp{j}.mapt, func(model.comp{j}.f), '-')];
    hold on;
    bh = [bh plot(model.comp{j}.mapt, func(model.comp{j}.f+2*sqrt(model.comp{j}.varf)),'--')];
    bh = [bh plot(model.comp{j}.mapt, func(model.comp{j}.f-2* ...
                                           sqrt(model.comp{j}.varf)),'--')];
    lin = [lin plot(model.comp{j}.t(1:5), func(truef), 'rx')];
    title('g(f) Profile');
  end

  set(bh, 'lineWidth', 2);
  set(lin, 'lineWidth', 4);
  set(lin, 'markersize', 20);
  set(gca, 'fontname', 'arial', 'fontsize', 24, 'xlim', xlim)

  kernName = model.comp{j}.kern.type;
  kernName(1) = upper(kernName(1));

  switch nonLinearity
   case 'linear'
    set(gca, 'ylim', [-2 4])
   case {'exp', 'negLogLogit'}
    set(gca, 'ylim', [0 8])
%    otherwise
%     set(gca, 'ylim', [0 2])
  end

  fileName = ['dem' type num2str(expNo) kernName ...
              nonLinearity '_profile' num2str(j) '_slide'];
  if printResults
    print('-deps', ['./results/' fileName]);
  end
  pos = get(gcf, 'paperposition');
  origpos = pos;
  pos(3) = pos(3)/2;
  pos(4) = pos(4)/2;
  set(gcf, 'paperposition', pos);
  lineWidth = get(gca, 'lineWidth');
  set(gca, 'lineWidth', lineWidth*2);
  if printResults
    print('-dpng', ['./results/' fileName])
  end
  set(gca, 'lineWidth', lineWidth);
  set(gcf, 'paperposition', origpos)
  switch nonLinearity
   case 'linear'
    xStart = min(model.comp{1}.mapt);
    xEnd = max(model.comp{1}.mapt);
    axis([xStart xEnd -2 3.5]);
  end

  for index = 1:model.comp{j}.numGenes
      figure;
      lin = plot(model.comp{j}.mapt, model.comp{j}.ypred(:,index)*scale(index), '-');
      hold on;
      bh = plot(model.comp{j}.mapt, model.comp{j}.ypred(:,index)*scale(index) + ...
                2*sqrt(model.comp{j}.ypredVar(:,index)*scale(index)*scale(index)),'--');
      bh = [bh plot(model.comp{j}.mapt, model.comp{j}.ypred(:,index)*scale(index) - ...
                2*sqrt(model.comp{j}.ypredVar(:,index)*scale(index)*scale(index)) ,'--')];
      lin = [lin plot(model.comp{j}.t, model.comp{j}.y(:,index)*scale(index), 'rx')];
      titleText = [gene{index} ' mRNA'];
      title(titleText,'fontsize', 20);

      textB = ['B = ' num2str(modelB(index))];
      textD = ['D = ' num2str(model.comp{j}.D(index))];
      textS = ['S = ' num2str(modelS(index))];
      textAlpha = ['Alpha = ' num2str(modelAlpha(index))];
      textGamma = ['Gamma = ' num2str(model.comp{j}.gParam(1,index))];
      texPos = ylim;
      scalePos = (texPos(2) - texPos(1))/14;
      texh = text(35, texPos(1)+5*scalePos, textB);
      texh = [texh text(35, texPos(1)+4*scalePos,textD)];
      texh = [texh text(35, texPos(1)+3*scalePos, textS)];
      texh = [texh text(35, texPos(1)+2*scalePos, textAlpha)];
      texh = [texh text(35, texPos(1)+1*scalePos, textGamma)];
      set(bh, 'lineWidth', 3);
      set(lin, 'lineWidth', 4);
      set(lin, 'markersize', 20);
      set(texh, 'fontsize', 16);
      set(gca, 'fontname', 'arial', 'fontsize', 24, 'xlim', [min(model.comp{j}.mapt) max(model.comp{j}.mapt)])

      if printResults
          fileName = ['dem' type num2str(expNo) kernName ...
              nonLinearity '_ExprsProfile_Rep' num2str(j) '_Gene' num2str(index)];
          print('-deps', ['./results/' fileName]);
          pos = get(gcf, 'paperposition');
          origpos = pos;
          pos(3) = pos(3);
          pos(4) = pos(4);
          set(gcf, 'paperposition', pos);
          lineWidth = get(gca, 'lineWidth');
          set(gca, 'lineWidth', lineWidth*2);
          print('-dpng', ['./results/' fileName]);
          set(gca, 'lineWidth', lineWidth);
          set(gcf, 'paperposition', origpos);
      end
  end
end

order = 1:model.comp{1}.numGenes;

counter = 0;

% Plot first basal transcription rates.
figure
bar([modelB(order)]', 0.6); colormap([0 0 0; 1 1 1]);
set(gca, 'xticklabel', gene)
if printResults
  fileName = ['dem' type num2str(expNo) kernName ...
              nonLinearity '_basal'];
  print('-deps', ['./results/' fileName]);
end
pos = get(gcf, 'paperposition');
origpos = pos;
pos(3) = pos(3)/2;
pos(4) = pos(4)/2;
set(gcf, 'paperposition', pos);
lineWidth = get(gca, 'lineWidth');
set(gca, 'lineWidth', lineWidth*2);
if printResults
  print('-dpng', ['./results/' fileName]);
end
set(gcf, 'paperposition', origpos);
set(gca, 'lineWidth', lineWidth);

% Plot the sensitivities.
figure
bar([modelS(order)]', 0.6); colormap([0 0 0; 1 1 1]);
set(gca, 'xticklabel', gene);
if printResults
  fileName = ['dem' type num2str(expNo) kernName ...
              nonLinearity '_sensitivity'];
  print('-deps', ['./results/' fileName]);
end
pos = get(gcf, 'paperposition');
origpos = pos;
pos(3) = pos(3)/2;
pos(4) = pos(4)/2;
set(gcf, 'paperposition', pos);
lineWidth = get(gca, 'lineWidth');
set(gca, 'lineWidth', lineWidth*2);
if printResults
  print('-dpng', ['./results/' fileName]);
end
set(gcf, 'paperposition', origpos)
set(gca, 'lineWidth', lineWidth);

% Finally plot degradation rates.
figure
bar([model.comp{1}.D(order)]', 0.6); colormap([0 0 0; 1 1 1]);
set(gca, 'xticklabel', gene);
if printResults
  fileName = ['dem' type num2str(expNo) kernName ...
              nonLinearity '_decay'];
  print('-deps', ['./results/' fileName]);
end
pos = get(gcf, 'paperposition');
origpos = pos;
pos(3) = pos(3)/2;
pos(4) = pos(4)/2;
set(gcf, 'paperposition', pos);
lineWidth = get(gca, 'lineWidth');
set(gca, 'lineWidth', lineWidth*2);
if printResults
  print('-dpng', ['./results/' fileName]);
end
set(gcf, 'paperposition', origpos);
set(gca, 'lineWidth', lineWidth);


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

figure(hCERR)

hold on
[x, y, button]  = ginput(1);

if isempty(button)
  xV = [];
  yV = [];
  return
end

hPlot = getActiveSegment;
segS = get(hPlot,'userdata');
xV = segS.GUI_xV;
yV = segS.GUI_yV;

switch button

  case 1 %replace nearest point

    rV = ((x - xV).^2 + (y - yV).^2 ).^0.5;
    indV = find(rV == min(rV));
    ind = indV(1);
    xV(ind) = x;
    yV(ind) = y;
    delete(hPlot)
    hPlot = plot([xV,xV(1)],[yV,yV(1)],'rs-');  %plot closed contour
    set(hPlot,'markersize',4)

  case 3 %add point along nearest edge

    %Cycle through all the line segments just to be sure we get the nearest perpindicular
    rV = ((x - xV).^2 + (y - yV).^2 ).^0.5;
    indV = find(rV == min(rV));
    ind0 = indV(1);
    r0V = [x, y];
    cycleLength = length(xV);
    nearest = -1;
    bestDist = 999999;
    ind_a = [];
    for i = 1 : cycleLength;
      indTest1 = ind0 + i - 1;
      [ind1] = cycle(indTest1, cycleLength);
      indTest2 = ind0 + i;
      [ind2] = cycle(indTest2, cycleLength);
      r1V = [xV(ind1),yV(ind1)];
      r2V = [xV(ind2),yV(ind2)];
      %Does the perpendicular hit on the line segment?
      [dist, flag1] = dist2seg(r0V, r1V, r2V);
      if flag1 == 0 & dist < bestDist
        ind_a = ind1;
        ind_b = ind2;
        bestDist = dist;
        nearest = i;
      end
    end

    %Compare with distances to nearest neighbors
    indTest = ind0 -1;
    cycleLength = length(xV);
    [ind1] = cycle(indTest, cycleLength);
    indTest = ind0 + 1;
    [ind2] = cycle(indTest, cycleLength);
    r1 = ((x - xV(ind1)).^2 + (y - yV(ind1)).^2 )^0.5;
    r2 = ((x - xV(ind2)).^2 + (y - yV(ind2)).^2 )^0.5;
    if  (r1 < r2 & r1 < bestDist)
        ind_a = ind0;
        ind_b = ind1;
    elseif r2 < bestDist
        ind_a = ind0;
        ind_b = ind2;
    end

    %Plug the new point in:
    if abs(ind_a - ind_b) == 1 %no end-wrapping
      insert1 = min(ind_a,ind_b);
      xV = [xV(1:insert1), x, xV(insert1+1:end)];
      yV = [yV(1:insert1), y, yV(insert1+1:end)];
    else  %case of end-wrapping
      xV = [xV, x];
      yV = [yV, y];
    end

    %redraw
    delete(hPlot)
    hPlot = plot([xV,xV(1)],[yV,yV(1)],'rs-');  %Only plot with same first and last point; store no duplicates
    set(hPlot,'markersize',4)

end

hold off

%segmentsS contains all the information about the plotted segments - it gets written into
%the userdata portion of any segment.

segS.active = 1;
stateS.activeSegment = segS.segNum;
segS.hPlot  = hPlot;
segS.GUI_xV = xV;
segS.GUI_yV = yV;

set(hPlot,'tag','newSegment')
set(hPlot,'userdata',segS)


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Spherical Harmonic Modeling and Analysis Toolkit (SPHARM-MAT) is a 3D
% shape modeling and analysis toolkit.
% It is a software package developed at Shenlab in Center for Neuroimaging,
% Indiana University (SpharmMat@gmail.com, http://www.iupui.edu/~shenlab/)
% It is available to the scientific community as copyright freeware
% under the terms of the GNU General Public Licence.
%
% Copyright 2009, 2010, ShenLab, Center for Neuroimaging, Indiana University
%
% This file is part of SPHARM-MAT.
%
% SPHARM-MAT is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% SPHARM-MAT is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with SPHARM-MAT. If not, see <http://www.gnu.org/licenses/>.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%  internal function

%  'xform_nii.m' is an internal function called by "load_nii.m", so
%  you do not need run this program by yourself. It does simplified
%  NIfTI sform/qform affine transform, and supports some of the
%  affine transforms, including translation, reflection, and
%  orthogonal rotation (N*90 degree).
%
%  For other affine transforms, e.g. any degree rotation, shearing
%  etc. you will have to use the included 'reslice_nii.m' program
%  to reslice the image volume. 'reslice_nii.m' is not called by
%  any other program, and you have to run 'reslice_nii.m' explicitly
%  for those NIfTI files that you want to reslice them.
%
%  Since 'xform_nii.m' does not involve any interpolation or any
%  slice change, the original image volume is supposed to be
%  untouched, although it is translated, reflected, or even
%  orthogonally rotated, based on the affine matrix in the
%  NIfTI header.
%
%  However, the affine matrix in the header of a lot NIfTI files
%  contain slightly non-orthogonal rotation. Therefore, optional
%  input parameter 'tolerance' is used to allow some distortion
%  in the loaded image for any non-orthogonal rotation or shearing
%  of NIfTI affine matrix. If you set 'tolerance' to 0, it means
%  that you do not allow any distortion. If you set 'tolerance' to
%  1, it means that you do not care any distortion. The image will
%  fail to be loaded if it can not be tolerated. The tolerance will
%  be set to 0.1 (10%), if it is default or empty.
%
%  Because 'reslice_nii.m' has to perform 3D interpolation, it can
%  be slow depending on image size and affine matrix in the header.
%
%  After you perform the affine transform, the 'nii' structure
%  generated from 'xform_nii.m' or new NIfTI file created from
%  'reslice_nii.m' will be in RAS orientation, i.e. X axis from
%  Left to Right, Y axis from Posterior to Anterior, and Z axis
%  from Inferior to Superior.
%
%  NOTE: This function should be called immediately after load_nii.
%
%  Usage: [ nii ] = xform_nii(nii, [tolerance], [preferredForm])
%
%  nii	- NIFTI structure (returned from load_nii)
%
%  tolerance (optional) - distortion allowed for non-orthogonal rotation
%	or shearing in NIfTI affine matrix. It will be set to 0.1 (10%),
%	if it is default or empty.
%
%  preferredForm (optional)  -  selects which transformation from voxels
%	to RAS coordinates; values are s,q,S,Q.  Lower case s,q indicate
%	"prefer sform or qform, but use others if preferred not present".
%	Upper case indicate the program is forced to use the specificied
%	tranform or fail loading.  'preferredForm' will be 's', if it is
%	default or empty.	- Jeff Gunter
%
%  NIFTI data format can be found on: http://nifti.nimh.nih.gov
%
%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)
%
function nii = xform_nii(nii, tolerance, preferredForm)

   %  save a copy of the header as it was loaded.  This is the
   %  header before any sform, qform manipulation is done.
   %
   nii.original.hdr = nii.hdr;

   if ~exist('tolerance','var') | isempty(tolerance)
      tolerance = 0.1;
   elseif(tolerance<=0)
      tolerance = eps;
   end

   if ~exist('preferredForm','var') | isempty(preferredForm)
      preferredForm= 's';				% Jeff
   end

   %  if scl_slope field is nonzero, then each voxel value in the
   %  dataset should be scaled as: y = scl_slope * x + scl_inter
   %  I bring it here because hdr will be modified by change_hdr.
   %
   if nii.hdr.dime.scl_slope ~= 0 & ...
	ismember(nii.hdr.dime.datatype, [2,4,8,16,64,256,512,768]) & ...
	(nii.hdr.dime.scl_slope ~= 1 | nii.hdr.dime.scl_inter ~= 0)

      nii.img = ...
	nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter;

      if nii.hdr.dime.datatype == 64

         nii.hdr.dime.datatype = 64;
         nii.hdr.dime.bitpix = 64;
      else
         nii.img = single(nii.img);

         nii.hdr.dime.datatype = 16;
         nii.hdr.dime.bitpix = 32;
      end

      nii.hdr.dime.glmax = max(double(nii.img(:)));
      nii.hdr.dime.glmin = min(double(nii.img(:)));

      %  set scale to non-use, because it is applied in xform_nii
      %
      nii.hdr.dime.scl_slope = 0;

   end

   %  However, the scaling is to be ignored if datatype is DT_RGB24.

   %  If datatype is a complex type, then the scaling is to be applied
   %  to both the real and imaginary parts.
   %
   if nii.hdr.dime.scl_slope ~= 0 & ...
	ismember(nii.hdr.dime.datatype, [32,1792])

      nii.img = ...
	nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter;

      if nii.hdr.dime.datatype == 32
         nii.img = single(nii.img);
      end

      nii.hdr.dime.glmax = max(double(nii.img(:)));
      nii.hdr.dime.glmin = min(double(nii.img(:)));

      %  set scale to non-use, because it is applied in xform_nii
      %
      nii.hdr.dime.scl_slope = 0;

   end

   %  There is no need for this program to transform Analyze data
   %
   if nii.filetype == 0 & exist([nii.fileprefix '.mat'],'file')
      load([nii.fileprefix '.mat']);	% old SPM affine matrix
      R=M(1:3,1:3);
      T=M(1:3,4);
      T=R*ones(3,1)+T;
      M(1:3,4)=T;
      nii.hdr.hist.qform_code=0;
      nii.hdr.hist.sform_code=1;
      nii.hdr.hist.srow_x=M(1,:);
      nii.hdr.hist.srow_y=M(2,:);
      nii.hdr.hist.srow_z=M(3,:);
   elseif nii.filetype == 0
      nii.hdr.hist.rot_orient = [];
      nii.hdr.hist.flip_orient = [];
      return;				% no sform/qform for Analyze format
   end

   hdr = nii.hdr;

   [hdr,orient]=change_hdr(hdr,tolerance,preferredForm);

   %  flip and/or rotate image data
   %
   if ~isequal(orient, [1 2 3])

      old_dim = hdr.dime.dim([2:4]);

      %  More than 1 time frame
      %
      if ndims(nii.img) > 3
         pattern = 1:prod(old_dim);
      else
         pattern = [];
      end

      if ~isempty(pattern)
         pattern = reshape(pattern, old_dim);
      end

      %  calculate for rotation after flip
      %
      rot_orient = mod(orient + 2, 3) + 1;

      %  do flip:
      %
      flip_orient = orient - rot_orient;

      for i = 1:3
         if flip_orient(i)
            if ~isempty(pattern)
               pattern = flipdim(pattern, i);
            else
               nii.img = flipdim(nii.img, i);
            end
         end
      end

      %  get index of orient (rotate inversely)
      %
      [tmp rot_orient] = sort(rot_orient);

      new_dim = old_dim;
      new_dim = new_dim(rot_orient);
      hdr.dime.dim([2:4]) = new_dim;

      new_pixdim = hdr.dime.pixdim([2:4]);
      new_pixdim = new_pixdim(rot_orient);
      hdr.dime.pixdim([2:4]) = new_pixdim;

      %  re-calculate originator
      %
      tmp = hdr.hist.originator([1:3]);
      tmp = tmp(rot_orient);
      flip_orient = flip_orient(rot_orient);

      for i = 1:3
         if flip_orient(i) & ~isequal(tmp(i), 0)
            tmp(i) = new_dim(i) - tmp(i) + 1;
         end
      end

      hdr.hist.originator([1:3]) = tmp;
      hdr.hist.rot_orient = rot_orient;
      hdr.hist.flip_orient = flip_orient;

      %  do rotation:
      %
      if ~isempty(pattern)
         pattern = permute(pattern, rot_orient);
         pattern = pattern(:);

         if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ...
		hdr.dime.datatype == 128 | hdr.dime.datatype == 511

            tmp = reshape(nii.img(:,:,:,1), [prod(new_dim) hdr.dime.dim(5:8)]);
            tmp = tmp(pattern, :);
            nii.img(:,:,:,1) = reshape(tmp, [new_dim       hdr.dime.dim(5:8)]);

            tmp = reshape(nii.img(:,:,:,2), [prod(new_dim) hdr.dime.dim(5:8)]);
            tmp = tmp(pattern, :);
            nii.img(:,:,:,2) = reshape(tmp, [new_dim       hdr.dime.dim(5:8)]);

            if hdr.dime.datatype == 128 | hdr.dime.datatype == 511
               tmp = reshape(nii.img(:,:,:,3), [prod(new_dim) hdr.dime.dim(5:8)]);
               tmp = tmp(pattern, :);
               nii.img(:,:,:,3) = reshape(tmp, [new_dim       hdr.dime.dim(5:8)]);
            end

         else
            nii.img = reshape(nii.img, [prod(new_dim) hdr.dime.dim(5:8)]);
            nii.img = nii.img(pattern, :);
            nii.img = reshape(nii.img, [new_dim       hdr.dime.dim(5:8)]);
         end
      else
         if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ...
		hdr.dime.datatype == 128 | hdr.dime.datatype == 511

            nii.img(:,:,:,1) = permute(nii.img(:,:,:,1), rot_orient);
            nii.img(:,:,:,2) = permute(nii.img(:,:,:,2), rot_orient);

            if hdr.dime.datatype == 128 | hdr.dime.datatype == 511
               nii.img(:,:,:,3) = permute(nii.img(:,:,:,3), rot_orient);
            end
         else
            nii.img = permute(nii.img, rot_orient);
         end
      end
   else
      hdr.hist.rot_orient = [];
      hdr.hist.flip_orient = [];
   end

   nii.hdr = hdr;

   return;					% xform_nii

%-----------------------------------------------------------------------
function [hdr, orient] = change_hdr(hdr, tolerance, preferredForm)

   orient = [1 2 3];
   affine_transform = 1;

   %  NIFTI can have both sform and qform transform. This program
   %  will check sform_code prior to qform_code by default.
   %
   %  If user specifys "preferredForm", user can then choose the
   %  priority.					- Jeff
   %
   useForm=[];					% Jeff

   if isequal(preferredForm,'S')
       if isequal(hdr.hist.sform_code,0)
           error('User requires sform, sform not set in header');
       else
           useForm='s';
       end
   end						% Jeff

   if isequal(preferredForm,'Q')
       if isequal(hdr.hist.qform_code,0)
           error('User requires qform, qform not set in header');
       else
           useForm='q';
       end
   end						% Jeff

   if isequal(preferredForm,'s')
       if hdr.hist.sform_code > 0
           useForm='s';
       elseif hdr.hist.qform_code > 0
           useForm='q';
       end
   end						% Jeff

   if isequal(preferredForm,'q')
       if hdr.hist.qform_code > 0
           useForm='q';
       elseif hdr.hist.sform_code > 0
           useForm='s';
       end
   end						% Jeff

   if isequal(useForm,'s')
      R = [hdr.hist.srow_x(1:3)
           hdr.hist.srow_y(1:3)
           hdr.hist.srow_z(1:3)];

      T = [hdr.hist.srow_x(4)
           hdr.hist.srow_y(4)
           hdr.hist.srow_z(4)];

      if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
         hdr.hist.old_affine = [ [R;[0 0 0]] [T;1] ];
         R_sort = sort(abs(R(:)));
         R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0;
         hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ];

         if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
            msg = [char(10) char(10) '   Non-orthogonal rotation or shearing '];
            msg = [msg 'found inside the affine matrix' char(10)];
            msg = [msg '   in this NIfTI file. You have 3 options:' char(10) char(10)];
            msg = [msg '   1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)];
            msg = [msg '      file. I strongly recommand this, because it will not cause' char(10)];
            msg = [msg '      negative effect, as long as you remember not to do slice' char(10)];
            msg = [msg '      time correction after using ''reslice_nii.m''.' char(10) char(10)];
            msg = [msg '   2. Using included ''load_untouch_nii.m'' program to load image' char(10)];
            msg = [msg '      without applying any affine geometric transformation or' char(10)];
            msg = [msg '      voxel intensity scaling. This is only for people who want' char(10)];
            msg = [msg '      to do some image processing regardless of image orientation' char(10)];
            msg = [msg '      and to save data back with the same NIfTI header.' char(10) char(10)];
            msg = [msg '   3. Increasing the tolerance to allow more distortion in loaded' char(10)];
            msg = [msg '      image, but I don''t suggest this.' char(10) char(10)];
            msg = [msg '   To get help, please type:' char(10) char(10) '   help reslice_nii.m' char(10)];
            msg = [msg '   help load_untouch_nii.m' char(10) '   help load_nii.m'];
            error(msg);
         end
      end

   elseif isequal(useForm,'q')
      b = hdr.hist.quatern_b;
      c = hdr.hist.quatern_c;
      d = hdr.hist.quatern_d;

      if 1.0-(b*b+c*c+d*d) < 0
         if abs(1.0-(b*b+c*c+d*d)) < 1e-5
            a = 0;
         else
            error('Incorrect quaternion values in this NIFTI data.');
         end
      else
         a = sqrt(1.0-(b*b+c*c+d*d));
      end

      qfac = hdr.dime.pixdim(1);
      i = hdr.dime.pixdim(2);
      j = hdr.dime.pixdim(3);
      k = qfac * hdr.dime.pixdim(4);

      R = [a*a+b*b-c*c-d*d     2*b*c-2*a*d        2*b*d+2*a*c
           2*b*c+2*a*d         a*a+c*c-b*b-d*d    2*c*d-2*a*b
           2*b*d-2*a*c         2*c*d+2*a*b        a*a+d*d-c*c-b*b];

      T = [hdr.hist.qoffset_x
           hdr.hist.qoffset_y
           hdr.hist.qoffset_z];

      %  qforms are expected to generate rotation matrices R which are
      %  det(R) = 1; we'll make sure that happens.
      %
      %  now we make the same checks as were done above for sform data
      %  BUT we do it on a transform that is in terms of voxels not mm;
      %  after we figure out the angles and squash them to closest
      %  rectilinear direction. After that, the voxel sizes are then
      %  added.
      %
      %  This part is modified by Jeff Gunter.
      %
      if det(R) == 0 | ~isequal(R(find(R)), sum(R)')

         %  det(R) == 0 is not a common trigger for this ---
         %  R(find(R)) is a list of non-zero elements in R; if that
         %  is straight (not oblique) then it should be the same as
         %  columnwise summation. Could just as well have checked the
         %  lengths of R(find(R)) and sum(R)' (which should be 3)
         %
         hdr.hist.old_affine = [ [R * diag([i j k]);[0 0 0]] [T;1] ];
         R_sort = sort(abs(R(:)));
         R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0;
         R = R * diag([i j k]);
         hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ];

         if det(R) == 0 | ~isequal(R(find(R)), sum(R)')
            msg = [char(10) char(10) '   Non-orthogonal rotation or shearing '];
            msg = [msg 'found inside the affine matrix' char(10)];
            msg = [msg '   in this NIfTI file. You have 3 options:' char(10) char(10)];
            msg = [msg '   1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)];
            msg = [msg '      file. I strongly recommand this, because it will not cause' char(10)];
            msg = [msg '      negative effect, as long as you remember not to do slice' char(10)];
            msg = [msg '      time correction after using ''reslice_nii.m''.' char(10) char(10)];
            msg = [msg '   2. Using included ''load_untouch_nii.m'' program to load image' char(10)];
            msg = [msg '      without applying any affine geometric transformation or' char(10)];
            msg = [msg '      voxel intensity scaling. This is only for people who want' char(10)];
            msg = [msg '      to do some image processing regardless of image orientation' char(10)];
            msg = [msg '      and to save data back with the same NIfTI header.' char(10) char(10)];
            msg = [msg '   3. Increasing the tolerance to allow more distortion in loaded' char(10)];
            msg = [msg '      image, but I don''t suggest this.' char(10) char(10)];
            msg = [msg '   To get help, please type:' char(10) char(10) '   help reslice_nii.m' char(10)];
            msg = [msg '   help load_untouch_nii.m' char(10) '   help load_nii.m'];
            error(msg);
         end

      else
         R = R * diag([i j k]);
      end					% 1st det(R)

   else
      affine_transform = 0;	% no sform or qform transform
   end

   if affine_transform == 1
      voxel_size = abs(sum(R,1));
      inv_R = inv(R);
      originator = inv_R*(-T)+1;
      orient = get_orient(inv_R);

      %  modify pixdim and originator
      %
      hdr.dime.pixdim(2:4) = voxel_size;
      hdr.hist.originator(1:3) = originator;

      %  set sform or qform to non-use, because they have been
      %  applied in xform_nii
      %
      hdr.hist.qform_code = 0;
      hdr.hist.sform_code = 0;
   end

   %  apply space_unit to pixdim if not 1 (mm)
   %
   space_unit = get_units(hdr);

   if space_unit ~= 1
      hdr.dime.pixdim(2:4) = hdr.dime.pixdim(2:4) * space_unit;

      %  set space_unit of xyzt_units to millimeter, because
      %  voxel_size has been re-scaled
      %
      hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,1,0));
      hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,2,1));
      hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,3,0));
   end

   return;					% change_hdr

%-----------------------------------------------------------------------
function orient = get_orient(R)

   orient = [];

   for i = 1:3
      switch find(R(i,:)) * sign(sum(R(i,:)))
      case 1
         orient = [orient 1];		% Left to Right
      case 2
         orient = [orient 2];		% Posterior to Anterior
      case 3
         orient = [orient 3];		% Inferior to Superior
      case -1
         orient = [orient 4];		% Right to Left
      case -2
         orient = [orient 5];		% Anterior to Posterior
      case -3
         orient = [orient 6];		% Superior to Inferior
      end
   end

   return;					% get_orient

%-----------------------------------------------------------------------
function [space_unit, time_unit] = get_units(hdr)

   switch bitand(hdr.dime.xyzt_units, 7)	% mask with 0x07
   case 1
      space_unit = 1e+3;		% meter, m
   case 3
      space_unit = 1e-3;		% micrometer, um
   otherwise
      space_unit = 1;			% millimeter, mm
   end

   switch bitand(hdr.dime.xyzt_units, 56)	% mask with 0x38
   case 16
      time_unit = 1e-3;			% millisecond, ms
   case 24
      time_unit = 1e-6;			% microsecond, us
   otherwise
      time_unit = 1;			% second, s
   end

   return;					% get_units


function stim = prt2par(prtFile, TR, parFile);
%
% stim = prt2par(prtFile, [TR=2], [parFile]);
%
% Convert BrainVoyager .prt files to mrVista .par files.
%
% prtFile and parFile can be a path string, or a cell of such strings, in
% which case it iterates. TR is the frame period to use when reading the
% prt files.
%
%
% ras, 07/06.
if notDefined('TR'), TR = 2; end

if notDefined('prtFile')
    [f p] = uigetfile({'*.prt' '*.*'}, 'Choose a Brain Voyager .prt file');
    prtFiles = fullfile(p,f);
elseif isequal(prtFile, 'all')
    w = dir('*.prt');
    prtFile = {w.name};
end

if iscell(prtFile)
    if notDefined('parFile')
        for i = 1:length(prtFile)
            stim(i) = prt2par(prtFile{i}, TR, []);
        end
    else
        for i = 1:length(prtFile)
            stim(i) = prt2par(prtFile{i}, TR, parFile{i});
        end
    end
    return
end

if notDefined('parFile') % use same filename, different extension
    [p f ext] = fileparts(fullpath(prtFile));
    parFile = fullfile(p, [f '.par']);
end

stim = stimReadPrt(prtFile, TR);

par = stim; par.onset = stim.onsetSecs;
par.color = cell(1, length(par.cond));
for i = 1:par.nConds
    ind = find(par.cond==par.condNums(i));
    if ~isempty(ind)
        par.color{ind(1)} = par.condColors{i};
    end
end
writeParfile(par, parFile);

return


function [p1,p2]=rbline(varargin)
%function to draw a rubberband line and return the start and end points
%Usage: [p1,p2]=rbline;     uses current axes
% or    [p1,p2]=rbline(h);  uses axes refered to by handle, h

% Created by Sandra Martinka March 2002

switch nargin
case 0
  h=gca;
case 1
  h=varargin{1};
  axes(h);
otherwise
  disp('Too many input arguments.');
end
cudata=get(gcf,'UserData'); %current UserData
hold on;
k=waitforbuttonpress;
p1=get(h,'CurrentPoint');       %get starting point
p1=p1(1,1:2);                   %extract x and y
lh=plot(p1(1),p1(2),'+:');      %plot starting point
udata.p1=p1;
udata.h=h;
udata.lh=lh;
set(gcf,'UserData',udata,'WindowButtonMotionFcn','wbmf','DoubleBuffer','on');
k=waitforbuttonpress;
p2=get(h,'Currentpoint');       %get end point
p2=p2(1,1:2);                   %extract x and y
set(gcf,'UserData',cudata,'WindowButtonMotionFcn','','DoubleBuffer','off'); %reset UserData, etc..
delete(lh);

function wbmf
%window motion callback function

utemp=get(gcf,'UserData');
ptemp=get(utemp.h,'CurrentPoint');
ptemp=ptemp(1,1:2);
set(utemp.lh,'XData',[utemp.p1(1),ptemp(1)],'YData',[utemp.p1(2),ptemp(2)]);


function [ y, ierror ] = y_check_eg_civil ( y )

%*****************************************************************************80
%
%% Y_CHECK_EG_CIVIL checks an Egyptian Civil year.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 March 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input/output, integer Y, the year, which must not be 0.
%
%    Output, integer IERROR, is 0 if Y is legal, and 1 otherwise.
%
  if ( 0 < y )
    ierror = 0;
  else
    ierror = 1;
  end

  return
end


function msh = mrmInflate(meshFile,iter,inflatedMeshFile)
% Inflate (unfold) mesh while minimizing distance and area distortions.
%
%   msh = mrmInflate([meshFile],[iterations=400],[inflatedMeshFile]);
%
% Must be run on a Linux machine, as this mainly uses Jonas Larson's
% surfrelax (Linux executable)
%

if ieNotDefined('meshFile'),
  [filename, pathname] = uigetfile('*.mat','Select mrMesh file');
  meshFile             = fullfile(pathname,filename);
  drawnow;
end;
if ieNotDefined('iter'),
    iter = 400;
end;
if ieNotDefined('inflatedMeshFile')
    if(nargout<1)
        [p,f,e]          = fileparts(meshFile);
        inflatedMeshFile = fullfile(p, [f '_surfRelaxed_' num2str(iter) e]);
    else
        inflatedMeshFile = '';
    end
end;

surfrelaxFile = fullfile(fileparts(which('mrMesh')), 'surfrelax');

if(ischar(meshFile))
    load(meshFile);
elseif(isstruct(meshFile))
    msh = meshFile;
    clear meshFile;
else
    error('First argument must be either a filename or a mesh structure.');
end

% if data field exists this is probably an older mesh
if(isfield(msh,'data'))
    triangles = msh.data.triangles;
    origin    = msh.data.origin;
    vertices  = msh.data.vertices;
else
    triangles = msh.triangles;
    origin    = msh.origin;
    vertices  = msh.vertices;
end
if(isfield(msh,'initVertices')&&~isempty(msh.initVertices))
    vertices = msh.initVertices;
end

% Save mesh in OOGL (.off) format
inFile = [tempdir 'tempOOGL.off'];
outFile = [tempdir 'tempOOGL_relaxed.off'];

%
% WRITE .off FILE
%
% Open file
fid = fopen(inFile, 'w');
if fid == -1,
  delete(inFile);
  error('Could not open file');
end;

% Header
fprintf(fid, 'OFF\n');
fprintf(fid, '%d %d %d\n', ...
        size(vertices,2), size(triangles,2), 0);

% Vertices
for ii=1:3
  vertices(ii,:) = vertices(ii,:) + origin(ii);
end
fprintf(fid, '%f %f %f\n', vertices);

% Faces
fprintf(fid, '3 %d %d %d\n', triangles);

% Close file
fclose(fid);

%--- actual smoothing
opt = ['-iter ' num2str(iter) ' -dist 0.1'];
system([surfrelaxFile ' ' opt ' ' inFile ' ' outFile]);

% Read new vertex locations
fid = fopen(outFile, 'r', 'ieee-be');
if fid == -1,
  delete(inFile);
  delete(outFile);
  error('Could not open file');
end

%--- read .off output file
% Header
hdr = fgetl(fid); %#ok<NASGU>
n = fread(fid, 3, 'int32');

% Read the vertices
vertices = fread(fid, [3 n(1)], 'float32');
newTriangles = fread(fid, [5 n(2)], 'int32');
fclose(fid);

for ii=1:3
  vertices(ii,:) = vertices(ii,:) - origin(ii);
end

newTriangles = newTriangles(2:4,:);
if(sum(newTriangles(:)-triangles(:))>0)
    error('triangles do not match!');
end

%--- put back
if isfield(msh,'data'),
  % mrMesh will recompute the normals if we remove them
  if(isfield(msh.data,'normals'))
    msh.data =  rmfield(msh.data,'normals');
  end;
  msh.data.vertices = vertices;
else
  if(isfield(msh,'normals'))
    msh =  rmfield(msh,'normals');
  end;
  msh.vertices = vertices;
end;
msh.lights = [];
msh.id = -1;

if(~isempty(inflatedMeshFile))
    % save
    [p, msh.name] = fileparts(inflatedMeshFile);
    msh.fileName = inflatedMeshFile;
    save(inflatedMeshFile, 'msh');
end
% clean up
delete(inFile);
delete(outFile);
if(nargout==0)
    clear msh;
end

return;


function dummy = makeDummyModel(numMets, numRxns)
% Makes an empty model with numMets rows for metabolites and numRxns columns
% for reactions. Includes all fields that are necessary to join models.
%
% USAGE:
%
%   dummy = makeDummyModel(numMets, numRxns)
%
% INPUTS:
%   numMets:       Number of metabolites
%   numRxns:       Number of reactions
%
% OUTPUT:
%   dummy:         Empty COBRA model structure
%
% .. Authors: SM June 2016

dummy.mets = cell(numMets, 1);
dummy.rxns = cell(numRxns, 1);
dummy.c = zeros(numRxns, 1);
dummy.S = zeros(numMets, numRxns);
dummy.lb = zeros(numRxns, 1);
dummy.ub = zeros(numRxns, 1);
dummy.b = zeros(numMets, 1);
dummy.metNames = cell(numMets, 1);
dummy.metFormulas = cell(numMets, 1);
dummy.rxnNames = cell(numRxns, 1);
dummy.subSystems = cell(numRxns, 1);
dummy.genes = {};
dummy.rxnGeneMat = sparse(numRxns, size(dummy.genes, 1));
dummy.rules = cell(numRxns, 1);
dummy.grRules = cell(numRxns, 1);
dummy.comments = cell(numRxns, 1);
dummy.citations = cell(numRxns, 1);
dummy.confidenceScores = cell(numRxns, 1);
dummy.ecNumbers = cell(numRxns, 1);
dummy.rxnKeggID = cell(numRxns, 1);
dummy.metCHEBIID = cell(numMets, 1);
dummy.metCharge = zeros(numMets, 1);
dummy.metFormulas = cell(numMets, 1);
dummy.metHMDB = cell(numMets, 1);
dummy.metInchiString = cell(numMets, 1);
dummy.metKeggID = cell(numMets, 1);
dummy.metSmile = cell(numMets, 1);
dummy.metPubChemID = cell(numMets, 1);
dummy.description.organism = {}; dummy.description.author = {}; dummy.description.name = {};
dummy.description.geneindex = {}; dummy.description.genedate = {}; dummy.description.genesource = {}; dummy.description.notes = {};
dummy.disabled = {};


function out = read_tiny_big_metadata(words_in,ind,fields)

%%% Routine to read in metadata for tiny images from giant binary file
%holding all metdata.

% valid field names:
% 1. keyword
% 2. filename
% 3. width
% 4. height
% 5. colors
% 6. date
% 7. engine
% 8. thumb_url
% 9. source_url
% 10. page
% 11. ind_page
% 12. ind_engine
% 13. ind_overall
% 14. label (1 = correct, 0 = incorrect, -1 = unlabelled)

%%% There are two modes of operation:
%
% 1. raw indexing mode
% --------------------
% Inputs:
% 1. words_in - 1 x nImages vector, each element being an integer
% in the range 1 to 73,777,893 (or whatever the total number of images in the dataset is).
% 2.  ind - 1 x nFields cell array holding requested fields (see
% list above) [optional - if ommitted, all fields will be returned].
% Outputs:
% 1. out - 1 x nImages structure holding the fields requested

% e.g. out = read_tiny_big_metadata([1:10],{'filename' 'label'});

% ++++++++++++++++++++++++++++++++++++++++++++++++++++
%
% 2. word based indexing.
% -----------------------
% Inputs:
% 1. word_in - 1 x nWords cell array, each entry holding one noun
% 2. ind - 1 x nWords cell array, each holding indices of images
% for the corresponding noun
% 3.  fields - 1 x nFields cell array holding requested fields (see
% list above)
% Outputs:
% 1. out - 1 x nImages structure holding the fields requested

% e.g. out = read_tiny_big_metadata('aardvark',[1:10],{'filename' 'label'});

% Requires: read_tiny_metadata_big_core.mex file

%%%% Hints regarding operation:
% The access time per image drops as the total number of images to be
% gathered increases. e.g. for 500,000 images load time is
% 1ms/image. For 100,000 images it is 2ms/image. So try and pass in big
% blocks of indices.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SET PATHS BELOW TO tiny_images.bin AND tiny_index.mat

% path to giant binary file
binary_fname = '/mit/tiny/data/tiny_metadata.bin';
% indexing info .mat file
binary_data_fname = '/mit/tiny/data/tiny_index.mat';

% END
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

TOTAL_NUM_IMGS = 79302017;

field_names = {'keyword' 'filename' 'width' 'height' 'colors' 'date' ...
    'engine' 'thumb_url' 'source_url' 'page' 'ind_page' ...
    'ind_engine' 'ind_overall' 'label'};

% if field actually a number
numeric_flag = [0 0 1 1 1 0 0 0 0 1 1 1 1 1];

if (nargin==1)
    RAW_INDEX_MODE = 1;
    ind = field_names;
elseif (nargin==2)
    RAW_INDEX_MODE = 1;
    if ~iscell(ind)
        ind = {ind};
    end
else % full 3 inputs
    RAW_INDEX_MODE = 0;

    if ~iscell(ind)
        % convert to cell arrays
        words_in = {words_in};
        ind = {ind};
    end

    if ~iscell(fields)
        fields = {fields};
    end

    nWords = length(words_in);

end

if RAW_INDEX_MODE

    %% just go and call binary with raw indices

    %% convert field names into numbers
    field_ind = [];
    for a=1:length(ind)
        field_ind = [ field_ind , strmatch(ind{a},field_names) ];
    end

    if isempty(field_ind)
        error('No valid field names');
    else
        nFields = length(field_ind);
    end

    %% check that all indices are >0 and <TOTAL_NUM_IMGS
    if ((sum(words_in<1)==0) & (sum(words_in>TOTAL_NUM_IMGS)==0))

        % now sort for speed
        [sorted_words,sort_ind] = sort(words_in);

        %% use MEX for sppedy reading in
        out = read_tiny_metadata_big_core(binary_fname, uint64(sorted_words), uint32(field_ind));
    else
        error('indices <0 or greater than total number of images');
    end

else % words_in and indices passed in

    %% convert field names into numbers
    field_ind = [];
    for a=1:length(fields)
        field_ind = [ field_ind , strmatch(fields{a},field_names) ];
    end

    if isempty(field_ind)
        error('No valid field names');
    else
        nFields = length(field_ind);
    end

    % load up index data
    load(binary_data_fname);

    % compute hashes for all words_in
    hashes = compute_hash_function(words_in,hash_key);

    img_offsets = [];

    for a=1:nWords

        % find word using hash
        match_ind = find(hashes(a) == hash);

        if any(match_ind)
            % check all ind are >=1 and <=total number of images

            if ((sum(ind{a}<1)==0) & (sum(ind{a}>num_imgs(match_ind))==0))
                word_offset = offset(match_ind);
                img_offsets = [img_offsets , uint64(double(word_offset) + [ind{a}])];
            else
                error('indices <0 or greater than number per image');
            end
        else
            error('word not found');
        end

        % now sort for speed
        [sorted_offsets,sort_ind] = sort(img_offsets);

        out = read_tiny_metadata_big_core( binary_fname, uint64(sorted_offsets), uint32(field_ind));

    end
end

% convert to string and trim whitespace
for a=1:nFields
    if numeric_flag(field_ind(a))
        tmp_out = int16(str2num(char(out{a}')));

        %%% hack workaround due to 'co' and 'in' problem
        if (field_ind(a)==14) & isempty(tmp_out)
            out{a}(find(out{a}(:)==99)) = 49;
            out{a}(find(out{a}(:)==105)) = 48;
            out{a}(find(out{a}(:)==110)) = 32;
            out{a}(find(out{a}(:)==111)) = 32;
            tmp_out = int16(fast_str2num(char(out{a}')));
        end

        out{a} = tmp_out;

    else
        out{a} = cellstr(char(out{a}'));
    end
    % sort back
    out{a}(sort_ind,:) = out{a};
end

% put into cell array
out = cell2struct(out,field_names(field_ind),2);


function ans = eq_str (s1,s2);
%
%		ans = eq_str (s1,s2);
%
%	returns 1 if strings are equal
%          0 if they're not
%
%		Ziad Saad July 20 97

ls1 = length (s1);
ls2 = length (s2);

if (ls1 ~= ls2),
	ans = 0;
	return;
else
	ans = 1;
	for i=1:1:ls1,
		if (s1(i) ~= s2(i)),
			ans=0;
		end;
	end;
	return;
end;


function [ y, j, ierror ] = yj_check_republican ( y, j )

%*****************************************************************************80
%
%% YJ_CHECK_REPUBLICAN checks a Republican YJ date.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    07 March 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input/output, integer Y, J, the YJ date.
%
%    Output, integer IERROR, is 0 if no serious error was found in
%    the date, and 1 otherwise.
%

%
%  Check the year.
%
  [ y, ierror ] = y_check_republican ( y );

  if ( ierror ~= 0 )
    return
  end
%
%  Make sure J is not too small or too big.
%
  [ y, j ] = j_borrow_republican ( y, j );

  [ y, j ] = j_carry_republican ( y, j );

  return
end


function images = publish3View(vw, savePath,plotTitle)
%
% images = publish3View(vw, [savePath], [plotTitle]);
%
% Shows the selected sagittal, coronal, and axial slices
% in a 3View window in a separate figure, with colorbar
% if relevant.
%
% If the optional 'savePath' argument is provided, exports
% the figure with the given image path.
%
% Returns the images in a cell array.
%
% ras, 01/2007.
if notDefined('vw'),      vw = getCurView;      end
if notDefined('savePath'),  savePath = '';          end

% check for 3-view
if ~isfield(vw, 'loc') || isempty(vw.loc)
    error('Not a 3-view or need a location defined in view.loc field.');
end

% get color map
if checkfields(vw, 'ui', 'displayMode')
    mode = sprintf('%sMode', vw.ui.displayMode);
    cmap = vw.ui.(mode).cmap;
end

% ROI prefs
if length(vw.ui.showROIs) <= 1
    switch vw.ui.showROIs
        case -2, roiList = 1:length(vw.ROIs);
        case -1, roiList = vw.selectedROI;
        case 0, roiList = [];
        otherwise, roiList = vw.ui.showROIs;  % manually specify
    end
else
    roiList = vw.ui.showROIs;
end

if roiList==0, roiList = []; end

% get images
[vw axi] = recompute3ViewImage(vw, vw.loc(1), 1);
[vw cor] = recompute3ViewImage(vw, vw.loc(2), 2);
[vw sag] = recompute3ViewImage(vw, vw.loc(3), 3);
axi = ind2rgb(axi, cmap);
cor = ind2rgb(cor, cmap);
sag = ind2rgb(sag, cmap);
images = {sag cor axi};

% open figure
hFig = figure('Color', 'w', 'Position', [0 300 600 400]);
centerfig(hFig, 0);
colormap(cmap);

% sagittal
subplot('Position', [0 0 1/3 1]);
image(sag); axis image; axis off;
drawROIs3View(vw, roiList, gca, 3, vw.loc)
axis([vw.ui.zoom(2,:) vw.ui.zoom(1,:)]);
sagAxesHandle = gca;

% corornal
subplot('Position', [1/3 0 1/3 1]);
image(cor); axis image; axis off;
drawROIs3View(vw, roiList, gca, 2, vw.loc)
axis([vw.ui.zoom(3,:) vw.ui.zoom(1,:)]);
corAxesHandle = gca;

% axial
subplot('Position', [2/3 0 1/3 1]);
image(axi); axis image; axis off;
drawROIs3View(vw, roiList, gca, 1, vw.loc)
axis([vw.ui.zoom(3,:) vw.ui.zoom(2,:)]);
axiAxesHandle = gca;

% Add crosshairs. The function to render crosshairs requires a vw structure
% as input. In order to add cross hairs to the figure to publish, we add
% the 3 axis handles (axial, sagittal, and coronal) to the view struct ui,
% and then call renderCrosshairs. This is a bit of a hack, but it seems
% nicer than writing a new function that renders cross hairs only for
% published figures.
vw.ui.axiAxesHandle = axiAxesHandle;
vw.ui.sagAxesHandle = sagAxesHandle;
vw.ui.corAxesHandle = corAxesHandle;
renderCrosshairs(vw, 1);

if checkfields(vw, 'ui', 'displayMode') && ...
    ismember(vw.ui.displayMode, {'co' 'amp' 'ph' 'map'})

    % put up color bar
    addCbarLegend(vw, hFig, .16);
end

if exist('plotTitle', 'var'), suptitle(plotTitle); end

if ~isempty(savePath)
    saveas(hFig, savePath);
%     close(hFig);
end

return
% /----------------------------------------------------------------/ %

% /----------------------------------------------------------------/ %
function drawROIs3View(vw, roiList, axs, ori, loc)
% draw the ROIs, parsing the relevant settings, on the axes
% specified by [axs]. This is implemented a little differently
% from drawROIs or drawROIsPerim, in that it doesn't select a given
% orientation each time (which caused some extra time during refreshes).
% roiList is an array into the vw.ROIs field.
ui = vw.ui;

% build prefs for the outline function.
prefs.method = ui.roiDrawMethod;
prefs.axesHandle = axs;

for r = roiList
    R = vw.ROIs(r);
    if ~isempty(R.coords)
        prefs.color = R.color;
        if r==vw.selectedROI, prefs.color=viewGet(vw,'selRoiColor'); end

        switch ori
            case 1, pts = R.coords([2 3], R.coords(1,:)==loc(1));
            case 2, pts = R.coords([1 3], R.coords(2,:)==loc(2));
            case 3, pts = R.coords([1 2], R.coords(3,:)==loc(3));
        end

        if isfield(ui, 'flipLR') && ui.flipLR==1 && ori<3
            % L/R flip affects columns of axi + coronal, but not sag, orientations
            dims = viewGet(vw,'Size');
            pts(2,:) = dims(3) - pts(2,:);
        end

        % restrict to voxels within zoom range

        h = outline(pts, prefs);
        if ishandle(h)
            set(h, 'ButtonDownFcn', sprintf('recenter3View(%s,%i);',vw.name, ori));
        end
    end
end

return


function dataout = ft_nirs_referencechannelsubtraction(cfg, datain)

% FT_NIRS_REFERENCECHANNELSUBTRACTION performs NIRS reference channel subtraction
%
% Use as
%   dataout = ft_nirs_referencechannelsubtraction(cfg, datain)
% where cfg is a configuration structure and indata is raw NIRS-data that is
% represented according to the output of FT_PREPROCESSING.
%
% The configuration should contain the following options
%   cfg.shortdistance = scalar, below which distance a channel is regarded
%                       as short in cm (default = 1.5)
%   cfg.closedistance = scalar, defines the maximal distance between a
%                       long and a short channel in cm (default = 15).
%                       NOT APPLIED CURRENTLY!
%   cfg.method        = string, 'regstat2', 'QR' or 'OLS' (default = 'QR')
%   cfg.verbose       = boolean, whether text output is desired (default = false)
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
%   cfg.outputfile  =  ...
% If you specify one of these (or both) the input data will be read from a *.mat
% file on disk and/or the output data will be written to a *.mat file. These mat
% files should contain only a single variable, corresponding with the
% input/output structure.
%
% See also FT_NIRS_SCALPCOUPLINGINDEX, FT_NIRS_SIGNALQUALITYINDEX, FT_NIRS_TRANSFORM_ODS

% You are using the FieldTrip NIRS toolbox developed and maintained by
% Artinis Medical Systems (http://www.artinis.com). For more information
% on FieldTrip, see http://www.fieldtriptoolbox.org
%
% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
% International License. To view a copy of this license, visit
% http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to
% Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
%
% Creative Commons Attribution-ShareAlike 4.0 International License:
% -----------------------------------
% You are free to:
%
%     Share - copy and redistribute the material in any medium or format
%     Adapt - remix, transform, and build upon the material
%     for any purpose, even commercially.
%
%     The licensor cannot revoke these freedoms as long as you follow the
%     license terms.
%
% Under the following terms:
%
%     Attribution - You must give appropriate credit, provide a link to
%                    the license, and indicate if changes were made. You
%                    may do so in any reasonable manner, but not in any way
%                    that suggests the licensor endorses you or your use.
%
%     ShareAlike - If you remix, transform, or build upon the material,
%                   you must distribute your contributions under the same
%                   license as the original.
%
%     No additional restrictions - You may not apply legal terms or
%                                   technological measures that legally
%                                   restrict others from doing anything the
%                                   license permits.
%
% -----------------------------------
%
% This toolbox is not to be used for medical or clinical purposes.
%
% Copyright (c) 2016 by Artinis Medical Systems.
% Contact: askforinfo@artinis.com
%
% Main programmer:
% Marc van Wanrooij, DCN, http://www.neural-code.com
% Jörn M. Horschig, Artinis Medical Systems BV, http://www.artinis.com
% $Id$

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% the initial part deals with parsing the input options and data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function

% the ft_preamble function works by calling a number of scripts from
% fieldtrip/utility/private that are able to modify the local workspace

ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar    datain
ft_preamble provenance datain

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  % do not continue function execution in case the outputfile is present and the user indicated to keep it
  return
end

% ensure that the input data is raw NIRS-data, this will also do
% backward-compatibility conversions of old data that for example was
% read from an old *.mat file
datain = ft_checkdata(datain, 'datatype', 'raw', 'senstype', 'nirs');

% get the options
cfg.shortdistance = ft_getopt(cfg, 'shortdistance', 1.5);
cfg.closedistance = ft_getopt(cfg, 'closedistance', 15);
cfg.method        = ft_getopt(cfg, 'method', 'QR');
cfg.verbose       = ft_getopt(cfg, 'verbose', false);

% check the options
cfg = ft_checkopt(cfg, 'method', 'char', {'regstat2', 'QR', 'OLS'});

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% the actual computation is done in the middle part
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% compute distances
distances   = ft_nirs_optodedistance(datain);

% compute correlation
selshallow	= distances<cfg.shortdistance;
shallowIdx  = find(selshallow);
seldeep     = distances>=cfg.shortdistance;
deepIdx     = find(seldeep);

dataout			 = datain;
dataout.label = datain.label(seldeep);

for tr=1:numel(datain.trial)
  dat = datain.trial{tr};
  shallow		= dat(selshallow,:);
  shallow		= bsxfun(@minus,shallow,mean(shallow,2)); % mean detrend
  shallowlabel = datain.label(selshallow);

  deep		= dat(seldeep,:);
  deep		= bsxfun(@minus,deep,mean(deep,2)); % mean detrend
  deeplabel = datain.label(seldeep);

  time		= datain.time{tr};

  %% Reference channel subtraction
  ndeep		= size(deep,1);
  signal		= NaN(size(deep));
  x			= shallow';
  for dpIdx	= 1:ndeep
    y				= deep(dpIdx,:)';
    switch (cfg.method)
      case 'regstat2'
        b				= regstats2(y,x,'linear',{'beta','r'});
        beta    = b.beta;
        res			= b.r;

      case 'QR'
        [Q,R] = qr(x,0);
        beta = R\(Q'*y);
        yhat = x*beta;
        res = y - yhat;

      case 'OLS'
        x2 = [ones(size(x, 1), 1) x];
        beta = x2\y;
        yhat = x2*beta;
        res  = y - yhat;
        beta(1) = [];

      otherwise % it should never come here as we use ft_checkopt
        error('unrecognized method');
    end

    signal(dpIdx,:) = res';

    % sanity check of results
    if cfg.verbose
      fprintf('Found the following meaningful shallow channels for deep channel %s:"', deeplabel{dpIdx});
      shIdx = find(beta>0.5);
      deepPos = datain.opto.chanpos(deepIdx(dpIdx), :);
      shallowPos = datain.opto.chanpos(shallowIdx(shIdx), :);
      posDist = sqrt(sum((shallowPos - repmat(deepPos, size(shallowPos, 1), 1)).^2, 2));
      closeChan = posDist < cfg.closedistance;
      for s=1:numel(shIdx)
        if (closeChan(s))
          fprintf('\n\t%s (d=%.2fcm)', shallowlabel{shIdx(s)}, posDist(s))
        else
          fprintf('\n\t#%s (d=%.2fcm)', shallowlabel{shIdx(s)}, posDist(s))
        end
      end
      fprintf('\n\n')
    end
  end

  % overwrite
  dataout.time{tr}	 = time;
  dataout.trial{tr} = signal;
end

% Auto-correlation
% rshallow	= corrcoef(shallow'); % correlation matrix shallow channels
% rdeep     = corrcoef(deep');    % correlation matrix deep channels
% rsignal		= corrcoef(signal');  % correlation matrix signal channels

% this might involve more active checking of whether the input options
% are consistent with the data and with each other

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% deal with the output
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% do the general cleanup and bookkeeping at the end of the function

% the ft_postamble function works by calling a number of scripts from
% fieldtrip/utility/private that are able to modify the local workspace

ft_postamble debug

ft_postamble previous   datain
ft_postamble provenance dataout
ft_postamble history    dataout
ft_postamble savevar    dataout


function c = ch_cap ( c )

%*****************************************************************************80
%
%% CH_CAP capitalizes a single character.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    23 April 2011
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character C, the character to capitalize.
%
%    Output, character C, the capitalized character.
%
  if ( 'a' <= c && c <= 'z' )
    c = c + 'A' - 'a';
  end

  c = char ( c );

  return
end