function results_image = PTKGetAirwayImageFromCentreline(label_bronchi, airway_root, template, colour_by_segment_index)
    % PTKGetAirwayImageFromCentreline. Generates an image of the airways
    %     coloured according to a number of subtrees. The start branch of the
    %     subtrees are specified in label_bronchi. Any bronchi serving more than
    %     one subtree is coloured grey.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2012.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    results_image = template.BlankCopy;

    % Create a vector containing every centreline voxel in the tree, and a cell
    % array, containing a vector of centreline points for each label segment
    all_centreline_voxels = [];
    segment_centreline_voxels_extended = [];
    for label_bronchus_index = 1 : length(label_bronchi)
        centreline_voxels = PTKTreeUtilities.GetCentrelineVoxelsForTheseBranchesExtended(label_bronchi(label_bronchus_index), template);
        segment_centreline_voxels_extended{label_bronchus_index} = centreline_voxels;
        all_centreline_voxels = [all_centreline_voxels; centreline_voxels];
    end
    all_centreline_voxels = unique(all_centreline_voxels);

    results_image_raw = zeros(template.ImageSize, 'uint8');

    bronchi_to_do = airway_root;

    % Find the voxel index of the endpoint of each label bronchus. We want to ensure
    % that the bronchus
    endpoint_voxels = [];
    for label_bronchus_index = 1 : length(label_bronchi)
        end_centreline_point = PTKTreeUtilities.GetEndmostPoint(label_bronchi(label_bronchus_index));
        endpoint_voxels(label_bronchus_index) = PTKTreeUtilities.CentrelinePointsToLocalIndices(end_centreline_point, template);
    end

    while ~isempty(bronchi_to_do)
        airway_bronchus = bronchi_to_do(end);
        bronchi_to_do(end) = [];
        voxel_indices_in_segment = airway_bronchus.GetAllAirwayPoints;
        voxel_indices_in_segment = template.GlobalToLocalIndices(voxel_indices_in_segment);

        label_bronchus_indices = [];

        for label_bronchus_index = 1 : length(label_bronchi)
            if ismember(endpoint_voxels(label_bronchus_index), voxel_indices_in_segment)
                label_bronchus_indices = label_bronchus_index;
            end
        end

        if isempty(label_bronchus_indices)
            number_of_centreline_points_in_bronchus = sum(ismember(all_centreline_voxels, voxel_indices_in_segment));

            % Search through all segments to see if the voxels match a bronchus
            % Only match bronchi if more than 50% of the centreline voxels in the
            % bronchus are part of this bronchus
            for label_bronchus_index = 1 : length(label_bronchi)
                number_of_centreline_points_in_bronchus_for_this_centreline = sum(ismember(segment_centreline_voxels_extended{label_bronchus_index}, voxel_indices_in_segment));
                if number_of_centreline_points_in_bronchus_for_this_centreline > (number_of_centreline_points_in_bronchus/2)
                    label_bronchus_indices = [label_bronchus_indices, label_bronchus_index];
                end
            end
        end
        % Colour the bronchus if it matches only one segment; otherwise colour it grey
        if length(label_bronchus_indices) == 1
            if colour_by_segment_index
                colour = label_bronchus_indices;
            else
                colour = 1 + mod(label_bronchus_indices - 1, 6);
            end
        else
            if colour_by_segment_index
                colour = 0;
            else
                colour = 7;
            end
        end
        results_image_raw(voxel_indices_in_segment) = colour;

        bronchi_to_do = [bronchi_to_do airway_bronchus.Children];
    end

    results_image.ChangeRawImage(results_image_raw);
    results_image.ImageType = PTKImageType.Colormap;
end


function read(obj,filename,varargin)
%READ Load sequence from file.
%   READ(seqObj, filename, ...) Read the given filename and load sequence
%   data into sequence object.
%
%   optional parwameter 'detectRFuse' can be given to let the function
%   infer the currently missing flags concerning the intended use of the RF
%   pulses (excitation, refocusing, etc). These are important for the
%   k-space trajectory calculation
%
%   Examples:
%   Load the sequence defined in gre.seq in my_sequences directory
%
%       read(seqObj,'my_sequences/gre.seq')
%
% See also  write

detectRFuse=false;
if ~isempty(varargin) && ~isempty(strfind(varargin{:},'detectRFuse'))
    detectRFuse=true;
end

fid = fopen(filename);

if fid<0
    error('filed to open file ''%s''', filename);
end

% Clear sequence data
%obj.blockEvents = [];
obj.blockEvents = {};
obj.definitions = containers.Map();
obj.gradLibrary = mr.EventLibrary();
obj.shapeLibrary = mr.EventLibrary();
obj.rfLibrary = mr.EventLibrary();
obj.adcLibrary = mr.EventLibrary();
%obj.delayLibrary = mr.EventLibrary();
obj.trigLibrary = mr.EventLibrary();
obj.labelsetLibrary = mr.EventLibrary();
obj.labelincLibrary = mr.EventLibrary();
obj.extensionStringIDs={};
obj.extensionNumericIDs=[];

version_combined=0;

% Load data from file
while true
    section = skipComments(fid);
    if section == -1
        break
    end

    switch section
        case '[DEFINITIONS]'
            obj.definitions = readDefinitions(fid);
            v=obj.getDefinition('GradientRasterTime');
            if ~isempty(v)
                obj.gradRasterTime=v;
            end
            v=obj.getDefinition('RadiofrequencyRasterTime');
            if ~isempty(v)
                obj.rfRasterTime=v;
            end
            v=obj.getDefinition('AdcRasterTime');
            if ~isempty(v)
                obj.adcRasterTime=v;
            end
            v=obj.getDefinition('BlockDurationRaster');
            if ~isempty(v)
                obj.blockDurationRaster=v;
            end
        case '[SIGNATURE]'
            tmpSignDefs = readDefinitions(fid);
            if isKey(tmpSignDefs,'Type')
                obj.signatureType=tmpSignDefs('Type');
            end
            if isKey(tmpSignDefs,'Hash')
                obj.signatureValue=tmpSignDefs('Hash');
                obj.signatureFile='Text'; % we are reading a text file, so much is known for sure
            end
        case '[VERSION]'
            [version_major, ...
             version_minor, ...
             version_revision] = readVersion(fid);
            assert(version_major == obj.version_major, ...
                    'Unsupported version_major %d', version_major)
            %
            version_combined=1000000*version_major+1000*version_minor+version_revision;
            %
            if version_combined < 1002000
                error('Unsupported version %d.%d.%d, only file format revision 1.2.0 and above are supported', version_major, version_minor, version_revision);
            end
            if version_combined < 1003001
                warning('Loading older Pulseq format file (version %d.%d.%d) some code may function not as expected', version_major, version_minor, version_revision);
            end
        case '[BLOCKS]'
            if ~exist('version_major')
                error('Pulseq file MUST include [VERSION] section prior to [BLOCKS] section');
            end
            [obj.blockEvents,obj.blockDurations,delayInd_tmp] = readBlocks(fid, obj.blockDurationRaster, version_combined);
        case '[RF]'
            if version_combined >= 1004000
                obj.rfLibrary = readEvents(fid, [1 1 1 1 1e-6 1 1]); % this is 1.4.x format
            else
                obj.rfLibrary = readEvents(fid, [1 1 1 1e-6 1 1]); % this is 1.3.x and below
                % we will have to scan through the library later after all the shapes have been loaded
            end
        case '[GRADIENTS]'
            if version_combined >= 1004000
                obj.gradLibrary = readEvents(fid, [1 1 1 1e-6], 'g' ,obj.gradLibrary); % this is 1.4.x format
            else
                obj.gradLibrary = readEvents(fid, [1 1 1e-6], 'g' ,obj.gradLibrary); % this is 1.3.x and below
            end
        case '[TRAP]'
            obj.gradLibrary = readEvents(fid, [1 1e-6 1e-6 1e-6 1e-6], 't', obj.gradLibrary);
        case '[ADC]'
            obj.adcLibrary = readEvents(fid, [1 1e-9 1e-6 1 1]);
        case '[DELAYS]'
            if version_combined >= 1004000
                error('Pulseq file revision 1.4.0 and above MUST NOT contain [DELAYS] section');
            end
            tmp_delayLibrary = readEvents(fid, 1e-6);
        case '[SHAPES]'
            obj.shapeLibrary = readShapes(fid, (version_major==1 && version_minor<4));
        case '[EXTENSIONS]'
            obj.extensionLibrary = readEvents(fid);
        otherwise
            if     strncmp('extension TRIGGERS', section, 18)
                id=str2num(section(19:end));
                obj.setExtensionStringAndID('TRIGGERS',id);
                obj.trigLibrary = readEvents(fid, [1 1 1e-6 1e-6]);
            elseif strncmp('extension LABELSET', section, 18)
                id=str2num(section(19:end));
                obj.setExtensionStringAndID('LABELSET',id);
                obj.labelsetLibrary = readAndParseEvents(fid,@str2num,@(s)find(strcmp(mr.getSupportedLabels,s)));
            elseif strncmp('extension LABELINC', section, 18)
                id=str2num(section(19:end));
                obj.setExtensionStringAndID('LABELINC',id);
                obj.labelincLibrary = readAndParseEvents(fid,@str2num,@(s)find(strcmp(mr.getSupportedLabels,s)));
            else
                error('Unknown section code: %s', section);
            end
    end
end
fclose(fid);

%
if version_combined < 1002000
    error('Unsupported version %07d, only file format revision 1.2.0 (1002000) and above are supported', version_combined);
