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

%%
clear; close all; clc;

%% Syntax
% |[L1t,L2t,L3t]=ongrid(V1,V2,V3,cm,vm);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function [state] = CliffLeftSensorRoomba(serPort);
%[state] = CliffLeftSensorRoomba(serPort)
% Specifies the state of the Cliff Left sensor
% Either triggered or not triggered

% By; Joel Esposito, US Naval Academy, 2011

%Initialize preliminary return values
state = nan;

try

%Flush Buffer
N = serPort.BytesAvailable();
while(N~=0)
fread(serPort,N);
N = serPort.BytesAvailable();
end

warning off
global td

fwrite(serPort, [142]);  fwrite(serPort,9);
CliffLft = dec2bin(fread(serPort, 1));
state = bin2dec(CliffLft(end));

pause(td)
catch
    disp('WARNING:  function did not terminate correctly.  Output may be unreliable.')
end


function [event] = PL2EventTs(filename, channel)
% PL2EventTs(filename, channel): read event timestamps from a .pl2 file
%
% [event] = PL2EventTs(filename, channel)
% [event] = PL2EventTs(filename, 1)
% [event] = PL2EventTs(filename, 'KBD2')
%
% INPUT:
%   filename - if empty string, will use File Open dialog
%   channel - event channel name or 1-based channel number
%           1-based channel number is the index of the event channel in pl2.EventChannels cell array
%           shown in the Name column of the output produced by PL2Print(pl2.EventChannels)
%           To print the list of event channels, use these commands:
%
%           pl2 = PL2GetFileIndex(filename);
%           PL2Print(pl2.EventChannels);
%
% OUTPUT:
%   event.Ts - array of timestamps (in seconds)
%   event.Strobed - array of strobed event values

event = internalPL2EventTs(filename, channel);

end


function [d] = readraw_Gannet(filename, type, ds, endian,offset)

if( nargin == 3 )
	endian = 'b';
end

fp=fopen(filename, 'rb', endian);
%fseek(fp, 0, -1);
%fseek(fp, 262144, -1);
fseek(fp, offset, -1);
d = fread(fp, prod(ds), type);
fclose(fp);
d = reshape(d, ds);


classdef LaunchVehicleViewProfile < matlab.mixin.SetGet
    %LaunchVehicleViewProfile Summary of this class goes here
    %   Detailed explanation goes here

    properties
        %profile properties
        name(1,:) char = 'Untitled View Profile';
        desc(1,1) string = "";

        %axes properties
        backgroundColor(1,1) ColorSpecEnum = ColorSpecEnum.White;
        gridType(1,1) ViewGridTypeEnum = ViewGridTypeEnum.Major;
        majorGridColor(1,1) ColorSpecEnum = ColorSpecEnum.DarkGrey;
        minorGridColor(1,1) ColorSpecEnum = ColorSpecEnum.DarkGrey;
        gridTransparency(1,1) double = 0.15;
        meshEdgeAlpha(1,1) double = 0.1;

        %render mode
        renderer(1,1) FigureRendererEnum = FigureRendererEnum.OpenGL;

        %axis properties
        dispXAxis(1,1) logical = false;
        dispYAxis(1,1) logical = false;
        dispZAxis(1,1) logical = false;

        %trajectory view options
        trajEvtsViewType(1,1) ViewEventsTypeEnum = ViewEventsTypeEnum.All %either chunked by event/SoI or all
        frame AbstractReferenceFrame
        projType ViewProjectionTypeEnum = ViewProjectionTypeEnum.Perspective;

        %body fixed options
        showLongLatAnnotations(1,1) logical = true;

        %thrust vectors
        showThrustVectors(1,1) logical = false;
        thrustVectColor(1,1) ColorSpecEnum  = ColorSpecEnum.Red;
        thrustVectLineType(1,1) LineSpecEnum = LineSpecEnum.SolidLine;
        thrustVectScale(1,1) double = 1;
        thrustVectEntryIncr(1,1) double = 1;

        %SoI and other body options
        showSoIRadius(1,1) logical = false;
        bodiesToPlot(1,:) KSPTOT_BodyInfo = KSPTOT_BodyInfo.empty(1,0);
        bodyPlotStyle(1,1) ViewProfileBodyPlottingStyle = ViewProfileBodyPlottingStyle.Dot;

        %show sc body axes
        showScBodyAxes(1,1) logical = false;
        scBodyAxesScale(1,1) double = 100; %km

        %lighting
        showLighting(1,1) logical = false;
        showSunVect(1,1) logical = false;

        %show atmosphere
        showAtmosphere(1,1) logical = false;

        %ground objects
        groundObjsToPlot(1,:) LaunchVehicleGroundObject
        showGndTracks(1,1) logical = true;
        showGrdObjLoS(1,1) logical = true;

        %geometric points
        pointsToPlot(1,:) AbstractGeometricPoint

        %geometric vectors
        vectorsToPlot(1,:) AbstractGeometricVector

        %geometric reference frames
        refFramesToPlot(1,:) AbstractGeometricRefFrame

        %geometric angles
        anglesToPlot(1,:) AbstractGeometricAngle

        %geometric planes
        planesToPlot(1,:) AbstractGeometricPlane

        %sensors
        sensorsToPlot(1,:) AbstractSensor

        %sensor targets
        sensorTgtsToPlot(1,:) AbstractSensorTarget

        %central body transform
        hCBodySurfXForm = []; %matlab.graphics.GraphicsPlaceholder()

        %view properties (set by user indirectly through UI controls)
        updateViewAxesLimits(1,1) logical = false;
        orbitNumToPlot(1,1) double = 1;
        viewAzEl(1,2) = [-37.5, 30]; %view(3)
        viewZoomAxLims(3,2) double = NaN(3,2);
        viewCameraPosition(1,3) double = NaN(1,3);
        viewCameraTarget(1,3) double = NaN(1,3);
        viewCameraUpVector(1,3) double = NaN(1,3);
        viewCameraViewAngle(1,1) double = NaN(1,1);
    end

    properties(Transient)
        markerTrajData(1,:) LaunchVehicleViewProfileTrajectoryData = LaunchVehicleViewProfileTrajectoryData.empty(1,0);
        markerBodyData(1,:) LaunchVehicleViewProfileBodyData = LaunchVehicleViewProfileBodyData.empty(1,0);
        markerTrajAxesData(1,:) LaunchVehicleViewProfileBodyAxesData = LaunchVehicleViewProfileBodyAxesData.empty(1,0);
        markerGrdObjData(1,:) LaunchVehicleViewProfileGroundObjData = LaunchVehicleViewProfileGroundObjData.empty(1,0);
        sunLighting(1,:) LaunchVehicleViewProfileSunLighting = LaunchVehicleViewProfileSunLighting.empty(1,0);
        centralBodyData(1,:) LaunchVehicleViewProfileCentralBodyData = LaunchVehicleViewProfileCentralBodyData.empty(1,0);

        pointData(1,:) LaunchVehicleViewProfilePointData = LaunchVehicleViewProfilePointData.empty(1,0);
        vectorData(1,:) LaunchVehicleViewProfileVectorData = LaunchVehicleViewProfileVectorData.empty(1,0);
        refFrameData(1,:) LaunchVehicleViewProfileRefFrameData = LaunchVehicleViewProfileRefFrameData.empty(1,0);
        angleData(1,:) LaunchVehicleViewProfileAngleData = LaunchVehicleViewProfileAngleData.empty(1,0);
        planeData(1,:) LaunchVehicleViewProfilePlaneData = LaunchVehicleViewProfilePlaneData.empty(1,0);

        sensorData(1,:) LaunchVehicleViewProfileSensorData = LaunchVehicleViewProfileSensorData.empty(1,0);
        sensorTgtData(1,:) LaunchVehicleViewProfileSensorTargetData = LaunchVehicleViewProfileSensorTargetData.empty(1,0);

        userDefinedRefFrames(1,:)
    end

    properties(Access=private)
        generic3DTrajView(1,1) Generic3DTrajectoryViewType = Generic3DTrajectoryViewType();
    end

    methods
        function obj = LaunchVehicleViewProfile()

        end

        function removeGrdObjFromList(obj, grdObj)
            obj.groundObjsToPlot([obj.groundObjsToPlot] == grdObj) = [];
        end

        function removeGeoPointFromList(obj, point)
            obj.pointsToPlot([obj.pointsToPlot] == point) = [];
        end

        function removeGeoVectorFromList(obj, vector)
            obj.vectorsToPlot([obj.vectorsToPlot] == vector) = [];
        end

        function removeGeoRefFrameFromList(obj, refFrame)
            obj.refFramesToPlot([obj.refFramesToPlot] == refFrame) = [];
        end

        function removeGeoAngleFromList(obj, angle)
            obj.anglesToPlot([obj.anglesToPlot] == angle) = [];
        end

        function removeGeoPlaneFromList(obj, plane)
            obj.planesToPlot([obj.planesToPlot] == plane) = [];
        end

        function removeSensorFromList(obj, sensor)
            obj.sensorsToPlot([obj.sensorsToPlot] == sensor) = [];
        end

        function removeSensorTargetFromList(obj, target)
            obj.sensorTgtsToPlot([obj.sensorTgtsToPlot] == target) = [];
        end

        function plotTrajectory(obj, lvdData, handles, app)
            obj.generic3DTrajView.plotStateLog(obj.orbitNumToPlot, lvdData, obj, handles, app);
        end

        function createTrajectoryMarkerData(obj, subStateLogs, evts)
            obj.clearAllTrajData();
            trajMarkerData = obj.createTrajData();

            for(j=1:length(subStateLogs)) %#ok<*NO4LP>
                if(size(subStateLogs{j},1) > 0)
                    [times, rVects, evtColor] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, j, evts);

                    if(length(unique(times)) > 0)
                        trajMarkerData.addData(times, rVects, evtColor);
                    end
                end
            end
        end

        function createBodyMarkerData(obj, dAxes, subStateLogs, viewInFrame, showSoI, meshEdgeAlpha, evts)
            obj.clearAllBodyData();

            timesArr = {};
            rVectArr = {};
            bodyColorsArr = {};
            bodyMarkerDataObjs = {};

            for(i=1:length(obj.bodiesToPlot))
                bodyToPlot = obj.bodiesToPlot(i);

                if(bodyToPlot == viewInFrame.getOriginBody())
                    continue;
                end

                bColorRGB = bodyToPlot.getBodyRGB();

                bodyOrbitPeriod = [];
                if(bodyToPlot.sma > 0)
                    gmu = bodyToPlot.getParentGmuFromCache();

                    if(not(isempty(gmu)) && not(isnan(gmu)) && isfinite(gmu))
                        bodyOrbitPeriod = computePeriod(bodyToPlot.sma, gmu);
                    end
                else
                    bodyOrbitPeriod = Inf;
                end

                bodyMarkerData = obj.createBodyData(bodyToPlot, viewInFrame, obj.bodyPlotStyle, showSoI, meshEdgeAlpha);

                times = [];
                for(j=1:length(subStateLogs)) %#ok<NO4LP>
                    if(size(subStateLogs{j},1) > 0)
                        times = [times; subStateLogs{j}(:,1)]; %#ok<AGROW>
                    end
                end

                if(isfinite(bodyOrbitPeriod))
                    numPeriods = (max(times) - min(times))/bodyOrbitPeriod;
                    times = linspace(min(times), max(times), max(10*numPeriods,length(times)));
                else
                    times = linspace(min(times), max(times), length(times));
                end

                states = bodyToPlot.getElementSetsForTimes(times);
                if(numel(states) >= 1)
                    doConversion = states(1).frame ~= viewInFrame;
                else
                    doConversion = true;
                end

                if(doConversion)
                    states = convertToFrame(states, viewInFrame);
                end

                rVects = [states.rVect];

                [times,ia,~] = unique(times,'stable');
                rVects = rVects(:,ia);

                [times,I] = sort(times);
                rVects = rVects(:,I);

                timesInner = times;
                rVectsInner = rVects;

                timesArr{i} = timesInner; %#ok<AGROW>
                rVectArr{i} = rVectsInner; %#ok<AGROW>

                bodyColorsArr{i} = bColorRGB; %#ok<AGROW>
                bodyMarkerDataObjs{i} = bodyMarkerData; %#ok<AGROW>
            end

            for(i=1:length(timesArr))
                timesInner = timesArr{i};
                rVectsInner = rVectArr{i};
                bColorRGB = bodyColorsArr{i};
                bodyMarkerData = bodyMarkerDataObjs{i};

                if(not(isempty(timesInner)))
                    obj.markerBodyData(end+1) = bodyMarkerData;

                    times = timesInner;
                    rVects = rVectsInner;

                    if(length(unique(times)) > 1)
                        plot3(dAxes, rVects(1,:), rVects(2,:), rVects(3,:), '-', 'Color',bColorRGB, 'LineWidth',1.5);
                        bodyMarkerData.addData(times, rVects);
                    end
                end
            end
        end

        function createSunLightSrc(obj, dAxes, viewInFrame)
            obj.sunLighting = LaunchVehicleViewProfileSunLighting(dAxes, viewInFrame, obj.showLighting, obj.showSunVect);
        end

        function updateLightPosition(obj, time)
            obj.sunLighting.updateSunLightingPosition(time);
        end

        function createBodyAxesData(obj, vehPosVelData, vehAttData) %lvdStateLogEntries, evts, viewInFrame
            obj.clearAllBodyAxesData();
            obj.markerTrajAxesData = LaunchVehicleViewProfileBodyAxesData(vehPosVelData, vehAttData, obj.scBodyAxesScale, obj.showScBodyAxes);

