function varargout = spm_mip_ui(varargin)
% GUI for displaying MIPs with interactive pointers
% FORMAT hMIPax = spm_mip_ui(Z,XYZ,M,DIM,F,units)
% Z       - {1 x ?} vector point list of SPM values for MIP
% XYZ     - {3 x ?} matrix of coordinates of points (Talairach coordinates)
% M       - voxels - > mm matrix
% DIM     - image dimensions {voxels}
% F       - Figure (or axes) to work in [Defaults to gcf]
% hMIPax  - handle of MIP axes
% units   - units of space
%
% FORMAT xyz = spm_mip_ui('GetCoords',h)
% h       - Handle of MIP axes, or figure containing MIP axis [default gcf]
% xyz     - Current Talairach coordinates of cursor
%
% FORMAT [xyz,d] = spm_mip_ui('SetCoords',xyz,h,hC)
% xyz     - (Input) {3 x 1} vector of desired Talairach coordinates
% h       - Handle of MIP axes, or figure containing MIP axis [default gcf]
% hC      - Handle of calling object, if used as a callback. [Default 0]
% xyz     - (Output) {3 x 1} vector of voxel centre nearest desired xyz co-ords
% d       - Euclidian distance from desired co-ords & nearest voxel
%__________________________________________________________________________
%
% spm_mip_ui displays a maximum intensity projection (using spm_mip)
% with draggable cursors.
%
% See spm_mip.m for details of MIP construction, display, and the brain
% outlines used.
%                           ----------------
%
% The cursor can be dragged to new locations in three ways:
%
% (1) Point & drop: Using the primary "select" mouse button, click on a
%     cursor and drag the crosshair which appears to the desired location.
%     On dropping, the cursors jump to the voxel centre nearest the drop
%     site.
%
% (2) Dynamic drag & drop: Using the middle "extend" mouse button, click on
%     a cursor and drag it about. The cursor follows the mouse, jumping to
%     the voxel centre nearest the pointer. A dynamically updating
%     information line appears above the MIP and gives the current
%     co-ordinates. If the current voxel centre is in the XYZ pointlist,
%     then the corresponding image value is also printed.
%
% (3) Magnetic drag & drop: As with "Dynamic drag & drop", except the cursors
%     jump to the voxel centre in the pointlist nearest to the cursor. Use
%     the right "alt" mouse button for "magnetic drag & drop".
%
% In addition a ContextMenu is provided, giving the option to jump the
% cursors to the nearest suprathreshold voxel, the nearest local
% maximum, or to the global maximum. (Right click on the MIP to bring up
% the ContextMenu.) A message in the MATLAB command window describes the
% jump.
%
%                           ----------------
%
% The current cursor position (constrained to lie on a voxel) can be
% obtained by xyz=spm_mip_ui('GetCoords',hMIPax), and set with
% xyz=spm_mip_ui('SetCoords',xyz,hMIPax), where hMIPax is the handle of
% the MIP axes, or of the figure containing a single MIP [default gcf].
% The latter rounds xyz to the nearest voxel center, returning the
% result.
%
% spm_mip_ui handles all the callbacks required for moving the cursors, and
% is "registry" enabled (See spm_XYZreg.m). Programmers help is below in the
% main body of the function.
%
%__________________________________________________________________________
% Copyright (C) 1996-2018 Wellcome Trust Centre for Neuroimaging

% Andrew Holmes
% $Id: spm_mip_ui.m 7388 2018-08-06 12:04:26Z guillaume $

%==========================================================================
% - FORMAT specifications for embedded CallBack functions
%==========================================================================
%( This is a multi function function, the first argument is an action  )
%( string, specifying the particular action function to take.          )
%
% FORMAT hMIPax = spm_mip_ui(Z,XYZ,V,F,units)
% [ShortCut] Defaults to hMIPax=spm_mip_ui('Display',Z,XYZ,V,F,units)
%
% FORMAT hMIPax = spm_mip_ui('Display',Z,XYZ,M,DIM,F,units)
% Displays the MIP and sets up cursors
% Z       - {1 x ?} vector point list of SPM values for MIP
% XYZ     - {3 x ?} matrix of coordinates of points (Talairach coordinates)
% M       - voxels - > mm matrix
% DIM     - image dimensions {voxels}
% F       - Handle of figure (or axes) to work in [Defaults to gcf]
% hMIPax  - handle of MIP axes
% units   - units of space
%
% FORMAT xyz = spm_mip_ui('GetCoords',h)
% Returns coordinates of current cursor position
% h       - Handle of MIP axes [defaults to spm_mip_ui('FindMIPax')]
% xyz     - Current Talairach coordinates of cursor
%
% FORMAT [xyz,d] = spm_mip_ui('SetCoords',xyz,h,hC)
% Sets cursor position
% xyz     - (Input) {3 x 1} vector of desired Talairach coordinates
% h       - Handle of MIP axes [defaults to spm_mip_ui('FindMIPax')]
% hC      - Handle of calling object, if used as a callback. [Default 0]
% xyz     - (Output) {3 x 1} vector of voxel centre nearest desired xyz co-ords
% d       - Euclidian distance from desired co-ords & nearest voxel
%
% FORMAT spm_mip_ui('PosnMarkerPoints',xyz,h)
% Utility routine: Positions cursor markers
% xyz     - {3 x 1} vector of Talairach coordinates for cursor
% h       - Handle of MIP axes [defaults to spm_mip_ui('FindMIPax')]
%
% FORMAT [xyz,d] = spm_mip_ui('Jump',h,loc)
% Utility routine (CallBack of UIcontextMenu) to jump cursor
% h       - Handle of MIP axes [defaults to spm_mip_ui('FindMIPax')]
% loc     - String defining jump: 'dntmv' - don't move
%                                 'nrvox' - nearest suprathreshold voxel
%                                 'nrmax' - nearest local maximum
%                                 'glmax' - global maximum
% xyz     - co-ordinates of voxel centre jumped to {3 x 1} vector
% d       - (square) Euclidian distance jumped
%
% FORMAT hMIPax = spm_mip_ui('FindMIPax',h)
% Looks for / checks MIP axes 'Tag'ged 'hMIPax'... errors if no valid MIP axes
% h       - Handle of MIP axes, or figure containing MIP axis [default gcf]
% hMIPax  - Handle of valid MIP axis found (errors if multiple found)
%
% FORMAT spm_mip_ui('MoveStart')
% Utility routine: CallBack for starting cursor dragging
% This is the ButtonDownFcn for the cursor markers
%
% FORMAT spm_mip_ui('Move',DragType)
% Utility routine: Initiate cursor move
% DragType - 0 = Point & drop (no dragging)
%            1 = Drag'n'drop with dynamic coordinate/value updating
%            2 = Magnetic drag'n'drop with dynamic coordinate updating
%
% FORMAT spm_mip_ui('MoveEnd')
% Utility routine: End cursor move
%__________________________________________________________________________

%-Condition arguments
%==========================================================================
if nargin==0
    error('Insufficient arguments')
elseif ~ischar(varargin{1})
    varargout={spm_mip_ui('Display',varargin{1:end})}; return
end

%-Axis offsets for 3d MIPs:
%==========================================================================
%-MIP pane dimensions, origin offsets and #pixels per mm
%-See spm_project.c for derivation
mipmat = char(spm_get_defaults('stats.results.mipmat'));
load(mipmat, 'DXYZ', 'CXYZ', 'scale');
% DMIP = [DXYZ(2)+DXYZ(1), DXYZ(1)+DXYZ(3)];
%-Coordinates of Talairach origin in multipane MIP image (Axes are 'ij' + rot90)
% Transverse: [Po(1), Po(2)]
% Sagittal  : [Po(1), Po(3)]
% Coronal   : [Po(4), Po(3)]
% 4 voxel offsets in Y since using character '<' as a pointer.
Po(1)  =                  CXYZ(2) -2;
Po(2)  = DXYZ(3)+DXYZ(1) -CXYZ(1) +2;
Po(3)  = DXYZ(3)         -CXYZ(3) +2;
Po(4)  = DXYZ(2)         +CXYZ(1) -2;

