classdef Generic3DTrajectoryViewType < AbstractTrajectoryViewType
    %Inertial3DTrajectoryViewType Summary of this class goes here
    %   Detailed explanation goes here

    properties

    end

    methods
        function obj = Generic3DTrajectoryViewType()

        end

        function [hCBodySurf, childrenHGs] = plotStateLog(obj, orbitNumToPlot, lvdData, viewProfile, handles, app)
%             dAxes = app.dispAxes;
            dAxes = handles.dispAxes;
            hFig = app.ma_LvdMainGUI;
            celBodyData = lvdData.celBodyData;
            stateLog = lvdData.stateLog;

%             axes(dAxes);
%             cla(dAxes);
%             cla(dAxes,'reset');
            delete(dAxes.Children);
            dAxes.Color = viewProfile.backgroundColor.color;

            hFig.Renderer = viewProfile.renderer.renderer;
            if(viewProfile.renderer == FigureRendererEnum.OpenGL && ~isunix())
                d = opengl('data');
                if(strcmpi(d.HardwareSupportLevel,'full'))
                    opengl hardware;
                elseif(strcmpi(d.HardwareSupportLevel,'basic'))
                    opengl hardwarebasic;
                end
            end

            hFig.GraphicsSmoothing = 'on';

            if(stateLog.getNumberOfEntries() == 0)
                return;
            end

            viewInFrame = viewProfile.frame;
            viewCentralBody = viewInFrame.getOriginBody();
            lvdStateLogEntries = LaunchVehicleStateLogEntry.empty(1,0);
            switch viewProfile.trajEvtsViewType
                case ViewEventsTypeEnum.SoIChunk
                    entries = stateLog.getAllEntries();
%                     maStateLog = stateLog.getMAFormattedStateLogMatrix(false);

%                     chunkedStateLog = breakStateLogIntoSoIChunks(maStateLog);
                    chunkedStateLog = stateLog.breakUpStateLogBySoIChunk();
                    if(orbitNumToPlot > size(chunkedStateLog,1))
                        orbitNumToPlot = size(chunkedStateLog,1);
                        set(dAxes,'UserData',orbitNumToPlot);
                    end
                    subStateLogs = chunkedStateLog(orbitNumToPlot,:);

                    bodyId = subStateLogs{1}(1,8);
                    bodyInfo = celBodyData.getBodyInfoById(bodyId);
                    evtIds = [];
                    for(i=1:length(subStateLogs))
                        subStateLog = subStateLogs{i};

                        if(isempty(subStateLog))
                            continue;
                        end

                        bodyId = subStateLog(1,8);
                        bodyInfo = celBodyData.getBodyInfoById(bodyId);
                        inertialFrame = bodyInfo.getBodyCenteredInertialFrame();

                        elemSet = CartesianElementSet(subStateLog(:,1), subStateLog(:,2:4)', subStateLog(:,5:7)', inertialFrame);