%             if(obj.showScBodyAxes)
%                 for(i=1:length(evts))
%                     evt = evts(i);
%                     evtStateLogEntries = lvdStateLogEntries([lvdStateLogEntries.event] == evt);
%                     evtStateLogEntries = evtStateLogEntries(:)';
%
%                     cartElem = convertToFrame(getCartesianElementSetRepresentation(evtStateLogEntries), viewInFrame);
%
%                     times = [evtStateLogEntries.time];
%
%                     rVects = [cartElem.rVect];
%                     rotMatsBodyToView = NaN(3, 3, length(evtStateLogEntries));
%                     for(j=1:length(evtStateLogEntries))
%                         %get body position in view frame
%                         entry = evtStateLogEntries(j);
%
%                         %get body axes in view frame
%                         rotMatBodyToInertial = entry.steeringModel.getBody2InertialDcmAtTime(entry.time, entry.position, entry.velocity, entry.centralBody);
%
%                         [~, ~, ~, rotMatToInertial12] = viewInFrame.getOffsetsWrtInertialOrigin(entry.time, cartElem(j));
%                         [~, ~, ~, rotMatToInertial32] = entry.centralBody.getBodyCenteredInertialFrame().getOffsetsWrtInertialOrigin(entry.time, cartElem(j));
%
%                         rotMatsBodyToView(:,:,j) = rotMatToInertial12' * rotMatToInertial32 * rotMatBodyToInertial; %body to inertial -> inertial to inertial -> inertial to view frame
%                     end
%
%                     [times,ia,~] = unique(times,'stable');
%                     rVects = rVects(:,ia);
%                     rotMatsBodyToView = rotMatsBodyToView(:,:,ia);
%
%                     [times,I] = sort(times);
%                     rVects = rVects(:,I);
%                     rotMatsBodyToView = rotMatsBodyToView(:,:,I);
%
%                     switch(evt.plotMethod)
%                         case EventPlottingMethodEnum.PlotContinuous
%                             %nothing
%
%                         case EventPlottingMethodEnum.SkipFirstState
%                             times = times(2:end);
%                             rVects = rVects(:,2:end);
%                             rotMatsBodyToView = rotMatsBodyToView(:,:,2:end);
%
%                         case EventPlottingMethodEnum.DoNotPlot
%                             times = [];
%                             rVects = [];
%                             rotMatsBodyToView = [];
%
%                         otherwise
%                             error('Unknown event plotting method: %s', EventPlottingMethodEnum.DoNotPlot.name);
%                     end
%
%                     if(length(times) >= 2 && all(diff(times)>0))
%                         obj.markerTrajAxesData.addData(times, rVects, rotMatsBodyToView);
%                     end
%                 end
%             end
        end

        function createGroundObjMarkerData(obj, dAxes, lvdStateLogEntries, vehPosVelData, evts, viewInFrame, celBodyData)
            obj.clearAllGrdObjData();

            for(i=1:length(obj.groundObjsToPlot))
                grdObj = obj.groundObjsToPlot(i);

                if(ismember(grdObj.centralBodyInfo, obj.bodiesToPlot) || grdObj.centralBodyInfo == viewInFrame.getOriginBody())
                    grdObjData = LaunchVehicleViewProfileGroundObjData(grdObj, vehPosVelData, celBodyData);
                    obj.markerGrdObjData(end+1) = grdObjData;

                    for(j=1:length(evts))
                        evt = evts(j);
                        evtStateLogEntries = lvdStateLogEntries([lvdStateLogEntries.event] == evt);
                        numEntries = length(evtStateLogEntries);

%                         scCartElem = convertToFrame(getCartesianElementSetRepresentation(evtStateLogEntries), viewInFrame);
                        allTimes = [evtStateLogEntries.time];

                        elemSet = GeographicElementSet.empty(1,0);

                        for(k=1:numEntries)
                            ge = grdObj.getStateAtTime(allTimes(k));
                            if(not(isempty(ge)))
                                elemSet(end+1) = ge; %#ok<AGROW>
                            end
                        end
                        elemSet = convertToFrame(convertToCartesianElementSet(elemSet), viewInFrame);

                        times = [elemSet.time];
                        rVectsGrdObj = [elemSet.rVect];
%                         rVectsSc= [scCartElem.rVect];

                        [times,ia,~] = unique(times);
                        rVectsGrdObj = rVectsGrdObj(:,ia);
%                         rVectsSc = rVectsSc(:,ia);

                        switch(evt.plotMethod)
                            case EventPlottingMethodEnum.PlotContinuous
                                %nothing

                            case EventPlottingMethodEnum.SkipFirstState
                                times = times(2:end);
                                rVectsGrdObj = rVectsGrdObj(2:end,:);