end
% fix blocks, gradients and RF objects imported from older versions
if version_combined < 1004000  % MZ: FIXME: the code below does not update key-to-id mapping, so the libraries are not fully functional... these are only partially updated by the shaped gradients patching below (first/last detection)
    % scan through the RF objects
    for i=1:length(obj.rfLibrary.data)
        % % need to (partially) decode the magnitude shape to find out the pulse duration
        %magSamples = obj.shapeLibrary.data(obj.rfLibrary.data(i).array(2)).array(1);
        % % create time shape
        %timeShape = mr.compressShape((1:magSamples)-0.5); % time shape is stored in units of RF raster
        %data = [timeShape.num_samples timeShape.data];
        %timeID = obj.shapeLibrary.find_or_insert(data);
        obj.rfLibrary.data(i).array = [obj.rfLibrary.data(i).array(1:3), 0, obj.rfLibrary.data(i).array(4:end)];
        obj.rfLibrary.lengths(i) = obj.rfLibrary.lengths(i) + 1;
    end

    % scan through the gradient objects and update 't'-s (trapezoids) und 'g'-s (free-shape gradients)
    for i=1:length(obj.gradLibrary.data)
        if obj.gradLibrary.type(i)=='t'
            if obj.gradLibrary.data(i).array(2)==0
                if abs(obj.gradLibrary.data(i).array(1))==0 && obj.gradLibrary.data(i).array(3) > 0
                    obj.gradLibrary.data(i).array(3)=obj.gradLibrary.data(i).array(3)-obj.gradRasterTime;
                    obj.gradLibrary.data(i).array(2)=obj.gradRasterTime;
                end
            end
            if obj.gradLibrary.data(i).array(4)==0
                if abs(obj.gradLibrary.data(i).array(1))==0 && obj.gradLibrary.data(i).array(3) > 0
                    obj.gradLibrary.data(i).array(3)=obj.gradLibrary.data(i).array(3)-obj.gradRasterTime;
                    obj.gradLibrary.data(i).array(4)=obj.gradRasterTime;
                end
            end
        end
        if obj.gradLibrary.type(i)=='g'
            % % need to (partially) decode the shape to find out the duration
            %nSamples = obj.shapeLibrary.data(obj.gradLibrary.data(i).array(2)).array(1);
            % % create time shape
            %timeShape = mr.compressShape((1:nSamples)-0.5); % time shape is stored in units of grad raster
            %data = [timeShape.num_samples timeShape.data];
            %timeID = obj.shapeLibrary.find_or_insert(data);
            obj.gradLibrary.data(i).array = [obj.gradLibrary.data(i).array(1:2), 0, obj.gradLibrary.data(i).array(3:end)];
            obj.gradLibrary.lengths(i) = obj.gradLibrary.lengths(i) + 1;
        end
    end

    % for versions prior to 1.4.0 blockDurations have not been initialized
    obj.blockDurations=zeros(1,length(obj.blockEvents));
    % scan trhough blocks and calculate durations
    for iB = 1:length(obj.blockEvents)
        b=obj.getBlock(iB);
        if delayInd_tmp(iB) > 0
            b.delay.type = 'delay';
            b.delay.delay = tmp_delayLibrary.data(delayInd_tmp(iB)).array;
        end
        obj.blockDurations(iB)=mr.calcDuration(b);
    end
end