%                         elemSet = repmat(CartesianElementSet.getDefaultElements(), [1, size(subStateLog,1)]);
%                         for(j=1:size(subStateLog,1))
%                             elemSet(j) = CartesianElementSet(subStateLog(j,1), subStateLog(j,2:4)', subStateLog(j,5:7)', inertialFrame);
%                         end
                        elemSet = convertToFrame(elemSet, viewInFrame);

                        subStateLog(:,2:4) = [elemSet.rVect]';
                        subStateLog(:,5:7) = [elemSet.vVect]';

                        evtIds = [evtIds, unique(subStateLog(:,13))']; %#ok<AGROW>

                        subStateLogs{i} = subStateLog;
                    end

                    evtIds = unique(evtIds);
                    for(i=1:length(entries))
                        entry = entries(i);

                        if(not(isempty(entry.event.getEventNum())) && ...
                           ismember(entry.event.getEventNum(), evtIds) && ...
                           entry.centralBody == bodyInfo)
                            lvdStateLogEntries(end+1) = entry; %#ok<AGROW>
                        end
                    end

                    numTotMissionSegs = size(chunkedStateLog,1);

%                     curMissionSegStr = num2str(orbitNumToPlot);
%                     totalMissionSegStr = num2str(numTotMissionSegs);

                    if(numTotMissionSegs <= 1)
                        app.decrOrbitToPlotNum.Enable = 'off';
                        app.incrOrbitToPlotNum.Enable = 'off';
                    else
                        app.decrOrbitToPlotNum.Enable = 'on';
                        app.incrOrbitToPlotNum.Enable = 'on';
                    end
                case ViewEventsTypeEnum.All
                    entries = stateLog.getAllEntries();
                    maStateLogMatrix = stateLog.getMAFormattedStateLogMatrix(false);
                    numRows = size(maStateLogMatrix,1);
%                     subStateLogsMat = NaN(numRows, 13);

                    cartesianEntry = convertToFrame(getCartesianElementSetRepresentation(entries, false),viewInFrame);
                    times = [cartesianEntry.time]';
                    rVect = [cartesianEntry.rVect]';
                    vVect = [cartesianEntry.vVect]';
                    bodyId = viewCentralBody.id + zeros(numRows,1);
                    subStateLogsMat = [times, rVect, vVect, bodyId, maStateLogMatrix(:,9:13)];
%                     for(i=1:numRows)
%                         tempMaMatrix = entries(i).getMAFormattedStateLogMatrix(false);
%
%                         cartesianEntry =
%                         cartesianEntry = cartesianEntry.convertToFrame(viewInFrame);
%
%                         subStateLogsMat(i,:) = [entries(i).time, cartesianEntry.rVect', cartesianEntry.vVect', viewCentralBody.id, maStateLogMatrix(i,9:13)];
%                     end

                    subStateLogs = {};
                    for(evtNum=1:max(subStateLogsMat(:,13)))
                        subStateLogs{evtNum} = subStateLogsMat(subStateLogsMat(:,13) == evtNum,:); %#ok<AGROW>
                    end

%                     curMissionSegStr = num2str(1);
%                     totalMissionSegStr = num2str(1);

                    lvdStateLogEntries = entries;
                otherwise
                    error('Unknown trajectory view type when plotting trajectory: %s', viewProfile.frame.name);
            end

            eventsList = [];
            minTime = Inf;
            maxTime = -Inf;
            for(i=1:length(subStateLogs))
                if(~isempty(subStateLogs{i}))
                    eventsList = [eventsList;unique(subStateLogs{i}(:,13))]; %#ok<AGROW>
                end
                if(i>1)
                    prevSubStateLog = subStateLogs{i-1};
                else
                    prevSubStateLog = NaN(1,size(subStateLogs{i},2));
                end

                if(size(subStateLogs{i},1)>1)
                    [childrenHGs] = plotSubStateLog(subStateLogs{i}, prevSubStateLog, lvdData, dAxes);

                    minTime = min([minTime, min(subStateLogs{i}(:,1))]);
                    maxTime = max([maxTime, max(subStateLogs{i}(:,1))]);
                end
            end

            if(viewInFrame.typeEnum == ReferenceFrameEnum.BodyFixedRotating && ...
               viewProfile.showLongLatAnnotations)
                plotBodyFixedGrid(dAxes, viewCentralBody);
            end

            showSoI = viewProfile.showSoIRadius;

            if(viewProfile.showThrustVectors)
                entryInc = viewProfile.thrustVectEntryIncr;
                scale = viewProfile.thrustVectScale;
                color = viewProfile.thrustVectColor.color;
                lineStyle = viewProfile.thrustVectLineType.linespec;

                subsetLvdStateLogEntries = lvdStateLogEntries(1:entryInc:length(lvdStateLogEntries));
                subsetLvdStateLogEntries = subsetLvdStateLogEntries(:)';
                cartesianEntries = convertToFrame(getCartesianElementSetRepresentation(subsetLvdStateLogEntries), viewInFrame);

                rVects = [cartesianEntries.rVect];
                tVects = [];

                [~, ~, ~, rotMatToInertial12] = viewInFrame.getOffsetsWrtInertialOrigin([cartesianEntries.time], cartesianEntries);
                for(i=1:length(subsetLvdStateLogEntries)) %#ok<*NO4LP>
                    entry = subsetLvdStateLogEntries(i);
                    cartesianEntry = cartesianEntries(i);

                    tVect = lvd_ThrottleTask(entry, 'thrust_vector', cartesianEntry.frame);

                    if(norm(tVect) > 0)
                        [~, ~, ~, rotMatToInertial32] = entry.centralBody.getBodyCenteredInertialFrame().getOffsetsWrtInertialOrigin(entry.time, cartesianEntry);

                        tVectNew = rotMatToInertial32 * rotMatToInertial12(:,:,i)' * tVect;
                    else
                        tVectNew = [0;0;0];
                    end

                    tVects = [tVects, tVectNew]; %#ok<AGROW>
                end

                tVects = scale .* tVects;

                hold(dAxes,'on');
                quiver3(dAxes, rVects(1,:),rVects(2,:),rVects(3,:), tVects(1,:),tVects(2,:),tVects(3,:), 0, 'Color',color, 'LineStyle',lineStyle);
                hold(dAxes,'off');
            end

%             lfm = LiftForceModel();
%             rVects = NaN([3, length(lvdStateLogEntries)]);
%             forceVect = NaN([3, length(lvdStateLogEntries)]);
%             for(i=1:length(lvdStateLogEntries))
%                 stateLogEntry = lvdStateLogEntries(i);
%
%                 bodyInfo = stateLogEntry.centralBody;
%                 ut = stateLogEntry.time;
%                 rVect = stateLogEntry.position;
%                 vVect = stateLogEntry.velocity;
%                 aero = stateLogEntry.aero;
%                 mass = stateLogEntry.getTotalVehicleMass();
%                 attState = stateLogEntry.attitude;
%
%                 rVects(:,i) = rVect;
%                 force = lfm.getForce(ut, rVect, vVect, mass, bodyInfo, aero, [], [], [], [], [], [], [], [], [], [], attState);
%                 forceVect(:,i) = 1000*force;
%             end
%
%             hold(dAxes,'on');
%             quiver3(dAxes, rVects(1,:),rVects(2,:),rVects(3,:), forceVect(1,:),forceVect(2,:),forceVect(3,:), 0, 'Color','c', 'LineStyle','-');
%             hold(dAxes,'off');

            if(showSoI && ~isempty(viewCentralBody.getParBodyInfo(celBodyData)))
                hold(dAxes,'on');

                r = getSOIRadius(viewCentralBody, viewCentralBody.getParBodyInfo(celBodyData));

                x = r*sin(0:0.01:2*pi);
                y = r*cos(0:0.01:2*pi);
                z = zeros(size(x));
                plot3(dAxes, x, y, z, 'k--','LineWidth',0.5);
                plot3(dAxes, y, z, x, 'k--','LineWidth',0.5);
                plot3(dAxes, z, x, y, 'k--','LineWidth',0.5);

                hold(dAxes,'off');
            end

            if(viewProfile.dispXAxis || viewProfile.dispYAxis || viewProfile.dispZAxis)
                axisLength = 2*viewCentralBody.radius;

                hold(dAxes,'on');
                if(viewProfile.dispXAxis)
                    quiver3(dAxes, 0,0,0, axisLength,0,0, 'r', 'LineWidth',2);
                end

                if(viewProfile.dispYAxis)
                    quiver3(dAxes, 0,0,0, 0,axisLength,0, 'g', 'LineWidth',2);
                end

                if(viewProfile.dispZAxis)
                    quiver3(dAxes, 0,0,0, 0,0,axisLength, 'b', 'LineWidth',2);
                end
                hold(dAxes,'off');
            end

            %plot central body
            [hCBodySurf, hCBodySurfXForm] = ma_initOrbPlot(hFig, dAxes, viewCentralBody);
            hCBodySurf.EdgeAlpha = viewProfile.meshEdgeAlpha;

            if(viewProfile.showAtmosphere && viewCentralBody.atmohgt > 0)
                hold(dAxes,'on');
                atmoRadius = viewCentralBody.radius + viewCentralBody.atmohgt;
                [X,Y,Z] = sphere(50);
                hCBodySurf = surf(dAxes, atmoRadius*X,atmoRadius*Y,atmoRadius*Z, 'BackFaceLighting','lit', 'FaceLighting','gouraud', 'FaceColor',[223 223 223]/255, 'FaceAlpha',0.2, 'EdgeLighting','gouraud', 'LineWidth',0.1, 'EdgeColor','none');
                hold(dAxes,'off');
            end

            eventsList = unique(eventsList);
            minEventNum = min(eventsList);
            maxEventNum = max(eventsList);

            if(minEventNum < maxEventNum)
                eventStr = ['Events ', num2str(minEventNum), ' - ', num2str(maxEventNum)];
            else
                eventStr = ['Event ', num2str(minEventNum)];
            end

            hDispAxisTitleLabel = handles.dispAxisTitleLabel;
            titleStr = sprintf('%s Orbit -- %s\n%s', viewCentralBody.name, eventStr, viewInFrame.getNameStr());
            hDispAxisTitleLabel.String = titleStr;
            hDispAxisTitleLabel.TooltipString = sprintf('Frame: %s', viewInFrame.getNameStr());

            set(dAxes,'LineWidth',1);
            set(dAxes,'Box','on');
            grid(dAxes,viewProfile.gridType.gridStr);
            dAxes.GridColor = viewProfile.majorGridColor.color;
            dAxes.MinorGridColor = viewProfile.minorGridColor.color;
            dAxes.GridAlpha = viewProfile.gridTransparency;
%             axis(dAxes,'equal');
%             axis(dAxes,'tight');

%             xlabel(dAxes, '');
%             ylabel(dAxes, '');

            set(dAxes,'XTickLabel',[]);
            set(dAxes,'YTickLabel',[]);
            set(dAxes,'ZTickLabel',[]);

            hold(dAxes,'off');

            if(not(viewProfile.updateViewAxesLimits))
%                 setappdata(handles.ma_LvdMainGUI,'dispOrbitXLim',xlim(dAxes));
%                 setappdata(handles.ma_LvdMainGUI,'dispOrbitYLim',ylim(dAxes));
%                 setappdata(handles.ma_LvdMainGUI,'dispOrbitZLim',zlim(dAxes));

%                 zoom reset;

%                 view(dAxes,viewProfile.viewAzEl);

                camPos = viewProfile.viewCameraPosition;
                camTgt = viewProfile.viewCameraTarget;
                camUpVec = viewProfile.viewCameraUpVector;
                camVA = viewProfile.viewCameraViewAngle;
                if(not(any(isnan(camPos))))
                    dAxes.CameraPosition = camPos;
                end

                if(not(any(isnan(camTgt))))
                    dAxes.CameraTarget = camTgt;
                end

                if(not(any(isnan(camUpVec))))
                    dAxes.CameraUpVector = camUpVec;
                end

                if(not(any(isnan(camVA))))
                    dAxes.CameraViewAngle = camVA;
                end

%                 if(any(isnan(viewProfile.viewZoomAxLims)))
%                     viewProfile.viewZoomAxLims = [xlim(dAxes);
%                                                   ylim(dAxes);
%                                                   zlim(dAxes)];
%                 else
%                     dAxes.XLim = viewProfile.viewZoomAxLims(1,:);
%                     dAxes.YLim = viewProfile.viewZoomAxLims(2,:);
%                     dAxes.ZLim = viewProfile.viewZoomAxLims(3,:);
%                 end
%
            else
                cameratoolbar('ResetCamera');
                view(dAxes, 3);
            end

            vehPosVelData = LaunchVehicleViewProfile.createVehPosVelData(subStateLogs, lvdData.script.evts, viewInFrame);
            vehAttData = LaunchVehicleViewProfile.createVehAttitudeData(vehPosVelData, lvdStateLogEntries, lvdData.script.evts, viewInFrame);

            hold(dAxes,'on');
            viewProfile.createBodyMarkerData(dAxes, subStateLogs, viewInFrame, showSoI, viewProfile.meshEdgeAlpha, lvdData.script.evts);
            viewProfile.createTrajectoryMarkerData(subStateLogs, lvdData.script.evts);
            viewProfile.createBodyAxesData(vehPosVelData, vehAttData); %lvdStateLogEntries, lvdData.script.evts, viewInFrame
            viewProfile.createSunLightSrc(dAxes, viewInFrame);
            viewProfile.createGroundObjMarkerData(dAxes, lvdStateLogEntries, vehPosVelData, lvdData.script.evts, viewInFrame, celBodyData);
            viewProfile.createCentralBodyData(viewCentralBody, hCBodySurfXForm, viewInFrame);
            viewProfile.createPointData(viewInFrame, subStateLogs, lvdData.script.evts);
            viewProfile.createVectorData(viewInFrame, subStateLogs, lvdData.script.evts);
            viewProfile.createRefFrameData(viewInFrame, subStateLogs, lvdData.script.evts);
            viewProfile.createAngleData(viewInFrame, subStateLogs, lvdData.script.evts);
            viewProfile.createPlaneData(viewInFrame, subStateLogs, lvdData.script.evts);
            viewProfile.createSensorData(lvdStateLogEntries, vehPosVelData, vehAttData, viewInFrame);
            viewProfile.createSensorTargetData(viewInFrame);

            viewProfile.configureTimeSlider(minTime, maxTime, subStateLogs, handles, app);
            hold(dAxes,'off');

            switch viewProfile.projType
                case ViewProjectionTypeEnum.Orthographic
                    camproj(dAxes, 'orthographic');

                case ViewProjectionTypeEnum.Perspective
                    camproj(dAxes, 'perspective');

                otherwise
                    error('Unknown projection type: %s', viewProfile.projType.name);

            end
        end
    end
end

function [childrenHGs] = plotSubStateLog(subStateLog, prevSubStateLog, lvdData, dAxes)
    if(isempty(subStateLog))
        childrenHGs = [];
        return;
    end

%     bodyID = subStateLog(1,8);
%     bodyInfo = getBodyInfoByNumber(bodyID, celBodyData);

    eventNum = subStateLog(1,13);
    event = lvdData.script.getEventForInd(eventNum);
    if(isempty(event))
        childrenHGs = [];
        return;
    end

    plotLineColor = event.colorLineSpec.color.color;
    plotLineStyle = event.colorLineSpec.lineSpec.linespec;
    plotLineWidth = event.colorLineSpec.lineWidth;
    plotMarkerType = event.colorLineSpec.markerSpec.shape;
    plotMarkerSize = event.colorLineSpec.markerSize;
    plotMethodEnum = event.plotMethod;

    hold(dAxes,'on');

    switch plotMethodEnum
        case EventPlottingMethodEnum.PlotContinuous
            t = [prevSubStateLog(end,1);subStateLog(1:end,1)];
            x = [prevSubStateLog(end,2);subStateLog(1:end,2)];
            y = [prevSubStateLog(end,3);subStateLog(1:end,3)];
            z = [prevSubStateLog(end,4);subStateLog(1:end,4)];

            [~,I] = sort(t);
            x = x(I);
            y = y(I);
            z = z(I);

        case EventPlottingMethodEnum.SkipFirstState
            x = subStateLog(2:end,2);
            y = subStateLog(2:end,3);
            z = subStateLog(2:end,4);
        case EventPlottingMethodEnum.DoNotPlot
            x = [];
            y = [];
            z = [];
        otherwise
            error('Unknown event plotting method enum: %s', plotMethodEnum.name);
    end

    plot3(dAxes, x, y, z, 'Color', plotLineColor, 'LineStyle', plotLineStyle, 'LineWidth',plotLineWidth, 'Marker',plotMarkerType, 'MarkerSize',plotMarkerSize, 'MarkerEdgeColor','none', 'MarkerFaceColor',plotLineColor);
    childrenHGs = cell(0,4);
end

function plotBodyFixedGrid(dAxes, bodyInfo)
    r = 1.2*bodyInfo.radius;
    rTxt = 1.3*bodyInfo.radius;

    %draw longitude circle and text
    th = linspace(0, 2*pi, 100);
    xunit = r * cos(th);
    yunit = r * sin(th);
    patch(dAxes, 'XData',xunit,'YData',yunit, 'FaceColor', 'k', 'FaceAlpha',0.15);

    th = linspace(0,2*pi - (1/12)*2*pi, 12);
    xunit = r * cos(th);
    yunit = r * sin(th);

    xToPlot = [];
    yToPlot = [];
    for(i=1:length(th))
        xToPlot = [xToPlot, 0, xunit(i), NaN]; %#ok<AGROW>
        yToPlot = [yToPlot, 0, yunit(i), NaN]; %#ok<AGROW>
    end

    plot(dAxes, xToPlot, yToPlot, 'k');

    xunitTxt = rTxt * cos(th);
    yunitTxt = rTxt * sin(th);

    for(i=1:length(xunitTxt))
        text(dAxes, xunitTxt(i),yunitTxt(i),sprintf('%.0f%s', rad2deg(th(i)), char(176)));
    end

    %draw latitude circle and text
    th = linspace(-pi/2, pi/2, 100);
    xunit = r * cos(th);
    yunit = zeros(size(th));
    zunit = r * sin(th);
    patch(dAxes, 'XData',xunit,'YData',yunit,'ZData',zunit, 'FaceColor', 'k', 'FaceAlpha',0.15);

    th = rad2deg(linspace(-pi/2, pi/2, 7));
    xunit = r * cosd(th);
    yunit = zeros(size(th));
    zunit = r * sind(th);

    xToPlot = [];
    yToPlot = [];
    zToPlot = [];
    for(i=1:length(th))
        xToPlot = [xToPlot, 0, xunit(i), NaN]; %#ok<AGROW>
        yToPlot = [yToPlot, 0, yunit(i), NaN]; %#ok<AGROW>
        zToPlot = [zToPlot, 0, zunit(i), NaN]; %#ok<AGROW>
    end

    plot3(dAxes, xToPlot, yToPlot, zToPlot, 'k');

    xunitTxt = rTxt * cosd(th);
    yunitTxt = zeros(size(th));
    zunitTxt = rTxt * sind(th);

    for(i=1:length(xunitTxt))
        text(dAxes, xunitTxt(i),yunitTxt(i),zunitTxt(i), sprintf('%.0f%s', th(i), char(176)));
    end
end


% MSH_TO_VTK: Export to VTK format for plotting.
%
%  MSH_to_vtk (pts, values, filename, fieldnames)
%
% INPUT:
%
%     pts:        points at which the field was computed
%     values:     cell-array, with values of the fields at the selected points
%     filename:   name of the output file
%     fieldnames: how to name the saved variables in the vtk file
%
% OUTPUT:
%
%    a vtk structured mesh file named <filename> is produced
%
% Copyright (C) 2009, 2010 Carlo de Falco, Rafael Vazquez
% Copyright (C) 2011, 2015 Rafael Vazquez
%
%    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 msh_to_vtk (pts, values, filename, fieldnames)

  if (iscell (values))
    if (numel (values) ~= numel (fieldnames))
      error ('The number of fields and the number of names should be the same')
    end
  else
    values = {values};
    if (~iscell (fieldnames))
      fieldnames = {fieldnames};
    end
  end

  ndim = numel (size (pts)) - 1;
  rdim = size (pts, 1);

  str1 = cat (2,'<?xml version="1.0"?> \n', ...
'<VTKFile type="StructuredGrid" version="0.1"> \n', ...
'<StructuredGrid WholeExtent="0 %d 0 %d 0 %d"> \n', ...
'<Piece Extent="0 %d 0 %d 0 %d"> \n', ...
'<PointData>\n');

  str1b = cat (2, ...
'<DataArray type="Float32" Name="%s" format="ascii" NumberOfComponents="%d"> \n');

  str1c = cat (2,'</DataArray> \n');

  str2 = cat (2,'</PointData> \n', ...
'<Points> \n', ...
'<DataArray type="Float32" NumberOfComponents="3"> \n');

  str3 = cat (2, '\n', ...
'</DataArray>\n', ...
'</Points> \n', ...
'</Piece> \n', ...
'</StructuredGrid> \n', ...
'</VTKFile> \n');

% Even for 2D data, everything is saved in 3D
% ndims (or size) do not work properly for the 1D case. I remove singleton
% dimensions using this trick

  size_pts = size (pts);
  npts = size_pts (2:end);

  if (ndim < 3)
    npts (ndim+1:3) = 1;
  end
  if (rdim < 3)
    pts(ndim+1:3,:,:) = 0;
  end

  if (length (filename) < 4 || ~strcmp (filename(end-3:end), '.vts'))
    filename = cat (2, filename, '.vts');
  end

  fid = fopen (filename, 'w');
  if (fid < 0)
    error ('msh_to_vtk: could not open file %s', filename);
  end

  fprintf (fid, str1, ...
           npts(1)-1, npts(2)-1, npts(3)-1, ...
           npts(1)-1, npts(2)-1, npts(3)-1);

  for iopt = 1:numel(values)
    if (sum (size (values{iopt}) > 1) == ndim)
      ncomp = 1;
    elseif (sum (size (values{iopt}) > 1) == ndim + 1)
      ncomp = 3;
    else
      ncomp = 9;
    end
    if (ncomp == 3 && rdim < 3)
      values{iopt}(rdim+1:3,:,:) = 0;
    elseif (ncomp == 9 && rdim < 3)
      values{iopt}(rdim+1:3,rdim+1:3,:,:) = 0;
    end
    fprintf (fid, str1b, fieldnames{iopt}, ncomp);
    fprintf (fid, '%g ', values{iopt}(:));
    fprintf (fid, str1c);
  end
  fprintf (fid, str2);
  fprintf (fid, '%g ', pts(:));
  fprintf (fid, str3);

  fclose (fid);

end


%SPH   Implement spherical IBVS for point features
%
%  results = sph(T)
%  results = sph(T, params)
%
%  Simulate IBVS with for a square target comprising 4 points is placed
%  in the world XY plane. The camera/robot is initially at pose T and is
%  driven to the orgin.
%
%  Two windows are shown and animated:
%   1. The camera view, showing the desired view (*) and the
%      current view (o)
%   2. The external view, showing the target points and the camera
%
% The results structure contains time-history information about the image
% plane, camera pose, error, Jacobian condition number, error norm, image
% plane size and desired feature locations.
%
% The params structure can be used to override simulation defaults by
% providing elements, defaults in parentheses:
%
%   target_size    - the side length of the target in world units (0.5)
%   target_center  - center of the target in world coords (0,0,2)
%   niter          - the number of iterations to run the simulation (500)
%   eterm          - a stopping criteria on feature error norm (0)
%   lambda         - gain, can be scalar or diagonal 6x6 matrix (0.01)
%   ci             - camera intrinsic structure (camparam)
%   depth          - depth of points to use for Jacobian, scalar for
%                    all points, of 4-vector.  If null take actual value
%                    from simulation      ([])
%
% SEE ALSO: ibvsplot

% Copyright (C) 1993-2017, by Peter I. Corke
%
% This file is part of The Machine Vision Toolbox for Matlab (MVTB).
%
% MVTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% MVTB 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with MVTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

% IMPLEMENTATION NOTE
%
% 1.  As per task function notation (Chaumette papers) the error is
%     defined as actual-demand, the reverse of normal control system
%     notation.
% 2.  The gain, lambda, is always positive
% 3.  The negative sign is written into the control law

function results = sph(T0, params)

    %% default parameters

    % define default target geometry
    vw = 2; vh = 2;       % dimensions of target
    Tct_star = transl(0, 0, -2);    % desired pose of target wrt camera

    % define default IBVS parameters
    niter = 500;            % number of iterations
    eterm = 0;
    lambda = 0.04;         % control gain
    cam = scamera;
    depth = [];
    estim = [];

    if nargin == 2,
        % override default target size
        if isfield(params, 'target_size'),
            vw = params.target_size(1);
            vh = params.target_height(1);
        end
        % override default camera desired pose
        if isfield(params, 'target_center'),
            Tct_star = params.Tct_star;
        end
        % override default number of iterations
        if isfield(params, 'niter'),
            niter = params.niter;
        end
        % override default gain (can be scalar or diag 6x6)
        if isfield(params, 'lambda'),
            lambda = params.lambda;
        end
        % override default camera intrinsics
        if isfield(params, 'ci'),
            ci = params.ci;
        end
        % override default point depth
        if isfield(params, 'depth'),
            if length(params.depth) == 1,
                depth = params.depth * ones(4,1);
            else
                depth = params.depth;
            end
        end
        if isfield(params, 'estim'),
            estim = params.estim;
        end
    end

    % define feature points in XY plane
    %  make vertices of a square
    P = mkgrid(2, [vw vh]);

    % show the reference location, this is the view we wish to achieve
    % when Tc = Tct_star
    figure(1)
    clf
    P
    Tct_star
    % HACKuv_star = cam.plot(P, inv(Tct_star));    % create the camera view
    uv_star = cam.plot(P, (Tct_star));    % create the camera view
    k=1
    plot(uv_star(2,k), uv_star(1,k), '*');
    hold on
    for k=2:4
        plot(uv_star(2,k), uv_star(1,k), '*');
    end
    %plot(uv_star(:,1), uv_star(:,2), '*');  % show desired view
    %hold off
    %cam.plot(P, T0);    % show initial view
    pause(1)

    % this is the 'external' view of the points and the camera
    figure(2)
    plot3(P(1,:), P(2,:), P(3,:), '*')
    showcam(T0, P)
    grid
    xlabel('x');
    ylabel('y');
    zlabel('z');
    pause
    figure(1)
    %cam2 = showcamera(T0);
    %camup([0,-1,0]);

    %% initialize the vservo variables

    Tcam = T0;                % initial camera/robot pose

    % initialize some history variables, suffix _h, to hold time series data
    % about the IBVS run
    v_h = [];       % velocity demand to robot
    e_h = [];       % feature error
    en_h = [];      % scalar norm of the feature error
    c_h = [];       % Jacobian condition number
    uv_h = [];      % image plane feature coordinates
    Tcam_h = [];
    Z_h = [];
    PP = eye(1,1);
    theta = 0;
    smoothing = 0.95;

    for k=1:niter,
         % set and show the camera pose
        cam.Tcam = Tcam;
        %showcamera(cam2, Tcam);
        figure(2)
        plot3(P(1,:), P(2,:), P(3,:), '*')
        grid on
        xlabel('x');
        ylabel('y');
        zlabel('z');
        showcam(Tcam, P);
        figure(1)

        % compute the view
        uv = cam.plot(P);

        e = uv - uv_star;   % as per task function notation
        uv
        uv_star
        e
        if 1
            for i=1:numcols(e)
                % vectorize this
                if e(2,i) > pi
                    e(2,i) = e(2,i) - 2*pi;
                elseif e(2,i) < -pi
                    e(2,i) = 2*pi + e(2,i);
                end
            end
        end
        e
        e = reshape(e, numel(e), 1);

        if estim,
            % run the depth estimator
            if k > 1,

                % compute Jacobian for unit depth, r=1
                J = jac(uv, ones(numcols(uv),1));
                Jv = J(:,1:3);  % velocity part, depends on 1/z
                Jw = J(:,4:6);  % rotational part, indepedent of 1/z

                % estimate image plane velocity
                uv_d =  reshape(uv, 1, [])' - reshape(uv_p, 1, [])';

                % estimate coefficients for A (1/z) = B
                B = uv_d - Jw*v(4:6);
                A = Jv * v(1:3);

                eta = A\B;          % least squares solution

                % first order smoothing
                theta = (1-smoothing) * 1/eta + smoothing * theta;

                pt = transformp(inv(Tcam), P);

                fprintf('depth %.4g, est depth %.4g, rls depth %.4g\n', pt(3,1), 1/eta, theta);
                Z_h(k,:) = [theta pt(3,1)];   % estimated depth, true depth
            end
            uv_p = uv;
        end

        % compute the Jacobian
        if isempty(depth),
            % exact depth from simulation (not possible in practice)
            pt = transformp(inv(Tcam), P);
            J = jac(uv, colnorm(pt) );
        else
            J = jac(uv, depth );
        end
        J

        % compute the velocity of camera in camera frame

        v = -lambda * pinv(J) * e;
        fprintf('v: %.3f %.3f %.3f %.3f %.3f %.3f\n', v);
        norm(e)

        % update the camera pose
        Td = (eye(4,4) + delta2tr( v ) );    % differential motion
        Td = trnorm(Td);

        Tcam = Tcam * Td;       % apply it to current pose

        % update the history variables
        uv_h(k,:) = reshape(uv([2 1],:), 1, []);
        e_h(k,:) = e';
        en_h(k,:) = norm(e);
        c_h(k,:) = cond(J);
        v_h(k,:) = v';
        Tcam_h(:,:,k) = Tcam;

        if norm(e) < eterm,
            fprintf('completed on error tolerance\n');
            break;
        end
        drawnow
        pause(0.05)
        pause
    end
    fprintf('completed on iteration count\n');

    results.uv = uv_h;
    results.uv_labels = {'\phi (rad)', '\theta (rad)'};
    results.c = c_h;
    results.e = e_h;
    results.en = en_h;
    results.v = v_h;
    results.Tcam = Tcam_h;
    %results.limits = [0 cam.nu 0 cam.nv];
    results.limits = [-pi pi 0 pi];
    results.uv_star = uv_star([2 1],:);


data = caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('data')).get_data();
rois = squeeze(caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('rois')).get_data());
labels = squeeze(caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('labels')).get_data());
bbox_targets = squeeze(caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('bbox_targets')).get_data());
bbox_loss_weights = squeeze(caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('bbox_loss_weights')).get_data());
cls_score = squeeze(caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('cls_score')).get_data());