%                                 rVectsSc = rVectsSc(2:end,:);

                            case EventPlottingMethodEnum.DoNotPlot
                                times = [];
                                rVectsGrdObj = [];
%                                 rVectsSc = [];

                            otherwise
                                error('Unknown event plotting method: %s', EventPlottingMethodEnum.DoNotPlot.name);
                        end

                        if(length(times) >= 2 && all(diff(times)>0))
                            grdObjData.addData(times, rVectsGrdObj, viewInFrame, obj.showGrdObjLoS);

                            if(obj.showGndTracks)
                                hold(dAxes,'on');
                                plot3(dAxes, rVectsGrdObj(1,:), rVectsGrdObj(2,:), rVectsGrdObj(3,:), 'Color',grdObj.grdTrkLineColor.color, 'LineStyle',grdObj.grdTrkLineSpec.linespec);
                                hold(dAxes,'off');
                            end
                        end
                    end
                end
            end
        end

        function configureTimeSlider(obj, minTime, maxTime, subStateLogs, handles, app)
            timeSlider = app.DispAxesTimeSlider;
            curSliderTime = timeSlider.Value;
            if(not(isfinite(minTime) && isfinite(maxTime)) || ...
               minTime == maxTime)
                onlyTime = subStateLogs{1}(1,1);

                minTime = onlyTime;
                maxTime = onlyTime + 1;
            end
            timeSlider.Limits = [minTime maxTime];
            timeSlider.MajorTicks = linspace(minTime, maxTime, 10);
            timeSlider.MinorTicks = linspace(minTime, maxTime, 100);
            timeSlider.MajorTickLabels = "";

            if(curSliderTime > maxTime)
                timeSlider.Value = maxTime;
            elseif(curSliderTime < minTime)
                timeSlider.Value = minTime;
            end

            lvdData = getappdata(handles.ma_LvdMainGUI,'lvdData');
            timeSliderCb = @(src,evt) timeSliderStateChanged(src,evt, lvdData, handles, app);
            timeSlider.ValueChangingFcn = timeSliderCb;

            timeSlider.ValueChangingFcn(timeSlider, true);
        end

        function trajData = createTrajData(obj)
            trajData = LaunchVehicleViewProfileTrajectoryData();
            obj.markerTrajData = trajData;
        end

        function createCentralBodyData(obj, bodyInfo, hCBodySurfXForm, viewFrame)
            obj.centralBodyData = LaunchVehicleViewProfileCentralBodyData(bodyInfo, hCBodySurfXForm, viewFrame);
        end

        function bodyData = createBodyData(obj, bodyInfo, viewInFrame, bodyPlotStyle, showSoI,meshEdgeAlpha)
            bodyData = LaunchVehicleViewProfileBodyData(bodyInfo, viewInFrame, bodyPlotStyle, showSoI, meshEdgeAlpha);
            obj.markerBodyData(end+1) = bodyData;
       end

        function createPointData(obj, viewFrame, subStateLogs, evts)
            obj.clearPointData();
            points = obj.pointsToPlot;

            for(i=1:length(points))
                obj.pointData(end+1) = LaunchVehicleViewProfilePointData(points(i), viewFrame);
            end

            for(i=1:length(subStateLogs))
                if(size(subStateLogs{i},1) > 0)
                    [times, rVects, ~, vVects] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, i, evts);

                    if(length(unique(times)) > 1)
                        for(j=1:length(obj.pointData))
                            obj.pointData(j).addData(times, rVects, vVects);
                        end
                    end
                end
            end
        end

        function createVectorData(obj, viewFrame, subStateLogs, evts)
            obj.clearVectorData();
            vectors = obj.vectorsToPlot;

            for(i=1:length(vectors))
                obj.vectorData(end+1) = LaunchVehicleViewProfileVectorData(vectors(i), viewFrame);
            end

            for(i=1:length(subStateLogs))
                if(size(subStateLogs{i},1) > 0)
                    [times, rVects, ~, vVects] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, i, evts);

                    if(length(unique(times)) > 1)
                        for(j=1:length(obj.vectorData))
                            obj.vectorData(j).addData(times, rVects, vVects);
                        end
                    end
                end
            end
        end

        function createRefFrameData(obj, viewFrame, subStateLogs, evts)
            obj.clearRefFrameData();
            refFrames = obj.refFramesToPlot;

            for(i=1:length(refFrames))
                obj.refFrameData(end+1) = LaunchVehicleViewProfileRefFrameData(refFrames(i), viewFrame);
            end

            for(i=1:length(subStateLogs))
                if(size(subStateLogs{i},1) > 0)
                    [times, rVects, ~, vVects] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, i, evts);

                    if(length(unique(times)) > 1)
                        for(j=1:length(obj.refFrameData))
                            obj.refFrameData(j).addData(times, rVects, vVects);
                        end
                    end
                end
            end
        end

        function createAngleData(obj, viewFrame, subStateLogs, evts)
            obj.clearAngleData();
            angles = obj.anglesToPlot;

            for(i=1:length(angles))
                obj.angleData(end+1) = LaunchVehicleViewProfileAngleData(angles(i), viewFrame);
            end

            for(i=1:length(subStateLogs))
                if(size(subStateLogs{i},1) > 0)
                    [times, rVects, ~, vVects] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, i, evts);

                    if(length(unique(times)) > 1)
                        for(j=1:length(obj.angleData))
                            obj.angleData(j).addData(times, rVects, vVects);
                        end
                    end
                end
            end
        end

        function createPlaneData(obj, viewFrame, subStateLogs, evts)
            obj.clearPlaneData();
            planes = obj.planesToPlot;

            for(i=1:length(planes))
                obj.planeData(end+1) = LaunchVehicleViewProfilePlaneData(planes(i), viewFrame);
            end

            for(i=1:length(subStateLogs))
                if(size(subStateLogs{i},1) > 0)
                    [times, rVects, ~, vVects] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, i, evts);

                    if(length(unique(times)) > 1)
                        for(j=1:length(obj.planeData))
                            obj.planeData(j).addData(times, rVects, vVects);
                        end
                    end
                end
            end
        end

        function createSensorData(obj, lvdStateLogEntries, vehPosVelData, vehAttData, viewFrame)
            obj.clearSensorData();

            for(i=1:length(obj.sensorsToPlot))
                sensor = obj.sensorsToPlot(i);
                obj.sensorData(i) = LaunchVehicleViewProfileSensorData(sensor, obj.sensorTgtsToPlot, lvdStateLogEntries, vehPosVelData, vehAttData, viewFrame);
            end
        end

        function createSensorTargetData(obj, viewFrame)
            obj.clearSensorTgtData();

            for(i=1:length(obj.sensorTgtsToPlot))
                target = obj.sensorTgtsToPlot(i);
                obj.sensorTgtData(i) = LaunchVehicleViewProfileSensorTargetData(target, viewFrame);
            end
        end

        function clearAllTrajData(obj)
            obj.markerTrajData = LaunchVehicleViewProfileTrajectoryData.empty(1,0);
        end

        function clearAllBodyData(obj)
            obj.markerBodyData = LaunchVehicleViewProfileBodyData.empty(1,0);
        end

        function clearAllBodyAxesData(obj)
            obj.markerTrajAxesData = LaunchVehicleViewProfileBodyAxesData.empty(1,0);
        end

        function clearAllGrdObjData(obj)
            obj.markerGrdObjData = LaunchVehicleViewProfileGroundObjData.empty(1,0);
        end

        function clearPointData(obj)
            obj.pointData = LaunchVehicleViewProfilePointData.empty(1,0);
        end

        function clearVectorData(obj)
            obj.vectorData = LaunchVehicleViewProfileVectorData.empty(1,0);
        end

        function clearRefFrameData(obj)
            obj.refFrameData = LaunchVehicleViewProfileRefFrameData.empty(1,0);
        end

        function clearAngleData(obj)
            obj.angleData = LaunchVehicleViewProfileAngleData.empty(1,0);
        end

        function clearPlaneData(obj)
            obj.planeData = LaunchVehicleViewProfilePlaneData.empty(1,0);
        end

        function clearSensorData(obj)
            obj.sensorData = LaunchVehicleViewProfileSensorData.empty(1,0);
        end

        function clearSensorTgtData(obj)
            obj.sensorTgtData = LaunchVehicleViewProfileSensorTargetData.empty(1,0);
        end

    end

    methods(Static)
        function vehPosVelData = createVehPosVelData(subStateLogs, evts, viewFrame)
            vehPosVelData = LaunchVehicleViewPosVelInterp(viewFrame);

            for(i=1:length(subStateLogs))
                if(size(subStateLogs{i},1) > 0)
                    [times, rVects, ~, vVects] = LaunchVehicleViewProfile.parseTrajDataFromSubStateLogs(subStateLogs, i, evts);

                    if(length(unique(times)) > 1)
                        vehPosVelData.addData(times, rVects, vVects);
                    end
                end
            end
        end

        function vehAttData = createVehAttitudeData(vehPosVelData, lvdStateLogEntries, evts, viewInFrame)
            vehAttData = LaunchVehicleViewProfileAttitudeData();

            for(i=1:length(evts))
                evt = evts(i);
                bool = [lvdStateLogEntries.event] == evt;
                evtStateLogEntries = lvdStateLogEntries(bool);
                evtStateLogEntries = evtStateLogEntries(:)';

                if(isempty(evtStateLogEntries))
                    continue;
                end

                cartElem = convertToFrame(getCartesianElementSetRepresentation(evtStateLogEntries), viewInFrame);

                times = [evtStateLogEntries.time];

                rotMatsBodyToView = NaN(3, 3, length(evtStateLogEntries));
                for(j=1:length(evtStateLogEntries))
                    %get body position in view frame
                    entry = evtStateLogEntries(j);

                    %get body axes in view frame
                    rotMatBodyToInertial = entry.steeringModel.getBody2InertialDcmAtTime(entry.time, entry.position, entry.velocity, entry.centralBody);

                    [~, ~, ~, rotMatToInertial12] = viewInFrame.getOffsetsWrtInertialOrigin(entry.time, cartElem(j));
                    [~, ~, ~, rotMatToInertial32] = entry.centralBody.getBodyCenteredInertialFrame().getOffsetsWrtInertialOrigin(entry.time, cartElem(j));

                    rotMatsBodyToView(:,:,j) = rotMatToInertial12' * rotMatToInertial32 * rotMatBodyToInertial; %body to inertial -> inertial to inertial -> inertial to view frame
                end

                switch(evt.plotMethod)
                    case EventPlottingMethodEnum.PlotContinuous
                        %nothing

                    case EventPlottingMethodEnum.SkipFirstState
                        times = times(2:end);
                        rotMatsBodyToView = rotMatsBodyToView(:,:,2:end);

                    case EventPlottingMethodEnum.DoNotPlot
                        times = [];
                        rotMatsBodyToView = [];

                    otherwise
                        error('Unknown event plotting method: %s', evt.plotMethod);
                end

                [times,ia,~] = unique(times,'stable');
                rotMatsBodyToView = rotMatsBodyToView(:,:,ia);

                [times,I] = sort(times);
                rotMatsBodyToView = rotMatsBodyToView(:,:,I);

                if(length(times) >= 2 && all(diff(times)>0))
                    vehAttData.addData(times, rotMatsBodyToView);
                end
            end
        end
    end

    methods(Static, Access=private)
        function [times, rVects, evtColor, vVects] = parseTrajDataFromSubStateLogs(subStateLogs, j, evts)
            times = subStateLogs{j}(:,1);
            rVects = subStateLogs{j}(:,2:4);
            vVects = subStateLogs{j}(:,5:7);

            evtNum = subStateLogs{j}(1,13);
            if(isempty(evtNum) || isnan(evtNum))
                times = [];
                rVects = [];
                evtColor = [];
                vVects = [];

                return;
            end
            evt = evts(evtNum);
            evtColor = evt.colorLineSpec.color;

            switch(evt.plotMethod)
                case EventPlottingMethodEnum.PlotContinuous
                    %nothing

                case EventPlottingMethodEnum.SkipFirstState
                    times = times(2:end);
                    rVects = rVects(2:end,:);
                    vVects = vVects(2:end,:);

                case EventPlottingMethodEnum.DoNotPlot
                    times = [];
                    rVects = [];
                    vVects = [];

                otherwise
                    error('Unknown event plotting method: %s', event.plotMethod);
            end

            if(not(isempty(times)))
                [times,ia,~] = unique(times,'stable','rows');
                rVects = rVects(ia,:);
                vVects = vVects(ia,:);

                [times,I] = sort(times);
                rVects = rVects(I,:);
                vVects = vVects(I,:);
            end
        end
    end