gradChannels={'gx','gy','gz'};
gradPrevLast=zeros(1,length(gradChannels));
for iB = 1:length(obj.blockEvents)
    b=obj.getBlock(iB);
    block_duration=obj.blockDurations(iB);
    %obj.blockDurations(iB)=block_duration;
    % we also need to keep track of the event IDs because some Pulseq files written by external software may contain repeated entries so searching by content will fail
    eventIDs=obj.blockEvents{iB};
    % update the objects by filling in the fields not contained in the
    % pulseq file
    for j=1:length(gradChannels)
        grad=b.(gradChannels{j});
        if isempty(grad)
            gradPrevLast(j)=0;
            continue;
        end
        if strcmp(grad.type,'grad')
            if grad.delay>0
                gradPrevLast(j)=0;
            end
            if isfield(grad,'first')
                continue;
            end
            grad.first = gradPrevLast(j);
            % is this an extended trapezoid?
            if grad.time_id~=0
                grad.last=grad.waveform(end);
                grad_duration=grad.delay+grad.tt(end);
            else
                % restore samples on the edges of the gradient raster intervals
                % for that we need the first sample
                odd_step1=[grad.first 2*grad.waveform'];
                odd_step2=odd_step1.*(mod(1:length(odd_step1),2)*2-1);
                waveform_odd_rest=(cumsum(odd_step2).*(mod(1:length(odd_step2),2)*2-1))';
                grad.last = waveform_odd_rest(end);
                grad_duration=grad.delay+length(grad.waveform)*obj.gradRasterTime;
            end
            % bookkeeping
            gradPrevLast(j) = grad.last;
            if grad_duration+eps<block_duration
                gradPrevLast(j)=0;
            end
            %b.(gradChannels{j})=grad;
            % update library object
% this does not work s we don't know how the amplitude was defined
%             amplitude = max(abs(grad.waveform));
%             if amplitude>0
%                 [~,~,fnz]=find(grad.waveform,1); % find the first non-zero value and make it positive
%                 amplitude=amplitude*sign(fnz);
%             end
            % need to recover the amplidute from the library data directly...
            id=eventIDs(j+2);
            amplitude=obj.gradLibrary.data(id).array(1);
            %
            if version_combined>=1004000
                old_data = [amplitude grad.shape_id grad.time_id grad.delay];
            else
                old_data = [amplitude grad.shape_id grad.delay];
            end
            new_data = [amplitude grad.shape_id grad.time_id grad.delay grad.first grad.last];
            update_data(obj.gradLibrary, id, old_data, new_data,'g');
        else
            gradPrevLast(j)=0;
        end
    end
    %% copy updated objects back into the event library
    %obj.setBlock(iB,b);

%for iB=1:size(obj.blockEvents,1)
%     % update the objects by filling in the fields not contained in the
%     % pulseq file
%     for j=1:length(gradChannels)
%         grad=b.(gradChannels{j});
%         if isempty(grad)
%             continue;
%         end
%         if strcmp(grad.type,'grad')
%             grad.first = grad.waveform(1); % MZ: eventually we should use extrapolation by 1/2 gradient rasters here
%             grad.last = grad.waveform(end);
%             b.(gradChannels{j})=grad;
%         end;
%     end
%     % copy updated objects back into the event library
%     obj.setBlock(iB,b);
end

if detectRFuse
    % find the RF pulses, list flip angles
    % and work around the current (rev 1.2.0) Pulseq file format limitation
    % that the RF pulse use is not stored in the file
    for k=obj.rfLibrary.keys
        libData=obj.rfLibrary.data(k).array;
        rf=obj.rfFromLibData(libData);
        %flipAngleDeg=abs(sum(rf.signal))*rf.t(1)*360; %we use rfex.t(1) in place of opt.system.rfRasterTime
        flipAngleDeg=abs(sum(rf.signal(1:end-1).*(rf.t(2:end)-rf.t(1:end-1))))*360;
        offresonance_ppm=1e6*rf.freqOffset/obj.sys.B0/obj.sys.gamma;
        % fix library %%%% if length(obj.rfLibrary.type)>=eventInd(2)
        if flipAngleDeg < 90.01 % we add 0.01 degree to account for rounding errors which we've experienced for very short RF pulses
            obj.rfLibrary.type(k) = 'e';
        else
            if rf.shape_dur > 6e-3 && offresonance_ppm >= -3.5 && offresonance_ppm <= -3.4 % approx -3.45 ppm
                obj.rfLibrary.type(k) = 's'; % saturation (fat-sat)
            else
                obj.rfLibrary.type(k) = 'r';
            end
        end
%         % fix libData
%         if length(libData) < 9
%             if flipAngleDeg < 90.01 % we add 0.01 degree to account for rounding errors which we've experienced for very short RF pulses
%                 libData(9) = 0; % or 1 ?
%             else
%                 libData(9) = 2; % or 1 ?
%             end
%             obj.rfLibrary.data(k).array=libData;
%         end
    end
end

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% Helper functions  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    function def = readDefinitions(fid)
        %readDefinitions Read the [DEFINITIONS] section of a sequence file.
        %   defs=readDefinitions(fid) Read user definitions from file
        %   identifier of an open MR sequence file and return a map of
        %   key/value entries.

        def = containers.Map;
        %line = strip(fgetl(fid));
        line = strip(skipComments(fid));
        while ischar(line) && ~(isempty(line) || line(1) == '#')
            tok = textscan(line, '%s');
            def(tok{1}{1}) = str2double(tok{1}(2:end));
            if ~all(isfinite(def(tok{1}{1})))
                def(tok{1}{1}) = strtrim(line((length(tok{1}{1})+2):end));
            end
            line = fgetl(fid);
        end
    end

    function [major, minor, revision] = readVersion(fid)
        %readVersion Read the [VERSION] section of a sequence file.
        %   defs=readVersion(fid) Read Pulseq version from file
        %   identifier of an open MR sequence file and return it

        major = [];
        minor = [];
        revision = [];
        line = fgetl(fid);
        while ischar(line) && ~(isempty(line) || line(1)=='#')
            tok = textscan(line,'%s');
            switch tok{1}{1}
                case 'major'
                    major = str2double(tok{1}(2:end));
                case 'minor'
                    minor = str2double(tok{1}(2:end));
                case 'revision'
                    revision = str2double(tok{1}(2:end));
            end
            line = fgetl(fid);
        end
    end

    function [eventTable,blockDurations,delayIDs_tmp] = readBlocks(fid, blockDurationRaster, version_combined)
        %readBlocks Read the [BLOCKS] section of a sequence file.
        %   library=readBlocks(fid) Read blocks from file identifier of an
        %   open MR sequence file and return the event table.

        eventTable = {};
        blockDurations = [];
        delayIDs_tmp = [];
        line = fgetl(fid);
        while ischar(line) && ~(isempty(line) || line(1) == '#')
            blockEvents = sscanf(line, '%f')';
            %eventTable = [eventTable; blockEvents(2:end)];
            if version_combined<=1002001
                eventTable{blockEvents(1)} = [0 blockEvents(3:end) 0];
            else
                eventTable{blockEvents(1)} = [0 blockEvents(3:end)];
            end
            if version_combined>=1004000
                blockDurations(blockEvents(1)) = blockEvents(2)*blockDurationRaster;
            else
                delayIDs_tmp(blockEvents(1)) = blockEvents(2);
            end
            line = fgetl(fid);
        end
    end

    function eventLibrary = readEvents(fid, scale, type, eventLibrary)
        %readEvents Read an event section of a sequence file.
        %   library=readEvents(fid) Read event data from file identifier of
        %   an open MR sequence file and return a library of events.
        %
        %   library=readEvents(fid,scale) Read event data and scale
        %   elements according to column vector scale.
        %
        %   library=readEvents(fid,scale,type) Attach the type string to
        %   elements of the library.
        %
        %   library=readEvents(...,library) Append new events to the given
        %   library.
        if nargin < 2
            scale = 1;
        end
        if nargin < 4
            eventLibrary = mr.EventLibrary();
        end
        line = fgetl(fid);
        while ischar(line) && ~(isempty(line) || line(1) == '#')
            data = sscanf(line,'%f')';
            id = data(1);
            data = scale.*data(2:end);
            if nargin < 3
                eventLibrary.insert(id, data);
            else
                eventLibrary.insert(id, data, type);
            end

            line=fgetl(fid);
        end
    end

    function eventLibrary = readAndParseEvents(fid, varargin)
        %readAndParseEvents Read an event section of a sequence file.
        %   library=readAndParseEvents(fid) Read event data from file
        %   identifier of an open MR sequence file and return a library of
        %   events.
        %
        %   library=readAndParseEvents(fid,parser1,parser2,...) Read event
        %   data and convert the elements using to the provided parser.
        %   Default parser is str2num()
        %
        eventLibrary = mr.EventLibrary();
        line = fgetl(fid);
        while ischar(line) && ~(isempty(line) || line(1) == '#')
            datas=regexp(line, '(\s+)','split');
            data=zeros(1,length(datas)-1);
            id = str2num(datas{1});
            for i=2:length(datas)
                if i>nargin
                    data(i-1) = str2num(datas{i});
                else
                    data(i-1) = varargin{i-1}(datas{i});
                end
            end
            eventLibrary.insert(id, data);

            line=fgetl(fid);
        end
    end

    function shapeLibrary = readShapes(fid, forceConvertUncompressed)
        %readShapes Read the [SHAPES] section of a sequence file.
        %   library=readShapes(fid) Read shapes from file identifier of an
        %   open MR sequence file and return a library of shapes.

        shapeLibrary = mr.EventLibrary();
        line = skipComments(fid);
        while ~(~ischar(line) || isempty(line) || ~strcmp(line(1:8), 'shape_id'))
            tok = textscan(line, '%s');
            id = str2double(tok{1}(2));
            line = skipComments(fid);
            tok = textscan(line, '%s');
            num_samples = str2double(tok{1}(2));
            data = [];
            line = skipComments(fid);   % first sample
            while ischar(line) && ~(isempty(line) || line(1) == '#')
                data = [data sscanf(line, '%f')];
                %data = [data single(sscanf(line, '%f'))]; % C-code uses single precision and we had problems already due to the rounding during reading in of the shapes...
                line = fgetl(fid);
            end

            line = skipComments(fid, true); % MZ: second parameter to prevent readShapes from reading into the next section (long-standing bug)

            % check if conversion is needed: in v1.4.x we use length(data)==num_samples
            % as a marker for the uncompressed (stored) data. In older versions this condition could occur by chance
            if forceConvertUncompressed && length(data)==num_samples
                shape.data=data;
                shape.num_samples=num_samples;
                shape = mr.compressShape(mr.decompressShape(shape,true));
                data = [shape.num_samples shape.data];
            else
                data = [num_samples data];
            end
            shapeLibrary.insert(id, data);
        end
    end

    function nextLine = skipComments(fid, stopBeforeSection)
        %skipComments Read lines of skipping blank lines and comments.
        %   line=skipComments(fid) Read lines from valid file identifer and
        %   return the next non-comment line.

        if (nargin<2)
            stopBeforeSection=false;
        end

        tmpPos=ftell(fid);
        line = fgetl(fid);
        while ischar(line) && (isempty(line) || line(1) == '#')
            tmpPos=ftell(fid);
            line = fgetl(fid);
        end
        if ischar(line)
            if stopBeforeSection && line(1)=='['
                fseek(fid,tmpPos,-1); % restore the file position
                nextLine = ''; % feasible (non-error) dummy return
            else
                nextLine = line;
            end
        else
            nextLine = -1;
        end
    end
end


function [Rob, Frm] = rob2frm(Rob, Frm)

% FRM2ROB Robot to Frame information transfer
%   [Rob, Frm] = ROB2FRM(Rob, Frm) Updates the frame Frm from information
%   in Rob.
%
%   See also FRM2ROB.

%   Copyright 2015 Joan Sola @ IRI-CSIC-UPC

% Set 'used'
Frm.used = true;

% State
Frm.state.x = Rob.state.x;

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function title = p21_title ( )

%*****************************************************************************80
%
%% P21_TITLE returns the title of problem P21.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    17 February 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, string TITLE, the title of the problem.
%
  title = 'Problem 21, Enright and Pryce #E1';

  return
end


%% An example of MATLAB GUI to control KUKA iiwa manipualtor using KST toolbox

%% To run the program:
% 1- Start the MatlabToolboxServer application from the teach pendant of
% the robot
% 2- Run the following script

% Copyright: Mohammad SAFEEA

% Add KST path to MATLAB
cDir = pwd;
cDir=getTheKSTDirectory(cDir);
addpath(cDir);

% Run the interface
a0_interface


function g = uniformPriorGradient(prior, x)

% UNIFORMPRIORGRADIENT Gradient wrt x of the uniform prior.

% PRIOR

% Compute gradient of prior
g = 0 * x;


function nmt_sourceoriplot(cfg)
% NMT_SOURCEORIPLOT
% plots functional source orientation data on slices or on
% a surface, optionally as an overlay on anatomical MRI data, where
% statistical data can be used to determine the opacity of the mask. Input
% data comes from FT_SOURCEANALYSIS.
%
% Use as
%   ft_sourceoriplot(cfg, data)
%
% No cfg options are implemented yet.
%
% Requires spm_ov_quivernmt.m to be installed as SPM plugin in
% spm_orthviews subdirectory

global st

% check that spm_ov_quivernmt has been installed in the SPM plug-in directory
if(~exist('spm_orthviews/spm_ov_quivernmt.m','file'))
    error(['Please move or link ' which('spm_ov_quivernmt') ' to: ' fileparts(which('spm_ov_reorient.m'))])
end

if(size(st.nmt.ori,3) > 1)
    ori = st.nmt.ori(:,:,st.nmt.cfg.time_idx(1));
else
    ori = st.nmt.ori;
end
ori = ori ./ sqrt(sum(ori.^2,2)); % normalize

fname{1} = fullfile(tempdir,'oriX.nii');
fname{2} = fullfile(tempdir,'oriY.nii');
fname{3} = fullfile(tempdir,'oriZ.nii');
fname{4} = fullfile(tempdir,'orimask.nii');

for ii=1:3
    oriwrapped{ii} = st.vols{1}.blobs{1}.vol;
    oriwrapped{ii}(st.nmt.cfg.inside_idx) = ori(:,ii);
end
oriwrapped{4} = double(isfinite(oriwrapped{1}));

for ii=1:4
    vol.fname = fname{ii};
    vol.dim = size(st.vols{1}.blobs{1}.vol);
    vol.mat = st.vols{1}.blobs{1}.mat;
    vol.dt = [16 0];
    spm_write_vol(vol,oriwrapped{ii});
end

%%
for ii=1:3
    orivol{ii}=spm_vol(fname{ii});
end
orimaskvol=spm_vol(fname{4});
spm_orthviews('quivernmt','delete',1);
spm_orthviews('quivernmt','init',1,orivol,orimaskvol)
spm_orthviews('redraw')


function imdb = getImdbDCFNet(varargin)
opts = [];
opts.dataDir = fullfile('..','data');

opts.visualization = false;
opts.output_size = 125;
opts.padding = 1.5;
opts = vl_argparse(opts, varargin);

bbox_mode = 'axis_aligned';
if strcmp(bbox_mode,'axis_aligned')
    get_bbox = @get_axis_aligned_BB;
elseif strcmp(bbox_mode,'minmax')
    get_bbox = @get_minmax_BB;
end
% -------------------------------------------------------------------------
%   full dataset:
%           vid2015: 464873
% -------------------------------------------------------------------------
set_name = {'vid2015'};
num_all_frame = 464873;
%% Be careful!!! It takes a HUGE RAM for fast speed!
imdb.images.set = int8(ones(1, num_all_frame));
imdb.images.set(randperm(num_all_frame, 1000)) = int8(2);
imdb.images.images = zeros(opts.output_size, opts.output_size, 3, num_all_frame, 'uint8');
imdb.images.up_index = zeros(1, num_all_frame, 'double'); % The farthest frame can it touch
now_index = 0;

% -------------------------------------------------------------------------
%                                                                   VID2015
% -------------------------------------------------------------------------
if any(strcmpi(set_name, 'vid2015'))
    disp('VID2015 Data:');
    if exist('vid_2015_seg.mat', 'file')
        load('vid_2015_seg.mat');%% use dataPreprocessing;
    else
        error('You should generate <vid_2015_seg.mat> according ''dataPreprocessing'' at first.')
    end
    videos = seg;
    n_videos = numel(videos);
    for  v = 1:n_videos
        video = videos{v};fprintf('%3d / %3d \n', v, n_videos);

        img_files = video.path;

        ground_truth = cell2mat((video.rect)');
        ground_truth_4xy = [ground_truth(:,1),ground_truth(:,2),...
            ground_truth(:,1),ground_truth(:,2)+ground_truth(:,4),...
            ground_truth(:,1)+ground_truth(:,3),ground_truth(:,2)+ground_truth(:,4),...
            ground_truth(:,1)+ground_truth(:,3),ground_truth(:,2)];
        invaildindex = any(isnan(ground_truth), 2);
        img_files(invaildindex) = [];
        ground_truth_4xy(invaildindex,:) = [];

        im_frist = vl_imreadjpeg(img_files(1));
        [H, W, ~] = size(im_frist{1});
        im_bank = vl_imreadjpeg(img_files, 'Pack', 'Resize', [H, W], 'numThreads', 32);
        bbox_gt = get_bbox(ground_truth_4xy);
        n_frames = size(bbox_gt,1);
        imdb.images.images(:,:,:,now_index+(1:n_frames)) = uint8(...
            imcrop_pad(im_bank{1}, bbox_gt, opts.padding, opts.output_size([1,1])));
        imdb.images.up_index(now_index+(1:n_frames)) = (n_frames:-1:1)-1;
        imdb.images.set(now_index+n_frames) = 4; %should not be selected as x.
        now_index = now_index + n_frames;
    end %%end v
end %%end VID2017

% dataMean = single(mean(imdb.images.images,4));
dataMean(1,1,1:3) = single([123,117,104]);
imdb.images.data_mean(1, 1, 1:3) = dataMean;
imdb.meta.sets = {'train', 'val'} ;
end %%end function


function disp(TC)

%DISP Display threecomp object
%   DISP(TC)

% Author: Michael West, Geophysical Institute, Univ. of Alaska Fairbanks
% $Date$
% $Revision$

% TEST FOR NON-EMPTY TRACE DATA

if numel(TC)>1
    [n,m] = size(TC); % char returns a cell array
    disp([num2str(n) ' x ' num2str(m) ' threecomp object']);
end

if numel(TC)==1
    if any(isempty(TC.traces))
        disp('**Empty waveforms. Use VERIFY function');
    else
        net = get(TC.traces(1),'NETWORK');
        sta = get(TC.traces(1),'STATION');
        chan1 = get(TC.traces(1),'CHANNEL');
        chan2 = get(TC.traces(2),'CHANNEL');
        chan3 = get(TC.traces(3),'CHANNEL');
        chan = [chan1(1:end-1) '[' chan1(end) chan2(end) chan3(end) ']'];
        loc = get(TC.traces(1),'LOCATION');
        disp(['    net_sta_chan_loc:    ' net '_' sta '_' chan '_' loc]);

        disp(['    duration:            ' get(TC.traces(1),'DURATION_STR') ]);

        disp(['    sample rate:         ' num2str(get(TC.traces(1),'FREQ')) ' Hz']);

        disp(['    type:                ' char(get(TC,'TYPE')) ]);

        disp(['    trigger:             ' datestr(get(TC,'TRIGGER'),'yyyy/mm/dd HH:MM:SS.FFF') ]);

        if isempty(TC.backAzimuth)
            disp(['    backAzimuth:         ' 'property not set' ]);
        else
            disp(['    backAzimuth:         ' num2str(TC.backAzimuth) ]);
        end

        if isempty(TC.orientation)
            disp(['    orientation:         ' 'property not set' ]);
        else
            disp(['    orientation(' chan1(3) '):      ' num2str(TC.orientation(1)) '(h) '  num2str(TC.orientation(2)) '(v)' ]);
            disp(['    orientation(' chan2(3) '):      ' num2str(TC.orientation(3)) '(h) '  num2str(TC.orientation(4)) '(v)' ]);
            disp(['    orientation(' chan3(3) '):      ' num2str(TC.orientation(5)) '(h) '  num2str(TC.orientation(6)) '(v)' ]);

        end

        if isempty(TC.rectilinearity)
            disp(['    rectilinearity:      property not set' ]);
        else
            disp(['    rectilinearity:      ' num2str(get(TC.rectilinearity,'FREQ')) ' Hz, ' num2str(get(TC.rectilinearity,'THREECOMP_WINDOW')) ' sec window']);
        end

        if isempty(TC.planarity)
            disp(['    planarity:           property not set' ]);
        else
            disp(['    planarity:           ' num2str(get(TC.rectilinearity,'FREQ')) ' Hz, ' num2str(get(TC.rectilinearity,'THREECOMP_WINDOW')) ' sec window']);
        end

        if isempty(TC.energy)
            disp(['    energy:              property not set' ]);
        else
            disp(['    energy:              ' num2str(get(TC.rectilinearity,'FREQ')) ' Hz, ' num2str(get(TC.rectilinearity,'THREECOMP_WINDOW')) ' sec window']);
        end

        if isempty(TC.azimuth)
            disp(['    azimuth:             property not set' ]);
        else
            disp(['    azimuth:             ' num2str(get(TC.rectilinearity,'FREQ')) ' Hz, ' num2str(get(TC.rectilinearity,'THREECOMP_WINDOW')) ' sec window']);
        end

        if isempty(TC.inclination)
            disp(['    inclination:         property not set' ]);
        else
            disp(['    inclination:         ' num2str(get(TC.rectilinearity,'FREQ')) ' Hz, ' num2str(get(TC.rectilinearity,'THREECOMP_WINDOW')) ' sec window']);
        end
    end
end


function C=vec(M);

C=M(:);


function createTreeView(handles, FT_Width, FT_Height)
%
% 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/>.

%     Para.Rigid_Single_Modality = 'MS';

    Para.Rigid = 'MI';

    Para.Non_Rigid.OpticalFlow = 'OpticalFlow';
    Para.Non_Rigid.Demons3D = 'Demons3D';
%     Para.Non_Rigid.SymmetricDemons3D = 'SymmetricDemons';
%     Para.Non_Rigid.BSplineMethod = 'BSplineMethod';
%     Para.Non_Rigid.LevelSetMethod = 'LevelSetMethod';

    expFcn = createParaTree(Para, handles, [0, 1-FT_Height, FT_Width, FT_Height]);
    handles = guidata(handles.mainframe);
    set(handles.FTreeView, 'Visible', true);

    guidata(handles.mainframe, handles);

end


function kern = kernExpandParamTransformSettings(kern, paramtransformsettings)

% KERNEXPANDPARAMTRANSFORMSETTINGS Expand parameters' transform settings to form a kernel structure.
%
% FORMAT
%
% DESC returns a kernel structure filled with the parameter
% transform settings in the given cell array. This is used as a
% helper function to enable parameters to be optimised in, for
% example, the NETLAB optimisation functions.
%
% ARG kern : the kernel structure in which the parameter
% transformation settings are to be placed.
%
% ARG paramtransformsettings : cell array (nParams x 1) of
% parameter transform settings which are to be placed in the
% kernel structure. Each transformation setting can be of any
% form, depending on what information the corresponding
% transformation needs. For example, a transformation setting
% might be a desired output range like [0 100], or it could
% be some more complicated structure.
%
% RETURN kern : kernel structure with the given parameter
% transform settings in the relevant locations.
%
% SEEALSO : kernExtractParam, scg, conjgrad
%
% COPYRIGHT : Neil D. Lawrence, 2003, 2004, 2005, 2006
%
% COPYRIGHT : Jaakko Peltonen, 2011
%
% COPYRIGHT : Antti Honkela, 2011

% KERN

%fprintf(1,'kernExpandParamTransformSettings step1\n');

% If no transformation settings are provided, do nothing, otherwise
% simply call the kernel-specific function.
if ~isempty(paramtransformsettings),
  % Get the handle of the kernel-specific function and call it
  fhandle = str2func([kern.type 'KernExpandParamTransformSettings']);
  kern = fhandle(kern, paramtransformsettings);
end;

if isfield(kern, 'priors')
  if iscell(kern.priors)
    for k=1:length(kern.priors),
      if isfield(kern.priors{k}, 'isBounded') && ...
	    kern.priors{k}.isBounded,
	l = kern.priors{k}.index;
	if length(l) > 1,
	  error('Unable to set bounds to a prior shared by multiple params')
	end
	kern.priors{k} = priorSetBounds(kern.priors{k}, paramtransformsettings{l});
      end
    end
  else
    for k=1:length(kern.priors),
      if isfield(kern.priors(k), 'isBounded') && ...
	    kern.priors(k).isBounded,
	l = kern.priors(k).index;
	if length(l) > 1,
	  error('Unable to set bounds to a prior shared by multiple params')
	end
	kern.priors(k) = priorSetBounds(kern.priors(k), paramtransformsettings{l});
      end
    end
  end
end

%fprintf(1,'kernExpandParamTransformSettings done\n');


%% void show_4dvideo(4dmatrix)
%
function [] = show_4dvideo(video)
  for i = 1 : size(video,4)
    frame = video(:,:,:,i);
    imshow(frame,[],'InitialMagnification','fit');
    disp(i);
    pause(0.01);
  end
end


function disp(obj)
% Disp a NIFTI-1 object
% _______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

%
% $Id: disp.m 1143 2008-02-07 19:33:33Z spm $

sz = size(obj);
fprintf('NIFTI object: ');
if length(sz)>4,
    fprintf('%d-D\n',length(sz));
else
    for i=1:(length(sz)-1),
        fprintf('%d-by-',sz(i));
    end;
    fprintf('%d\n',sz(end));
end;
if prod(sz)==1,
    display(structn(obj))
end;
return;


function [lp,dlp] = priorDeltaMulti(x)

% Dummy hyperparameter prior distribution to fix the value of a hyperparameter.
% The function is not intended to be evaluated but exists merely to make
% the user aware of the possibility to use it. The function is equivalent
% to priorClampedMulti.
%
% For more help on design of priors, try "help priorDistributions".
%
% Copyright (c) by Roman Garnett and Hannes Nickisch, 2014-12-06.
%
% See also PRIORDISTRIBUTIONS.M.

error('The function is not intended to be called directly.')


function X = matrixReadFromFID(FID, varargin)

% MATRIXREADFROMFID Read a matrix from an FID.
% FORMAT
% DESC reads a matrix from an FID.
% ARG FID: the file ID to read the matrix from.
% RETURN X : the returned matrix read from the file.
%
% COPYRIGHT : Neil D. Lawrence, 2008
%
% SEEALSO : modelReadFromFID, doublematrixReadFromFID

% MLTOOLS

modelType = readStringFromFID(FID, 'type');
feval = str2func([modelType 'ReadFromFID']);
X = feval(FID, varargin{:});


function test_suite = test_monotonic2

%   Run specific demo, save values and compare the results to the expected.
%   Works for both xUnit Test Framework package by Steve Eddins and for
%   the built-in Unit Testing Framework (as of Matlab version 2013b).
%
%   See also
%     TEST_ALL, DEMO_MONOTONIC2
%
% Copyright (c) 2011-2012 Ville Tolvanen
% Copyright (c) 2014 Tuomas Sivula

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

  % Check which package to use
  if exist('initTestSuite', 'file')
    initTestSuite_path = which('initTestSuite');
  else
    initTestSuite_path = '';
  end
  if ~isempty(initTestSuite_path) && ...
     exist([initTestSuite_path(1:end-15) 'runtests'], 'file')
    % xUnit package
    initTestSuite;
  else
    % Built-in package
    % Use all functions except the @setup
    tests = localfunctions;
    tests = tests(~cellfun(@(x)strcmp(func2str(x),'setup'),tests));
    test_suite = functiontests(tests);
  end
end

% -------------
%     Tests
% -------------

function testRunDemo(testCase)
  % Run the correspondin demo and save the values. Note this test has to
  % be run at lest once before the other test may succeed.
  rundemo(getName())
end

function testPredictiveMeanAndVariance_normal(testCase)
  verifyVarsEqual(testCase, getName(), {'Eft','Varft'}, ...
    'RelTolElement', 0.05, 'RelTolRange', 0.01)
end

function testPredictiveMeanAndVariance_monotonic(testCase)
  verifyVarsEqual(testCase, getName(), {'Eftm','Varftm'}, ...
    'RelTolElement', 0.05, 'RelTolRange', 0.01)
end

% ------------------------
%     Helper functions
% ------------------------

function testCase = setup
  % Helper function to suply empty array into variable testCase as an
  % argument for each test function, if using xUnit package. Not to be
  % used with built-in test framework.
  testCase = [];
end

function name = getName
  % Helperfunction that returns the name of the demo, e.g. 'binomial1'.
  name = mfilename;
  name = name(6:end);
end


% [succeeded,aviname] = make_ctrax_result_movie('param',value,...)
% Options:
% 'moviename': name of raw movie to annotate
% 'trxname': name of mat file containing trajectories, or struct of
% trajectories
% 'aviname': name of movie to output to
% 'colors': colors to plot each fly
% 'nzoomr': number of rows of zoom fly boxes
% 'nzoomc': number of columns of zoom fly boxes
% 'boxradius': radius of the zoom fly box in pixels
% 'taillength': number of frames of trajectory to plot behind each fly
% 'zoomflies': flies to zoom in on
% 'fps': frames per second of output movie
% 'maxnframes': number of frames to output
% 'firstframe': first frame to output
% 'compression': compressor to use when outputting (pc only, will be set to
% 'none' for linux).
% 'figpos': position of figure
% if any parameters are not given, the user will be prompted for these
function [succeeded,aviname,figpos,height,width] = make_jaaba_result_movie_plotallflies(expdir,varargin)

succeeded = false;
defaults.boxradius = 1.5;
defaults.taillength = 30;
defaults.fps = 20;
defaults.zoomflies = [];
defaults.nzoomr = 5;
defaults.nzoomc = 3;
defaults.compression = 'None';
allowedcompressions = {'Indeo3', 'Indeo5', 'Cinepak', 'MSVC', 'RLE', 'None','Uncompressed AVI','Motion JPEG AVI'};
useVideoWriter = exist('VideoWriter','file');
mencoderoptions = '';
mencoder_maxnframes = inf;

% hand-chosen colors
% behavior2color = {'WingExtension',[.4392,.1882,.6275]
%   'Stop',[0,0,.5156]
%   'Copulation',[0,0,.875]
%   'AttemptCopulation',[0,.2344,1]
%   'Backup',[0,.9688,1]
%   'Crabwalk',[.3281,1,.6719]
%   'PivotHead',[.6875,1,.3125]
%   'Walk',[0,.75,0]
%   'PivotTail',[.9,.9,0]
%   'Jump',[1,.5938,0]
%   'Righting',[1,.2188,0]
%   'Chase',[.8594,0,0]
%   'Touch',[.5,0,0]
%   'WingGrooming',[.6353,.1804,.4510]};

% behavior2color = {'WingGrooming',[1,.4,.755]
%   'Stop',[0,0,.5156]
%   'WingExtension',[0,0,.875]
%   'AttemptCopulation',[0,.2344,1]
%   'Copulation',[0,.9688,1]
%   'Righting',[.3281,1,.6719]
%   'Backup',[0,.75,0]
%   'Crabwalk',[.9,.9,0]
%   'PivotHead',[1,.75,0]
%   'PivotTail',[1,.5938,0]
%   'Touch',[1,.2188,0]
%   'Chase',[.8594,0,0]
%   'Walk',[.5,0,0]
%   'Jump',[.6353,.1804,.4510]};

%  'Crabwalk',[0,.6094,1]

[moviefilestr,trxfilestr,aviname,colors,behavior_cm,...
  zoomflies,nzoomr,nzoomc,boxradius,...
  taillength,fps,maxnframes,firstframes,compression,figpos,movietitle,...
  useVideoWriter,mencoderoptions,mencoder_maxnframes,...
  avifileTempDataFile,titletext,dynamicflyselection,...
  doshowsex,doplotwings,...
  classifierparamsfiles,...
  behaviors,...
  scorefns,...
  score_norm_prctile,...
  behaviorweight,...
  printnone,...
  DEBUG,...
  animaltype,...
  behavior2color] = ...
  myparse(varargin,'moviefilestr','movie.ufmf','trxfilestr','registered_trx.mat',...
  'aviname','','colors',[],'behavior_cm',@jet,...
  'zoomflies',[],'nzoomr',defaults.nzoomr,'nzoomc',defaults.nzoomc,...
  'boxradius',nan,'taillength',defaults.taillength,'fps',defaults.fps,...
  'maxnframes',inf,'firstframes',1,'compression',defaults.compression,...
  'figpos',[50,50,1500,1000],'movietitle','','useVideoWriter',useVideoWriter,...
  'mencoderoptions',mencoderoptions,'mencoder_maxnframes',mencoder_maxnframes,...
  'avifileTempDataFile','',...
  'titletext',true,...
  'dynamicflyselection',true,...
  'doshowsex',true,...
  'doplotwings',true,...
  'classifierparamsfiles',{},...
  'behaviors',{},...
  'scorefns',{},...
  'score_norm_prctile',[],...
  'behaviorweight',[],...
  'printnone',false,...
  'debug',false,...
  'animaltype','fly',...
  'behavior2color',{});

if ~ischar(compression),
  compression = '';
end
if ~isempty(compression) && ~any(strcmpi(compression,allowedcompressions)),
  fprintf('Unknown compressor %s\n',compression);
  compression = '';
end

if isempty(behaviors) || isempty(scorefns),
  classifierparams = ReadClassifierParamsFile(classifierparamsfiles,'isrelativepath',true);
  for j = 1:numel(classifierparams),
    if iscell(classifierparams(j).behaviors.names),
      behaviors{end+1} = sprintf('%s_',classifierparams(j).behaviors.names{:}); %#ok<AGROW>
      behaviors{end} = behaviors{end}(1:end-1);
    else
      behaviors{end+1} = classifierparams(j).behaviors.names; %#ok<AGROW>
    end
    behaviors{end} = regexprep(behaviors{end},'(_|^)([a-z])','${upper($2)}');
    scorefns{end+1} = classifierparams(j).file.scorefilename; %#ok<AGROW>
  end
end

nbehaviors = numel(behaviors);

if isempty(behavior2color),
  behavior2color = [behaviors(:),mat2cell(jet(nbehaviors)*.9,ones(nbehaviors,1),3)];
end

idxremove = ~ismember(lower(behaviors),lower(behavior2color(:,1)));
if any(idxremove),
  fprintf('Ignoring the following behaviors:\n');
  fprintf('  %s\n',behaviors{idxremove});
  behaviors(idxremove) = [];
  scorefns(idxremove) = [];
end

idxremove = ~ismember(lower(behavior2color(:,1)),lower(behaviors));
if any(idxremove),
  fprintf('Ignoring the following behavior colors:\n');
  fprintf('  %s\n',behavior2color{idxremove,1});
  behavior2color(idxremove,:) = [];
end

nbehaviors = numel(behaviors);

%% open files

trxname = fullfile(expdir,trxfilestr);
[trx,trxname,loadsucceeded,timestamps] = load_tracks(trxname);
if ~loadsucceeded,
  error('Could not load trx from file %s',trxname);
end

if ~isempty(moviefilestr)
  moviename = fullfile(expdir,moviefilestr);
  [readframe,nframes,fid] = get_readframe_fcn(moviename);
  im = readframe(1);
  [nr,nc,ncolors] = size(im);
  showgrid = false;
else
  maxx = max([trx.x]+[trx.a]*2);
  maxy = max([trx.y]+[trx.a]*2);
  nr = ceil(maxy);
  nc = ceil(maxx);
  ncolors = 1;
  nframes = max([trx.endframe]);
  readframe = @(x)(uint8(256*ones(nr,nc)));
  fid = 0;
  showgrid = true;
  % grid width
  gridwidth = nanmean([trx.a])*5;

  % create new grid
  handles.guidata.bkgdgrid = nan(2,1);
  xgrid = gridwidth/2:gridwidth:nc;
  xgrid1 = [xgrid;xgrid;nan(1,numel(xgrid))];
  xgrid2 = [zeros(1,numel(xgrid));nr+ones(1,numel(xgrid));nan(1,numel(xgrid))];
  ygrid = gridwidth/2:gridwidth:nr;
  ygrid2 = [ygrid;ygrid;nan(1,numel(ygrid))];
  ygrid1 = [zeros(1,numel(ygrid));nc+ones(1,numel(ygrid));nan(1,numel(ygrid))];
%   for i = 1:numel(handles.guidata.axes_previews),
%     holdstate = ishold(handles.guidata.axes_previews(i));
%     hold(handles.guidata.axes_previews(i),'on');
%     handles.guidata.bkgdgrid(1,i) = plot(handles.guidata.axes_previews(i),xgrid1(:),xgrid2(:),'--','Color',[.7,.7,.7],'LineWidth',.5,'HitTest','off');
%     handles.guidata.bkgdgrid(2,i) = plot(handles.guidata.axes_previews(i),ygrid1(:),ygrid2(:),'--','Color',[.7,.7,.7],'LineWidth',.5,'HitTest','off');
%     if ~holdstate,
%       hold(handles.guidata.axes_previews(i),'off');
%     end
%
%   end

end

nids = length(trx);
trxendframes = [trx.endframe];
trxfirstframes = [trx.firstframe];
T0 = min(trxfirstframes);
T1 = max(trxendframes);
endframes = min(T1,firstframes+maxnframes-1);

scores = cell(1,nids);
score_norms = nan(1,nbehaviors);
for j = 1:nids,
  scores{j} = nan(nbehaviors,trx(j).nframes);
end
isscore = false(1,nbehaviors);
for i = 1:nbehaviors,
  scorefilename = fullfile(expdir,scorefns{i});
  if ~exist(scorefilename,'file'),
    warning('Scores file %s does not exist, skipping',scorefilename);
    continue;
  end
  isscore(i) = true;
  tmp = load(scorefilename);
  score_norms(i) = tmp.allScores.scoreNorm;
  for j = 1:nids,
    scores{j}(i,:) = tmp.allScores.scores{j}(trxfirstframes(j):trxendframes(j));
  end
end
for j = 1:nids,
  scores{j} = scores{j}(isscore,:);
end
behaviors = behaviors(isscore);
scorefns = scorefns(isscore);
nbehaviors = numel(behaviors);

%% output avi name
haveaviname = false;
if ischar(aviname) && ~isempty(aviname)
  [~,ext] = splitext(aviname);
  if strcmpi(ext,'.avi'),
    haveaviname = true;
  end
end
if ~haveaviname,
  fprintf('Choose avi file to output annotated version of %s\n',moviename);
  [base,~] = splitext(movienameonly);
  aviname = [moviepath,'ctraxresults_',base,'.avi'];
  helpmsg = {};
  helpmsg{1} = 'Choose avi file to write annotated movie to.';
  helpmsg{2} = sprintf('Raw movie input: %s',moviename);
  helpmsg{3} = sprintf('Trx file name input: %s',trxname);
  [avinameonly,avipath] = uiputfilehelp('*.mat',sprintf('Choose output avi for %s',movienameonly),aviname,'helpmsg',helpmsg);
  if ~ischar(avinameonly),
    return;
  end
  aviname = [avipath,avinameonly];
end

%% more parameter parsing

doshowsex = doshowsex && isfield(trx,'sex') && ~any(cellfun(@isempty,{trx.sex}));
if doshowsex,
  sexes = {};
  for i = 1:numel(trx),
    sexes = union(sexes,unique(trx(i).sex));
  end
  sexes = upper(sexes);
  sexes = sort(sexes);
  sexmarkers = {'none','*','x','o','+','d','s','p','h'};
  if numel(sexes) > numel(sexmarkers),
    sexmarkers = repmat(sexmarkers,[1,ceil(numel(sexes)/numel(sexmarkers))]);
  end
  sexmarkers = sexmarkers(1:numel(sexes));
end

doplotwings = doplotwings && all(isfield(trx,{'xwingl','ywingl','xwingr','ywingr'}));

if isnan(boxradius),
  meana = 4*nanmean([trx.a]);
  boxradius = ceil(defaults.boxradius*meana);
end

%% which flies to zoom in on

if ~isempty(zoomflies),
  nzoom = numel(zoomflies);
  if max(zoomflies) > nids || min(zoomflies) < 1,
    error('Illegal values for zoomflies');
  end
  nzoomc = ceil(nzoom/nzoomr);
else
  nzoom = nzoomr*nzoomc;
end

% choose some random flies to zoom in on
nframesoverlap = zeros(1,numel(trx));
for i = 1:numel(firstframes),
  nframesoverlap = nframesoverlap + ...
      max(0,min(endframes(i),trxendframes)-max(firstframes(i),trxfirstframes) + 1);
end
fliesmaybeplot = find(nframesoverlap > 0);

if isempty(zoomflies),
  if numel(fliesmaybeplot) < nzoom,
    zoomflies = [fliesmaybeplot,nan(1,nzoom-length(fliesmaybeplot))];
    fprintf('Not enough flies to plot\n');
  else

    if dynamicflyselection,

      % choose flies to plot in each frame
      fliesplotperframe = nan(nzoom,nframes);

      % flies currently chosen
      fliesplotcurr = nan(nzoom,1);
      allfliesplotted = [];

      % loop through all frames

      for i = 1:numel(firstframes),
        for f = firstframes(i):endframes(i),

          % flies that are currently alive
          isalive = trxfirstframes <= f & trxendframes >= f;
          fliesalive = find(isalive);

          % remove newly dead flies
          openzoomboxes = isnan(fliesplotcurr) | ~ismember(fliesplotcurr,fliesalive);
          fliesplotcurr(openzoomboxes) = nan;

          % how many flies do we need to choose
          nflieschoose = nnz(openzoomboxes);

          % flies we can choose to add
          fliesleft = setdiff(fliesalive,fliesplotcurr);

          nfliesshort = max(0,nflieschoose-numel(fliesleft));
          if nfliesshort > 0,
            newfliesplot = fliesleft;
          else
            [~,order] = sort(-[trx(fliesleft).endframe]);
            newfliesplot = fliesleft(order(1:nflieschoose));
          end
          allfliesplotted = [allfliesplotted,newfliesplot]; %#ok<AGROW>
          fliesplotcurr(openzoomboxes) = [newfliesplot,nan(1,nfliesshort)];
          fliesplotperframe(:,f) = fliesplotcurr;

        end
      end

      zoomflies = fliesplotperframe(:,firstframes(1));

    else
      fliesmaybeplot = fliesmaybeplot(randperm(length(fliesmaybeplot)));
      [~,flieswithmostframes] = sort(-nframesoverlap(fliesmaybeplot));
      zoomflies = sort(fliesmaybeplot(flieswithmostframes(1:nzoom)));
    end
  end
elseif nzoom > length(zoomflies),
  zoomflies = [zoomflies,nan(1,nzoom-length(zoomflies))];
end
if nzoomr>0
  zoomflies = reshape(zoomflies,[nzoomr,nzoomc]);
end
rowszoom = floor(nr/nzoomr);

%% choose colors for each behavior

nadd = nbehaviors - size(colors,1);
colors = cat(1,colors,nan(nadd,3));
[ism,idx] = ismember(lower(behaviors),lower(behavior2color(:,1)));
ism = ism & any(isnan(colors),2)';
if any(ism),
  colors(ism,:) = cat(1,behavior2color{idx(ism),2});
end

ism = any(isnan(colors),2)';
if any(ism),
  tmp = behavior_cm(256);
  d = inf(256,1);
  for i = find(~ism),
    d = min(d,sum(abs(bsxfun(@minus,tmp,colors(i,:))),2));
  end
  for i = find(ism),
    [~,j] = max(d);
    colors(i,:) = tmp(j,:);
    d = min(d,sum(abs(bsxfun(@minus,tmp,colors(i,:))),2));
  end
end

%% get the score normalizations

if ~isempty(score_norm_prctile),
  %   score_norms = nan(nbehaviors,1);
  for i = 1:nbehaviors,
    tmp = [];
    for j = 1:nids,
      tmp = [tmp,scores{j}(i,:)]; %#ok<AGROW>
    end
    score_norms(i) = prctile(abs(tmp),score_norm_prctile);
  end
end

for fly = 1:nids,
  for behaviori = 1:nbehaviors,
    scores{fly}(behaviori,:) = min(1,max(-1,scores{fly}(behaviori,:)/score_norms(behaviori)));
  end
end

%% choose weights for each behavior: upweight if less common

if isempty(behaviorweight),
  behaviorweight = nan(nbehaviors,1);
  for behaviori = 1:nbehaviors,
    tmp = [];
    for j = 1:nids,
      tmp = [tmp,scores{j}(behaviori,:)]; %#ok<AGROW>
    end
    behaviorweight(behaviori) = nnz(tmp<=0);
  end
  behaviorweight = behaviorweight / sum(behaviorweight);
end
if numel(behaviorweight) == 1,
  behaviorweight = ones(nbehaviors,1);
end
behaviorweight = behaviorweight(:);

%% initialize plots

figure(1);
clf;
hold on;
hax = gca;
set(hax,'position',[0,0,1,1]);
axis off;
isdisplay = ispc || ~strcmpi(get(1,'XDisplay'),'nodisplay');

% corners of zoom boxes in plotted image coords
x0 = nc+(0:nzoomc-1)*rowszoom+1;
y0 = (0:nzoomr-1)*rowszoom+1;
x1 = x0 + rowszoom - 1;
y1 = y0 + rowszoom - 1;

% relative frame offset
nframesoff = trxfirstframes - 1;

% pre-allocate
himzoom = zeros(nzoomr,nzoomc);
htail = zeros(2*taillength+1,nids);
htri = zeros(1,nids);
hwing = zeros(1,nids);
hsexmarker = zeros(1,nids);
hsexmarker_zoom = zeros(1,nids);
scalefactor = rowszoom / (2*boxradius+1);
hzoom = zeros(nzoomr,nzoomc);
hzoomwing = zeros(nzoomr,nzoomc);
htextzoom = zeros(nzoomr,nzoomc);
hzoombox = zeros(nzoomr,nzoomc);
if strcmpi(animaltype,'larvacontour')
  hextra = zeros(nids,2);
  hextrazoom = zeros(nzoomr,nzoomc,2);
end

% initialize plots
him = image([1,nc],[1,nr],zeros([nr,nc,3]));
axis image;
axis tight;

if nzoom >0
  axis([.5,x1(end)+.5,.5,y1(end)+.5]);
else
  axis([.5,nc+.5,.5,nr+.5]);
end
axis off;

if titletext && isdisplay,
  htext = text(.5,.5,'frame info','Parent',hax,'BackgroundColor','k','Color','g','VerticalAlignment','bottom','interpreter','none');
end

for i = 1:nzoomr,
  for j = 1:nzoomc,
    himzoom(i,j) = image([x0(j),x1(j)],[y0(i),y1(i)],repmat(uint8(123),[boxradius*2+1,boxradius*2+1,3]));
  end
end

for fly = 1:nids,
  htail(:,fly) = plot(nan(2,2*taillength+1),nan(2,2*taillength+1),'k-');
  if doshowsex,
    hsexmarker(fly) = plot(nan,nan,'k.');
  end
  htri(fly) = plot(nan,nan,'k-');
  if doplotwings,
    hwing(fly) = plot(nan,nan,'k.-');
  end
  if strcmpi(animaltype,'larvacontour')
    hextra(fly,1) = plot(nan,nan,'Marker','.','LineStyle','-');
    hextra(fly,2) = plot(nan,nan,'Marker','o','LineStyle','None');
  end

end

for i = 1:nzoomr,
  for j = 1:nzoomc,
    hzoom(i,j) = plot(nan,nan,'k-');
    hzoomwing(i,j) = plot(nan,nan,'k.-');
    xtmp = x0(j) + [0,1]*(x1(j)-x0(j)) + [1,-1];
    ytmp = y0(i) + [0,1]*(y1(i)-y0(i)) + [1,-1];
    hzoombox(i,j) = plot(xtmp([1,2,2,1,1]),ytmp([1,1,2,2,1]),'k-','LineWidth',2,...
      'Visible','off');
    htextzoom(i,j) = text((x0(j)+x1(j))/2,.95*y0(i)+.05*y1(i),'',...
      'color','k','horizontalalignment','center',...
      'verticalalignment','bottom','fontweight','bold',...
      'FontSize',18);
    if doshowsex,
      hsexmarker_zoom(i,j) = plot(nan,nan,'k.');
    end

    if strcmpi(animaltype,'larvacontour')
      hextrazoom(i,j,1) = plot(nan,nan,'Marker','.','LineStyle','-');
      hextrazoom(i,j,2) = plot(nan,nan,'Marker','o','LineStyle','None');
    end

  end
end

mencoder_nframes = 0;

tailx = nan(2*taillength+1,nids);
taily = nan(2*taillength+1,nids);
tailcolor = zeros(2*taillength+1,3,nids);
tailisbehavior = false(2*taillength+1,nids);

%%

for segi = 1:numel(firstframes),
  firstframe = firstframes(segi);
  endframe = endframes(segi);

  frame = firstframe - 1;
  idx = frame - nframesoff;
  tailend = 1;
  for fly = 1:nids,
    tailx(:,fly) = padgrab(trx(fly).x,nan,1,1,idx(fly)-taillength,idx(fly)+taillength);
    taily(:,fly) = padgrab(trx(fly).y,nan,1,1,idx(fly)-taillength,idx(fly)+taillength);
    tmpscores = padgrab(scores{fly},0,1,nbehaviors,idx(fly)-taillength,idx(fly)+taillength);
    tailisbehavior(:,fly) = any(tmpscores > 0,1);
    w = max(0,bsxfun(@times,tmpscores,behaviorweight));
    w = permute(w,[1,3,2]);
    z = sum(w,1);
    z(z==0) = 1;
    w = bsxfun(@rdivide,w,z);
    tailcolortmp = sum(bsxfun(@times,w,colors),1);
    tailcolortmp = max(0,min(1,tailcolortmp));
    tailcolor(:,:,fly) = permute(tailcolortmp,[3,2,1]);
    for i = 1:taillength*2,
      set(htail(i,fly),'XData',tailx([i,i+1],fly),'YData',taily([i,i+1],fly),'Color',tailcolor(i,:,fly));
      if tailisbehavior(i,fly),
        set(htail(i,fly),'LineWidth',2);
      else
        set(htail(i,fly),'LineWidth',1);
      end
    end
  end
  %tailcolor = max(0,min(1,tailcolor));

  for frame = firstframe:endframe,
    if mod(frame - firstframe,20) == 0,
      fprintf('frame %d\n',frame);
    end

    % relative frame
    idx = frame - nframesoff;

    isalive = frame >= trxfirstframes & ...
      frame <= trxendframes;

    % draw the unzoomed image
    im = uint8(readframe(frame));
    if ncolors == 1,
      im = repmat(im,[1,1,3]);
    end
    set(him,'cdata',im);

    % draw frame number text box
    % text doesn't show up in no display mode
    if titletext && isdisplay,
      framestr = sprintf('Frame %d, t = %.2f s',frame,timestamps(frame)-timestamps(1));
      if ~isempty(movietitle),
        framestr = {framestr,movietitle}; %#ok<AGROW>
      end
      set(htext,'String',framestr);
    end

    % draw the zoomed image
    if dynamicflyselection && exist('fliesplotperframe','var'),
      zoomflies = reshape(fliesplotperframe(:,frame),[nzoomr,nzoomc]);
    end
    for i = 1:nzoomr,
      for j = 1:nzoomc,
        fly = zoomflies(i,j);

        % fly not visible?
        if isnan(fly) || ~isalive(fly),
          set(himzoom(i,j),'cdata',repmat(uint8(123),[boxradius*2+1,boxradius*2+1,3]));
          continue;
        end

        % grab a box around (x,y)
        x = round(trx(fly).x(idx(fly)));
        y = round(trx(fly).y(idx(fly)));
        boxradx1 = min(boxradius,x-1);
        boxradx2 = min(boxradius,size(im,2)-x);
        boxrady1 = min(boxradius,y-1);
        boxrady2 = min(boxradius,size(im,1)-y);
        box = uint8(zeros(2*boxradius+1));
        box(boxradius+1-boxrady1:boxradius+1+boxrady2,...
          boxradius+1-boxradx1:boxradius+1+boxradx2) = ...
          im(y-boxrady1:y+boxrady2,x-boxradx1:x+boxradx2);
        set(himzoom(i,j),'cdata',repmat(box,[1,1,3]));

      end
    end

    idxfuture = frame + taillength - nframesoff;
    colorfuture = zeros(nids,3);
    isalivefuture = frame + taillength >= trxfirstframes & ...
      frame + taillength <= trxendframes;

    tailx = cat(1,tailx(2:end,:),nan(1,nids));
    taily = cat(1,taily(2:end,:),nan(1,nids));
    tailisbehavior = cat(1,tailisbehavior(2:end,:),false(1,nids));
    for fly = 1:nids,
      if ~isalivefuture(fly), continue; end
      w = max(0,scores{fly}(:,idxfuture(fly))).*behaviorweight;
      tailisbehavior(end,fly) = any(scores{fly}(:,idxfuture(fly))>0,1);
      z = sum(w);
      if z == 0,
        colorfuture(fly,:) = [0,0,0];
      else
        w = w /z;
        %colortmp(fly,:) = sum(bsxfun(@times,w,colors),1)/z*max(scores{fly}(:,idx(fly)));
        colorfuture(fly,:) = sum(bsxfun(@times,w,colors),1);
      end
      tailx(end,fly) = trx(fly).x(idxfuture(fly));
      taily(end,fly) = trx(fly).y(idxfuture(fly));
    end
    colorfuture = max(0,min(1,colorfuture));

    tailcolor = cat(1,tailcolor(2:end,:,:),permute(colorfuture,[3,2,1]));
    colortmp = permute(tailcolor(taillength+1,:,:),[3,2,1]);
    isbehavior = tailisbehavior(taillength+1,:);

    % sanity check
    if DEBUG,
      tmpcolortmp = zeros(nids,3);
      tmptailx = nan(2*taillength+1,nids);
      tmptaily = nan(2*taillength+1,nids);
      tmpisbehavior = false(1,nids);
      for fly = 1:nids,
        if ~isalive(fly), continue; end
        w = max(0,scores{fly}(:,idx(fly))).*behaviorweight;
        z = sum(w);
        if z == 0,
          tmpcolortmp(fly,:) = [0,0,0];
        else
          w = w /z;
          %colortmp(fly,:) = sum(bsxfun(@times,w,colors),1)/z*max(scores{fly}(:,idx(fly)));
          tmpcolortmp(fly,:) = sum(bsxfun(@times,w,colors),1);
        end
        tmptailx(:,fly) = padgrab(trx(fly).x,nan,1,1,idx(fly)-taillength,idx(fly)+taillength);
        tmptaily(:,fly) = padgrab(trx(fly).y,nan,1,1,idx(fly)-taillength,idx(fly)+taillength);

        tmpisbehavior(fly) = any(scores{fly}(:,idx(fly)) > 0);

      end
      if ~all(isnan(tailx(:)) & isnan(tmptailx(:)) | (tailx(:) == tmptailx(:)))
        error('tailx does not pass sanity check');
      end
      if ~all(isnan(taily(:)) & isnan(tmptaily(:)) | (taily(:) == tmptaily(:)))
        error('taily does not pass sanity check');
      end
      if ~all(colortmp(:) == tmpcolortmp(:)),
        error('colortmp does not pass sanity check');
      end
      if ~all(isbehavior(:) == tmpisbehavior(:)),
        error('isbehavior does not pass sanity check');
      end
    end

    % plot the zoomed out position
    for fly = 1:nids,
      if isalive(fly),
        set(htail(tailend,fly),'XData',tailx(end-1:end,fly),...
          'YData',taily(end-1:end,fly),...
          'Color',tailcolor(end-1,:,fly));
        if tailisbehavior(end-1,fly),
          set(htail(tailend,fly),'LineWidth',2);
        else
          set(htail(tailend,fly),'LineWidth',1);
        end
%         i0 = max(1,idx(fly)-taillength);
%         set(htail(fly),'xdata',trx(fly).x(i0:idx(fly)),...
%           'ydata',trx(fly).y(i0:idx(fly)),'color',colortmp(fly,:));
        if doshowsex,
          sexi = find(strcmpi(trx(fly).sex{idx(fly)},sexes),1);
          sexmarker = sexmarkers{sexi};
          sexcolorcurr = [1,1,1];
          set(hsexmarker(fly),'xdata',trx(fly).x(idx(fly)),...
            'ydata',trx(fly).y(idx(fly)),...
            'color',sexcolorcurr,...
            'marker',sexmarker,...
            'markerfacecolor',sexcolorcurr);
        end
        if strcmpi(animaltype,'fly'),
          updatefly(htri(fly),trx(fly),idx(fly));
        elseif strcmpi(animaltype,'larvacontour'),
          pos.xcontour = trx(fly).xcontour{idx(fly) };
          pos.ycontour = trx(fly).ycontour{idx(fly) };
          pos.xspine = trx(fly).xspine(:,idx(fly) );
          pos.yspine = trx(fly).yspine(:,idx(fly));

          updatelarvacontour(htri(fly),hextra(fly,:),pos);

        end
        set(htri(fly),'Color',colortmp(fly,:));
        if strcmpi(animaltype,'larvacontour'),
          set(hextra(fly,:),'Color',colortmp(fly,:));
        end
        if isbehavior(fly),
          set(htri(fly),'Linewidth',2);
        else
          set(htri(fly),'Linewidth',1);
        end
        if doplotwings,
          xwing = [trx(fly).xwingl(idx(fly)),trx(fly).x(idx(fly)),trx(fly).xwingr(idx(fly))];
          ywing = [trx(fly).ywingl(idx(fly)),trx(fly).y(idx(fly)),trx(fly).ywingr(idx(fly))];
          set(hwing(fly),'XData',xwing,'YData',ywing,'Color',colortmp(fly,:));
        end
      else
        set(htail(:,fly),'xdata',nan,'ydata',nan);
        set(htri(fly),'xdata',[],'ydata',[]);
        if doshowsex,
          set(hsexmarker(fly),'xdata',[],'ydata',[]);
        end
        if doplotwings,
          set(hwing(fly),'XData',[],'YData',[]);
        end
        if strcmpi(animaltype,'larvacontour'),
          set(hextra(fly,1),'XData',[],'YData',[]);
          set(hextra(fly,2),'XData',[],'YData',[]);
        end
      end
    end
    tailend = tailend + 1;
    if tailend >= 2*taillength+1,
      tailend = 1;
    end

    % plot the zoomed views
    for i = 1:nzoomr,
      for j = 1:nzoomc,
        fly = zoomflies(i,j);
        if ~isnan(fly) && isalive(fly),
          x = trx(fly).x(idx(fly));
          y = trx(fly).y(idx(fly));
          x = boxradius + (x - round(x))+.5;
          y = boxradius + (y - round(y))+.5;
          x = x * scalefactor;
          y = y * scalefactor;
          x = x + x0(j) - 1;
          y = y + y0(i) - 1;
          a = trx(fly).a(idx(fly))*scalefactor;
          b = trx(fly).b(idx(fly))*scalefactor;
          theta = trx(fly).theta(idx(fly));
          if strcmpi(animaltype,'larvacontour')
            pos.xcontour = trx(fly).xcontour{idx(fly)};
            pos.ycontour = trx(fly).ycontour{idx(fly)};
            pos.xspine = trx(fly).xspine(:,idx(fly));
            pos.yspine = trx(fly).yspine(:,idx(fly));
            cx = trx(fly).x(idx(fly));
            cy = trx(fly).y(idx(fly));
            pos.xcontour = pos.xcontour + boxradius - round(cx) + 0.5;
            pos.ycontour = pos.ycontour + boxradius - round(cy) + 0.5;
            pos.xspine = pos.xspine + boxradius - round(cx) + 0.5;
            pos.yspine = pos.yspine + boxradius - round(cy) + 0.5;

            pos.xcontour = pos.xcontour*scalefactor + x0(j) -1;
            pos.ycontour = pos.ycontour*scalefactor + y0(i) -1;
            pos.xspine = pos.xspine*scalefactor + x0(j) -1;
            pos.yspine = pos.yspine*scalefactor + y0(i) -1;

          end

          labelis = find(scores{fly}(:,idx(fly)) > 0);
          tmpscores = scores{fly}(labelis,idx(fly));
          s = cell(1,numel(labelis));
          for iii = 1:numel(labelis),
            s{iii} = sprintf('\\color[rgb]{%f %f %f}%s',tmpscores(iii)*colors(labelis(iii),:),behaviors{labelis(iii)});
          end

          % try to fit on two lines, with at most two behaviors per-line
          if numel(labelis) > 2,
            nperline = 2;
            s0 = s;
            s = cell(1,ceil(numel(labelis)/nperline));
            for linei = 1:numel(s),
              s{linei} = sprintf('%s, ',s0{2*(linei-1)+1:min(numel(labelis),2*linei)});
              s{linei} = s{linei}(1:end-2);
            end
          end

          if printnone && isempty(s),
            s = {'None'};
          end

%           if doshowsex,
%             s = sprintf('%d, %s',fly,trx(fly).sex{idx(fly)});
%           else
%             s = sprintf('%d',fly);
%           end
          if doplotwings,
            xwingl = trx(fly).xwingl(idx(fly)) - round(trx(fly).x(idx(fly))) + boxradius + .5;
            ywingl = trx(fly).ywingl(idx(fly)) - round(trx(fly).y(idx(fly))) + boxradius + .5;
            xwingl = xwingl * scalefactor;
            ywingl = ywingl * scalefactor;
            xwingl = xwingl + x0(j) - 1;
            ywingl = ywingl + y0(i) - 1;
            xwingr = trx(fly).xwingr(idx(fly)) - round(trx(fly).x(idx(fly))) + boxradius + .5;
            ywingr = trx(fly).ywingr(idx(fly)) - round(trx(fly).y(idx(fly))) + boxradius + .5;
            xwingr = xwingr * scalefactor;
            ywingr = ywingr * scalefactor;
            xwingr = xwingr + x0(j) - 1;
            ywingr = ywingr + y0(i) - 1;
            xwing = [xwingl,x,xwingr];
            ywing = [ywingl,y,ywingr];
          end

          if strcmpi(animaltype,'fly'),
            updatefly(hzoom(i,j),x,y,theta,a,b);
          elseif strcmpi(animaltype,'larvacontour'),
            updatelarvacontour(hzoom(i,j),hextrazoom(i,j,:),pos);
          end
          if doplotwings,
            set(hzoomwing(i,j),'XData',xwing,'YData',ywing,'Color',colortmp(fly,:));
          end
          set(htextzoom(i,j),'string',s,'color',colortmp(fly,:));
          if isbehavior(fly),
            set(hzoombox(i,j),'Visible','on','color',colortmp(fly,:));
            set(hzoom(i,j),'Linewidth',2);
          else
            set(hzoombox(i,j),'Visible','off');
            set(hzoom(i,j),'Linewidth',1);
          end
          if doshowsex,
            sexcolorcurr = [1,1,1];
            sexi = find(strcmpi(trx(fly).sex{idx(fly)},sexes),1);
            sexmarker = sexmarkers{sexi};
            set(hsexmarker_zoom(i,j),'Visible','on','XData',x,'YData',y,'color',sexcolorcurr,'MarkerFaceColor',sexcolorcurr,'Marker',sexmarker);
          end

%           if doshowsex,
%             sexi = find(strcmpi(trx(fly).sex{idx(fly)},sexes),1);
%             sexmarker = sexmarkers{sexi};
%             set(htextzoom(i,j),'xdata',x,...
%               'ydata',y,...
%               'color',colortmp(fly,:),...
%               'marker',sexmarker,...
%               'markerfacecolor',colortmp(fly,:));
%           end
          set(hzoom(i,j),'color',colortmp(fly,:));
        else
          set(hzoom(i,j),'xdata',[],'ydata',[]);
          if doplotwings,
            set(hzoomwing(i,j),'XData',[],'YData',[]);
          end
          set(htextzoom(i,j),'string','');
          if doshowsex,
            set(hsexmarker_zoom(i,j),'Visible','off');
          end
%           if doshowsex,
%             set(htextzoom(i,j),'xdata',[],'ydata',[]);
%           end
        end
      end
    end

    if frame == firstframes(1),
      if ~isempty(figpos),
        set(1,'Position',figpos);
      else
        input('Resize figure 1 to the desired size, hit enter when done.');
        figpos = get(1,'Position');
      end
      if ~DEBUG,
        set(1,'visible','off');
        if useVideoWriter,
          if strcmpi(compression,'None') || strcmpi(compression,'Uncompressed AVI'),
            profile = 'Uncompressed AVI';
          else
            profile = 'Motion JPEG AVI';
          end
          aviobj = VideoWriter(aviname,profile); %#ok<TNMLP>
          set(aviobj,'FrameRate',fps);
          if ~strcmpi(profile,'Uncompressed AVI'),
            set(aviobj,'Quality',100);
          end
          open(aviobj);
        else
          if isempty(avifileTempDataFile),
            aviobj = avifile(aviname,'fps',fps,'quality',100,'compression',compression);  %#ok<REMFF1>
          else
            aviobj = myavifile(aviname,'fps',fps,'quality',100,'compression',compression,...
              'TempDataFile',avifileTempDataFile);
            fprintf('Temporary data file for avi writing: %s\n',aviobj.TempDataFile);
          end
        end
      end
    end

    if ~DEBUG,
    if frame == firstframes(1),
      fr = getframe_invisible(hax);
      [height,width,~] = size(fr);
      gfdata = getframe_initialize(hax);
      [fr,height,width] = getframe_invisible_nocheck(gfdata,[height,width],false,false);
%       height = ceil(height/4)*4;
%       width = ceil(width/4)*4;
%       fr = getframe_invisible(hax,[height,width]);
    else
      fr = getframe_invisible_nocheck(gfdata,[height,width],false);
    end
    if useVideoWriter,
      writeVideo(aviobj,fr);
    else
      aviobj = addframe(aviobj,fr);
    end
    set(1,'Position',figpos);
    else
      drawnow;
%       if any(isbehavior(:)),
%         input('');
%       end
    end
    %drawnow;

  end

end

%% clean up

fprintf('Finishing AVI...\n');
if ~DEBUG,
if useVideoWriter,
  close(aviobj);
else
  aviobj = close(aviobj); %#ok<NASGU>
end
fprintf('Cleanup...\n');
getframe_cleanup(gfdata);
end
if fid > 0,
  fclose(fid);
end

succeeded = true;


function [stats, ntmaps, hh, hhfill, table_group, multcomp_group] = hansen_neurotransmitter_maps(fmri_data_obj, varargin)
% Apply a set of PET neurotransmitter-related maps from Hansen 2022 Nature Neuroscience
% - Uses cosine similarity metric on gray matter masked images
%
% :Usage:
% ::
%
%     [stats, ntmaps, hh, hhfill, table_group, multcomp_group] = hansen_neurotransmitter_maps(fmri_data_obj, [optional inputs])
%
% :Inputs:
%
%   **fmri_data_obj:**
%        An fmri_data object with one or more images; can be a signature
%
% :Optional Inputs:
%   **colors:**
%        Followed by plot colors in cell array
%
%   **nofigure:**
%        Do not create a new figure; use existing
%
%   **noplot:**
%        Do not plot polar plot, return tables only
%
%   **correlation:**
%        Use correlation similarity metric instead of cosine similarity
%
%   **dofixrange:**
%        Followed by range of values [min max] for plot (see
%        image_similarity_plot)
%
% :Outputs:
%
%   **stats:**
%        Stats structure with associations
%
%   **ntmaps:**
%        fmri_data object with neurotransmitter maps and meta-data, in
%        sorted order
%
%   **'hh'**
%        line handles
%
%   **'hhfill'**
%        fill handles
%
%    **table_group**
%              multiple one-way ANOVA tables (one for each
%              spatial basis) with group as column factor (requires
%              'average' to be specified)
%
%    **multcomp_group**
%              mutiple comparisons of means across groups, one output
%              cell for each spatial basis, critical value determined
%              by Tukey-Kramer method (see multcompare)
%
% :Examples:
% ::
%
% % Load and plot profile for a negative affect neuromarker from Chang et al. 2015, Plos Biology:
% % ------------------------------------------------------------------
% pines = load_image_set('pines');
% [stats, ntmaps] = hansen_neurotransmitter_maps(pines);
%
% figure;
% [stats, ntmaps] = hansen_neurotransmitter_maps(pines, 'nofigure', 'colors', [0 0 1]);
% [stats, ntmaps] = hansen_neurotransmitter_maps(pines, 'noplot');
%
% % Load a drug craving neuromarker from Koban et al. 2022, Nature
% % Neuroscience, and plot the profiles for drug-only and drug+food signatures on top of
% % one another:
% % ------------------------------------------------------------------
% ncs_all = load_image_set('ncs'); % craving
% ncsdrug = get_wh_image(ncs_all, 2);
% ncs = get_wh_image(ncs_all, 1);
% [stats, ntmaps, hh, fillh, table_group] = hansen_neurotransmitter_maps(ncsdrug);
% [stats, ntmaps, hh, fillh, table_group] = hansen_neurotransmitter_maps(ncs, 'colors', {[0 0 1]}, 'nofigure');

% :References:
%   Hansen et al. 2022, Nature Neuroscience
%
% :See also:
%   - image_similarity_plot
%

%
% ..
%     Author and copyright information:
%
%     Copyright (C) 2023 Mijin Kwon and Tor Wager
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.
% ..

% ..
%    Programmers' notes:
%    Tor: Created from Mijin's script -- plus resort rows of image names and
%    fullpath fields
% ..

% -------------------------------------------------------------------------
% DEFAULT ARGUMENT VALUES
% -------------------------------------------------------------------------

colors = [1 0 0];
dofigure = true;
doplot = true;
similarity_metric = 'cosine_similarity';
dofixrange = [];

% -------------------------------------------------------------------------
% OPTIONAL INPUTS
% -------------------------------------------------------------------------

% This is a compact way to assign multiple variables. The input argument
% names and variable names must match, however:

allowable_inputs = {'colors' 'doplot' 'similarity_metric' 'dofixrange'};

keyword_inputs = {'noplot' 'nofigure' 'correlation'};

% optional inputs with default values - each keyword entered will create a variable of the same name

for i = 1:length(varargin)
    if ischar(varargin{i})
        switch varargin{i}

            case allowable_inputs

                eval([varargin{i} ' = varargin{i+1}; varargin{i+1} = [];']);

            case keyword_inputs
                % Skip deal with this below

            otherwise, warning(['Unknown input string option:' varargin{i}]);
        end
    end
end

% 2nd pass: Keyword inputs. These supersede earlier inputs
for i = 1:length(varargin)
    if ischar(varargin{i})
        switch varargin{i}

            case 'noplot'
                doplot = false;

            case 'nofigure'
                dofigure = false;

            case 'correlation'
                similarity_metric = 'correlation';

        end
    end
end

% -------------------------------------------------------------------------
% MAIN FUNCTION
% -------------------------------------------------------------------------

ntmaps = load_image_set('hansen22');

ntmaps = reorder_and_add_metadata(ntmaps);

% These are already gray-matter masked in repo, but make sure:
ntmaps = apply_mask(ntmaps, which('gray_matter_mask.nii'));

% This may not be masked...so mask with gray matter:
fmri_data_obj = apply_mask(fmri_data_obj, which('gray_matter_mask.nii'));

if dofigure
    create_figure('Neurotransmitter polar plot')
end

if doplot
    if ~iscell(colors), colors = {colors}; end

    if isempty(dofixrange)

        [stats, hh, hhfill, table_group, multcomp_group] = image_similarity_plot(fmri_data_obj, 'mapset', ntmaps, similarity_metric, 'plotstyle', 'polar', 'networknames', ntmaps.metadata_table.target, 'colors', colors, 'nofigure');

    else % we have fixed range

        [stats, hh, hhfill, table_group, multcomp_group] = image_similarity_plot(fmri_data_obj, 'mapset', ntmaps, similarity_metric, 'plotstyle', 'polar', 'networknames', ntmaps.metadata_table.target, 'colors', colors, 'nofigure', 'dofixrange', dofixrange);

    end

else
    [stats, hh, hhfill, table_group, multcomp_group] = image_similarity_plot(fmri_data_obj, 'mapset', ntmaps, similarity_metric, 'noplot');

end

end % main function

% ---------------------------------------------------------------
% Subfunctions
% ---------------------------------------------------------------

function ntmaps = reorder_and_add_metadata(ntmaps)

nrows = height(ntmaps.metadata_table);

transmitter = cell(nrows, 1);
ntmaps.metadata_table = addvars(ntmaps.metadata_table, transmitter,'After','target');
ntmaps.metadata_table.Properties.VariableNames{2} = 'transmitter';

for i = 1:nrows

    if ismember(ntmaps.metadata_table.target{i}, {'5HT1a', '5HT1b', '5HT2a', '5HT4', '5HT6', '5HTT'})
        ntmaps.metadata_table.transmitter{i} = 'serotonin';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'CB1'})
        ntmaps.metadata_table.transmitter{i} = 'cannabinoid';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'D1', 'D2', 'DAT'})
        ntmaps.metadata_table.transmitter{i} = 'dopamine';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'GABAa', 'GABAabz'})
        ntmaps.metadata_table.transmitter{i} = 'GABA';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'H3'})
        ntmaps.metadata_table.transmitter{i} = 'histamine';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'M1', 'a4b2', 'VAChT'})
        ntmaps.metadata_table.transmitter{i} = 'acetylcholine';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'MOR'})
        ntmaps.metadata_table.transmitter{i} = 'opioid';
    end
    if ismember(ntmaps.metadata_table.target{i}, {'NET'})
        ntmaps.metadata_table.transmitter{i} = 'norepinephrine';
    end

    if ismember(ntmaps.metadata_table.target{i}, {'mGluR5'})
        ntmaps.metadata_table.transmitter{i} = 'glutamate';
    end
end

% display(ntmaps.metadata_table)

[ntmaps.metadata_table, wh_indx_all] = sortrows(ntmaps.metadata_table,'transmitter','ascend');

ntmaps.dat = ntmaps.dat(:, wh_indx_all);

% Save original image names in sorted order
ntmaps.metadata_table = addvars(ntmaps.metadata_table, ntmaps.image_names(wh_indx_all, :), 'NewVariableName', {'image_names'}, 'Before', 'modeling_notes');

% re-sort
ntmaps.fullpath = ntmaps.fullpath(wh_indx_all, :);

end % end subfunction