scores = squeeze(caffe_solver.net.blobs('cls_score').get_data());
classes = [{'bg'} imdb_train{1}.classes];
figure(1);
for k = 1:size(data,4)

  img = permute((data(:,:,:,k)+128)/256, [2 1 3 4 5 6]);
  img_ = img(:,:, [3 2 1]);
inds = rois(1,:)==k-1;
  boxes = rois(2:end,inds)';

  bbox_reg_boxes = find( bbox_loss_weights(5,inds) > 0 ) ;
  bbox_reg_weights = bbox_targets(:,bbox_reg_boxes) ;

  boxes_cell = {boxes(bbox_reg_boxes,:)};

  subplot(1,size(data,4),k);
  try
    track_deltas = caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('bbox_disp')).get_data();
    track_boxes = caffe_solver.net.blob_vec(caffe_solver.net.name2blob_index('rois_disp')).get_data();
    track_boxes = squeeze(track_boxes)' +1;
    pred_boxes = rfcn_bbox_transform_inv(track_boxes(:,2:end), squeeze(track_deltas)');
    pred_boxes = pred_boxes(:, 5:end);
    if k==track_boxes(1),   boxes_cell = {track_boxes(:,2:end)};
    else
       boxes_cell = {pred_boxes};
    end
  catch
  end
  this_scrs = scores(:,inds);
  [votes,predictions] = sort(this_scrs(:,bbox_reg_boxes), 1, 'descend') ;
        top5pred =  squeeze(predictions(1:5,:));
        highest_class_names = strrep(classes(top5pred), '_', '\_');

  if size(img,3) > 3
    subplot(2,2,1);showboxes(img_, boxes_cell);
    subplot(2,2,2); image(img(:,:,[4 : 6],:)); axis image;
    if size(img,3) > 10
      subplot(2,2,3); image(img(:,:,[7 : 9],:)); axis image;
      subplot(2,2,4); image(img(:,:,[9 : 11],:)); axis image;
    end
  else
    showboxes(img_, boxes_cell);
  end
  title(['top5 classes=' highest_class_names{1:5} ]);

end


% POP_INTERP - interpolate data channels
%
% Usage: EEGOUT = pop_interp(EEG, badchans, method, t_range);
%
% Inputs:
%     EEG      - EEGLAB dataset
%     badchans - [integer array] indices of channels to interpolate.
%                For instance, these channels might be bad.
%                [chanlocs structure] channel location structure containing
%                either locations of channels to interpolate or a full
%                channel structure (missing channels in the current
%                dataset are interpolated).
%     method   - [string] method used for interpolation (default is 'spherical').
%                'invdist'/'v4' uses inverse distance on the scalp
%                'spherical' uses superfast spherical interpolation.
%                'spacetime' uses griddata3 to interpolate both in space
%                and time (very slow and cannot be interrupted).
%     t_range  - [integer array with just two elements] time interval of the
%                badchans which should be interpolated. First element is
%                the start time and the second element is the end time.
% Output:
%     EEGOUT   - data set with bad electrode data replaced by
%                interpolated data
%
% Author: Arnaud Delorme, CERCO, CNRS, 2009-

% Copyright (C) Arnaud Delorme, CERCO, 2009, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [EEG com] = pop_interp(EEG, bad_elec, method, t_range)

    com = '';
    t_range = '';
    if nargin < 1
        help pop_interp;
        return;
    end
    if nargin > 1 && nargin < 3
        method = 'spherical';
    end

    if nargin < 2
        disp('Warning: interpolation can be done on the fly in studies');
        disp('         this function will actually create channels in the dataset');
        disp('Warning: do not interpolate channels before running ICA');
        disp('You may define channel location to interpolate in the channel');
        disp('editor and declare such channels as non-data channels');

        enablenondat = 'off';
        if isfield(EEG.chaninfo, 'removedchans')
            if ~isempty(EEG.chaninfo.removedchans)
                enablenondat = 'on';
            end
        end
        if isempty(EEG.epoch)
            uilist = { { 'Style' 'text' 'string' 'What channel(s) do you want to interpolate' 'fontweight' 'bold' } ...
                       { 'style' 'text' 'string' 'none selected' 'tag' 'chanlist' } ...
                       { 'style' 'pushbutton' 'string' 'Select from removed channels' 'callback' 'pop_interp(''nondatchan'',gcbf);' 'enable' enablenondat } ...
                       { 'style' 'pushbutton' 'string' 'Select from data channels'    'callback' 'pop_interp(''datchan'',gcbf);' } ...
                       { 'style' 'pushbutton' 'string' 'Use specific channels of other dataset' 'callback' 'pop_interp(''selectchan'',gcbf);'} ...
                       { 'style' 'pushbutton' 'string' 'Use all channels from other dataset' 'callback' 'pop_interp(''uselist'',gcbf);'} ...
                       { } ...
                       { 'style' 'text'  'string' 'Interpolation method'} ...
                       { 'style' 'popupmenu'  'string' 'Spherical|Planar (slow)'  'tag' 'method' } ...
                       { } ...
                       { 'Style', 'text', 'string', 'Time range [min max] (s)', 'fontangle', fastif(length(EEG)>1, 'italic', 'normal') } ...
                       { 'Style', 'edit', 'string', '', 'enable', fastif(length(EEG)>1, 'off', 'on') } ...
                       {} { 'Style' 'text' 'string' 'Note: for group level analysis, interpolate in STUDY' } ...
                       };

            geom     = { 1 1 1 1 1 1 1 [1.1 1] 1 [1.1 1] 1   1 };
            geomvert = [ 1 1 1 1 1 1 1 1       0.5 1      0.5  1 ];
        else
            uilist = { { 'Style' 'text' 'string' 'What channel(s) do you want to interpolate' 'fontweight' 'bold' } ...
                   { 'style' 'text' 'string' 'none selected' 'tag' 'chanlist' } ...
                   { 'style' 'pushbutton' 'string' 'Select from removed channels' 'callback' 'pop_interp(''removedchans'',gcbf);' 'enable' enablenondat } ...
                   { 'style' 'pushbutton' 'string' 'Select from data channels'    'callback' 'pop_interp(''datchan'',gcbf);' } ...
                   { 'style' 'pushbutton' 'string' 'Use specific channels of other dataset' 'callback' 'pop_interp(''selectchan'',gcbf);'} ...
                   { 'style' 'pushbutton' 'string' 'Use all channels from other dataset' 'callback' 'pop_interp(''uselist'',gcbf);'} ...
                   { } ...
                   { 'style' 'text'  'string' 'Interpolation method'} ...
                   { 'style' 'popupmenu'  'string' 'Spherical|Planar (slow)'  'tag' 'method' } ...
                   {} { 'Style' 'text' 'string' 'Note: for group level analysis, interpolate in STUDY' } ...
                   };

            geom     = { 1 1 1 1 1 1 1 [1.1 1] 1   1 };
            geomvert = [ 1 1 1 1 1 1 1 1       0.5 1 ];
        end
        [res, userdata, ~, restag ] = inputgui( 'uilist', uilist, 'title', 'Interpolate channel(s) -- pop_interp()', 'geometry', geom, 'geomvert', geomvert, 'helpcom', 'pophelp(''pop_interp'')');
        if isempty(res) || isempty(userdata), return; end

        if restag.method == 1
             method = 'spherical';
        else method = 'invdist';
        end
        bad_elec = userdata.chans;
        if nargin < 4
            if numel(res) > 1
                t_range = res{2};
            else
                t_range = '';
            end
        end
        if isempty(t_range)
            t_range = [EEG.xmin EEG.xmax];
        else
            t_range = eval( [ '[' t_range ']' ] );
        end
        if size(t_range,2) ~= 2
            error('Time/point range must contain 2 columns exactly');
        end
        if floor(max(t_range)) > EEG.xmax
            error('Time/point range exceed upper data limits');
        end
        if min(t_range) < EEG.xmin
            error('Time/point range exceed lower data limits');
        end

        com = sprintf('EEG = pop_interp(EEG, %s, ''%s'');', userdata.chanstr, method);
        if ~isempty(findstr('removedchans', userdata.chanstr))
            eval( [ userdata.chanstr '=[];' ] );
        end

    elseif ischar(EEG)
        command = EEG;
        clear EEG;
        fig = bad_elec;
        userdata = get(fig, 'userdata');

        if strcmpi(command, 'removedchans')
            global EEG;
            tmpchaninfo = EEG.chaninfo;
            [chanlisttmp, chanliststr] = pop_chansel( { tmpchaninfo.removedchans.labels } );
            if ~isempty(chanlisttmp)
                userdata.chans   = EEG.chaninfo.removedchans(chanlisttmp);
                userdata.chanstr = [ 'EEG.chaninfo.removedchans([' num2str(chanlisttmp) '])' ];
                set(fig, 'userdata', userdata);
                set(findobj(fig, 'tag', 'chanlist'), 'string', chanliststr);
            end
        elseif strcmpi(command, 'datchan')
            global EEG;
            tmpchaninfo = EEG.chanlocs;
            [chanlisttmp, chanliststr] = pop_chansel( { tmpchaninfo.labels } );
            if ~isempty(chanlisttmp)
                userdata.chans   = chanlisttmp;
                userdata.chanstr = [ '[' num2str(chanlisttmp) ']' ];
                set(fig, 'userdata', userdata);
                set(findobj(fig, 'tag', 'chanlist'), 'string', chanliststr);
            end
        else
            global ALLEEG EEG;
            tmpanswer = inputdlg2({ 'Dataset index' }, 'Choose dataset', 1, { '' });
            if ~isempty(tmpanswer)
                tmpanswernum = round(str2num(tmpanswer{1}));
                if ~isempty(tmpanswernum)
                    if tmpanswernum > 0 && tmpanswernum <= length(ALLEEG)
                        TMPEEG = ALLEEG(tmpanswernum);

                        tmpchans1 = TMPEEG.chanlocs;
                        if strcmpi(command, 'selectchan')
                            chanlist = pop_chansel( { tmpchans1.labels } );
                        else
                            chanlist = 1:length(TMPEEG.chanlocs); % use all channels
                        end

                        % look at what new channels are selected
                        tmpchans2 = EEG.chanlocs;
                        [tmpchanlist, chaninds] = setdiff_bc( { tmpchans1(chanlist).labels }, { tmpchans2.labels } );
                        if ~isempty(tmpchanlist)
                            if length(chanlist) == length(TMPEEG.chanlocs)
                                userdata.chans   = TMPEEG.chanlocs;
                                userdata.chanstr = [ 'ALLEEG(' tmpanswer{1} ').chanlocs' ];
                            else
                                userdata.chans   = TMPEEG.chanlocs(chanlist(sort(chaninds)));
                                userdata.chanstr = [ 'ALLEEG(' tmpanswer{1} ').chanlocs([' num2str(chanlist(sort(chaninds))) '])' ];
                            end
                            set(fig, 'userdata', userdata);
                            tmpchanlist(2,:) = { ' ' };
                            set(findobj(gcbf, 'tag', 'chanlist'), 'string', [ tmpchanlist{:} ]);
                        else
                            warndlg2('No new channels selected');
                        end
                    else
                        warndlg2('Wrong index');
                    end
                end
            end
        end
        return;
    end

    % remove from removedchans if interpolated
    if isfield(EEG.chaninfo, 'removedchans') && ~isempty(EEG.chaninfo.removedchans) && isstruct(bad_elec)
        for iChan = 1:length(bad_elec)
            ind = strmatch(lower(bad_elec(iChan).labels), lower({EEG.chaninfo.removedchans.labels}), 'exact');
            if ~isempty(ind)
                EEG.chaninfo.removedchans(ind) = [];
            end
        end
    end

    EEG = eeg_interp(EEG, bad_elec, method, t_range);


function [sFile, ChannelMat] = in_fopen_spm(DataFile)
% IN_FOPEN_SPM: Open a SPM .mat/.dat file.

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2017

%% ===== READ HEADER =====
% Check if SPM is in the path
if ~exist('file_array', 'file')
    error('SPM must be in the Matlab path to use this feature.');
end
% Get the two input file names: .mat and .dat
[fPath, fBase, fExt] = bst_fileparts(DataFile);
MatFile = bst_fullfile(fPath, [fBase, '.mat']);
DatFile = bst_fullfile(fPath, [fBase, '.dat']);
% If one is missing: error
if ~file_exist(MatFile) || ~file_exist(DatFile)
    error('The two files .dat and .mat must be available in the same folder.');
end
% Read header
sMat = load(MatFile, 'D');
D = sMat.D;
nChannels = length(D.channels);

% Warning: Supporting only files with one epoch
if (length(D.trials) > 1)
    error(['Only continuous SPM files are currently supported. Files with multiple trials cannot be imported.' 10 ...
           'Please contact us through the Brainstorm user forum to request this feature.']);
end

%% ===== FILL STRUCTURE =====
% Initialize returned file structure
sFile = db_template('sfile');
% Add information read from header
sFile.byteorder    = 'l';
sFile.filename     = MatFile;
sFile.format       = 'SPM-DAT';
sFile.prop.sfreq   = double(D.Fsample);
sFile.prop.nAvg    = 1;
sFile.prop.times   = (round(D.timeOnset(1) .* sFile.prop.sfreq) + [0, (D.Nsamples - 1)]) ./ sFile.prop.sfreq;
sFile.channelflag  = ones(nChannels,1);
sFile.device       = 'SPM';
sFile.comment      = fBase;
if isa(D.data, 'file_array')
    sFile.header.file_array = D.data;
elseif isstruct(D.data) && isfield(D.data, 'y') && isa(D.data.y, 'file_array')
    sFile.header.file_array = D.data.y;
else
    error('Could not find the file_array object in the SPM structure.');
end
sFile.header.nChannels  = nChannels;
sFile.header.gain       = ones(nChannels,1);

%% ===== CHANNEL FILE =====
% Initialize structure
ChannelMat = db_template('ChannelMat');
ChannelMat.Comment = [sFile.device ' channels'];
ChannelMat.Channel = repmat(db_template('channeldesc'), [1, nChannels]);
% Loop on each channel
for i = 1:nChannels
    if (D.channels(i).bad)
        sFile.channelflag(i) = -1;
    end
    if iscell(D.channels(i).label) && ~isempty(D.channels(i).label)
        ChannelMat.Channel(i).Name = D.channels(i).label{1};
    elseif ischar(D.channels(i).label) && ~isempty(D.channels(i).label)
        ChannelMat.Channel(i).Name = D.channels(i).label;
    else
        disp(sprintf('BST> Warning: No information avaible for channel #%d.', i));
    end
    % Convert channel types
    switch upper(D.channels(i).type)
        case 'MEGPLANAR',   ChannelMat.Channel(i).Type = 'MEG GRAD';
        case 'MEGMAG',      ChannelMat.Channel(i).Type = 'MEG MAG';
        otherwise,          ChannelMat.Channel(i).Type = upper(D.channels(i).type);
    end
    % Channel gains
    if isfield(D.channels(i), 'units') && ~isempty(D.channels(i).units)
        switch (D.channels(i).units)
            case 'fT',        sFile.header.gain(i) = 1e-15;
            case 'fT/mm',     sFile.header.gain(i) = 1e-12;
            case 'mV',        sFile.header.gain(i) = 1e-3;
            case {'uV','?V'}, sFile.header.gain(i) = 1e-6;
            otherwise,        sFile.header.gain(i) = 1;
        end
    end
end
% Read detailed information from .meg and .eeg fields
ChannelMat = read_fieldtrip_chaninfo(ChannelMat, D.sensors);

% Convert head points
if isfield(D, 'fiducials') && isfield(D.fiducials, 'pnt') && isfield(D.fiducials, 'label')
    for i = 1:length(D.fiducials.label)
        ChannelMat.HeadPoints.Label = D.fiducials.label(:)';
        ChannelMat.HeadPoints.Type  = repmat({'EXTRA'}, size(ChannelMat.HeadPoints.Label));
        ChannelMat.HeadPoints.Loc   = bst_units_ui(D.fiducials.unit, D.fiducials.pnt');
    end
end
% Convert fiducials
if isfield(D, 'fiducials') && isfield(D.fiducials, 'fid') && isfield(D.fiducials.fid, 'label') && isfield(D.fiducials.fid, 'pnt')
    for i = 1:length(D.fiducials.fid.label)
        switch lower(D.fiducials.fid.label{i})
            case {'nas', 'nasion', 'nz', 'fidnas', 'fidnz'}  % NASION
                ChannelMat.SCS.NAS = bst_units_ui(D.fiducials.unit, D.fiducials.fid.pnt(i,:));
            case {'lpa', 'pal', 'og', 'left', 'fidt9', 'leftear'} % LEFT EAR
                ChannelMat.SCS.LPA = bst_units_ui(D.fiducials.unit, D.fiducials.fid.pnt(i,:));
            case {'rpa', 'par', 'od', 'right', 'fidt10', 'rightear'} % RIGHT EAR
                ChannelMat.SCS.RPA = bst_units_ui(D.fiducials.unit, D.fiducials.fid.pnt(i,:));
        end
    end
    % Force re-alignment on the new set of NAS/LPA/RPA
    if ~isempty(ChannelMat.SCS) && ~isempty(ChannelMat.SCS.NAS) && ~isempty(ChannelMat.SCS.LPA) && ~isempty(ChannelMat.SCS.RPA)
        ChannelMat = channel_detect_type(ChannelMat, 1, 0);
    end
end

%% ===== EVENTS =====
if isfield(D, 'trials') && isfield(D.trials, 'events') && isfield(D.trials.events, 'type')
    % Get all the event types
    evtList = {D.trials.events.type};
    % Events list
    [uniqueEvt, iUnique] = unique(evtList);
    uniqueEvt = evtList(sort(iUnique));
    % Initialize events list
    sFile.events = repmat(db_template('event'), 1, length(uniqueEvt));
    % Build events list
    for iEvt = 1:length(uniqueEvt)
        % Find all the occurrences of this event
        iOcc = find(strcmpi(uniqueEvt{iEvt}, evtList));
        % Concatenate all times
        t = [D.trials.events(iOcc).time];
        % If there is a duration: add it
        occDuration = [D.trials.events(iOcc).duration];
        if (length(occDuration) == length(t))
            t(2,:) = t(1,:) + occDuration;
        end
        % Set event
        sFile.events(iEvt).label   = strtrim(uniqueEvt{iEvt});
        sFile.events(iEvt).times   = t;
        sFile.events(iEvt).epochs  = 1 + 0*t(1,:);
        sFile.events(iEvt).select  = 1;
        sFile.events(iEvt).channels = [];
        sFile.events(iEvt).notes    = [];
    end
end

end


function r = mldivide(a,b)
% o \ v
%
% Syntax
%   h = o \ r
%
% Input
%  o - @orientation
%  r - @vector3d
%
% Output
%  h - @Miller
%

r = inv(a) * b; %#ok<MINV>


function layout=cosmo_meeg_read_layout(fn)
% Read FieldTrip layout
%
% layout=cosmo_meeg_read_layout(fn)
%
% Inputs:
%   fn                  Filename of layout file, or a string containing the
%                       layout. In the latter case fn must contain at least
%                       one newline ('\n') character.
%                       A layout file is a text file with one line per
%                       sensor, with each line containing the following
%                       data separated by white-space:
%                       1) sensor number (integer)
%                       2) horizontal position (float)
%                       3) vertical position (float)
%                       4) width (float)
%                       5) height (float)
%                       6) label (string)
%
% Output:
%   layout              struct with fields containing data for N sensors:
%     .pos              Nx2 matrix with x and y position
%     .width            Nx1 vector
%     .height           Nx1 vector
%     .label            Nx1 cell string with channel labels
%
% Notes:
%   - whitespace is trimmed from the labels
%   - the sensor number is not used; the order of the sensors in layout is
%     the same as in the layout file
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

    check_layout_input(fn)

    if string_contains_newline(fn)
        lay_string=fn;
        fn_descr=@()sprintf('input:\n''%s''',lay_string);
    else
        lay_string=read_lay_string_from_file(fn);
        fn_descr=@()sprintf('file %s',fn);
    end

    layout=parse_layout(lay_string, fn_descr);