end


% Poster Figures
% Make figures for posters
%
% Author: Derek Macklin, macklin@stanford.edu
% Affiliation: Covert Lab, Department of Bioengineering, Stanford University
% Created: 9/13/2011
classdef PosterFigures
    methods (Static = true);
        function run(eventName, outputDir)
            import edu.stanford.covert.cell.sim.analysis.PosterFigures

            PosterFigures.(eventName)(outputDir);
        end

        function pioneer2011(outputDir)
            import edu.stanford.covert.cell.sim.analysis.SingleCell;
            import edu.stanford.covert.cell.sim.analysis.ChromosomeSpaceTimePlot;
            import edu.stanford.covert.cell.sim.util.SimulationDiskUtil;
            import edu.stanford.covert.cell.sim.util.PlotUtil;
            import edu.stanford.covert.cell.sim.util.DiskLogger;
            import edu.stanford.covert.cell.sim.analysis.ProcessMetaboliteUsage;

            simBatchDir = '2011_09_09_19_17_51';
            simNum = 1;
            [~, ~, sim] = SimulationDiskUtil.getSimulation([simBatchDir filesep num2str(simNum)]);

            %% Gene -> RNA -> Protein -> Growth Correlation
            [~, figHandle] = edu.stanford.covert.cell.sim.util.PlotUtil.newAxesHandle();
            SingleCell.growth(sim, simBatchDir, simNum, figHandle)
            saveas(figHandle, [outputDir filesep 'Growth.eps'], 'epsc');
            close(figHandle);

            %% Chromosomal stuff
            [simDir, ~, sim] = SimulationDiskUtil.getSimulation([simBatchDir filesep num2str(simNum)]);
            c = sim.state('Chromosome');

            simIdx = num2str(SimulationDiskUtil.getSimulationIndex(simDir));
            simTimeStamp = SimulationDiskUtil.getSimulationTimeStamp(simDir);
            stateNames = {'ProteinComplex' 'counts'
                'Chromosome' 'complexBoundSites'
                'Chromosome' 'monomerBoundSites'
                'Chromosome' 'polymerizedRegions'
                'RNAPolymerase' 'positionStrands'
                'RNAPolymerase' 'states'
                'RNAPolymerase' 'transcriptionFactorBindingProbFoldChange'
                'Time' 'values'};
            states = DiskLogger.load(simDir, stateNames, [], [], [], 'extract');

            height = 1000;
            width = 1000;

            RNAPSparseMat = ChromosomeSpaceTimePlot.makeRNAPSparseMat(states, sim);

            [ax, figHandle] = edu.stanford.covert.cell.sim.util.PlotUtil.newAxesHandle();
            [densityBEMatrix, maxTime, maxSpace] = ChromosomeSpaceTimePlot.makeSpaceTimeDensityPlots(...
                ax, sim, states, RNAPSparseMat, ...
                ChromosomeSpaceTimePlot.chromProteins, [], width, height, simTimeStamp, simIdx);
            close(figHandle);
            [ax, figHandle] = edu.stanford.covert.cell.sim.util.PlotUtil.newAxesHandle();
            set(figHandle, 'Color', [1 1 1]);
            ChromosomeSpaceTimePlot.plotCircularDensity(...
                ax, densityBEMatrix, maxTime, sim, false, ...
                sprintf('Circular Chromosome Density Plot\nSimulation: %s #%s', simTimeStamp, simIdx));
            set(ax, 'Position', [0.2 0.2 0.6 0.6])
            saveas(figHandle, [outputDir filesep 'CircularDensity.eps'], 'epsc');
            close(figHandle);

            selectedTime = 4 * 3600;
            [ax, figHandle] = edu.stanford.covert.cell.sim.util.PlotUtil.newAxesHandle();
            ChromosomeSpaceTimePlot.makeRingPlot(...
                ax, sim, states, RNAPSparseMat, ...
                ChromosomeSpaceTimePlot.chromProteins, [], selectedTime, ...
                maxSpace);
            saveas(figHandle, [outputDir filesep 'RingPlot.eps'], 'epsc');
            close(figHandle);

            [ax, figHandle] = edu.stanford.covert.cell.sim.util.PlotUtil.newAxesHandle();
            whatToShow = {'Topoisomerase IV', 'DnaA Complex', 'LuxR', 'HTH Regulator', 'Ferric uptake repressor', 'Helicase'};
            ChromosomeSpaceTimePlot.plotSpaceTimeOverlay(...
                ax, sim, states, RNAPSparseMat, ...
                ChromosomeSpaceTimePlot.chromProteins, whatToShow, ...
                sprintf('Chromosome 1\nSimulation: %s #%s', simTimeStamp, simIdx));
            set(figHandle, 'Renderer', 'Painters')
            saveas(figHandle, [outputDir filesep 'SpaceTimeOverlay.eps'], 'epsc');
            close(figHandle);
            clear('states', 'stateNames', 'RNAPSparseMat', 'whatToShow', 'height', 'width');
%
            %% Metabolite Usages
            wIDsToShow = {'ATP', 'GTP'};
            desiredInfo = 'Usages';
            [ax, figHandle] = edu.stanford.covert.cell.sim.util.PlotUtil.newAxesHandle();
            ProcessMetaboliteUsage.processSingleCellMetaboliteHandler(wIDsToShow, 'Usages', figHandle, '2011_09_09_19_17_51', simNum)
            saveas(figHandle, [outputDir filesep 'ProcessUsages.eps'], 'epsc');
        end
    end
end


function methods = get_method_configs()
% Get properties of all proposal methods. For examples, this includes paths to
% candidate files, intermediate files, and also information about how to sort
% candidates.

% If you want to add your own method, add it at the bottom.