%==========================================================================
switch lower(varargin{1}), case 'display'
%==========================================================================
    % hMIPax = spm_mip_ui('Display',Z,XYZ,M,DIM,F,units)
    if nargin<5
        F      = gcf;
        hMIPax = [];
    else
        F = varargin{6};
        if ischar(F), F=spm_figure('FindWin',F); end
        if ~ishandle(F), error('Invalid handle'), end
        switch get(F,'Type'), case 'figure'
            hMIPax = [];
            case 'axes'
                hMIPax = F;
                F      = get(hMIPax,'Parent');
            otherwise
                error('F not a figure or axis handle')
        end
    end
    if nargin<4, error('Insufficient arguments'), end
    Z       = varargin{2};
    XYZ     = varargin{3};
    M       = varargin{4};
    DIM     = varargin{5};
    try
        units = varargin{7};
    catch
        units = {'mm' 'mm' 'mm'};
    end

    xyz = spm_XYZreg('RoundCoords',[0;0;0],M,DIM);

    %-Display (MIP) transformation matrix
    %----------------------------------------------------------------------
    % Md maps various non-spatial data
    % Ms maps spatial (mm) to pixel coordinates, it must not change
    % mapping of non-spatial coordinates
    % Ms is left-multiplied to Md
    if isequal(units,{'mm' 'mm' 'mm'})
        Md      = eye(4);
        Ms      = diag([scale(1:3) 1]);
    elseif isequal(units,{'mm' 'mm' ''})
        Md      = eye(4);
        Md(3,4) = -100;  % out of field of view (MNI)
        Ms      = diag([scale(1:2) 1 1]);
    elseif isequal(units,{'mm' 'mm' 'ms'}) || isequal(units,{'mm' 'mm' 'Hz'})
        Md      = eye(4);
        Md(3,3) = 80 / (M(3,3)*DIM(3));
        Md(3,4) = -80 * M(3,4) / (M(3,3)*DIM(3));
        Ms      = diag([scale(1:2) 1 1]);
    elseif isequal(units,{'Hz' 'ms' ''})
        Md      = eye(4);
        Md(1,1) = -136 / (M(1,1)*DIM(1));
        Md(1,4) = M(1,4)*136 / (M(1,1)*DIM(1)) + 68;
        Md(2,2) = 172 / (M(2,2)*DIM(2));
        Md(2,4) = -M(2,4)*172 / (M(2,2)*DIM(2)) - 100;
        Md(3,4) = -100;
        Ms      = eye(4);
    elseif isequal(units,{'Hz' 'Hz' ''})
        Md      = eye(4);
        Md(1,1) = 136 / (M(1,1)*DIM(1));
        Md(1,4) = -M(1,4)*136 / (M(1,1)*DIM(1)) - 68;
        Md(2,2) = 172 / (M(2,2)*DIM(2));
        Md(2,4) = -M(2,4)*172 / (M(2,2)*DIM(2)) - 100;
        Md(3,4) = -100;
        Ms      = eye(4);
    elseif isequal(units,{'mm' 'mm' '%'})
        warning('Handling of data units changed: please re-estimate model.');
        units   = {'mm' 'mm' 'ms'};
        Md      = eye(4);
        Md(3,3) = 80 / (M(3,3)*DIM(3));
        Md(3,4) = -80 * M(3,4) / (M(3,3)*DIM(3));
        Ms      = diag([scale(1:2) 1 1]);
    else
        error('Unknown data units.');
    end

    %-Display MIP
    %----------------------------------------------------------------------
    Funits = get(F,'Units');
    set(F,'Units','normalized')
    if isempty(hMIPax)
        hMIPax = axes('Position',[0.24 0.54 0.62 0.42],'Parent',F);
    else
        axes(hMIPax), cla reset
    end

    %-NB: spm_mip's `image` uses a newplot, & screws stuff without the figure.
    figure(F)
    pXYZ = Ms*Md*[XYZ;ones(1,size(XYZ,2))];
    spm_mip(Z,pXYZ(1:3,:),Ms*Md*M,units);
    hMIPim = get(gca,'Children');

    %-Print coordinates
    %----------------------------------------------------------------------
    hMIPxyz = text(0,max(get(hMIPax,'YLim'))/2,...
        {'{\bfSPM}{\itmip}',sprintf('[%g, %g, %g]',xyz(1:3))},...
        'Interpreter','TeX','FontName',spm_platform('font','times'),...
        'Color',[1,1,1]*.7,...
        'HorizontalAlignment','Center',...
        'VerticalAlignment','Bottom',...
        'Rotation',90,...
        'Tag','hMIPxyz',...
        'UserData',xyz);

    %-Create point markers
    %----------------------------------------------------------------------
    xyz = Ms*Md*[xyz(:);1];
    xyz = xyz(1:3);
    hX1r  = text(Po(1)+xyz(2),Po(2)+xyz(1),'<',...
        'Color','r','Fontsize',16,...
        'Tag','hX1r',...
        'ButtonDownFcn','spm_mip_ui(''MoveStart'')');
    hX2r  = text(Po(1)+xyz(2),Po(3)-xyz(3),'<',...
        'Color','r','Fontsize',16,...
        'Tag','hX2r',...
        'ButtonDownFcn','spm_mip_ui(''MoveStart'')');
    hX3r  = text(Po(4)+xyz(1),Po(3)-xyz(3),'<',...
        'Color','r','Fontsize',16,...
        'Tag','hX3r',...
        'ButtonDownFcn','spm_mip_ui(''MoveStart'')');
    hXr   = [hX1r,hX2r,hX3r];

    if DIM(3) == 1
        %-2 dimensional data
        %------------------------------------------------------------------
        set(hXr(3),'Visible','off');
        set(hXr(2),'Visible','off');

    end

    %-Create UIContextMenu for marker jumping
    %-----------------------------------------------------------------------
    h = uicontextmenu('Tag','MIPconmen','UserData',hMIPax,'Parent',F);
    if isempty(XYZ), str='off'; else str='on'; end
    uimenu(h,'Separator','off','Label','goto nearest suprathreshold voxel',...
        'CallBack',['spm_mip_ui(''Jump'',',...
        'get(get(gcbo,''Parent''),''UserData''),''nrvox'');'],...
        'Interruptible','off','BusyAction','Cancel','Enable',str);
    uimenu(h,'Separator','off','Label','goto nearest local maximum',...
        'CallBack',['spm_mip_ui(''Jump'',',...
        'get(get(gcbo,''Parent''),''UserData''),''nrmax'');'],...
        'Interruptible','off','BusyAction','Cancel','Enable',str);
    uimenu(h,'Separator','off','Label','goto global maximum',...
        'CallBack',['spm_mip_ui(''Jump'',',...
        'get(get(gcbo,''Parent''),''UserData''),''glmax'');'],...
        'Interruptible','off','BusyAction','Cancel','Enable',str);
    uimenu(h,'Separator','on','Label','save MIP as...',...
        'CallBack',['spm_mip_ui(''Save'', ',...
        'get(get(gcbo,''Parent''),''UserData''));'],...
        'Interruptible','off','BusyAction','Cancel');
    h1 = uimenu(h,'Separator','on','Label','Extract data');
    h2 = uimenu(h1,'Label','raw y');
    uimenu(h2,'Label','This voxel',...
        'CallBack','y=spm_mip_ui(''Extract'', ''Y'', ''voxel'')',...
        'Interruptible','off','BusyAction','Cancel');
    uimenu(h2,'Label','This cluster',...
        'CallBack','y=spm_mip_ui(''Extract'', ''Y'', ''cluster'')',...
        'Interruptible','off','BusyAction','Cancel');
    h2 = uimenu(h1,'Label','whitened and filtered y');
    uimenu(h2,'Label','This voxel',...
        'CallBack','y=spm_mip_ui(''Extract'', ''KWY'', ''voxel'')',...
        'Interruptible','off','BusyAction','Cancel');
    uimenu(h2,'Label','This cluster',...
        'CallBack','y=spm_mip_ui(''Extract'', ''KWY'', ''cluster'')',...
        'Interruptible','off','BusyAction','Cancel');
    h2 = uimenu(h1,'Label','betas');
    uimenu(h2,'Label','This voxel',...
        'CallBack','beta=spm_mip_ui(''Extract'', ''beta'', ''voxel'')',...
        'Interruptible','off','BusyAction','Cancel');
    uimenu(h2,'Label','This cluster',...
        'CallBack','beta=spm_mip_ui(''Extract'', ''beta'', ''cluster'')',...
        'Interruptible','off','BusyAction','Cancel');

    % overlay sensor positions for M/EEG
    %----------------------------------------------------------------------
    if strcmp(spm('CheckModality'), 'EEG') && ...
            (isequal(units,{'mm' 'mm' 'ms'}) || isequal(units,{'mm' 'mm' 'Hz'}))
        uimenu(h,'Separator','on','Label','display/hide channels',...
            'CallBack',['spm_mip_ui(''Channels'', ',...
            'get(get(gcbo,''Parent''),''UserData''));'],...
            'Interruptible','off','BusyAction','Cancel','Enable',str);
        uimenu(h,'Separator','off','Label','goto nearest suprathreshold channel',...
            'CallBack',['spm_mip_ui(''Jump'',',...
        'get(get(gcbo,''Parent''),''UserData''),''nrchan'');'],...
            'Interruptible','off','BusyAction','Cancel','Enable',str);
    end

    set(hMIPim,'UIContextMenu',h)

    %-Save handles and data
    %----------------------------------------------------------------------
    set(hMIPax,'Tag','hMIPax','UserData',...
        struct(...
        'hReg',     [],...
        'hMIPxyz',  hMIPxyz,...
        'XYZ',      XYZ,...
        'Z',        Z,...
        'M',        M,...
        'Md',       Md,...
        'Ms',       Ms,...
        'DIM',      DIM,...
        'units',    {units},...
        'hXr',      hXr))

    varargout = {hMIPax};

    %======================================================================
    case 'getcoords'
    %======================================================================
        % xyz = spm_mip_ui('GetCoords',h)
        if nargin<2, h=spm_mip_ui('FindMIPax'); else h=varargin{2}; end
        varargout = {get(getfield(get(h,'UserData'),'hMIPxyz')  ,'UserData')};

    %======================================================================
    case 'setcoords'
    %======================================================================
        % [xyz,d] = spm_mip_ui('SetCoords',xyz,h,hC)
        if nargin<4, hC=NaN; else hC=varargin{4}; end
        if nargin<3, h=spm_mip_ui('FindMIPax'); else h=varargin{3}; end
        if nargin<2, error('Set coords to what?'), else xyz=varargin{2}; end

        MD  = get(h,'UserData');

        %-Check validity of coords only when called without a caller handle
        %------------------------------------------------------------------
        if ~ishandle(hC)
            [xyz,d] = spm_XYZreg('RoundCoords',xyz,MD.M,MD.DIM);
            if d>0 && nargout<2, warning(sprintf(...
                    '%s: Co-ords rounded to nearest voxel center: Discrepancy %.2f',...
                    mfilename,d));
            end
        else
            d = [];
        end

        %-Move marker points, update internal cache in hMIPxyz
        %------------------------------------------------------------------
        spm_mip_ui('PosnMarkerPoints',MD.Md(1:3,:)*[xyz;1],h);
        set(MD.hMIPxyz,'UserData',reshape(xyz(1:3),3,1))
        set(MD.hMIPxyz,'String',{'{\bfSPM}{\itmip}',sprintf('[%g, %g, %g]',xyz(1:3))})

        %-Tell the registry, if we've not been called by the registry...
        %------------------------------------------------------------------
        if ~isempty(MD.hReg) && ~isequal(MD.hReg,hC), spm_XYZreg('SetCoords',xyz,MD.hReg,h); end

        %-Return arguments
        %------------------------------------------------------------------
        varargout = {xyz,d};

    %======================================================================
    case 'posnmarkerpoints'
    %======================================================================
        % spm_mip_ui('PosnMarkerPoints',xyz,h)
        if nargin<3, h=spm_mip_ui('FindMIPax'); else h=varargin{3}; end
        if nargin<2, xyz = spm_mip_ui('GetCoords',h); else xyz = varargin{2}; end
        MD = get(h,'UserData');

        %-Get handles of marker points from UserData of hMIPax
        %------------------------------------------------------------------
        hX = MD.hXr;

        %-Set marker points
        %------------------------------------------------------------------
        set(hX,'Units','Data')
        if length(hX)==1
            vx  = sqrt(sum(MD.M(1:3,1:3).^2));
            tmp = MD.M\[xyz ; 1];
            tmp = tmp(1:2).*vx(1:2)';
            set(hX,'Position',[tmp(1), tmp(2), 1])
        else
            pxyz = MD.Ms*[xyz(1:3);1];
            set(hX(1),'Position',[ Po(1) + pxyz(2), Po(2) + pxyz(1), 0])
            set(hX(2),'Position',[ Po(1) + pxyz(2), Po(3) - pxyz(3), 0])
            set(hX(3),'Position',[ Po(4) + pxyz(1), Po(3) - pxyz(3), 0])
        end

    %======================================================================
    case 'jump'
    %======================================================================
        % [xyz,d] = spm_mip_ui('Jump',h,loc)
        if nargin<3, loc='nrvox'; else loc=varargin{3}; end
        if nargin<2, h=spm_mip_ui('FindMIPax'); else h=varargin{2}; end

        %-Get current location & MipData
        %------------------------------------------------------------------
        oxyz = spm_mip_ui('GetCoords',h);
        MD   = get(h,'UserData');

        %-Compute location to jump to
        %------------------------------------------------------------------
        if isempty(MD.XYZ), loc='dntmv'; end
        switch lower(loc), case 'dntmv'
            spm('alert!','No suprathreshold voxels to jump to!',mfilename,0);
            varargout = {oxyz, 0};
            return
            case 'nrvox'
                str       = 'nearest suprathreshold voxel';
                [xyz,i,d] = spm_XYZreg('NearestXYZ',oxyz,MD.XYZ);
            case 'nrmax'
                str       = 'nearest local maximum';
                iM        = inv(MD.M);
                XYZvox    = iM(1:3,:)*[MD.XYZ; ones(1,size(MD.XYZ,2))];
                [null,null,XYZvox] = spm_max(MD.Z,XYZvox);
                XYZ       = MD.M(1:3,:)*[XYZvox; ones(1,size(XYZvox,2))];
                [xyz,i,d] = spm_XYZreg('NearestXYZ',oxyz,XYZ);
            case 'glmax'
                str       = 'global maximum';
                [null, i] = max(MD.Z); i = i(1);
                xyz       = MD.XYZ(:,i);
                d         = sqrt(sum((oxyz-xyz).^2));
            case 'nrchan'
                str       = 'nearest suprathreshold channel';
                if ~isfield(MD, 'hChanPlot'), spm_mip_ui('Channels',h); end
                MD        = get(h,'UserData');
                if ~isfield(MD, 'hChanPlot'), return; end
                [xyz,i,d] = spm_XYZreg('NearestXYZ',[oxyz(1); oxyz(2); 0],MD.Channels.pos);
                xyz(3)    = oxyz(3);
                str       = [str sprintf(' (%s)',MD.Channels.name{i})];
            otherwise
                warning('Unknown jumpmode')
                varargout = {xyz,0};
                return
        end

        %-Write jump report, jump, and return arguments
        %------------------------------------------------------------------
        fprintf(['\n\t%s:\tJumped %0.2fmm from [%3.0f, %3.0f, %3.0f],\n\t\t\t',...
            'to %s at [%3.0f, %3.0f, %3.0f]\n'],...
            mfilename, d, oxyz, str, xyz)

        spm_mip_ui('SetCoords',xyz,h,h);
        varargout = {xyz, d};

    %======================================================================
    case 'findmipax'
    %======================================================================
        % hMIPax = spm_mip_ui('FindMIPax',h)
        % Checks / finds hMIPax handles
        %-**** h is handle of hMIPax, or figure containing MIP (default gcf)
        if nargin<2, h=get(0,'CurrentFigure'); else h=varargin{2}; end
        if ischar(h), h=spm_figure('FindWin',h); end
        if ~ishandle(h), error('invalid handle'), end
        if ~strcmp(get(h,'Tag'),'hMIPax'), h=findobj(h,'Tag','hMIPax'); end
        if isempty(h), error('MIP axes not found'), end
        if length(h)>1, error('Multiple MIPs in this figure'), end
        varargout = {h};

    %======================================================================
    case 'movestart'
    %======================================================================
        % spm_mip_ui('MoveStart')
        [cO,cF] = gcbo;
        hMIPax  = get(cO,'Parent');
        MD      = get(hMIPax,'UserData');

        %-Store useful quantities in UserData of gcbo, the object to be dragged
        %------------------------------------------------------------------
        set(hMIPax,'Units','Pixels')
        set(cO,'UserData',struct(...
            'hReg',     MD.hReg,...
            'xyz',      spm_mip_ui('GetCoords',hMIPax),...
            'MIPaxPos', get(hMIPax,'Position')*[1,0;0,1;0,0;0,0],...
            'hMIPxyz',  MD.hMIPxyz,...
            'M',        MD.M,...
            'Md',       MD.Md,...
            'Ms',       MD.Ms,...
            'DIM',      MD.DIM,...
            'hX',       MD.hXr))

        %-Initiate dragging
        %------------------------------------------------------------------
        if strcmp(get(cF,'SelectionType'),'normal') || isempty(MD.XYZ)
            %-Set Figure callbacks for drop but no drag (DragType 0)
            %--------------------------------------------------------------
            set(MD.hMIPxyz,'Visible','on','String',...
                {'{\bfSPM}{\itmip}','\itPoint & drop...'})
            set(cF,'WindowButtonUpFcn',    'spm_mip_ui(''Move'',0)',...
                'Interruptible','off')
            set(cF,'Pointer','CrossHair')

       %-Set Figure callbacks for drag'n'drop (DragType 1)
       %-------------------------------------------------------------------
        elseif strcmp(get(cF,'SelectionType'),'extend')
            set(MD.hMIPxyz,'Visible','on','String',...
                {'{\bfSPM}{\itmip}','\itDynamic drag & drop...'})
            set(cF,'WindowButtonMotionFcn','spm_mip_ui(''Move'',1)',...
                'Interruptible','off')
            set(cF,'WindowButtonUpFcn',    'spm_mip_ui(''MoveEnd'')',...
                'Interruptible','off')
            set(cF,'Pointer','Fleur')

        %-Set Figure callbacks for drag'n'drop with co-ord updating (DragType 2)
        %------------------------------------------------------------------
        elseif strcmp(get(cF,'SelectionType'),'alt')
            set(MD.hMIPxyz,'Visible','on','String',...
                {'{\bfSPM}{\itmip}','\itMagnetic drag & drop...'})
            set(cF,'WindowButtonMotionFcn','spm_mip_ui(''Move'',2)',...
                'Interruptible','off')
            set(cF,'WindowButtonUpFcn',    'spm_mip_ui(''MoveEnd'')',...
                'Interruptible','off')
            set(cF,'Pointer','Fleur')
        end

    %======================================================================
    case 'move'
    %======================================================================
        % spm_mip_ui('Move',DragType)
        if nargin<2, DragType = 2; else DragType = varargin{2}; end
        cF = gcbf;
        cO = gco(cF);

        %-Get useful data from UserData of gcbo, the object to be dragged
        %------------------------------------------------------------------
        MS  = get(cO,'UserData');

        %-Work out where we are moving to - Use HandleGraphics to give position
        %------------------------------------------------------------------
        set(cF,'Units','pixels')
        d = get(cF,'CurrentPoint') - MS.MIPaxPos;
        set(cO,'Units','pixels')
        set(cO,'Position',d)
        set(cO,'Units','data')
        d = get(cO,'Position');

        %-Work out xyz, depending on which view is being manipulated
        %------------------------------------------------------------------
        sMarker = get(cO,'Tag');
        pxyz = MS.Ms*[MS.xyz;1];
        if strcmp(sMarker,'hX1r')
            xyz = [d(2) - Po(2); d(1) - Po(1); pxyz(3)];
        elseif strcmp(sMarker,'hX2r')
            xyz = [pxyz(1); d(1) - Po(1); Po(3) - d(2)];
        elseif strcmp(sMarker,'hX3r')
            xyz = [d(1) - Po(4); pxyz(2); Po(3) - d(2)];
        else
            error('Can''t work out which marker point')
        end
        xyz = inv(MS.Ms*MS.Md) * [xyz(:);1]; xyz = xyz(1:3);

        %-Round coordinates according to DragType & set in hMIPxyz's UserData
        %------------------------------------------------------------------
        if DragType==0
            xyz    = spm_XYZreg('RoundCoords',xyz,MS.M,MS.DIM);
        elseif DragType==1
            xyz    = spm_XYZreg('RoundCoords',xyz,MS.M,MS.DIM);
            hMIPax = get(cO,'Parent');
            MD     = get(hMIPax,'UserData');
            i      = spm_XYZreg('FindXYZ',xyz,MD.XYZ);
        elseif DragType==2
            hMIPax = get(cO,'Parent');
            MD     = get(hMIPax,'UserData');
            [xyz,i,d] = spm_XYZreg('NearestXYZ',xyz,MD.XYZ);
        end
        set(MS.hMIPxyz,'UserData',xyz)

        %-Move marker points
        %------------------------------------------------------------------
        set(MS.hX,'Units','Data')
        xyz2 = MS.Ms * MS.Md * [xyz(:);1];
        xyz2 = xyz2(1:3);
        set(MS.hX(1),'Position',[ Po(1) + xyz2(2), Po(2) + xyz2(1), 0])
        set(MS.hX(2),'Position',[ Po(1) + xyz2(2), Po(3) - xyz2(3), 0])
        set(MS.hX(3),'Position',[ Po(4) + xyz2(1), Po(3) - xyz2(3), 0])

        %-Update dynamic co-ordinate strings (if appropriate DragType)
        %------------------------------------------------------------------
        if DragType==0
            spm_mip_ui('MoveEnd')
        elseif DragType==1
            if isempty(i)
                str = {'{\bfSPM}{\itmip}',sprintf('[%g, %g, %g]',xyz)};
            else
                str = {'{\bfSPM}{\itmip}: ',...
                    sprintf('[%g, %g, %g]: %.4f',xyz,MD.Z(i))};
            end
            set(MD.hMIPxyz,'String',str)
        elseif DragType==2
            set(MD.hMIPxyz,'String',...
                {'{\bfSPM}{\itmip}',sprintf('[%g, %g, %g]: %.4f',xyz,MD.Z(i))})
        else
            error('Illegal DragType')
        end

    %======================================================================
    case 'moveend'
    %======================================================================
        % spm_mip_ui('MoveEnd')
        cF = gcbf;
        cO = gco(cF);
        hMIPax  = get(cO,'Parent');
        MS      = get(cO,'UserData');

        %-Reset WindowButton functions, pointer & SPMmip info-string
        %------------------------------------------------------------------
        set(gcbf,'WindowButtonMotionFcn',' ')
        set(gcbf,'WindowButtonUpFcn',' ')
        set(gcbf,'Pointer','arrow')
        set(MS.hMIPxyz,'String',...
            {'{\bfSPM}{\itmip}',sprintf('[%g, %g, %g]',get(MS.hMIPxyz,'UserData'))})

        %-Set coordinates after drag'n'drop, tell registry
        %------------------------------------------------------------------
        % don't need to set internal coordinates 'cos 'move' does that
        if ~isempty(MS.hReg)
            spm_XYZreg('SetCoords',get(MS.hMIPxyz,'UserData'),MS.hReg,hMIPax);
        end

    %======================================================================
    case 'channels'
    %======================================================================
        % spm_mip_ui('Channels',h)
        % M/EEG specific to display channels on 1-slice MIP

        if nargin<2, h=spm_mip_ui('FindMIPax'); else h=varargin{2}; end
        MD  = get(h,'UserData');

        if ~isfield(MD, 'hChanPlot')
            % first time call
            D = spm_eeg_load;
            if isempty(D), return; end

            DIM = get(findobj('Tag','hFxyz'), 'UserData');

            [mod, Cind] = spm_eeg_modality_ui(D, 1, 1);
            otherind = setdiff(1:nchannels(D), Cind);
            if ~isempty(otherind)
                D = chantype(D, otherind, 'Other');
            end
            [Cel x, y] = spm_eeg_locate_channels(D, DIM.DIM(1), Cind);
            Cel = DIM.M * [Cel'; ones(2,size(Cel,1))];
            Cel = Cel(1:2,:)';
            pos = [Cel'; zeros(1,size(Cel,1))];
            Cel(:,1) = Cel(:,1) + Po(2);
            Cel(:,2) = Cel(:,2) + Po(1);
            hold(h,'on'), hChanPlot = plot(h,Cel(:, 2), Cel(:, 1), 'b*');

            hChanText = cell(1,size(Cel,1));
            name = D.chanlabels(Cind);
            figure(spm_figure('FindWin'));
            for i = 1:size(Cel, 1)
                hChanText{i} = text(Cel(i, 2)+0.5, Cel(i, 1), name{i}, 'Color', 'b');
            end

            MD.hChanPlot     = hChanPlot;
            MD.hChanText     = hChanText;
            MD.Channels.pos  = pos;
            MD.Channels.name = D.chanlabels(Cind);
            set(h, 'UserData', MD);
        else
            if strcmp(get(MD.hChanPlot, 'Visible'), 'on');
                % switch it off
                set(MD.hChanPlot, 'Visible', 'off');
                for i = 1:length(MD.hChanText)
                    set(MD.hChanText{i}, 'Visible', 'off');
                end
            else
                % switch it on
                set(MD.hChanPlot, 'Visible', 'on');
                for i = 1:length(MD.hChanText)
                    set(MD.hChanText{i}, 'Visible', 'on');
                end
            end

        end

    %======================================================================
    case 'save'
    %======================================================================
        % spm_mip_ui('Save',h)
        if nargin<2, h=spm_mip_ui('FindMIPax'); else h=varargin{2}; end
        %MD  = get(h,'UserData');
        %pXYZ = MD.Ms*MD.Md*[MD.XYZ;ones(1,size(MD.XYZ,2))];
        %mip  = spm_mip(MD.Z,pXYZ(1:3,:),MD.Ms*MD.Md*MD.M,MD.units);
        mip = get(findobj(h,'Type','image'),'CData');
        [f, p] = uiputfile('*.png', 'Save as', 'mip.png');
        if ~isequal(f,0) && ~isequal(p,0)
            if ndims(mip) == 3
                imwrite(mip,fullfile(p,f),'png');
            else
                imwrite(mip,gray(64),fullfile(p,f),'png');
            end
            fprintf('Saving SPM MIP in %s\n',fullfile(p,f));
        end

    %======================================================================
    case 'extract'
    %======================================================================
        % data = spm_mip_ui('Extract',what,where)
        if nargin<2, what='beta'; else what=varargin{2}; end
        if nargin<3, where='voxel'; else where=varargin{3}; end
        xSPM = evalin('base','xSPM');
        SPM  = evalin('base','SPM');

        XYZmm = spm_results_ui('GetCoords');
        [XYZmm,i] = spm_XYZreg('NearestXYZ',XYZmm,xSPM.XYZmm);
        if isempty(i), varargout = {[]}; return; end
        spm_results_ui('SetCoords',xSPM.XYZmm(:,i));

        switch lower(where)
            case 'voxel'
                % current voxel
                XYZ = SPM.xVol.iM(1:3,:)*[XYZmm;1];
            case 'cluster'
                % current cluster
                A   = spm_clusters(xSPM.XYZ);
                j   = find(A == A(i));
                XYZ = xSPM.XYZ(:,j);
            otherwise
                error('Unknown action.');
        end

        switch lower(what)
            case {'y','kwy'}
                data = spm_get_data(SPM.xY.VY,XYZ);
                if strcmpi(what,'kwy')
                    data = spm_filter(SPM.xX.K,SPM.xX.W*data);
                end
            case 'beta'
                data = spm_get_data(SPM.Vbeta,XYZ);
            otherwise
                error('Unknown action.');
        end

        varargout = {data};

    %======================================================================
    otherwise
    %======================================================================
        error('Unknown action string')

end


function setup_cnnimageretrieval()
% SETUP_CNNIMAGERETRIEVAL Setup the toolbox.
%
%   setup_cnnimageretrieval()  Adds the toolbox to MATLAB path.
%
% Authors: F. Radenovic, G. Tolias, O. Chum. 2017.

[root] = fileparts(mfilename('fullpath')) ;

% add paths from this package
addpath(fullfile(root, 'cnnblocks'));
addpath(fullfile(root, 'cnninit'));
addpath(fullfile(root, 'cnntrain'));
addpath(fullfile(root, 'cnnvecs'));
addpath(fullfile(root, 'examples'));
addpath(fullfile(root, 'whiten'));
addpath(fullfile(root, 'utils'));


function writeProcessedImg(rob,sen,frm,Raw,ExpOpt)

% WRITEPROCESSEDIMG  Write processed image data to file.
%   WRITEPROCESSEDIMG(R,S,F,Raw) writes the processed image in Raw for
%   sensor S in robot R corresponding to frame F. R and S are Rob and Sen
%   structures in SLAMTB. F is an integer corresponding to the frame
%   number.
%
%   The processed image contains simply the coordinates of all the features
%   detected, with an identifier. See below for the file format.
%
%   The file written has the following name format
%       Directory: ./data/ <ExpOpt.site> /
%       Name: ExpOpt.procImgName , e.g. 'procImg-r%02d-s%02d-i%06d.txt'
%   where the first two indices are the robot and sensor numbers (2 digits
%   each) and the third is the frame number (6 digits). The file contains
%   one line per feature, containing identifier and UV coordinates
%   separated by tabs:
%           id1  U1  V1
%           id2  U2  V2
%           ...
%           idn  Un  Vn
%
%   See also READPROCESSEDIMG, WRITECONTROLSIGNAL, READCONTROLSIGNAL.

%   Copyright 2013 Joan Sola

dir = [ExpOpt.root  'data/' ExpOpt.site '/'];
if ~isdir(dir)
    mkdir(dir);
end
filename = sprintf(ExpOpt.procImgName,rob,sen,frm);
filepath = [dir filename];

fid = fopen(filepath,'w');
ids = Raw.data.points.app;
coord = Raw.data.points.coord;
for i = 1:numel(ids)
    count = fprintf(fid,'%02d\t%f\t%f\n',ids(i),coord(1,i),coord(2,i));
end
fclose(fid);

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

%   # START GPL LICENSE

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

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

%   # END GPL LICENSE


function this = read_nifti_analyze(this, fileName, selectedVolumes)
% loads matrix into .data from nifti or analyze file using spm_read_vols
%
%   this = read_nifti_analyze(this, fileName, selectedVolumes)
%
% IN
%   fileName
%   selectedVolumes     [1,nVols] index of selected volumes to load,
%                       Inf for all; default: Inf
% OUT
%
% EXAMPLE
%   read_nifti_analyze
%
%   See also

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-04-16
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

if nargin < 2
    fileName = fullfile(this.parameters.path, ...
        this.parameters.unprocessedFile);
end

fileNameVolArray = tapas_uniqc_get_vol_filenames(fileName);

V = spm_vol(strvcat(fileNameVolArray));

hasSelectedVolumes = nargin > 2 && ~any(isinf(selectedVolumes));

if hasSelectedVolumes
    V = V(selectedVolumes);
end

% more than 4 dimensions not handles by spm_vol, read directly from
% file_array
if numel(V(1).private.dat.dim) > 4
    this.data = reshape(V.private.dat(:), size(V.private.dat));
else
    % use inbuilt SPM functionality
    try
        % this.data = transform_matrix_analyze2matlab(spm_read_vols(V));
        this.data = spm_read_vols(V);
        % maybe only header misalignment of volumes is the problem for nifti
        %...rename temporarily for loading
    catch err
        fnHdr = regexprep(fileName, '\.nii','\.mat');
        fnTmp = regexprep(fileName, '\.nii','\.tmp');
        if exist(fnHdr, 'file')
            movefile(fnHdr, fnTmp);
            warning('Headers of volumes not aligned, ignoring them...');
            V = spm_vol(fileName);
            this.data = spm_read_vols(V);
            movefile(fnTmp, fnHdr);
        else % nothing we can do, throw error
            throw(err);
        end
    end
end


function plugsStruct = readLeksellPlugFile(filename)
%"readLeksellPlugFile"
%   Reads a Leksell Plug File into meaningful data structures.  Some values
%   are still undetermined.  However, none of these values are currently
%   used in CERR.
%
%KRK 05/28/07
%
%Usage:
%   function plugStruct = readLeksellPlugFile(filename)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

fid = fopen(filename, 'r', 'b');

data = decodeLeksellData(fid);

fclose(fid);

%If data only has one cell, it's empty since there is always one cell that
%   contains no meaningful data
if length(data) < 2
    plugsStruct = [];
    return;
end

% Get meaningful data
plugsData = data{1};

% Type of plugging
plugsStruct.plugType = plugsData{1};
% Number of plugged holes in the helmet
plugsStruct.numSourcesPlugged = plugsData{4};

% Unsolved variables
plugsStruct.mystery_value_2 = plugsData{2}; % integer value
% mystery_value_3 appears to be a binary sequence showing which holes are
%   plugged, but this may be incorrect
plugsStruct.mystery_value_3 = plugsData{3};


function [model] = PrepareNetwork4Separation(dnnFile, hasClean, useGPU)
dnn = load(dnnFile);

layer = dnn.layer;
para = dnn.para;
para.local.useFileName = 1;
% para.topology.useFileName = 1;
% para.local.seglen = 100;
% para.local.segshift = 100;
para.useGPU = useGPU;

% mixture STFT
stft_idx = ReturnLayerIdxByName(layer, 'stft');
para.out_layer_idx(1) = stft_idx(1);

% mixture log spec
log_idx = ReturnLayerIdxByName(layer, 'log');
para.out_layer_idx(2) = log_idx(1);

% enhanced log spec
hadamard_idx = ReturnLayerIdxByName(layer, 'hadamard');
if ~isempty(hadamard_idx)
    for i=1:2
        para.out_layer_idx(end+1) = hadamard_idx(i)+layer{hadamard_idx(i)}.next;
    end
    for i=1:2
        para.out_layer_idx(end+1) = hadamard_idx(i)+layer{hadamard_idx(i)}.prev(1);
    end
    para.test.mask_idx = [-1 0] + length(para.out_layer_idx);
else
    cmn_idx = ReturnLayerIdxByName(layer, 'cmn');
    if ~isempty(cmn_idx)
        for i=2:3
            para.out_layer_idx = [para.out_layer_idx cmn_idx(i)+layer{cmn_idx(i)}.prev];
        end
    else
        delta_idx = ReturnLayerIdxByName(layer, 'delta');
        para.out_layer_idx = [para.out_layer_idx delta_idx(2:3)-1];
    end
    para.test.mask_idx = [];
end

% clean STFT and log spec
input_idx = ReturnLayerIdxByName(layer, 'input');
if hasClean==0
    layer = layer(1:input_idx(end)-1);
    para.IO = RemoveIOStream(para.IO, 2);
    para.test.clean_idx = [];
    para.test.clean_stft_idx = [];
else
    para.out_layer_idx = [para.out_layer_idx log_idx(end-1:end)];
    para.test.clean_idx = [-1 0] + length(para.out_layer_idx);
    para.out_layer_idx = [para.out_layer_idx stft_idx(end-1:end)];
    para.test.clean_stft_idx = [-1 0] + length(para.out_layer_idx);
end

model.layer= layer;
model.para = para;
end


function showStereoMatches(I1, I2, matches, current_time_flag, style, lineWidth,  ptSyle, ptWidth)
%SHOWSTEREOMATCHES Plot matches detected in two images using MATLAB
%
% INPUT:
%   - I2_l: left image at time t
%   - I2_r: right image at time t
%   - matches(1, N): array of structure conatining keypoints
%   - current_time_flag: flag for matches in previous time (=0) or current time (=1)
%   - style: defines color and type of plotted line
%   - lineWidth: defines thickness of plotted line
%   - ptSyle: defines type of points; eg: 'o'
%   - ptWidth: defines thickness of plotted points

% false color
imgOverlay = imfuse(I2, I1);
% create red-cyan image instead of the imfuse default
imgOverlay(:,:,1) = imgOverlay(:,:,2);
imgOverlay(:,:,2) = imgOverlay(:,:,3);

% show image
imshow(imgOverlay);
hold on

% for all matches
x_from = zeros(1, size(matches, 2));
x_to = zeros(1, size(matches, 2));
y_from = zeros(1, size(matches, 2));
y_to = zeros(1, size(matches, 2));
num = 0;
for match = matches
    num = num + 1;
    if(current_time_flag)
        x_from(num) = match.pt2_r.location(1);
        x_to(num)   = match.pt2_l.location(1);
        y_from(num) = match.pt2_r.location(2);
        y_to(num)   = match.pt2_l.location(2);
    else
        x_from(num) = match.pt1_r.location(1);
        x_to(num)   = match.pt1_l.location(1);
        y_from(num) = match.pt1_r.location(2);
        y_to(num)   = match.pt1_l.location(2);
    end
end

% plot line
plot([y_from; y_to], [x_from; x_to], style, 'Linewidth', lineWidth);
% plot end points
scatter(y_from, x_from, ptSyle, 'r', 'Linewidth', ptWidth);
scatter(y_to, x_to, ptSyle, 'g', 'Linewidth', ptWidth);
end


function I = not(I)

I.Z = ~I.Z;
I.name = ['not(' I.name ')'];


function add_display_menu(version)
    % adds a display menu to the current figure
    %
    % appently mostly used by the view_  functions

    % when called, hzma is the current axes;
    ZG=ZmapGlobal.Data;

    op2e = uimenu('Label','Display');
    switch version
        case 1
            circlefun=@plotci2;
            fixscalefun=@(data)fix_caxis(data,'horiz');
            uimenu(op2e,'Label','Plot Map in lambert projection','MenuSelectedFcn',@(~,~)plotmap);
            %overlayfun=@overlay;
        case 2
            circlefun=@plotci2;
            fixscalefun=@(data)fix_caxis(data,'');
            uimenu(op2e,'Label','Plot Map in lambert projection','MenuSelectedFcn',@(~,~)plotmap);
            %overlayfun=@overlay;
        case 3
            circlefun=@plotci3;
            fixscalefun=@(data)fix_caxis(data,'horiz');
            %overlayfun=@()zmap_update_displays();
        case 4
            circlefun=@plotci2;
            fixscalefun=@(data)fix_caxis(data,'horiz');
            uimenu(op2e,'Label','Plot Map in lambert projection','MenuSelectedFcn',@(~,~)plotmap)
            uimenu(op2e,'Label','Plot map on top of topography (white background)',...
                'MenuSelectedFcn', @(~,~) dramap_z('dramap2_z','w', valueMap)); % this is different from case
            uimenu(op2e,'Label','Plot map on top of topography (black background)',...
                'MenuSelectedFcn', @(~,~)dramap_z('dramap2_z','k', valueMap)); % this is different from case #1
            %overlayfun=@overlay;
        case 5
            circlefun=@plotci2;
            fixscalefun=@(data)fix_caxis(data,'horiz');
            add_colormap_section(op2e);
            add_shading_section(op2e);
            add_brighten_section(op2e);
            %overlayfun=@overlay;
    end

    uimenu(op2e,'Label','Fix color (z) scale','MenuSelectedFcn',@(~,~)fixscalefun(ZGvalueMap));
    uimenu(op2e,'Label','Show Grid','MenuSelectedFcn',@cb_showgrid);
    uimenu(op2e,'Label','Show Circles','MenuSelectedFcn',@(~,~)circlefun);
    add_colormap_section(op2e);
    add_shading_section(op2e);
    add_brighten_section(op2e);
    uimenu(op2e,'Label','Redraw Overlay',...
        'MenuSelectedFcn', "set(gca,'NextPlot','add');zmap_update_displays();"); % this is different from case #1

    function cb_shader(style)
        % set default shading style and apply to current axes
        axes(gca);
        ZG.shading_style=style;
        shading(ZG.shading_style);
    end

    function cb_showgrid(src,~)
        set(gca,'NextPlot','add');
        plot(newgri(:,1),newgri(:,2),'+k')
    end
    function add_brighten_section(parent)
        uimenu(parent,'Label','Brighten +0.4','MenuSelectedFcn', @(~,~)brighten(0.4));
        uimenu(parent,'Label','Brighten -0.4','MenuSelectedFcn', @(~,~)brighten(-0.4))
    end
    function add_colormap_section(parent)
        uimenu(parent,'Label','Colormap InvertGray',...
            'MenuSelectedFcn',@(~,~)flip_and_brighten(@gray,0.4));
        uimenu(parent,'Label','Colormap Invertjet',...
            'MenuSelectedFcn',@(~,~)flip_and_brighten(@jet,0));

        function flip_and_brighten(colorfn,brightenamount)
            colormap(flipud(colorfn(64)));
            if brightenamount; brighten(brightenamount); end
        end
    end

    function add_shading_section(parent)
        %TODO make this 1 option, simple inputdlg box, or flip the names
        uimenu(parent,'Label','shading flat',...
            'MenuSelectedFcn',@(~,~)cb_shader('flat'))
        uimenu(parent,'Label','shading interpolated',...
            'MenuSelectedFcn',@(~,~)cb_shader('interp'))
    end

end


function p = fig2plotly(varargin)

%------------------------------FIG2PLOTLY---------------------------------%

% Convert a MATLAB figure to a Plotly Figure

% [CALL]:

% p = fig2plotly
% p = fig2plotly(fig_han)
% p = fig2plotly(fig_han, 'property', value, ...)

% [INPUTS]: [TYPE]{default} - description/'options'

% fig_han: [handle]{gcf} - figure handle
% fig_struct: [structure array]{get(gcf)} - figure handle structure array

% [VALID PROPERTIES / VALUES]:

% filename: [string]{'untitled'} - filename as appears on Plotly
% fileopt: [string]{'new'} - 'new, overwrite, extend, append'
% world_readable: [boolean]{true} - public(true) / private(false)
% link: [boolean]{true} - show hyperlink (true) / no hyperlink (false)
% open: [boolean]{true} - open plot in browser (true)

% [OUTPUT]:

% p - plotlyfig object

% [ADDITIONAL RESOURCES]:

% For full documentation and examples, see https://plot.ly/matlab

%-------------------------------------------------------------------------%

%--FIGURE INITIALIZATION--%
if nargin == 0
    varargin{1} = gcf;
end

%-------------------------------------------------------------------------%

%--CONSTRUCT PLOTLY FIGURE OBJECT--%
p = plotlyfig(varargin{:});

%-------------------------------------------------------------------------%

%--MAKE CALL TO PLOTLY--%
p.plotly;

%-------------------------------------------------------------------------%

end


function [results] = niftiCheckIsEqual(nifti1,nifti2,quiet)
%
% results = niftiCheckIsEqual([nifti1],[nifti2],[quiet])
%
%
% DESCRIPTION:
%   Run 'isequal' on a pair of nifti files, field by field. If ~ quiet print
%   a summary of equal and ~equal values do the command window.
%
% USAGE:
%   results = niftiCheckIsEqual(nifti1,nifti2)
%
% INPUT:
%   nifti1  - The filename/path or structure of a niftifile
%   nifti2  - The filename/path or structure of a niftifile
%   quiet   - Supress output to command window. Only field names are
%             returned.
%
% OUTPUT:
%   results - A structure containing the following fields
%           - .allequal >> boolean where 1 == all equal and 0 == not.
%           - .equal    >> fieldnames of equal-fields
%           - .notequal >> filednames of unequal-fields
%           - .nonfield >> fieldnames that are absent in at least one
%                          nifti.
%
%
% (C) Stanford University, VISTA 2014 [ lmperry@stanford.edu ]
%
%
%#ok<*AGROW>

%% HANDLE INPUTS

results = {};

if notDefined('nifti1')
    nifti1 = mrvSelectFile('r','*.nii*','Select first nifti file',pwd);
    if isnumeric(nifti1) || isempty(nifti1)
        return
    end
end

if notDefined('nifti2')
    nifti2 = mrvSelectFile('r','*.nii*','Select second nifti file',pwd);
    if isnumeric(nifti2) || isempty(nifti2)
        return
    end
end

% Read the nifti files if not passed in as structs
if ~isstruct(nifti1)
    n1 = niftiRead(nifti1);
else
    n1 = nifti1;
end

if ~isstruct(nifti2)
    n2 = niftiRead(nifti2);
else
    n2 = nifti2;
end

% Quiet param will cause window output to be supressed
if notDefined('quiet')
    quiet = 0;
end

if ~quiet
    fprintf('Running %s ... \n\tNifti 1 = %s\n\tNifti 2 = %s\n',mfilename,n1.fname,n2.fname);
end

%% COMPARE NIFTI FIELDS

fields1 = fieldnames(n1);
fields2 = fieldnames(n2);

if ~isequal(fields1,fields2)
    warning('Nifti files do not have the same fields.');
    allfields = unique(sort(horzcat(fields1,fields2)));
else
    allfields = fields1;
end

% Initialize the return structure fields
notequal = {};
equal    = {};
nonfield = {};

% Go over the fields and compare
for ii = 1:numel(allfields)
    if isfield(n1, allfields{ii}) && isfield(n2, allfields{ii})
        if isequal(n1.(allfields{ii}),n2.(allfields{ii}))
            equal{end+1} = allfields{ii};
        else
            notequal{end+1} = allfields{ii};
        end
    else
       nonfield{end+1} = allfields{ii};
    end
end

%% DISPLAY THE RESULTS

if ~quiet
    if ~isempty(nonfield)
        fprintf('\nNON-EXISTING FIELDS:\n');
        for jj = 1:numel(nonfield)
            fprintf('\t%s \n',nonfield{jj});
        end
    end

    % Display results for equal fields
    if ~isempty(equal)
        fprintf('\nEQUAL FIELDS:\n');
        for jj = 1:numel(equal)
            fprintf('\t%s \n',equal{jj});
        end

        fprintf('\n:::: EQUAL DETAILED OUTPUT ::::\n')
        for jj = 1:numel(equal)
            disp(['>>> ' equal{jj} ' <<<']);

            % Do not display the data field
            if ~strcmpi(equal{jj},'data')
                disp(n1.(equal{jj}));
                disp('----');
            else disp(' Data fields are equal.');
                disp(' ----')
            end
        end
    end

    % Display results for non-equal fields
    if ~isempty(notequal)
        fprintf('\nNON-EQUAL FIELDS:\n');
        for jj = 1:numel(notequal)
            fprintf('\t%s \n',notequal{jj});
        end

        fprintf('\n:::: NON-EQUAL DETAILED OUTPUT ::::\n')
        for kk = 1:numel(notequal)
            disp(['>>> ' notequal{kk} ' <<<']);

            % Do not display the data field
            if ~strcmpi(notequal{kk},'data')
                disp(n1.(notequal{kk}));
                disp(n2.(notequal{kk}));
                disp('----');
            else disp(' Data field is not equal. Generating difference image.');
                disp(' ----')
                showMontage(n1.data-n2.data);
                set(gcf,'Name','Nifti DATA Difference Image (nifti1.data - nifti2.data)');
            end
        end
    else
        disp('ALL FIELDS ARE EQUAL');
    end
end

%% RETURN THE RESULTS

results.allequal = isequal(n1,n2);
results.equal    = equal;
results.notequal = notequal;
results.nonfield = nonfield;

if ~quiet
    fprintf('\nSUMMARY:\n\t%s fields were equal\n\t%s fields were not equal \n\t%s  fields were unique to one nifti\n\n',num2str(numel(equal)),num2str(numel(notequal)),num2str(numel(nonfield)));
end

return


function [dimensionsout, datatypesout]=byte2any(dimensions, datatypes)

% BYTE2ANY - Mask Initialization function for UDP Byte Un-Pack Block
% Derived from The MathWorks, Inc. MXPCREADME
% $Revision: 1 $ $Date: 2008/03/07 $

if ~isa(dimensions,'cell')
  error('dimensions argument must be a cell array');
end

if ~isa(datatypes,'cell')
  error('datatypes argument must be a cell array');
end

if length(dimensions)~=length(datatypes)
  error('dimension cell array and datatypes cell array must have the same length');
end

dimensionsout=[];
datatypesout=[];
for i=1:length(dimensions)
  dimension=dimensions{i};
  if ~isa(dimension,'double')
    error('dimensions elements must be double arrays');
  end
  if size(dimension,1)>1 | size(dimension,2)>2
    error('dimensions elements must be either scalar or a row vector with two elements');
  end
  if length(dimension)==1
    dimensionsout=[dimensionsout,dimension,0];
  else
    dimensionsout=[dimensionsout,dimension];
  end
  datatype=datatypes{i};
  if ~isa(datatype,'char')
    error('datatypes elements must be char arrays');
  end
  if strcmp(datatype,'double')
     dtypeout=0;
  elseif strcmp(datatype,'single')
    dtypeout=1;
  elseif strcmp(datatype,'int8')
    dtypeout=2;
  elseif strcmp(datatype,'uint8')
    dtypeout=3;
  elseif strcmp(datatype,'int16')
    dtypeout=4;
  elseif strcmp(datatype,'uint16')
    dtypeout=5;
  elseif strcmp(datatype,'int32')
    dtypeout=6;
  elseif strcmp(datatype,'uint32')
    dtypeout=7;
  elseif strcmp(datatype,'boolean')
    dtypeout=8;
  else
    error('the supported data types are: double, single, int, uint8, int16, uint16, int32, uint32, boolean');
  end
  datatypesout=[datatypesout,dtypeout];
end


function [feat_prob, locreg_pred, next_pred, rpn_prob, rpn_bbox] = cnn_process_image(input, net, sigmoid)

    % switch width and height for Caffe
    input = permute(input, [2 1 3]);

    blob_size = [size(input), 1];
    net.blobs('data').reshape(blob_size);

    blob_size = [size(input), 1];

    batch = zeros(blob_size, 'single');
    batch(:,:,:,1) = input;
    batches = cell(1,1);
    batches{1} = batch;
    zs = net.forward(batches);

    prob_idx = find_string(net.outputs, 'prob');
    locref_idx = find_string(net.outputs, 'loc_pred');
    next_idx = find_string(net.outputs, 'next_pred');
    rpn_prob_idx = find_string(net.outputs, 'rpn_prob');
    rpn_bbox_idx = find_string(net.outputs, 'rpn_bbox_pred');

    % fc6_data = net.blobs('fc6-conv').get_data();

    locreg_pred = [];
    next_pred = [];
    rpn_prob = [];
    rpn_bbox = [];

    if prob_idx ~= -1
        feat_prob = zs{prob_idx};
        feat_prob = permute(feat_prob, [2 1 3]);
        if ~sigmoid
            % remove background class
            feat_prob = feat_prob(:,:,2:end);
        end
    end

    if locref_idx ~= -1
        fmap = zs{locref_idx};
        sz = size(fmap);
        fmap = reshape(fmap, sz(1), sz(2), 2, []);
        fmap = permute(fmap, [2 1 4 3]);
        locreg_pred = fmap;
    end

    if next_idx ~= -1
        fmap = zs{next_idx};
        sz = size(fmap);
        fmap = reshape(fmap, sz(1), sz(2), 2, []);
        fmap = permute(fmap, [2 1 4 3]);
        next_pred = fmap;
    end

    if rpn_prob_idx ~= -1
        fmap = zs{rpn_prob_idx};
        fmap = permute(fmap, [2 1 3]);
        rpn_prob = fmap;
    end

    if rpn_bbox_idx ~= -1
        fmap = zs{rpn_bbox_idx};
        sz = size(fmap);
        fmap = reshape(fmap, sz(1), sz(2), 4, []);
        fmap = permute(fmap, [2 1 4 3]);
        rpn_bbox = fmap;
    end
end

function idx = find_string(list, str)
    for k = 1:length(list)
        cand = list{k};
        if ~isempty(strfind(cand, str))
            idx = k;
            return;
        end
    end
    idx = -1;
end


function vw = buildGrayCoords(vw, coordsPth, keepAll, classPth, numGrayLayers)
% vw = buildGrayCoords(vw, [coordsPth='Gray/coords.mat'], [keepAll=0], [classPth], [numGrayLayers]):
%
% Build a coords.mat file for a gray view. This was split
% off from getGrayCoords when I found the design was getting
% unwieldy -- even when a coords file had been built, there were
% still frequent checks on external files, and I was asked to
% rebuild this nearly every time. I consolidated all of the looking
% for external files into this function -- anything required for
% a gray view that's not already contained in a session directory
% should be loaded up here, and saved to coords.mat. This should
% happen a maximum of once per session/segmentation.
%
% INPUTS:
%	vw: gray or volume view [default: initialize a hidden volume view,
%	which gets converted to gray as the coords are built.]
%
%	coordsPth: path to the coords file to save. [default: Gray/coords.mat']
%
%	keepAll: flag to keep all gray matter nodes, rather than just the
%	intersection with the inplane data. [default: 0, don't do this.]
%	The advantage to keeping all nodes is that you can bring in data from
%	other sessions, and do analyses involving the whole gray matter graph.
%	The disadvantage is it can take more disk space / memory, and you can't
%	map back from the volume to the inplane (though this should be
%	fixable?)
%
%	classPth: optional argument to specify which class file to use
%
%
% ras 08/04.
% ras 06/09: allows user to provide the file paths externally, rather than
% being prompted.
global HOMEDIR

if notDefined('keepAll'),	keepAll=0;			end
if notDefined('classPth'),	classPth = {};		end
if notDefined('vw'),
    % convert from a volume view.
    vw = initHiddenVolume;
    vw = viewSet(vw, 'viewType','Gray');
    vw = viewSet(vw, 'subdir', 'Gray');
end
if notDefined('coordsPth')
    coordsPth = fullfile(HOMEDIR, 'Gray', 'coords.mat');
end

if ~isfield(vw,'leftPath'),  vw = viewSet(vw,'leftpath','(none assigned)');    end
if ~isfield(vw,'rightPath'), vw = viewSet(vw,'rightPath','(none assigned)');   end

% if external file paths are provided, attach them to the view, so that
% they'll be the default files accessed when building the
if isempty(classPth)
    startDir = getAnatomyPath();
    filterspec = {'*.nii.gz;*.nii', 'NIFTI class file'; '*.*', 'All files'};
    classPth = getPathStrDialog(startDir, 'Select class file', filterspec);
end

if iscell(classPth), classPth = classPth{1}; end

% Check class type. Should be nifti.
classType = mrGrayCheckClassType(classPth);

% Since we moved to NIFTI, left and right class files are the same
leftClassFile   = classPth;
rightClassFile  = classPth;
vw.leftPath     = classPth;
vw.rightPath    = classPth;

%% load the gray coords from the class/gray files
% Load the gray nodes and edges
[allLeftNodes,  allLeftEdges,  leftPath]  = loadGrayNodes('left', classPth, numGrayLayers);
[allRightNodes, allRightEdges, rightPath] = loadGrayNodes('right',leftPath, numGrayLayers);

% Load the inplane coords (loaded temporarily into vw.coords)
vw = getVolCoords(vw);
ipCoords = viewGet(vw, 'coords');

% Here, we have the option to include all the nodes in the gray view.
% However, we can't map back to the inplane any more.
if keepAll
    disp(['Warning: You are keeping all the gray nodes ' ...
        '(not computing an intersection with the inplane). ' ...
        'This can be useful in some circumstances. But it  ' ...
        'also means that you can never map things back to ' ...
        'the INPLANE view']);

    leftNodes = allLeftNodes;
    leftEdges = allLeftEdges;
    if ~isempty(allLeftNodes)
        leftCoords = uint16(allLeftNodes([2 1 3],:));
    else
        leftCoords = [];
    end
    keepLeft = 1:length(leftNodes); %#ok<*NASGU>

    rightNodes = allRightNodes;
    rightEdges = allRightEdges;
    if ~isempty(allRightNodes)
        rightCoords = uint16(allRightNodes([2 1 3],:));
    else
        rightCoords = [];
    end
    keepRight = 1:length(rightNodes);
else

    waitHandle = mrvWaitbar(0,'Computing gray coordinates.  Please wait...');
    if ~isempty(allLeftNodes)
        % Find gray nodes that are in the inplanes. Note: nodes are
        % (x,y,z) not (y,x,z), unlike everything else in mrLoadRet.
        [leftCoords,~,keepLeft] = intersectCols(uint16(ipCoords),uint16(allLeftNodes([2 1 3],:)));

        % Keep nodes that intersect with the inplanes
        [leftNodes,leftEdges,nLeftNotReached] = keepNodes(allLeftNodes,allLeftEdges,double(keepLeft));
    else
        leftNodes=[];
        leftEdges=[];
        leftCoords=[];
        keepLeft = [];
    end

    % Repeat for right hemisphere
    mrvWaitbar(1/2)
    if ~isempty(allRightNodes)
        [rightCoords, ~, keepRight] = ...
            intersectCols(uint16(ipCoords), uint16(allRightNodes([2 1 3],:)));
        [rightNodes, rightEdges, nRightNotReached] = ...
            keepNodes(allRightNodes, allRightEdges, keepRight);
    else
        rightNodes=[];
        rightEdges=[];
        rightCoords=[];
        keepRight = [];
    end
    close(waitHandle);
end % End check on whether we intersect with inplane

% Concatenate coords from the two hemispheres
coords = [double(leftCoords) double(rightCoords)];

% Concantenate the left and right gray graphs
if ~isempty(rightNodes)
    rightNodes(5,:) = rightNodes(5,:) + length(leftEdges);
    rightEdges = rightEdges + size(leftNodes,2);
end

nodes = [leftNodes rightNodes];
edges = [leftEdges rightEdges];

% make path local, if HOMEDIR is contained within it
% (this happens if, for instance, the vAnat directory is
% linked to from the session directory):
if strfind(leftPath,HOMEDIR);
    leftPath = leftPath(length(HOMEDIR)+2:end); % +2 b/c of filesep
    leftPath(leftPath=='\') = '/';  % \ win filesep -> / generic filesep
end

rightPath = leftPath; leftClassFile = leftPath; rightClassFile = leftPath;

% Verify save path exists - if not, make it
if ~exist(fileparts(coordsPth), 'dir'), mkdir(fileparts(coordsPth)); end

% Save to coords file
save(coordsPth,'coords','nodes','edges',...
    'allLeftNodes','allLeftEdges','allRightNodes','allRightEdges',...
    'leftPath','rightPath','keepLeft','keepRight');

if exist('leftClassFile','var'), save(coordsPth,'leftClassFile','-append'); end
if exist('rightClassFile','var'),save(coordsPth,'rightClassFile','-append');end

fprintf('Saved gray coords info to %s. \n',coordsPth);

return


function t = numel(obj)
% Number of simple file arrays involved.
% _______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

%
% $Id$

% Should be this, but it causes problems when accessing
% obj as a structure.
%t = prod(size(obj));

t  = numel(struct(obj));


function shim = cvx_sedumi( shim )

% CVX_SOLVER_SHIM	SeDuMi interface for CVX.
%   This procedure returns a 'shim': a structure containing the necessary
%   information CVX needs to use this solver in its modeling framework.

global cvx___
if ~isempty( shim.solve ),
    return
end
if isempty( shim.name ),
    fname = 'sedumi.m';
    fs = cvx___.fs;
    ps = cvx___.ps;
    int_path = [ cvx___.where, fs ];
    int_plen = length( int_path );
    shim.name = 'SeDuMi';
    shim.dualize = true;
    flen = length(fname);
    fpaths = { [ int_path, 'sedumi', fs, fname ] };
    fpaths = [ fpaths ; which( fname, '-all' ) ];
    old_dir = pwd;
    oshim = shim;
    shim = [];
    for k = 1 : length(fpaths),
        fpath = fpaths{k};
        if ~exist( fpath, 'file' ) || any( strcmp( fpath, fpaths(1:k-1) ) ),
            continue
        end
        new_dir = fpath(1:end-flen-1);
        cd( new_dir );
        tshim = oshim;
        tshim.fullpath = fpath;
        tshim.version = 'unknown';
        is_internal = strncmp( new_dir, int_path, int_plen );
        if is_internal,
            tshim.location = [ '{cvx}', new_dir(int_plen:end) ];
        else
            tshim.location = new_dir;
        end
        try
            fid = fopen(fname);
            otp = fread(fid,Inf,'uint8=>char')';
            fclose(fid);
        catch errmsg
            tshim.error = sprintf( 'Unexpected error:\n%s\n', errmsg.message );
        end
        if isempty( tshim.error ),
            otp = regexp( otp, 'SeDuMi \d\S+', 'match' );
            if ~isempty(otp), tshim.version = otp{end}(8:end); end
            vnum = str2double( tshim.version );
            tshim.check = @check;
            tshim.solve = @solve;
            tshim.eargs = { vnum >= 1.3 && vnum < 1.32 };
            if k ~= 2,
                tshim.path = [ new_dir, ps ];
                if ~isempty(cvx___.msub) && exist([new_dir,fs,cvx___.msub],'dir'),
                   tshim.path = [ new_dir, fs, cvx___.msub, ps, tshim.path ];
                end
            end
        end
        shim = [ shim, tshim ]; %#ok
    end
    cd( old_dir );
    if isempty( shim ),
        shim = oshim;
        shim.error = 'Could not find a SeDuMi installation.';
    end
else
    shim.check = @check;
    shim.solve = @solve;
    vnum = str2double( shim.version );
    shim.eargs = { vnum >= 1.3 && vnum < 1.32 };
end

function found_bad = check( nonls ) %#ok
found_bad = false;

function [ x, status, tol, iters, y, z ] = solve( At, b, c, nonls, quiet, prec, settings, nocplx )

n = length( c );
m = length( b );
K = struct( 'f', 0, 'l', 0, 'q', [], 'r', [], 's', [], 'scomplex', [], 'ycomplex', [] );
reord = struct( 'n', 0, 'r', [], 'c', [], 'v', [] );
reord = struct( 'f', reord, 'l', reord, 'a', reord, 'q', reord, 'r', reord, 's', reord, 'h', reord );
reord.f.n = n;
zinv = [];
for k = 1 : length( nonls ),
    temp = nonls( k ).indices;
    nn = size( temp, 1 );
    nv = size( temp, 2 );
    nnv = nn * nv;
    tt = nonls( k ).type;
    reord.f.n = reord.f.n - nnv;
    if strncmp( tt, 'i_', 2 ),
        error( 'SeDuMi does not support integer variables.' );
    elseif nn == 1 || isequal( tt, 'nonnegative' ),
        reord.l.r = [ reord.l.r ; temp(:) ];
        reord.l.c = [ reord.l.c ; reord.l.n + ( 1 : nnv )' ];
        reord.l.v = [ reord.l.v ; ones( nnv, 1 ) ];
        reord.l.n = reord.l.n + nnv;
    elseif isequal( tt, 'lorentz' ),
        if nn == 2,
            rr = [ temp ; temp ];
            cc = reshape( floor( 1 : 0.5 : 2 * nv + 0.5 ), 4, nv );
            vv = [1;1;-1;1]; vv = vv(:,ones(1,nv));
            reord.a.r = [ reord.a.r ; rr(:) ];
            reord.a.c = [ reord.a.c ; cc(:) + reord.a.n ];
            reord.a.v = [ reord.a.v ; vv(:) ];
            reord.a.n = reord.a.n + nnv;
            zinv = [ zinv ; temp(:) ]; %#ok
        else
            temp = temp( [ end, 1 : end - 1 ], : );
            reord.q.r = [ reord.q.r ; temp(:) ];
            reord.q.c = [ reord.q.c ; reord.q.n + ( 1 : nnv )' ];
            reord.q.v = [ reord.q.v ; ones(nnv,1) ];
            reord.q.n = reord.q.n + nnv;
            K.q = [ K.q, nn * ones( 1, nv ) ];
        end
    elseif isequal( tt, 'semidefinite' ),
        if nn == 3,
            temp = temp( [1,3,2], : );
            tempv = [sqrt(2);sqrt(2);1] * ones(1,nv);
            reord.r.r = [ reord.r.r ; temp(:) ];
            reord.r.c = [ reord.r.c ; reord.r.n + ( 1 : nnv )' ];
            reord.r.v = [ reord.r.v ; tempv(:) ];
            reord.r.n = reord.r.n + nnv;
            K.r = [ K.r, 3 * ones( 1, nv ) ];
            temp = temp(1:2,:);
            zinv = [ zinv ; temp(:) ]; %#ok
        else
            nn = 0.5 * ( sqrt( 8 * nn + 1 ) - 1 );
            str = cvx_create_structure( [ nn, nn, nv ], 'symmetric' );
            K.s = [ K.s, nn * ones( 1, nv ) ];
            [ cc, rr, vv ] = find( cvx_invert_structure( str, 'compact' ) );
            rr = temp( rr );
            reord.s.r = [ reord.s.r; rr( : ) ];
            reord.s.c = [ reord.s.c; cc( : ) + reord.s.n ];
            reord.s.v = [ reord.s.v; vv( : ) ];
            reord.s.n = reord.s.n + nn * nn * nv;
            reord.s.z = reord.s.v;
        end
    elseif isequal( tt, 'hermitian-semidefinite' ),
        if nn == 4,
            temp = temp( [1,4,2,3], : );
            tempv = [sqrt(2);sqrt(2);1;1] * ones(1,nv);
            reord.r.r = [ reord.r.r ; temp(:) ];
            reord.r.c = [ reord.r.c ; reord.r.n + ( 1 : nnv )' ];
            reord.r.v = [ reord.r.v ; tempv(:) ];
            reord.r.n = reord.r.n + nnv;
            reord.r.z = reord.r.v;
            K.r = [ K.r, 4 * ones( 1, nv ) ];
            temp = temp(1:2,:);
            zinv = [ zinv ; temp(:) ]; %#ok
        elseif nocplx,
            % SeDuMi's complex SDP support was broken with the 1.3 update. So
            % we must use the following complex-to-real SDP conversion to work
            % around it, at a modest cost of problem size.
            %   X >= 0 <==> exists [ Y1, Y2^T ; Y2, Y3 ] >= 0 s.t.
            %               Y1 + Y3 == real(X), Y2 - Y2^T == imag(X)
            nsq = nn; nn = sqrt( nn );
            str = cvx_create_structure( [ nn, nn, nv ], 'hermitian' );
            [ cc, rr, vv ] = find( cvx_invert_structure( str, 'compact' ) );
            cc = cc - 1;
            mm = floor( cc / nsq );
            cc = cc - mm * nsq;
            jj = floor( cc / nn );
            ii = cc - jj * nn + 1;
            jj = jj + 1;
            mm = mm + 1;
            vr = real( vv );
            vi = imag( vv );
            ii = [ ii + nn * ~vr ; ii + nn * ~vi ];
            jj = [ jj ; jj + nn ]; %#ok
            vv = sqrt( 0.5 ) * [ vr + vi ; vr - vi ];
            rr = [ rr ; rr ]; %#ok
            mm = [ mm ; mm ]; %#ok
            [ jj, ii ] = deal( min( ii, jj ), max( ii, jj ) );
            cc = ii + ( jj - 1 ) * ( 2 * nn ) + ( mm - 1 ) * ( 4 * nsq );
            K.s = [ K.s, 2 * nn * ones( 1, nv ) ];
            rr = temp( rr );
            reord.s.r = [ reord.s.r; rr( : ) ];
            reord.s.c = [ reord.s.c; cc( : ) + reord.s.n ];
            reord.s.v = [ reord.s.v; vv( : ) ];
            reord.s.n = reord.s.n + 4 * nsq * nv;
            reord.s.z = reord.s.v;
        else
            % SeDuMi's complex SDP support was restored in v1.33.
            K.scomplex = [ K.scomplex, length( K.s ) + ( 1 : nv ) ];
            nn = sqrt( nn );
            str = cvx_create_structure( [ nn, nn, nv ], 'hermitian' );
            K.s = [ K.s, nn * ones( 1, nv ) ];
            stri = cvx_invert_structure( str, 'compact' )';
            [ rr, cc, vv ] = find( stri );
            rr = temp( rr );
            reord.s.r = [ reord.s.r; rr( : ) ];
            reord.s.c = [ reord.s.c; cc( : ) + reord.s.n ];
            reord.s.v = [ reord.s.v; vv( : ) ];
            reord.s.n = reord.s.n + size( stri, 2 );
            reord.s.z = reord.s.v;
        end
    else
        error( 'Unsupported nonlinearity: %s', tt );
    end
end
if reord.f.n > 0,
    reord.f.r = ( 1 : n )';
    reord.f.r( [ reord.l.r ; reord.a.r ; reord.q.r ; reord.r.r ; reord.s.r ] ) = [];
    reord.f.c = ( 1 : reord.f.n )';
    reord.f.v = ones(reord.f.n,1);
end
n_d = max( m - n - reord.f.n + 1, isempty( At ) );
if n_d,
    reord.l.n = reord.l.n + n_d;
end
K.f = reord.f.n;
K.l = reord.l.n + reord.a.n;
n_out = reord.f.n;
reord.l.c = reord.l.c + n_out; n_out = n_out + reord.l.n;
reord.a.c = reord.a.c + n_out; n_out = n_out + reord.a.n;
reord.q.c = reord.q.c + n_out; n_out = n_out + reord.q.n;
reord.r.c = reord.r.c + n_out; n_out = n_out + reord.r.n;
reord.s.c = reord.s.c + n_out; n_out = n_out + reord.s.n;
reord = sparse( ...
    [ reord.f.r ; reord.l.r ; reord.a.r ; reord.q.r ; reord.r.r ; reord.s.r ], ...
    [ reord.f.c ; reord.l.c ; reord.a.c ; reord.q.c ; reord.r.c ; reord.s.c ], ...
    [ reord.f.v ; reord.l.v ; reord.a.v ; reord.q.v ; reord.r.v ; reord.s.v ], ...
    n, n_out );

At = reord' * At;
c  = reord' * c;
pars.free = K.f > 1 && nnz( K.q );
pars.eps     = prec(1);
pars.bigeps  = prec(3);
if quiet,
    pars.fid = 0;
end
add_row = isempty( At );
if add_row,
    K.f = K.f + 1;
    At = sparse( 1, 1, 1, n_out + 1, 1 );
    b = 1;
    c = [ 0 ; c ];
end
[ xx, yy, info ] = cvx_run_solver( @sedumi, At, b, c, K, pars, 'xx', 'yy', 'info', settings, 5 );
if add_row,
    xx = xx(2:end);
    yy = zeros(0,1);
    At = zeros(n_out,0);
    % b  = zeros(0,1);
    c  = c(2:end);
end
if ~isfield( info, 'r0' ) && info.pinf,
    info.r0 = 0;
    info.iter = 0;
    info.numerr = 0;
end
tol = info.r0;
iters = info.iter;
xx = full( xx );
yy = full( yy );
status = '';
if info.pinf ~= 0,
    status = 'Infeasible';
    x = NaN * ones( n, 1 );
    y = yy;
    z = - real( reord * ( At * yy ) );
    if add_row, y = zeros( 0, 1 ); end
elseif info.dinf ~= 0
    status = 'Unbounded';
    y = NaN * ones( m, 1 );
    z = NaN * ones( n, 1 );
    x = real( reord * xx );
else
    x = real( reord * xx );
    y = yy;
    z = real( reord * ( c - At * yy ) );
    if add_row, y = zeros( 0, 1 ); end
end
if ~isempty(zinv),
    z(zinv) = z(zinv) * 0.5;
end
if info.numerr == 2,
    status = 'Failed';
    if any( K.q == 2 ),
        warning( 'CVX:SeDuMi', cvx_error_format( 'This solver failure may possibly be due to a known bug in the SeDuMi solver. Try switching to SDPT3 by inserting "cvx_solver sdpt3" into your model.', ...
            [66,75], false, '' ) );
    end
else
    if isempty( status ),
        status = 'Solved';
    end
    if info.numerr == 1 && info.r0 > prec(2),
        status = [ 'Inaccurate/', status ];
    end
end

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function [fullname,ftime]=findobsrfile(filepath,sitename)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (C) 2020-2025, by Kai Chen, All rights reserved.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
global gls
fullname='';
ftime.ts=gls.gtime; ftime.te=gls.gtime; ftime.week=0; ftime.sow=0;
ftime.year=0; ftime.doy=0;

% search the obsr file
fileExt = '*.*O';
files = dir(fullfile(filepath,fileExt));
if isempty(files)
    fileExt = '*.*o';
    files = dir(fullfile(filepath,fileExt));
end
nf=size(files,1);
if nf==0,return,end
for i=1:nf
    if any(sitename)
        if ~isempty(strfind(files(i).name,sitename))&&...
                isempty(strfind(files(i).name,'base'))
            fullname=[filepath,files(i).name];
            break;
        end
    else
        if isempty(strfind(files(i).name,'base'))
            fullname=[filepath,files(i).name];
            break;
        end
    end
end

% get the observe time from obsr file
if ~isempty(fullname)
    ftime=getobstime(fullname,ftime);
end

return


function import_wizard( varargin )

if check_option(varargin,'EBSD')
  select = 2;
elseif check_option(varargin,'ODF')
  select = 3;
elseif check_option(varargin,'tensor')
  select = 4;
else
  select = 1;
end

api.presetDataImport    = select;
api.isPublishMode       = getMTEXpref('generatingHelpMode');

if check_option(varargin,'test')
  test_wizard_export(api,varargin{:});
  return
end

api = WizardDataApi(api);
api = WizardEmptyGUIApi(api);
api = WizardProgressApi(api,@pageImportData);
api.initProgressApi();

function test_wizard_export(api,varargin)

api = WizardDataApi(api);

% testcases

switch lower(get_option(varargin,'test',''))
  case '3d'

    api.loadDataFiles(1,fullfile(mtexDataPath,'EBSD','3dData','S00.ang'))
    api.loadDataFiles(1,fullfile(mtexDataPath,'EBSD','3dData','S01.ang'))
    api.loadDataFiles(1,fullfile(mtexDataPath,'EBSD','3dData','S02.ang'))
    api.Export.setOptEBSD('is3d',true);
    api.Export.setOptEBSD('Z',num2cell(1:3));

  case 'ebsd'

    api.loadDataFiles(1,fullfile(mtexDataPath,'EBSD','data.ctf'))

  case 'odf'

    api.loadDataFiles(2,fullfile(mtexDataPath,'ODF','odf.mtex'))

  case 'pf'

    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','dubna','Q(10-11)(01-11)_amp.cnv'));
    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','dubna','Q(02-21)_amp.cnv'));
    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','dubna','Q(11-20)_amp.cnv'));

    data = api.getData();

    data{1} = set(data{1},'CS',crystalSymmetry('trigonal'));

    api.setData(data);

  case 'bg'

    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','dubna','Q(02-21)_amp.cnv'));
    api.loadDataFiles(5,fullfile(mtexDataPath,'PoleFigure','dubna','Q(11-20)_amp.cnv'));

  case 'def'

    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','dubna','Q(02-21)_amp.cnv'));
    api.loadDataFiles(5,fullfile(mtexDataPath,'PoleFigure','dubna','Q(11-20)_amp.cnv'));
    api.loadDataFiles(6,fullfile(mtexDataPath,'PoleFigure','dubna','Q(02-21)_amp.cnv'));
    api.loadDataFiles(7,fullfile(mtexDataPath,'PoleFigure','dubna','Q(11-20)_amp.cnv'));

  case 'lcp'

    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','BearTex','Test_2.XPa'));
    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','beartex.XPa'));

  otherwise

    api.loadDataFiles(4,fullfile(mtexDataPath,'PoleFigure','dubna','Q(02-21)_amp.cnv'));

    api = WizardEmptyGUIApi(api);
    api = WizardProgressApi(api,@pageImportData);
    api.initProgressApi();

    for j = 1:7
      pause(.5)
      api.nextCallback();
      snapnow;
    end

    close(api.hFigure);

    return
    % end

    % api = WizardProgressApi(api,@(a) pageCS(a,3));
    % api = WizardProgressApi(api,@pageKernel);
    % api = WizardProgressApi(api,@page3dEBSD);
    % api = WizardProgressApi(api,@pageFinish);

end

WizardFinish(api)

% api = WizardProgressApi(api,@(a) pageCS(a,3));
% api = WizardProgressApi(api,@pageKernel);
% api = WizardProgressApi(api,@page3dEBSD);
% api = WizardProgressApi(api,@pageFinish);