function lay_string=read_lay_string_from_file(fn)
    if ~exist(fn,'file')
        error('layout file %s does ont exist', fn);
    end

    % read FT layout (.lay) file
    fid=fopen(fn);
    file_closer=onCleanup(@()fclose(fid));
    lay_string=fread(fid,inf,'char=>char')';

function check_layout_input(fn)
    if ~ischar(fn)
        error('first argument must be string, found %s', class(fn));
    end

function tf=string_contains_newline(fn)
    tf=any(fn==sprintf('\n'));

function layout=parse_layout(lay_string, fn_descr)
    % pattern to match is integer, then 4 numeric values followed by a
    % string that can contain whitespaces and plus characters, followed by
    % newline
    integer='(\d+)';
    float='([\d\.-]+)';
    space='\s+';
    channel_label='([\w \t\r\f\v\+\-]+)';
    single_newline='\n';

    pat=[integer, space, ...
         float, space, ...
         float, space, ...
         float, space, ...
         float, space, ...
         channel_label, single_newline];

    matches=regexp(sprintf('%s\n',lay_string),pat,'tokens');
    if isempty(matches)
        error('No valid layout definition found in %s', fn_descr());
    end

    % convert to (nchannel x 6) matrix
    layout_matrix=cat(1,matches{:});

    % convert values in first five columns to numeric
    num_values_cell=layout_matrix(:,1:5)';

    str_values=sprintf('%s %s %s %s %s; ', num_values_cell{:});
    num_values=str2num(str_values);

    % store layout information (omit channel number in first column)
    layout.pos    = num_values(:,2:3);
    layout.width  = num_values(:,4);
    layout.height = num_values(:,5);

    % trim whitespace around channel names
    label=layout_matrix(:,6);
    label=regexprep(label,'^\s*','');
    label=regexprep(label,'\s*$','');
    layout.label  = label;