colormap = [ ...
0, 0, 0; ...
228, 229, 97 ; ...
163, 163, 163 ; ...
218, 71, 56 ; ...
219, 135, 45 ; ...
145, 92, 146 ; ...
83, 136, 173 ; ...
135, 130, 174 ; ...
225, 119, 174 ; ...
142, 195, 129 ; ...
138, 180, 66 ; ...
223, 200, 51 ; ...
92, 172, 158 ; ...
177,89,40;
0, 255, 255;
188, 128, 189;
255, 255, 0;
] ./ 256;

 % config = get_config();
 % precomputed_prefix = config.precomputed_candidates;

methods = [];
  %{
   i = numel(methods) + 1;
  methods(i).name = 'LabelTransfer';
  methods(i).short_name = 'LT';
  prefix = ['/home/gneelima/work/data/intermediate/objectProposals/labelTransfer/nonflann'];
  methods(i).candidate_dir = [prefix ];
  methods(i).best_voc07_candidates_file = [prefix 'best_candidates.mat'];
  methods(i).best_imagenet_candidates_file = [prefix 'best_candidates_imagenet.mat'];
  methods(i).order = 'none';
  methods(i).abo_voc07_candidates_file=[prefix 'abo_candidates.mat'];
% methods(i).extract = @run_prop;
  methods(i).num_candidates = false;
  methods(i).color = colormap(i,:);
  methods(i).is_baseline = false;

  i = numel(methods) + 1;
  methods(i).name = 'Sel.Search';
  methods(i).short_name = 'SS';
  prefix = ['/home/gneelima/Selective_search_imagenet/val/'];
  methods(i).candidate_dir = [prefix ];
  methods(i).best_voc07_candidates_file = [prefix 'best_candidates.mat'];
  methods(i).abo_voc07_candidates_file=[prefix 'abo_candidates.mat'];
  methods(i).order = 'ascend';
  methods(i).num_candidates = true;
  methods(i).color = colormap(i,:);
  methods(i).is_baseline = false;
%}
  i = numel(methods) + 1;
  methods(i).name = 'EdgeBoxes';
  methods(i).short_name = 'EB';
  prefix = ['/home/gneelima/work/data/output/objectProposals/edgeBoxes/imagenetBoxesIOU7/'];
  methods(i).candidate_dir = [prefix ];
  methods(i).best_voc07_candidates_file = [prefix 'best_candidates.mat'];
  methods(i).abo_voc07_candidates_file=[prefix 'abo_candidates.mat'];
  methods(i).order = 'descend';
  methods(i).extract = @run_edge_boxes;
  methods(i).num_candidates = true;
  methods(i).color = colormap(i,:);
  methods(i).is_baseline = false;
%{
  i = numel(methods) + 1;
  methods(i).name = 'Edge Boxes 50';
  methods(i).short_name = 'EB50';
  prefix = ['/home/gneelima/work/data/output/objectProposals/edgeBoxes/imagenetBoxesIOU5/'];
  methods(i).candidate_dir = [prefix ];
  methods(i).best_voc07_candidates_file = [prefix 'best_candidates.mat'];
  methods(i).abo_voc07_candidates_file=[prefix 'abo_candidates.mat'];
  methods(i).order = 'descend';
  methods(i).num_candidates = true;
  methods(i).color = colormap(i,:);
  methods(i).is_baseline = false;

  i = numel(methods) + 1;
  methods(i).name = 'Edge Boxes 90';
  methods(i).short_name = 'EB90';
  prefix = ['/home/gneelima/work/data/output/objectProposals/edgeBoxes/imagenetBoxesIOU9/'];
  methods(i).candidate_dir = [prefix ];
  methods(i).abo_voc07_candidates_file=[prefix 'abo_candidates.mat'];
  methods(i).best_imagenet_candidates_file = [prefix 'best_candidates_imagenet.mat'];
  methods(i).order = 'descend';
  methods(i).is_baseline = false;
 %}
  % add your own method here:
  if false
  i = numel(methods) + 1;
  methods(i).name = 'The full name of your method';
  methods(i).short_name = 'a very short version of the name';
  prefix = [precomputed_prefix 'ours-wip/'];
  methods(i).candidate_dir = [prefix ];
  methods(i).repeatability_candidate_dir = [prefix 'repeatability_mat'];
  methods(i).best_voc07_candidates_file = [prefix 'best_candidates.mat'];
  methods(i).abo_voc07_candidates_file=[prefix 'abo_candidates.mat'];
  methods(i).best_imagenet_candidates_file = [prefix 'best_candidates_imagenet.mat'];
  methods(i).repeatability_matching_file = [prefix 'repeatability_matching.mat'];
  % This specifies how to order candidates so that the first n, are the best n
  % candidates. For example we run a method for 10000 candidates and then take
  % the first 10, instead of rerunning for 10 candidates. Valid orderings are:
  %   none: candidates are already sorted, do nothing
  %   ascend/descend: sort by score descending or ascending
  %   random: random order
  %   biggest/smallest: sort by size of the bounding boxes
  methods(i).order = 'descend';
  % A function pointer to a method that runs your proposal detector.
  methods(i).extract = @run_edge_boxes90;
  % If your method supports sorting this should be empty. If your method has to
  % be rerun for every number of candidates we want, specify the number of
  % candidates here:
  methods(i).rerun_num_candidates = []; % ceil(10 .^ (2:0.5:4));
  % Specifies whether or not your method takes the desired number of candidates
  % as an input.
  % TODO(hosang): Is this actually used anywhere?
  methods(i).num_candidates = true;
  % color for drawing
  methods(i).color = colormap(i,:);
  % This should be false. Is used for drawing baselines dashed.
  methods(i).is_baseline = false;
  end

  % do the sorting dance
  sort_keys = [num2cell([methods.is_baseline])', {methods.name}'];
  for i = 1:numel(methods)
    sort_keys{i,1} = sprintf('%d', sort_keys{i,1});
  end
  [~,idx] = sortrows(sort_keys);
  for i = 1:numel(methods)
    methods(idx(i)).sort_key = i;
  end
end


function ret = spm_ov_mesh(varargin)
% Mesh tool - plugin for spm_orthviews
%
% This routine is a plugin to spm_orthviews. For general help about
% spm_orthviews and plugins type
%             help spm_orthviews
% at the MATLAB prompt.
%__________________________________________________________________________
% Copyright (C) 2017-2019 Wellcome Trust Centre for Neuroimaging

% Torben Lund, Guillaume Flandin & Christian Gaser
% $Id: spm_ov_mesh.m 7678 2019-10-24 14:08:03Z guillaume $

switch lower(varargin{1})
    % Context menu and callbacks
    case 'context_menu'
        item0 = uimenu(varargin{3}, ...
            'Label', 'Mesh',...
            'UserData',...
                struct('style','','width',1,'id',varargin{2}));
        item1 = uimenu(item0,...
            'Label','Select mesh(es)...',...
            'Tag','menu',...
            'Callback',@(hObj,event) mesh_menu(hObj,event,varargin{2}));
        item2 = uimenu(item0,...
            'Label','Options');
        item2_1 = uimenu(item2,...
            'Label','Line style...',...
            'UserData',struct('str','Line style', 'field','style'),...
            'Callback',@(hObj,event) mesh_options(hObj,event,item0));
        item2_2 = uimenu(item2,...
            'Label','Line width...',...
            'UserData',struct('str','Line width', 'field','width'),...
            'Callback',@(hObj,event) mesh_options(hObj,event,item0));
        ret = item0;
    case 'display'
        if nargin < 2
            varargin{2} = spm_input('Select image', '!+1', 'e');
        end
        mesh_add(varargin{2:end});
        mesh_delete(varargin{2});
        mesh_display(varargin{2});
    case 'redraw'
        mesh_redraw(varargin{2:end});
    otherwise
end

%==========================================================================
function mesh_options(hObj,event,hM)
Finter = spm_figure('FindWin', 'Interactive');
spm_input('!DeleteInputObj',Finter);
UDm = get(hObj,'UserData');
UDc = get(hM,'UserData');
if isnumeric(UDc.(UDm.field))
    N = Inf;
    T = 'e';
else
    N = Inf;
    T = 's';
end
if T == 's' && iscell(UDc.(UDm.field))
    UDc.(UDm.field) = '';
end
UDc.(UDm.field) = spm_input(UDm.str, '!+1', T, UDc.(UDm.field), N);
if T == 's'
    if ~isempty(strfind(UDc.(UDm.field),'{'))
        UDc.(UDm.field) = eval(UDc.(UDm.field));
    end
end
set(hM,'UserData',UDc);
try, mesh_redraw(UDc.id); end

%==========================================================================
function mesh_menu(hObj,event,i,varargin)

global st

mesh_add(i);
mesh_redraw(i);

%-Display extra information in the presence of multiple meshes
if numel(st.vols{i}.mesh.meshes) > 1
    hM = findobj(st.vols{i}.ax{1}.cm,'Label','Mesh');
    UD = get(hM,'UserData');
    fprintf('\n');
    for j=1:numel(st.vols{i}.mesh.meshes)
        fprintf('%s: %s\n',UD.style{j},st.vols{i}.mesh.fname{j});
    end
end

%==========================================================================
function mesh_add(i,g)

global st