function signal = flt_beamformer(varargin)
% Recovers activity from the given ROIs via a beamformer.
% function Signal = flt_laplace(Signal,ROIs,)
%
% In:
%   Signal : EEGLAB data set, either continuous or epoched
%
%   ROIs   : Regions of interest from which to recover signals.
%
%   FieldType : type of field to recover ('axial' or 'normal')
%
%   ReferenceType : type of referencing used before this filter ('nasion' or 'common_average')
%
%   OverrideOriginal : whether to override the original signal
%
% Out:
%   Signal : activity from the given ROIs
%
% Notes:
%   This function currently does not perform activity renormalization.
%
% Examples:
%   % recover activity from four regions
%   eeg = flt_beamformer(eeg,{'Precentral_L', 'Precentral_R', 'Frontal_Sup_L', 'Frontal_Sup_R'},'normal')
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2014-02-11

if ~exp_beginfun('filter') return; end

declare_properties('name','Beamformer', 'follows',{'flt_selchans','flt_repair_bursts','flt_clean_settings'}, 'cannot_follow','flt_ica', 'independent_channels',false, 'independent_trials',true);

arg_define(varargin, ...
    arg_norep({'signal','Signal'}), ...
    arg({'roi_labels','ROIs','ROILabels'},{}, {'Precentral_L', 'Precentral_R', 'Frontal_Sup_L', 'Frontal_Sup_R', 'Frontal_Sup_Orb_L', 'Frontal_Sup_Orb_R', 'Frontal_Mid_L', 'Frontal_Mid_R', 'Frontal_Mid_Orb_L', 'Frontal_Mid_Orb_R', 'Frontal_Inf_Oper_L', 'Frontal_Inf_Oper_R', 'Frontal_Inf_Tri_L', 'Frontal_Inf_Tri_R', 'Frontal_Inf_Orb_L', 'Frontal_Inf_Orb_R', 'Rolandic_Oper_L', 'Rolandic_Oper_R', 'Supp_Motor_Area_L', 'Supp_Motor_Area_R', 'Frontal_Sup_Medial_L', 'Frontal_Sup_Medial_R', 'Frontal_Med_Orb_L', 'Frontal_Med_Orb_R', 'Insula_L', 'Insula_R', 'Cingulum_Ant_L', 'Cingulum_Ant_R', 'Cingulum_Mid_L', 'Cingulum_Mid_R', 'Cingulum_Post_L', 'Cingulum_Post_R', 'Hippocampus_L', 'Hippocampus_R', 'ParaHippocampal_L', 'ParaHippocampal_R', 'Calcarine_L', 'Calcarine_R', 'Cuneus_L', 'Cuneus_R', 'Lingual_L', 'Lingual_R', 'Occipital_Sup_L', 'Occipital_Sup_R', 'Occipital_Mid_L', 'Occipital_Mid_R', 'Occipital_Inf_L', 'Occipital_Inf_R', 'Fusiform_L', 'Fusiform_R', 'Postcentral_L', 'Postcentral_R', 'Parietal_Sup_L', 'Parietal_Sup_R', 'Parietal_Inf_L', 'Parietal_Inf_R', 'SupraMarginal_L', 'SupraMarginal_R', 'Angular_L', 'Angular_R', 'Precuneus_L', 'Precuneus_R', 'Paracentral_Lobule_L', 'Paracentral_Lobule_R', 'Temporal_Sup_L', 'Temporal_Sup_R', 'Temporal_Pole_Sup_L', 'Temporal_Pole_Sup_R', 'Temporal_Mid_L', 'Temporal_Mid_R', 'Temporal_Pole_Mid_L', 'Temporal_Pole_Mid_R', 'Temporal_Inf_L', 'Temporal_Inf_R', 'Olfactory_L', 'Olfactory_R', 'Rectus_L', 'Rectus_R', 'Amygdala_L', 'Amygdala_R', 'Caudate_L', 'Caudate_R', 'Thalamus_L', 'Thalamus_R', 'Heschl_L', 'Heschl_R'}, ...
        'Cortical anchor locations. List of locations to which components shall be constrained. The first k components are encouraged to lie close to the given locations, in the order of appearance. This is experimental and currently requires a) 10-20 locations and b) Guido Nolte''s source analysis toolbox (not included).','experimental',true), ...
    arg({'field_type','FieldType'},'normal',{'normal','axial'},'Regions of interest. These are the regions from which to recover signals.'), ...
    arg({'reference_type','ReferenceType'},'common_average',{'nasion','common_average'},'Referencing scheme. This is the type of re-referencing that was applied before flt_beamformer.'), ...
    arg({'override_original','OverrideOriginal'},true,[],'Override original data. If checked, the original signals will be replaced by the recovery.'), ...
    arg({'override_chanlabels','OverrideChanlabels'},{},[],'Override channel labels.'), ...
    arg({'cov_shrinkage','CovShrinkage'},0,[],'Covariance shrinkage parameter. For better conditioning.'), ...
    arg({'cov_robust','CovRobust'},false,[],'Use robust covariance estimate.'), ...
    arg_norep('M'), ...
    arg_norep('channel_mask'));