try
    m = st.vols{i}.mesh.meshes;
    f = st.vols{i}.mesh.fname;
catch
    m = [];
    f = {};
end

if nargin < 2
    [g,sts] = spm_select([1 Inf],'mesh','Select mesh(es)...');
    if ~sts, st.vols{i}.mesh.meshes = m; return; end
end
if ischar(g), fname = cellstr(g); else fname = repmat({''},1,numel(g)); end
g = export(gifti(g),'patch');
for j=1:numel(g)
    if ~isfield(g(j),'vertices') || ~isfield(g(j),'faces')
        error('Selected file must contain triangular meshes.');
    end
end

st.vols{i}.mesh.meshes = [m, g];
st.vols{i}.mesh.fname = [f; fname];

hM = findobj(st.vols{i}.ax{1}.cm,'Label','Mesh');
if numel(get(hM,'children')) < 3
    uimenu(hM,...
        'Label','Display 3D mesh(es)',...
        'Tag','tmp',...
        'Callback',@(hObj,event) mesh_render(i));
    uimenu(hM,...
        'Label','Remove mesh(es)',...
        'Tag','tmp',...
        'Callback',@(hObj,event) mesh_clear(i));
end

%==========================================================================
function mesh_clear(i)

global st

mesh_delete(i);
st.vols{i} = rmfield(st.vols{i},'mesh');
hM = findobj(st.vols{i}.ax{1}.cm,'Label','Mesh');
set(findobj(hM,'Tag','menu'),'Label','Select mesh(es)');
delete(findobj(get(hM,'Children'),'Tag','tmp'));

%==========================================================================
function mesh_display(i)

global st

if nargin < 1
    i = 1;
end
o = st.vols{i}.mesh.meshes;

hM = findobj(st.vols{i}.ax{1}.cm,'Label','Mesh');
set(findobj(hM,'Tag','menu'),'Label','Add mesh(es)');
UD = get(hM,'UserData');
linespec  = UD.style;
linewidth = UD.width;
if ~iscell(linespec)
    if ~isempty(linespec)
        disp('Wrong Linestyle format please use {''linestyle1'',''linestyle2'',...}.');
    end

    linespec = {'b-' 'g-' 'r-' 'c-' 'm-' 'y-' 'k-' 'w-' ...
        'b-.' 'g-.' 'r-.' 'c-.' 'm-.' 'y-.' 'k-.' 'w-.' ...
        'b--' 'g--' 'r--' 'c--' 'm--' 'y--' 'k--' 'w--' };
    if numel(o) > numel(linespec)
        % If there are more than 24 surfaces plot them all in red
        linespec = repmat({'r-'},1,numel(o));
    end
end
if numel(linespec) < numel(o)
    disp('Meshes with unspecified line style are displayed in black.')
    linespec(end+1:numel(o)) = {'k-'};
end
if numel(linewidth) < numel(o)
    linewidth(end+1:numel(o)) = linewidth(end);
end
UD.style = linespec;
UD.width  = linewidth;
set(hM,'UserData',UD);

% This requires more work: does not work in voxel space, ie handling of
% st.Space is incomplete
bb = st.bb;
is = inv(st.Space);
lh = {};
for n=1:numel(o)
    vert = (st.vols{i}.premul(1:3,:)*[o(n).vertices';ones(1,size(o(n).vertices,1))])';

    % 1
    set(st.vols{i}.ax{1}.ax,'NextPlot','add');
    s = spm_mesh_contour(...
        struct('faces',o(n).faces,'vertices',vert),...
        st.centre(3));
    for j=1:numel(s)
        c = is*[s(j).xdata; s(j).ydata;ones(2,size(s(j).xdata,2))];
        c(2,:) = c(2,:)-bb(1,2)+1;
        c(1,:) = c(1,:)-bb(1,1)+1;
        lh{end+1} = plot(st.vols{i}.ax{1}.ax,c(1,:),c(2,:),...
            linespec{n},'LineWidth',linewidth(n));
    end

    % 2
    set(st.vols{i}.ax{2}.ax,'NextPlot','add');
    s = spm_mesh_contour(...
        struct('faces',o(n).faces,'vertices',vert(:,[3 1 2])),...
        st.centre(2));
    for j=1:numel(s)
        c = is*[s(j).xdata;ones(1,size(s(j).xdata,2));s(j).ydata;ones(1,size(s(j).ydata,2))];
        c(3,:) = c(3,:)-bb(1,1)+1;
        c(1,:) = c(1,:)-bb(1,3)+1;
        lh{end+1} = plot(st.vols{i}.ax{2}.ax,c(3,:),c(1,:),...
            linespec{n},'LineWidth',linewidth(n));
    end

    % 3
    if st.mode == 0
        warning('Not handled.');
    else
        set(st.vols{i}.ax{3}.ax,'NextPlot','add');
        s = spm_mesh_contour(...
            struct('faces',o(n).faces,'vertices',vert(:,[2 3 1])),...
            st.centre(1));
        for j=1:numel(s)
            c = is*[ones(1,size(s(j).xdata,2));[s(j).xdata; s(j).ydata];ones(1,size(s(j).ydata,2))];
            c(2,:) = -c(2,:)+bb(2,2)+1;
            c(3,:) = c(3,:)-bb(1,3)+1;
            lh{end+1} = plot(st.vols{i}.ax{3}.ax,c(2,:),c(3,:),...
                linespec{n},'LineWidth',linewidth(n));
        end
    end

end

set(cat(1,lh{:}),'HitTest','off');

st.vols{i}.mesh.handles = lh;

%==========================================================================
function mesh_redraw(i,varargin) %i, TM0, TD, CM0, CD, SM0, SD

mesh_delete(i);
mesh_display(i);

%==========================================================================
function mesh_delete(i)

global st

try, delete(cat(1,st.vols{i}.mesh.handles{:})); end

%==========================================================================
function mesh_render(i)

global st

m = st.vols{i}.mesh.meshes;
for j=1:numel(m)
    H = spm_mesh_render(m(j));
    set(H.figure,'Name',spm_str_manip(st.vols{i}.mesh.fname{j},'a60'))
end
% data cursor should be linked to orthviews through spm_XYZreg
% see spm_ovhelper_3Dreg.m


function obj = coef_Jacob3_qt_hand_eye_func_new(in1)
coef_J3 = in1(:,3);
coef_J6 = in1(:,6);
coef_J8 = in1(:,8);
coef_J9 = in1(:,9);
coef_J16 = in1(:,16);
coef_J18 = in1(:,18);
coef_J19 = in1(:,19);
coef_J25 = in1(:,25);
coef_J26 = in1(:,26);
coef_J27 = in1(:,27);
coef_J28 = in1(:,28);
coef_J29 = in1(:,29);
coef_J30 = in1(:,30);
coef_J31 = in1(:,31);
coef_J38 = in1(:,38);
coef_J40 = in1(:,40);
coef_J41 = in1(:,41);
coef_J47 = in1(:,47);
coef_J48 = in1(:,48);
coef_J49 = in1(:,49);
coef_J50 = in1(:,50);
coef_J51 = in1(:,51);
coef_J52 = in1(:,52);
coef_J53 = in1(:,53);
coef_J59 = in1(:,59);
coef_J60 = in1(:,60);
coef_J61 = in1(:,61);
coef_J62 = in1(:,62);
coef_J63 = in1(:,63);
coef_J64 = in1(:,64);
coef_J65 = in1(:,65);
coef_J66 = in1(:,66);
coef_J67 = in1(:,67);
coef_J68 = in1(:,68);
coef_J69 = in1(:,69);
coef_J70 = in1(:,70);
obj = [coef_J3,coef_J6,coef_J8.*2.0,coef_J9,coef_J16,coef_J18.*2.0,coef_J19,coef_J25.*3.0,coef_J26.*2.0,coef_J27,coef_J28,coef_J29,coef_J30,coef_J31,coef_J38,coef_J40.*2.0,coef_J41,coef_J47.*3.0,coef_J48.*2.0,coef_J49,coef_J50,coef_J51,coef_J52,coef_J53,coef_J59.*4.0,coef_J60.*3.0,coef_J61.*2.0,coef_J62.*2.0,coef_J63.*2.0,coef_J64.*2.0,coef_J65.*2.0,coef_J66,coef_J67,coef_J68,coef_J69,coef_J70];


function value = p05_i4 ( action, name, value )

%*****************************************************************************80
%
%% P05_I4 sets or gets I4 parameters for problem 05.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 March 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string ACTION,
%    'D' to set a parameter to its default value.
%    'S' to set a parameter,
%    'G' to get a parameter,
%    'I' to increment a parameter.
%
%    Input, string NAME, the name of the variable.
%    '#' is the number of calls to the integrand routine.
%
%    Input/output, integer VALUE.
%    * If ACTION = 'I', then VALUE is an input quantity, and is the
%      new value to be added to NAME.
%    * If ACTION = 'S', then VALUE is an input quantity, and is the
%      new value to be assigned to NAME.
%    * If ACTION = 'G', then VALUE is an output quantity, and is the
%      current value of NAME.
%
  persistent calls;

  if ( action == 'D' | action == 'd' )

    if ( name == '#' | name == '*' )
      calls = 0;
    end

  elseif ( action == 'G' | action == 'g' )

    if ( name == '#' )
      value = calls;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P05_I4 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P05_I4 - Fatal error!' );
    end

  elseif ( action == 'I' | action == 'i' )

    if ( name == '#' )
      calls = calls + value;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P05_I4 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P05_I4 - Fatal error!' );
    end

  elseif ( action == 'S' | action == 's' )

    if ( name == '#' )
      calls = value;
    else
      fprintf ( 1, '\n' );
      fprintf ( 1, 'P05_I4 - Fatal error!\n' );
      fprintf ( 1, '  Unrecognized name = "%s".\n', name );
      error ( 'P05_I4 - Fatal error!' );
    end

  else

    fprintf ( 1, '\n' );
    fprintf ( 1, 'P05_I4 - Fatal error!\n' );
    fprintf ( 1, '  Unrecognized action = "%s".\n', action );
    error ( 'P05_I4 - Fatal error!' );

  end

  return
end


%   CLASS Settings_Interface
% =========================================================================
%
% DESCRIPTION
%   Abstract class with basic properties and methods that a setting class
%   must have
%
% COMMENTS
% Settings have been build with multiple inheritance
% A standard abstract interface have been created: Setting_Interface
% it add to each subclass the object log
% force the subclasses to implement three basic methods:
%  - import:       when a setting object of identical class, or that inherits from the same class is passed to this function, the relative parameters are copied in the calling object
%                  when an ini file is the input of the function, the object is updated with the settings contained into the file
%  - toString:     display the content of the object, in a human readable way, a goGPS user can "ask" for the value of the settings on screen
%  - export:       create a cell array of strings containing the settings in plain text ini format. The variable it's the raw data format of Ini_Manager
%

%--------------------------------------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  Written by:        Andrea Gatti
%  Contributors:      Andrea Gatti, ...
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%    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/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------

classdef Settings_Interface < Exportable
    properties (Abstract)
    end

    methods  (Abstract)
        import(obj, settings);
        % This function import the settings of the current class from another setting object having the same properties, or an ini file

        str = toString(obj, str)
        % Display the content of the class in a human readable format

        str_cell = export(obj, str_cell)
        % Conversion to string ini format of the minimal information needed to reconstruct the obj
    end

    methods (Access = 'public')
        function ini = save(this, file_path)
            % Save to a file (in INI fomat) the content of the Settings object
            % SYNTAX: <ini> = this.save(file_path);
            % return optionally the ini manager object used by the save function
            ini = Ini_Manager(file_path, this.export());
        end

        function [home_reset] = importIniFile(this, file_path)
            % Import from an INI file the content of the Settings object
            %
            % OUTPUT
            %   home_reset is empty if the home have not been changed
            %              otherwise it contains the old path
            % SYNTAX
            %   home_reset = this.importIniFile(file_path);
            ini = Ini_Manager(file_path);
            fnp = File_Name_Processor;
            prj_home = fnp.checkPath(ini.getData('prj_home'));
            home_reset = prj_home;
            if ~isempty(prj_home) && ~exist(prj_home, 'dir')
                [path_str, ~, ~] = fileparts(fnp.getFullDirPath(file_path));
                prj_home = fnp.getFullDirPath(strcat(path_str, [filesep '..' filesep]), pwd);
                if exist(prj_home, 'dir')
                    ini.setData('prj_home', prj_home);
                else
                    home_reset = '';
                end
            else
                home_reset = '';
            end
            this.import(ini);
        end

        function updateSettingsFile(this, file_path)
            this.importIniFile(file_path);
            this.save(file_path);
        end

    end

    % =========================================================================
    %%  SETTERS IO
    % =========================================================================
    methods
        function value = setProperty(this, prop_name, value)
            this.(prop_name) = value;
        end
    end

    % =========================================================================
    %%  GETTERS IO
    % =========================================================================
    methods
        function value = getProperty(this, prop_name)
            props = fieldnames(this);
            if any(strcmp(props,prop_name))
                value = this.(prop_name);
            else
                value = [];
            end
        end
    end

    % =========================================================================
    %  TEST PARAMETERS VALIDITY
    % =========================================================================
    methods (Access = 'protected')

        function checkLogicalField(this, field_name)
            % Check if a logical field of the object is a valid logical number
            % To make the function works it is needed to have defined the default
            % value of the field as a constant with same name but upper case
            % This superclass function must be copied in each child (to
            % have read / write permission on the parameters
            %
            % SYNTAX
            %   this.checkLogicalField(string_field_name);
            this.(field_name) = this.checkLogical(field_name, this.(field_name), this.(upper(field_name)));
        end

        function is_existing = checkStringField(this, field_name, empty_is_valid, check_existence)
            % Check if a string field of the object is a valid string
            % To make the function works it is needed to have defined the default
            % value of the field as a constant with same name but upper case
            % This superclass function must be copied in each child (to
            % have read / write permission on the parameters
            %
            % SYNTAX
            %   this.checkStringField(string_field_name, <empty_is_valid == false>, <check_existence == false>);
            switch nargin
                case 2, [this.(field_name), is_existing] = this.checkString(field_name, this.(field_name), this.(upper(field_name)));
                case 3, [this.(field_name), is_existing] = this.checkString(field_name, this.(field_name), this.(upper(field_name)), empty_is_valid);
                case 4, [this.(field_name), is_existing] = this.checkString(field_name, this.(field_name), this.(upper(field_name)), empty_is_valid, check_existence);
                otherwise, error('Settings checkStringField called with the wrong number of parameters');
            end
        end

        function checkNumericField(this, field_name, limits, valid_val)
            % Check if a numeric field of the object is valid
            % To make the function works it is needed to have defined the default
            % value of the field as a constant with same name but upper case
            % This superclass function must be copied in each child (to
            % have read / write permission on the parameters
            %
            % SYNTAX
            %   this.checkNumericField(string_field_name, <limits>, <valid_values>);
            switch nargin
                case 2, this.(field_name) = this.checkNumber(field_name, this.(field_name), this.(upper(field_name)));
                case 3, this.(field_name) = this.checkNumber(field_name, this.(field_name), this.(upper(field_name)), limits);
                case 4, this.(field_name) = this.checkNumber(field_name, this.(field_name), this.(upper(field_name)), limits, valid_val);
                otherwise, error('Settings checkNumericField called with the wrong number of parameters');
            end
        end

        function checked_val = checkLogical(this, field_name, field_val, default_val)
            % Check if a logical is a valid logical
            % This superclass function must be called in each child
            %
            % SYNTAX
            %   checked_val = this.checkLogicalField(string_field_name);
            checked_val = default_val;
            if (~isempty(field_val)) && (~isnan(field_val))
                checked_val = logical(field_val);
            else
                Core.getLogger.addWarning(sprintf('The settings field %s is not valid => using default %d', field_name, checked_val));
            end
        end

        function [checked_val, is_existing] = checkCellString(this, field_name, field_val, default_val, empty_is_valid, check_existence)
            % Check if a string is a valid string
            % check_existence 0: do not check
            %                 1: check and do nothing
            %                 2: check and try to correct
            % SYNTAX
            %   checked_val = this.checkString(string_field_name, <empty_is_valid == false>, <check_existence == false>);
            if (nargin < 5)
                empty_is_valid = false;
            end
            if (nargin < 6)
                check_existence = false;
            end

            [checked_val, is_existing] = this.checkString(field_name, field_val, default_val, empty_is_valid, check_existence);
            if ~iscell(checked_val)
                checked_val = {checked_val};
            end
        end

        function [checked_val, is_existing] = checkString(this, field_name, field_val, default_val, empty_is_valid, check_existence)
            % Check if a string is a valid string
            % check_existence 0: do not check
            %                 1: check and do nothing
            %                 2: check and try to correct
            %
            % SYNTAX
            %   [checked_val, is_existing] = this.checkString(string_field_name, <empty_is_valid == false>, <check_existence == 0>);
            if (nargin < 5)
                empty_is_valid = false;
            end
            if (nargin < 6)
                check_existence = 0;
            end

            is_existing = false;
            checked_val = default_val;
            default_val = field_val;
            if ~isempty(field_val) && iscell(field_val) % A cell of strings must contain at least one string
                field_val = field_val{1};
            end
            %if (ischar(field_val) || (empty_is_valid && isempty(field_val))) && ((~isempty(field_val)) || empty_is_valid) && ((exist(field_val,'file') || exist(field_val,'dir')) || ~check_existence)
            if ischar(field_val)  && (empty_is_valid || ~isempty(field_val)) && (~check_existence || exist(field_val, 'file'))
                is_existing = true;
                checked_val = default_val;
            else
                if check_existence
                    if exist(default_val, 'file')
                        checked_val = default_val;
                    else
                        checked_val = field_val;
                    end
                end

                if isempty(field_val) || ~any(strcmp(checked_val, field_val))
                    log = Core.getLogger();
                    if check_existence > 1 || ~empty_is_valid
                        if iscell(checked_val)
                            log.addWarning(sprintf('The value "%s" of the settings field %s is not valid => using default: "%s"', iif(isempty(field_val), 'empty', field_val), field_name, Ini_Manager.strCell2Str(checked_val)));
                        else
                            log.addWarning(sprintf('The value "%s" of the settings field %s is not valid => using default: "%s"', iif(isempty(field_val), 'empty', field_val), field_name, checked_val));
                        end
                    else
                        if empty_is_valid
                            checked_val = field_val;
                        else
                            log.addWarning(sprintf('The value "%s" of the settings field %s is not valid!!!', iif(isempty(field_val), 'empty', field_val), field_name));
                        end
                    end
                end
            end
        end

        function checked_val = checkNumber(this, field_name, field_val, default_val, limits, valid_val)
            % Check if a number is valid
            %
            % SYNTAX
            %   checked_val = this.checkNumericField(string_variable_name, value, default_value, <limits>, <valid_values>);
            checked_val = default_val;
            log = Core.getLogger();
            if isnumeric(field_val) && (~isempty(field_val)) && (any(~isnan(field_val)))
                checked_val = field_val;
                % if I have limits to check => check for out of bound
                for i = 1 : numel(checked_val)
                    if (nargin >= 5) && (numel(limits) == 2) && ...
                            ((checked_val(i) > limits(2)) || (checked_val(i) < limits(1)))
                        checked_val(i) = max(limits(1), min(limits(2), checked_val(i)));
                        log.addWarning(sprintf('The value %g of the settings field %s is not within the valid limits (%g .. %g) => updating it to %g', field_val(i), field_name, limits(1),limits(2), checked_val(i)));
                    end
                    % if I have a set of values => check for set intersection
                    if (nargin >= 6) && (~isempty(valid_val)) && (~ismember(checked_val(i), valid_val))
                        checked_val(i) = default_val;
                        log.addWarning(sprintf('The value %g for the settings field %s is not valid => using default %g. It should be one of: %s', field_val(i), field_name, checked_val(i), sprintf('%g ', valid_val)));
                    end
                end
            else
                log.addWarning(sprintf('The settings field %s is not valid => using default [%s ]', field_name, sprintf(' %g', checked_val)));
            end
        end
    end

    % =========================================================================
    %  TEST
    % =========================================================================

    methods (Access = 'protected')
        function testInterfaceRoutines(this)
            % test the class (Interface Routines)
            % SINTAX: this.testInterfaceRoutines();

            log = Core.getLogger;
            try
                vl = log.getVerbosityLev();
                log.setVerbosityLev(1e3);
                test = this;
                raw_data = test.export();

                ini = Ini_Manager('test__.ini', raw_data);
                ini.showData();

                test.import(ini);
                test_copy = repmat(test,2,1); test_copy = test_copy(2);
                test.import(test_copy);
                clear test_copy;
                fprintf('\n');
                disp(test.toString());
                %delete('test__.ini');
            catch ex
                log.addError(['Test failed: ' ex.message]);
                Core_Utils.printEx(ex);
            end
            log.setVerbosityLev(vl);
        end
    end
end


function result  = batch_omp_spr(Y, K, epsilon, sparse_output)
    if nargin < 4
        sparse_output = 1;
    end
    result = mex_batch_omp_spr(Y, K, epsilon, sparse_output);
end


function rmStimInfo(view, fid);
% rmStimInfo - get information of current loaded rm stimulus
% Useful if you want to predict the reponses to a scan using a previously
% solved rm and a new stimulus. Loading the model generally also loads the
% stimulus associated with the model, which may be different from the
% stimulus associated with the current scan.
%
% rmStimInfo(view, id);
%
% 11/2006 SOD: wrote it.
%  9/2008  JW: added 'fid' as input argument to permit printing to file

% to do: need more specific information?

if notDefined('view'), view = getCurView; end;
if notDefined('fid'), fid = 1; end

% load params
try
    params = viewGet(view,'rmparams');
catch,
    params = [];
end;

% if no parameters are loaded exit
if isempty(params),
    disp(sprintf('[%s]:No stimulus parameters loaded.',mfilename));
    return;
end;

nscans = numel(params.stim);

% give info:
fprintf(fid,'\n---------------------------------------\n');
fprintf(fid,'[%s]:There are stimulus descriptions for %d scan(s).\n',mfilename, nscans);

for n=1:nscans,
    fprintf(fid,'\tScan %d: %s\n',    n,params.stim(n).stimType);

    fprintf(fid,'\t\tStim size: %d deg (radius)\n',  params.stim(n).stimSize);

    fprintf(fid,'\t\tNumber of frames: %d\n', params.stim(n).nFrames);

    if strcmpi(params.stim(n).stimType, 'stimfromscan')
        fprintf(fid,'\t\tFilter Type: %s\n',     params.stim(n).imFilter);
        fprintf(fid,'\t\tImage file:\n\t\t\t%s\n',     params.stim(n).imFile);
        fprintf(fid,'\t\tParameter file:\n\t\t\t%s\n', params.stim(n).paramsFile);
    end

end
fprintf(fid,'---------------------------------------\n\n');

return


function varargout = spm_update(update)
% Check (and install) SPM updates from the FIL server
% FORMAT spm_update
% This function will contact the FIL server, compare the version number of
% the updates with the one of the SPM installation currently in the MATLAB
% path and display the outcome.
%
% FORMAT spm_update(update)
% Invoking this function with any input parameter will do the same as
% above but will also attempt to download and install the updates.
% Note that it will close any open window and clear the workspace.
%
% FORMAT [sts, msg] = spm_update(update)
% sts  - status code:
%        NaN - SPM server not accessible
%        Inf - no updates available
%        0   - SPM installation up to date
%        n   - new revision <n> is available for download
% msg  - string describing outcome, that would otherwise be displayed.
%__________________________________________________________________________
% Copyright (C) 2010-2020 Wellcome Trust Centre for Neuroimaging

% Guillaume Flandin
% $Id: spm_update.m 7770 2020-01-13 11:26:14Z guillaume $

vspm = spm('Ver');
url  = ['http://www.fil.ion.ucl.ac.uk/spm/download/' lower(vspm) '_updates/'];

if ~nargin
    update = false;
else
    update = true;
end

%-Get list of updates from SPM server
%--------------------------------------------------------------------------
[s,status] = urlread(url);
if ~status
    sts = NaN;
    msg = 'Cannot access SPM server.';
    if ~nargout, error(msg); else varargout = {sts, msg}; end
    return
end

%-Get revision number of latest update
%--------------------------------------------------------------------------
n = regexp(s,[lower(vspm) '_updates_r(\d.*?)\.zip'],'tokens','once');
if isempty(n)
    sts = Inf;
    msg = 'There are no updates available yet.';
    if ~nargout, fprintf([blanks(9) msg '\n']);
    else varargout = {sts, msg}; end
    return
end
n = str2double(n{1});

%-Get revision number of SPM installation
%--------------------------------------------------------------------------
try
    [v,r] = spm('Ver','',1); r = str2double(r);
catch
    error('SPM cannot be found in MATLAB path.');
end
if ~strcmp(v,vspm), error('Your SPM version is %s and not %s',v,vspm); end
rs = [6225 6470 6685 6906 7219 7487 7771];
if isnan(r), r = rs(1); end
if floor(r) == str2double(vspm(4:end))
    try
        r = rs(round((r-floor(r))*10)+1);
    catch
        r = rs(end);
    end
end

%-Compare versions
%--------------------------------------------------------------------------
if n > r
    sts = n;
    msg = sprintf('         A new version of %s is available on:\n',vspm);
    msg = [msg sprintf('   %s\n',url)];
    msg = [msg sprintf('        (Your version: %d - New version: %d)\n',r,n)];
    if ~nargout, fprintf(msg); else varargout = {sts, msg}; end
else
    sts = 0;
    msg1 = sprintf('Your version of %s is up to date.',vspm);
    msg2 = sprintf('(Your version: %d - Online version: %d)',r,n);
    if ~nargout, fprintf([blanks(9) msg1 '\n' blanks(5) msg2 '\n']);
    else varargout = {sts, sprintf('%s\n%s',msg1,msg2)}; end
    return
end

%-and install...
%--------------------------------------------------------------------------
if update
    d = spm('Dir');
    delete(get(0,'Children')); spm('clean'); evalc('spm_rmpath'); drawnow
    try
        lastwarn('');
        m = '          Download and install in progress...\n';
        if ~nargout, fprintf(m); else varargout = {sts, [msg m]}; end
        s = unzip([url sprintf('%s_updates_r%d.zip',lower(vspm),n)], d);
        m = sprintf('         Success: %d files have been updated.\n',numel(s));
        if ~nargout, fprintf(m); else varargout = {sts, [msg m]}; end
    catch
        le = lasterror;
        switch le.identifier
            case 'MATLAB:checkfilename:urlwriteError'
                fprintf('          Update failed: cannot download update file.\n');
            otherwise
                fprintf('\n%s\n',le.message);
        end
    end
    [warnmsg, msgid] = lastwarn;
    switch msgid
        case ''
        case 'MATLAB:extractArchive:unableToCreate'
            fprintf('          Update failed: check folder permission.\n');
        case 'MATLAB:extractArchive:unableToOverwrite'
            fprintf('          Update failed: check file permissions.\n');
        otherwise
            fprintf('          Update failed: %s.\n',warnmsg);
    end
    addpath(d);
    rehash toolboxcache;
end