% calculate spatial filter matrix, if necessary
if ~exist('M','var')
    if isempty(override_chanlabels)
        chan_labels = {signal.chanlocs.labels};
    else
        chan_labels = override_chanlabels;
    end
    [dummy,dummy,channel_mask,dummy,dummy,dummy,normField,leadField] = hlp_diskcache('filterdesign',@calc_beamformer_constraints,chan_labels,roi_labels,eye(signal.nbchan),reference_type); %#ok<ASGLU>
    if strcmp(field_type,'normal')
        LF = normField;
    elseif strcmp(field_type,'axial')
        LF = leadField(:,:);
    else
        error('Unsupported field type requested: %s',hlp_tostring(field_type,1000));
    end
    if cov_robust
        C = cov_blockgeom(signal.data(channel_mask,:)');
    else
        C = cov(signal.data(channel_mask,:)');
    end
    C = (1-cov_shrinkage)*C + cov_shrinkage*mean(trace(C))*eye(length(C));
    M = LF'/C;
end

% apply M
signal = utl_register_field(signal,'timeseries','srcpot',reshape(M*signal.data(channel_mask,:,:),[],size(signal.data,2),size(signal.data,3)));
signal.etc.roi_labels = roi_labels;

if override_original
    % override signal.data & relabel channels
    signal.data = signal.srcpot;
    if size(signal.data,1) == signal.nbchan
        signal.chanlocs = struct('labels',roi_labels);
    else
        signal.nbchan = size(signal.data,1);
        signal.chanlocs = hlp_nanocache('cached_labels',10,@make_labels,roi_labels);
    end
end

% append the M and ok arguments to the online expression
exp_endfun('append_online',{'M',M,'channel_mask',channel_mask});

function chanlocs = make_labels(roi_labels)
chanlocs = struct('labels',[cellfun(@(f){sprintf('%s_X',f)},roi_labels) cellfun(@(f){sprintf('%s_Y',f)},roi_labels) cellfun(@(f){sprintf('%s_Z',f)},roi_labels)]);


% plot(x,NC(2012,:),'o');
plot(x,dC(2012,:),'o');


% 'region' is a class of objects that contains information on groups of
% voxels ("regions") defined in various ways (contiguous voxels above a
% threshold in an analysis, based on atlases, etc.)
%
% the region class replaces the "clusters" structure in the scnlab toolbox,
% and all or almost all of the "clusters" functions should work for regions
% structures as well.  The advantage of 'region' is that the class
% definition can help constrain the use and provide additional error
% checking, etc.
%
% Defining a region and initializing:
%
% *Usage:*
%   - r = region(obj1, [obj2], [keywords] )
%     - obj1: [fmri_data/statistic_image object to define regions]
%             Can also be char array of image filename
%     - obj2: Optional: fmri_data/statistic_image object to extract data from
%     - keywords: Optional: 'unique_mask_values' or 'contiguous_regions'
%     - 'noverbose' : suppress verbose output
%
%   - cl = region;  % generates an empty structure
%                   % You can add fields yourself if you want to, but best to
%                   define based on existing file name or image_vector object
%
% Define regions based on continuous voxel values (in this example image,
% there is only one set of contiguous voxels, so 1 region...)
%
%   - mask_image = which('brainmask.nii');
%   - cl = region(mask_image);
%
% *Inputs:*
%
% There are two ways to define which voxels are grouped into a 'region',
% which becomes an element of the region variable cl.
%
% enter 'contiguous_regions' -> group by contiguous blobs
%
% or    'unique_mask_values' -> group by unique values in mask .dat field
%
% *Note:* 'contiguous_regions' uses contiguity/clustering information stored
% in mask.volInfo.cluster, which may not have veridical contiguity info if
% you have manipulated it or incorporated anatomical information in working with
% the mask object, or if you have borrowed the volInfo structure from
% another source in creating it.
%
% *Examples:*
%
% Define regions based on continuous values in an anatomical mask:
%   - mask_image = which('atlas_labels_combined.img');
%   - cl = region(mask_image, 'unique_mask_values');
%
% Resample mask_image to space of data_comb fmri_data object, and extract
% averages.  Space is defined by data_comb.
%   - cl = extract_roi_averages(data_comb, mask_image, 'unique_mask_values');
%
% Define regions based on unique voxels values in mask_image, and extract
% data stored in data_comb object, resampled to space of mask_image.
% Space is defined by mask_image:
%   - cl = region(mask_image, data_comb, 'unique_mask_values');
%
% Reslice mask to space of functional images and define regions based on
% mask values in the functional space (good for extracting data, etc.)
%   - mask_image = which('anat_lbpa_thal.img');
%   - mask = fmri_mask_image(mask_image);
%   - mask = resample_to_image_space(mask, image_names(1, :));
%   - cl = region(mask);
%
% *Methods*
%
% Try typing methods(cl)
%
% methods for 'regions' include:
%
% Visualization methods:
%     montage, orthviews, surf, etc.
%
% *Programmers' Notes:*
%
% 8/3/2015 : Tor Wager: Fixed bug when applying region to thresholded
% statistic_image object.  Did not consider thresholding.
%
% 5/24/2017: Tor and Phil Kragel: first attempt to make compatible with
% use of enforce_variable_types method.
%
% 7/2018 : Tor - check for empty mask and skip data extraction if so

classdef region

    properties
        title = 'Untitled';     % Title of region object
        shorttitle = 'region';
        descrip1 = 'Region: a group of voxels.'
        descrip2 ='Some methods: orthviews, montage, surface, extract_data, table';

        XYZ
        XYZmm
        val
        val_descrip = 'Description of values for each voxel in val field.';

        Z
        Z_descrip = 'Legacy values for each voxel; Z-scores or other max stat';

        threshold % legacy, for compatibility
        voxSize
        M
        dim
        numVox
        numpeaks

        center
        mm_center

        timeseries
        contrastdata
        dat
        all_data

        source_images

        custom_info1
        custom_info1_descrip

        custom_info2
        custom_info2_descrip

    end % properties

    methods

        % class constructor method
        % takes maskinput in one of several forms:
        % 1) mask object
        % 2) mask image file
        % 3) mask vector data (???)
        %
        % takes one of two string inputs for how to define regions (see below)
        % 'contiguous_regions' (default)
        % 'unique_mask_values'

        function obj = region(maskinput, varargin)

            % initialize empty
            % return if no additional args

            obj.title = 'Untitled';
            obj.shorttitle = 'region';
            obj.descrip1 = 'Region: a group of voxels.';
            obj.descrip2 ='Some methods: orthviews, montage, surface, extract_data, table';
            obj.XYZ = [];
            obj.XYZmm = [];
            obj.val = [];
            obj.Z = [];
            obj.val_descrip = 'Values for each voxel, usually max stat.';
            obj.Z_descrip = 'Legacy values for each voxel; Z-scores or other max stat';
            obj.threshold = []; % legacy, for compatibility
            obj.voxSize = [];
            obj.M = [];
            obj.dim = [];
            obj.numVox = 0;
            obj.numpeaks = NaN;
            obj.center = [];
            obj.mm_center = [];
            obj.timeseries = [];
            obj.contrastdata = [];
            obj.dat = [];
            obj.all_data = [];
            obj.source_images = char([]);
            obj.custom_info1 = [];
            obj.custom_info1_descrip = 'Mask image name for region definition';
            obj.custom_info2 = [];
            obj.custom_info2_descrip = char([]);

            if nargin == 0
                return
            end

            % ---------------------------------
            % define mask object
            % ---------------------------------

            % Load mask into object if needed, apply .sig field for
            % statistic_image objects

            [mask, maskData] = prep_mask_object(maskinput);

            % Check for an incompatibility. Don't try to fix here, force
            % proper intended use.
            if isa(maskinput, 'atlas') && isa(maskinput.dat, 'int32')

                %atlas_obj.dat = double(atlas_obj.dat);
                error('Do not use region() for atlas objects. Use atlas2region instead.');

            end

            % ---------------------------------
            % define what to average over
            % ---------------------------------

            average_over = 'contiguous_regions'; %'contiguous_regions'  or 'unique_mask_values';
            doverbose = true;

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

                        % reserved keywords
                        case 'contiguous_regions', average_over = 'contiguous_regions';
                        case 'unique_mask_values', average_over = 'unique_mask_values';

                        case 'noverbose'
                            doverbose = false;

                        otherwise
                            disp('region class constructor: Illegal string value for average_over.');
                            fprintf('You entered ''%s''\n Valid values are %s or %s\n', varargin{varg}, '''contiguous_regions''', '''unique_mask_values''');
                            error('Exiting');
                    end
                elseif isa(varargin{varg}, 'image_vector')
                        % data object to extract from at end

                        dataobj = varargin{varg};
                        varargin{varg} = [];

                        % Note: If you have manipulated an image_vector (e.g., fmri_data,
                        % statistic_image) object and eliminated some voxels, in order to create
                        % regions, contiguous voxels are automatically reparsed into regions using
                        % fmri_data.reparse_contiguous below.

                        dataobj = reparse_contiguous(dataobj, 'nonempty');
                else
                    % unknown input
                end
            end

            % If extracting data, we'll recreate the regions in
            % extract_roi_averages, so do that here and then exit.
            % Otherwise, continue to region definition.

            if exist('dataobj', 'var')
                % extract data
                if doverbose
                    disp('> Found image data, extracting region averages.');
                end

                dataobj = replace_empty(dataobj); % may need to do this to get voxels to line up

                cs = compare_space(dataobj, mask);

                % if empty, skip
                isemptymask = isempty(mask.dat) || all(mask.dat(:) == 0);
                if isemptymask
                    if doverbose
                        disp('No in-region voxels from which to extract data.');
                    end
                    return
                end

                if cs == 3
                    disp('Spaces for data object and mask object line up, but voxel numbers do not. Check.');
                    if doverbose, disp('> Resampling to mask space first.'); end
                    dataobj = resample_space(dataobj, mask); % resample data to mask space
                elseif cs
                    if doverbose, disp('> Resampling to mask space first.'); end
                    dataobj = resample_space(dataobj, mask); % resample data to mask space
                end

                if doverbose
                    obj = extract_roi_averages(dataobj, mask, average_over);
                else
                    obj = extract_roi_averages(dataobj, mask, average_over, 'noverbose');
                end

                return
            end

            % ---------------------------------
            % get which values to save later
            % ---------------------------------

            maskValues = maskData;  % values to save in .val field
            maskZ = maskData;       % values to save in .Z field later

            val_descrip = 'Input mask image values for each voxel.';
            Z_descrip = 'Input mask image values for each voxel.';

            if isa(mask, 'statistic_image')

                 val_descrip = 'Statistic effect value (.dat) for each voxel.';

                 mask = replace_empty(mask);            % after this p and dat now have all in-mask voxels

                 if ~isempty(mask.dat)
                     % Use values from image. These can be more precise and
                     % we don't want to convert them to Z-values if we
                     % don't have to.

                     maskZ = mask.dat;
                     Z_descrip = mask.type;

                 elseif ~isempty(mask.p)

                     if doverbose('Converting .dat data values to Z-values based on P-values'); end

                     Z_descrip = 'Z-score for each voxel.';
                     mask.p(mask.p==0) = eps;               % not to have Inf values in Z field
                     maskZ = sign(mask.dat) .* norminv(1 - mask.p ./ 2); % Z-score based on p-value, assuming two-tailed p-vals.

                 end

            end

            % ---------------------------------
            % get unique values for voxel grouping code
            % ---------------------------------

            switch average_over

                % Define integer codes for sets of voxels to average over.

                case 'unique_mask_values'

                    maskData = round(maskData);
                    u = unique(maskData)'; u(u == 0) = [];
                    nregions = length(u);

                    if doverbose
                        fprintf('Grouping voxels with unique mask values, assuming integer-valued mask: %3.0f regions\n', nregions);
                    end

                case 'contiguous_regions'

                    isinmask = maskData ~= 0 & ~isnan(maskData);

                    % re-make cluster ID for in-mask voxels
                    mask.volInfo.cluster(isinmask) = spm_clusters(mask.volInfo.xyzlist(isinmask, :)')';

                    % Old, no longer needed with newer SPM
                    %                     if sum(isinmask) < 50000
                    %                         mask.volInfo.cluster(isinmask) = spm_clusters(mask.volInfo.xyzlist(isinmask, :)')';
                    %                     else
                    %                         % don't, and print warning
                    %                         mask.volInfo.cluster(isinmask) = ones(sum(isinmask), 1);
                    %                         disp('Warning: spm_cluster will not parse clusters for masks with > 50000 voxels.');
                    %                     end

                    u = unique(mask.volInfo.cluster(isinmask)); u(u == 0) = [];
                    maskData(isinmask) = mask.volInfo.cluster(isinmask);
                    nregions = length(u);

                    if doverbose
                        fprintf('Grouping contiguous voxels: %3.0f regions\n', nregions);
                    end

                otherwise
                    error('This should never happen.');
            end

            % ---------------------------------
            % Now define the regions
            % ---------------------------------

            obj(1:nregions) = obj;  % fill in all empty fields

            for i = 1:nregions
                imgvec = maskData == u(i);

                obj(i).title = sprintf('Region %3.0f', i);
                obj(i).shorttitle = sprintf('Region%03d', i);
                obj(i).XYZ = double(mask.volInfo.xyzlist(imgvec,:))';

                myXYZ = double(obj(i).XYZ);                         % 5/24/17 tor&phil: recast as double. future: could recast XYZmm as int16
                obj(i).XYZmm = voxel2mm(myXYZ, mask.volInfo.mat);

                obj(i).val = maskValues(imgvec); %ones(1,size(obj(i).XYZ,2));
                obj(i).Z = maskZ(imgvec)'; %ones(1,size(obj(i).XYZ,2));

                obj(i).val_descrip = val_descrip;
                obj(i).Z_descrip = Z_descrip;

                obj(i).voxSize = abs(diag(mask.volInfo.mat(1:3, 1:3)));
                obj(i).M = mask.volInfo.mat;
                obj(i).dim = mask.volInfo.dim;
                obj(i).numVox = size(obj(i).XYZ, 2);

                obj(i).center = center_of_mass(myXYZ, double(obj(i).Z));
                obj(i).mm_center = center_of_mass(obj(i).XYZmm, double(obj(i).Z));

                if ~isempty(mask.volInfo.fname)
                    [~, ff, ee] = fileparts(mask.volInfo.fname);
                    obj(i).custom_info1 = [ff ee];
                    obj(i).custom_info1_descrip = 'Mask image name for region definition';
                else
                    obj(i).custom_info1 = 'No filename to associate';
                    obj(i).custom_info1_descrip = '';
                end

            end

            if nregions == 0
                % obligatory things even for empty regions
                obj(1).voxSize = abs(diag(mask.volInfo.mat(1:3, 1:3)));
                obj(1).M = mask.volInfo.mat;
                obj(1).dim = mask.volInfo.dim;

                obj(1).val_descrip = val_descrip;
                obj(1).Z_descrip = Z_descrip;

            end

        end % class constructor function

    end % methods

end % classdef

% Load mask into object if needed, apply .sig field for
% statistic_image objects
% * NOTE: some functionality may be redundant here; to-do is to refactor
% and run unit test

function  [mask, maskData] = prep_mask_object(maskinput)

if isa(maskinput, 'char') % string file name
    mask = fmri_mask_image(maskinput);

elseif isa(maskinput, 'image_vector')
    % case {'fmri_data', 'fmri_mask_image', 'statistic_image', 'image_vector'}

    % special for thresholded stats images: use threshold

    % if sig field exists, use sig voxels only
    if isa(maskinput, 'statistic_image') && ~isempty(maskinput.sig)

        for img = 1:size(maskinput.dat, 2)

            maskinput.dat(~maskinput.sig(:, img), img) = 0;

        end

    end

    mask = maskinput;

elseif isa(maskinput, 'atlas')
    error('Use atlas2region to convert an atlas object to a region object.');

else
    error('region class constructor: unknown mask input type.')
end

% Mask data can already be reduced to those indexed by
% wh_inmask or not.
% In addition, voxels/images with empty values may be removed.
% So insert those first.
% Note: .dat can sometimes have 2+ cols, so use only first one
mask = replace_empty(mask);

if size(mask.dat, 2) > 1
    disp('Warning: Mask has multiple images, will use first only.');
end

if size(mask.dat, 1) == mask.volInfo.n_inmask
    maskData = mask.dat(:, 1);

elseif size(mask.dat, 1) == mask.volInfo.nvox
    % We have a full-length vector
    maskData = mask.dat(mask.volInfo.wh_inmask(:, 1));
else
    error('Illegal size for mask.dat, because it does not match its volInfo structure.')
end

% If statistic_image, we need to consider thresholding
% so apply .sig field
if isa(mask, 'statistic_image')

    % old... we could delete these.. but just in case, I just comment them out for now (Wani).
    % if size(mask.dat, 1) == mask.volInfo.nvox
    %    error('statistic_image objects should not have data vector (.dat) the length of full image space.')
    % end

    maskData = maskData .* mask.sig(:, 1);

end

% may need to reparse contiguous voxels in the mask.
mask = reparse_contiguous(mask, 'nonempty');

end % function


%DYADICM Dyadic dataset mapping
%
%   B = [C1,C2]*DYADICM([],P,Q,SIZE)
%   B = [C1,C2]*DYADICM([],FNAME,[],SIZE)
%   W = A*DYADICM([],{U1,U2,FNAME},[],SIZE)
%   B = A*DYADICM([],{V1,V2,FNAME},[],SIZE)
%
% INPUT
%  C1,C2   Datasets / datafiles to be combined
%  A       Input dataset used for training or classification
%  P,Q     Scalar multiplication factors (default 1) to compute P*A1+Q*A2
%          or string (name of a routine)
%  FNAME   String with the name of a function to combine two datasets or
%          mappings, 'plus' for PLUS(U1,U2)
%  U1,U2   Untrained mappings to be combined
%  V1,V2   Trained or fixe mappings to be combined
%  SIZE    Desired images size of output dataset objects
%
% OUTPUT
%  B       Dataset
%  W       Combined mapping
%
% DESCRIPTION
% This special mapping is a low-level routine to facilitate dyadic
% operations on datafiles, datasets and mappings. Datasets to be combined
% should have the same number of objects. Image objects should have the
% same image size. Datafiles should be preprocessed or postprocessed
% versions of the same original datafile. Mappings should have the same
% input and output size.
%
% This routine has been written for use by PRTools programmers only. Users
% are discouraged to call it directly. The routine is called by the
% dyadic operations of the classes 'prmapping' and 'prdatafile'.
%
% SEE ALSO (PRTools Guide)
% DATASETS, MAPPINGS, DATAFILES

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function b = dyadicm(a,p,q,fsize)

  if nargin < 4, fsize = []; end
  if nargin < 3, q = []; end
  if nargin < 2, p = []; end

  if nargin < 1 | isempty(a)
    % note that this routine is sometimes externally set to a combiner
    b = prmapping(mfilename,'fixed',{p,q,fsize});
    b = setsize_out(b,prod(fsize));
    b = setname(b,'dyadicm');
    return
  end

  if isempty(p)
    p = 1;
  end

  if iscell(a) & ismapping(a{1})
    % combining mappings. We are just here because the dyadic mapping
    % operations like mapping/plus.m are programmed like this.
    if isfixed(a{1})
      b = prmapping(mfilename,'fixed',{{a{1},a{2},p}});
    elseif isuntrained(a{1})
      b = prmapping(mfilename,'untrained',{{a{1},a{2},p},[],[]});
    elseif istrained(a{1})
      if isempty(fsize), fsize = getsize(a{1},2); end
      b = prmapping(mfilename,'trained',{a{1},a{2},p},getlabels(a{1}),getsize(a{1},1));
    else
      error('Wrong type')
    end

  elseif isdatafile(a)
    nodatafile; % forces to store this routine as postprocessing

  elseif iscell(p) & isuntrained(p{1}) % train mappings stored in p
    v1 = p{1};     % untrained mapping
    v2 = p{2};     % fixed or trained mapping or constant
    p = p{3};      % operation
    v1 = a*v1;     % train first mapping
    [kin,kout] = size(v1);
    if ismapping(v2) & (isfixed(v2) | istrained(v2))
      ;            % leave it and hope for the best
    elseif ismapping(v2) & isuntrained(v2)
      v2 = a*v2;   % train it
    else           % should be scalar constant
      ;            % leave it
    end
    % store a standard trained mapping
    b = prmapping(mfilename,'trained',{v1,v2,p},getlabels(v1),kin,kout);

  elseif iscell(p) % execute mappings stored in p
    v1 = p{1};     % fixed or trained mapping
    v2 = p{2};     % fixed or trained mapping or constant
    p = p{3};      % operation
    a1 = a*v1;     % prepare datasets
    if ismapping(v2)
      a2 = a*v2;
    else
      a2 = setdata(a1,v2*ones(size(a1)));
    end
    b = feval(p,a1,a2);

  elseif ismapping(p) % execute standard PRTools trained mapping
    v1 = getdata(p,1);     % fixed or trained mapping
    v2 = getdata(p,2);     % fixed or trained mapping or constant
    p = getdata(p,3);      % operation
    a1 = a*v1;     % prepare datasets
    if ismapping(v2)
      a2 = a*v2;
    else
      a2 = setdata(a1,v2*ones(size(a1)));
    end
    b = feval(p,a1,a2);

  else % the basic call by data and parameters

    % The dataset A has horizontally to be split in two datasets A1, A2.
    % If P and Q are scalars or if Q = [], this is done half-half.
    % If P is a string (name of a routine) it is used to combine A1 and A2.

    [a1,a2,fsize] = split_dataset(a,p,q,fsize);

    if ischar(p) % function name in p
      b = feval(p,a1,a2);
    else % addition
      if isempty(q), q = 1; end
      b = a1*p + a2*q;
    end

    if isdataset(b)
      b = setfeatsize(b,fsize);
    end

  end

return

function [a1,a2,fsize] = split_dataset(a,p,q,fsize)

  if iscell(a)

    a1 = a{1};
    a2 = a{2};

  %elseif ischar(p) & ~isempty(q)
%   elseif ~isempty(q)
%     % split as defined by q
%     fsize = q;
%     a1 = a(:,1:q);
%     a2 = a(:,q+1:end);

  else
    % 50-50 split, but preserve possible image structure
    if isempty(fsize) | fsize == 0
      if isdataset(a)
        fsize = getfeatsize(a);
      else % may be a is set of images
        a = double(a);
        fsize = size(a);
      end
      while (fsize(end) == 1) & (length(fsize) > 1)
        fsize = fsize(1:end-1);
      end
      fsize(end) = fsize(end)/2;
    end
    k = size(a,2);

    if k ~= 2*floor(k/2)
      error('Feature size of dataset should be multiple of 2')
    end

    a1 = a(:,1:k/2);
    a2 = a(:,k/2+1:k);
    if isdataset(a)
      a1 = setfeatsize(a1,fsize);
      a2 = setfeatsize(a2,fsize);
    end

  end

return


%changeCobraSolver('tomlab_cplex','lp');
%modelClosed = modelConsistent;
clear FF R
% add demands for all metabolites in Recon
%modelClosed = model;
%modelClosed = addDemandReaction(modelClosed,modelClosed.mets);

modelexchanges1 = strmatch('Ex_',modelClosed.rxns);
modelexchanges4 = strmatch('EX_',modelClosed.rxns);
modelexchanges2 = strmatch('DM_',modelClosed.rxns);
modelexchanges3 = strmatch('sink_',modelClosed.rxns);
selExc = (find( full((sum(abs(modelClosed.S)==1,1) ==1) & (sum(modelClosed.S~=0) == 1))))';

modelexchanges = unique([modelexchanges1;modelexchanges2;modelexchanges3;modelexchanges4;selExc]);
modelClosed.lb(ismember(modelClosed.rxns,modelClosed.rxns(modelexchanges)))=0;
%modelClosed.ub(find(ismember(modelClosed.rxns,modelClosed.rxns(modelexchanges))))=0;

[LeakMets,modelClosed] = fastLeakTest(modelClosed, modelClosed.rxns(modelexchanges),0);


% std_setcomps2cell - convert .sets and .comps to cell array. The .sets and
%                     .comps format is useful for GUI but the cell array
%                     format is used for plotting and statistics.
%
% Usage:
%   [ struct setinds allinds ] = std_setcomps2cell(STUDY, clustind);
%   [ struct setinds allinds ] = std_setcomps2cell(STUDY, sets, comps);
%
% Author: Arnaud Delorme, CERCO/CNRS, UCSD, 2009-

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

function [ tmpstruct setinds allinds ] = std_setcomps2cell(STUDY, sets, comps, generateerror)

if nargin < 4
    generateerror = 0;
end;
if nargin < 3
    tmpstruct = STUDY.cluster(sets);
    sets  = tmpstruct.sets;
    comps = tmpstruct.comps; % old format
else
    tmpstruct     = [];
end;
comps   = repmat(comps, [size(sets,1) 1]);
oldsets = sets;
sets    = reshape(sets , 1, size(sets ,1)*size(sets ,2));
comps   = reshape(comps, 1, size(comps,1)*size(comps,2));

% get indices for all groups and conditions
% -----------------------------------------
setinfo       = STUDY.design(STUDY.currentdesign).cell;
allconditions = STUDY.design(STUDY.currentdesign).variable(1).value;
allgroups     = STUDY.design(STUDY.currentdesign).variable(2).value;
nc = max(length(allconditions),1);
ng = max(length(allgroups),    1);
allinds = cell( nc, ng );
setinds = cell( nc, ng );

for index = 1:length(setinfo)
    % get index of independent variables
    % ----------------------------------
    condind = std_indvarmatch( setinfo(index).value{1}, allconditions);
    grpind  = std_indvarmatch( setinfo(index).value{2}, allgroups    );
    if isempty(allconditions), condind = 1; end;
    if isempty(allgroups),     grpind  = 1; end;

    % get the position in sets where the dataset is
    % if several datasets check that they all have the same
    % ICA and component index
    % -----------------------
    datind  = setinfo(index).dataset;
    ind     = find(datind(1) == sets);
    if ~isempty(ind) && length(datind) > 1
        [ind1 ind2] = find(datind(1) == oldsets);
        columnica   = oldsets(:,ind2(1));
        if ~all(ismember(datind, columnica));
            disp('Warning: ***** change STUDY design as it combines datasets with different ICA decompositions');
        end;
    end;

    allinds{ condind, grpind } = [ allinds{ condind, grpind } comps(ind) ];
    setinds{ condind, grpind } = [ setinds{ condind, grpind } repmat(index, [1 length(ind)]) ];
end;
tmpstruct.allinds = allinds;
tmpstruct.setinds = setinds;

if generateerror && isempty(setinds{1})
    error( [ 'Some datasets not included in preclustering' 10 ...
             'because of partial STUDY design. You need to' 10 ...
             'use a STUDY design that includes all datasets.' ]);
end;


% Copyright 2011 Zdenek Kalal
%
% This file is part of TLD.
%
% TLD 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.
%
% TLD 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 TLD.  If not, see <http://www.gnu.org/licenses/>.

addpath(genpath('.')); init_workspace;

opt.source          = struct('camera',0,'input','_input/','bb0',[]); % camera/directory swith, directory_name, initial_bounding_box (if empty, it will be selected by the user)
opt.output          = '_output/'; mkdir(opt.output); % output directory that will contain bounding boxes + confidence

min_win             = 24; % minimal size of the object's bounding box in the scanning grid, it may significantly influence speed of TLD, set it to minimal size of the object
patchsize           = [15 15]; % size of normalized patch in the object detector, larger sizes increase discriminability, must be square
fliplr              = 0; % if set to one, the model automatically learns mirrored versions of the object
maxbbox             = 1; % fraction of evaluated bounding boxes in every frame, maxbox = 0 means detector is truned off, if you don't care about speed set it to 1
update_detector     = 1; % online learning on/off, of 0 detector is trained only in the first frame and then remains fixed
opt.plot            = struct('pex',1,'nex',1,'dt',1,'confidence',1,'target',1,'replace',0,'drawoutput',3,'draw',0,'pts',1,'help', 0,'patch_rescale',1,'save',0);

% Do-not-change -----------------------------------------------------------

opt.model           = struct('min_win',min_win,'patchsize',patchsize,'fliplr',fliplr,'ncc_thesame',0.95,'valid',0.5,'num_trees',10,'num_features',13,'thr_fern',0.5,'thr_nn',0.65,'thr_nn_valid',0.7);
opt.p_par_init      = struct('num_closest',10,'num_warps',20,'noise',5,'angle',20,'shift',0.02,'scale',0.02); % synthesis of positive examples during initialization
opt.p_par_update    = struct('num_closest',10,'num_warps',10,'noise',5,'angle',10,'shift',0.02,'scale',0.02); % synthesis of positive examples during update
opt.n_par           = struct('overlap',0.2,'num_patches',100); % negative examples initialization/update
opt.tracker         = struct('occlusion',10);
opt.control         = struct('maxbbox',maxbbox,'update_detector',update_detector,'drop_img',1,'repeat',1);

% Run TLD -----------------------------------------------------------------
%profile on;
[bb,conf] = tldExample(opt);
%profile off;
%profile viewer;

% Save results ------------------------------------------------------------
dlmwrite([opt.output '/tld.txt'],[bb; conf]');
disp('Results saved to ./_output.');