function varargout = spaceTimeOptions(varargin)
% SPACETIMEOPTIONS MATLAB code for spaceTimeOptions.fig
%      SPACETIMEOPTIONS, by itself, creates a new SPACETIMEOPTIONS or raises the existing
%      singleton*.
%
%      H = SPACETIMEOPTIONS returns the handle to a new SPACETIMEOPTIONS or the handle to
%      the existing singleton*.
%
%      SPACETIMEOPTIONS('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in SPACETIMEOPTIONS.M with the given input arguments.
%
%      SPACETIMEOPTIONS('Property','Value',...) creates a new SPACETIMEOPTIONS or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before spaceTimeOptions_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to spaceTimeOptions_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help spaceTimeOptions

% Last Modified by GUIDE v2.5 22-Oct-2020 04:46:05

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @spaceTimeOptions_OpeningFcn, ...
                   'gui_OutputFcn',  @spaceTimeOptions_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before spaceTimeOptions is made visible.
function spaceTimeOptions_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to spaceTimeOptions (see VARARGIN)

% Choose default command line output for spaceTimeOptions
handles.output = hObject;

[mov_file,trx_file,prev_st] = myparse(varargin,'mov_file','','trx_file','','prev_st',[]);
handles.mov_file = mov_file;
handles.trx_file = trx_file;

[rfn,nframes,fid,headerinfo] = get_readframe_fcn(mov_file);
handles.rfn = rfn;
handles.nframes = nframes;
ex_frame = rfn(1);
fr_sz = size(ex_frame);
handles.width = size(ex_frame,2);
handles.height = size(ex_frame,1);
handles.fid = fid;
handles.headerinfo = headerinfo;

if ~isempty(trx_file)
    trx = load_tracks(trx_file);
    handles.trx = trx;
    handles.n_trx = length(trx);
    handles.cur_fly = 1;
    handles.frame_num = randsample(trx(handles.cur_fly).nframes,1) - trx(handles.cur_fly).off;
else
    % if not trx then dummy tracks at the center of the frame.
    trx = struct('x',repmat(fr_sz(2)/2,[1,nframes]),...
                'y',repmat(fr_sz(1)/2,[1,nframes]),...
                'theta',repmat(90,[1,nframes]),...
                'firstframe',1,...
                'endframe',nframes,...
                'off',0,...
                'nframes',nframes);

    handles.trx = trx;
    handles.n_trx = 1;
    handles.cur_fly = 1;
    handles.frame_num = randsample(nframes,1);
end

if isempty(prev_st)
    prev_st = getSTParams();
end
if ~isfield(prev_st,'x_center')
  prev_st.x_center = 0;
  prev_st.y_center = 0;
  prev_st.imsz_display = prev_st.psize*max(prev_st.npatches_x,prev_st.npatches_y);
end
handles.params = prev_st;

updateGUI(handles);
updatePlots(handles);

handles.is_canceled = true;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes spaceTimeOptions wait for user response (see UIRESUME)
uiwait(handles.figure1);

function updateGUI(handles)
params = handles.params;
set(handles.edit_patch,'String',sprintf('%d',params.psize));
set(handles.edit_horz,'String',sprintf('%d',params.npatches_x));
set(handles.edit_vert,'String',sprintf('%d',params.npatches_y));
set(handles.edit_frame,'String',sprintf('%d',handles.frame_num));
set(handles.edit_trx,'String',sprintf('%d',handles.cur_fly));
set(handles.edit_x_center,'String',sprintf('%d',params.x_center));
set(handles.edit_y_center,'String',sprintf('%d',params.y_center));
method_ndx = find(strcmp(handles.params.methods,handles.params.cur_method));
set(handles.menu_methods,'String',handles.params.method_names);
set(handles.menu_methods,'Value',method_ndx);

function handles = updateimsz(handles)
params = handles.params;
grid_sz_x = params.npatches_x*params.psize;
grid_sz_y = params.npatches_y*params.psize;
x_c = params.x_center; y_c = params.y_center;
max_sz_x = max(x_c+grid_sz_x/2,-x_c+grid_sz_x/2);
max_sz_y = max(y_c+grid_sz_y/2,-y_c+grid_sz_y/2);
max_sz = max(max_sz_x,max_sz_y)*2;
params.imsz_display = max(params.imsz_display,max_sz);
handles.params = params;
set(handles.edit_imsz,'String',sprintf('%d',params.imsz_display));

function handles = updatePlots(handles)

set(handles.figure1, 'pointer', 'watch')
handles = updateimsz(handles);
trx = handles.trx;
cur_fly = handles.cur_fly;
frame_num = handles.frame_num;
rfn = handles.rfn;
trx_fn = frame_num + trx(cur_fly).off;
cur_frame = rfn(frame_num);
x = trx(cur_fly).x(trx_fn);
y = trx(cur_fly).y(trx_fn);
theta = trx(cur_fly).theta(trx_fn);
psize = handles.params.psize;
nbins = handles.params.nbins;

ncells_x = handles.params.npatches_x;
ncells_y = handles.params.npatches_y;
x_c = handles.params.x_center;
y_c = handles.params.y_center;
winwidth = handles.params.imsz_display;
winheight = handles.params.imsz_display;
hsz = handles.params.imsz_display/2;
grid_hsz_x = ncells_x*psize/2;
grid_hsz_y = ncells_y*psize/2;
handles.cur_im = CropImAroundTrx(cur_frame,x,y,theta-pi/2,...
    (winwidth-1)/2,(winheight-1)/2);
cla(handles.axes_hog);
cla(handles.axes_hof);

ftrs = genSTFeatures(handles.rfn,handles.headerinfo,frame_num,frame_num+1,...
    handles.trx,handles.params.is_stationary,handles.params.cur_method,handles.params);

% hog ftrs
scale = 6;
im1 = handles.cur_im;

axes(handles.axes_hog);
him = imshow(imresize(im1,scale));
axis image;
colormap gray;
hold on;
axis off;

colors = hsv(nbins);
colors = colors([ (end/2+1):end 1:end/2],:);

[nr,nc,~] = size(im1);
% maxv2 = max(H(:));
maxv2 = 0.03;
wd = 0.5;
hogpatch = [wd wd -wd -wd wd;-psize psize psize -psize -psize]/2;
h = [];
H = ftrs.hogftrs{cur_fly}(:,:,:,1);
bincenters = linspace(0,pi,nbins+1);
bincenters = bincenters(1:nbins);
dt = mean(diff(bincenters));
binedges = [bincenters(1)-dt/2,(bincenters(1:end-1)+bincenters(2:end))/2,bincenters(end)+dt/2];

for xi = 1:ncells_x
  cx = (psize/2 + (xi-1)*psize - grid_hsz_x + x_c + hsz )*scale+ 1 ;
  if cx+psize/2 > nc*scale
    break;
  end
  for yi = 1:ncells_y
    cy = (psize/2 + (yi-1)*psize - grid_hsz_y + y_c + hsz)*scale+ 1 ;
    if cy+psize/2 > nr*scale
      break;
    end

    for bini = 1:nbins
      tmp = bincenters(bini);
      curpatch = [cos(tmp) -sin(tmp); sin(tmp) cos(tmp)]*hogpatch;
      xcurr = cx + curpatch(1,:)*scale;
      ycurr = cy + curpatch(2,:)*scale;
      h(yi,xi,bini) = patch(xcurr,ycurr,colors(bini,:),...
          'LineStyle','none',...
          'FaceAlpha',min(1,H(yi,xi,bini)/maxv2));
    end
  end
end

for xi = 0:ncells_x
    plot([(xi*psize+x_c + hsz- grid_hsz_x)*scale,(xi*psize+x_c+hsz-grid_hsz_x)*scale],...
      [ (y_c+hsz-grid_hsz_y)*scale,(y_c+ncells_y*psize+hsz-grid_hsz_y)*scale],...
      'Color',[0.3,0.3,0.3,0.2]);
end
for yi = 0:ncells_y
    plot([(x_c+hsz-grid_hsz_x)*scale,(x_c+ncells_x*psize+hsz-grid_hsz_x)*scale],...
      [ (yi*psize+y_c+hsz-grid_hsz_y)*scale,(yi*psize+y_c+hsz-grid_hsz_y)*scale],...
      'Color',[0.3,0.3,0.3,0.2]);
end
scatter(hsz*scale,hsz*scale,'marker','*');
scatter((x_c+hsz)*scale,(y_c+hsz)*scale,'marker','o');

% Flow (hof) ftrs
if handles.params.is_stationary && frame_num < handles.nframes,
  ny = trx(cur_fly).y(trx_fn+1);
  nx = trx(cur_fly).x(trx_fn+1);
  ntheta = trx(cur_fly).theta(trx_fn+1);
  n_fr = rfn(frame_num+1);
else
  if frame_num == handles.nframes
      n_fr = cur_frame;
  else
      n_fr = rfn(frame_num+1);
  end
  ny = y; nx = x; ntheta = theta;
end

im2 = CropImAroundTrx(n_fr,nx,ny,ntheta-pi/2,(winwidth-1)/2,(winheight-1)/2);
axes(handles.axes_hof);
him = imshowpair(imresize(im1,scale),imresize(im2,scale),'Scaling','none');
axis image;
colormap gray;
hold on;

colors = hsv(nbins);
[nr,nc,~] = size(im1);
% maxv2 = max(F(:));
maxv2 = 1;

F = ftrs.flowftrs{cur_fly}(:,:,:,1);
h = [];
bincenters = linspace(0,pi,nbins+1);
bincenters = bincenters(1:nbins);

bincenters2 = bincenters*2;
dt = mean(diff(bincenters2));
binedges2 = [bincenters2(1)-dt/2,(bincenters2(1:end-1)+bincenters2(2:end))/2,bincenters2(end)+dt/2];

for xi = 1:ncells_x
  cx = (psize/2  + (xi-1)*psize  + x_c +hsz-grid_hsz_x)*scale+ 1;
  if cx+psize/2 > nc*scale
    break;
  end
  for yi = 1:ncells_y
    cy = (psize/2 + (yi-1)*psize + y_c+hsz-grid_hsz_y)*scale+ 1;
    if cy+psize/2 > nr*scale
      break;
    end

    for bini = 1:nbins
      tmp = linspace(binedges2(bini),binedges2(bini+1),20);
      xcurr = cx + [0,psize/2*cos(tmp),0]*scale;
      ycurr = cy + [0,psize/2*sin(tmp),0]*scale;
      h(yi,xi,bini) = patch(xcurr,ycurr,colors(bini,:),'LineStyle','none','FaceAlpha',min(1,F(yi,xi,bini)/maxv2));
    end

  end
end
for xi = 0:ncells_x
    plot([(xi*psize+x_c+hsz-grid_hsz_x)*scale,(xi*psize+x_c+hsz-grid_hsz_x)*scale],...
      [ (y_c+hsz-grid_hsz_y)*scale,(y_c+ncells_y*psize+hsz-grid_hsz_y)*scale],...
      'Color',[0.3,0.3,0.3,0.2]);
end
for yi = 0:ncells_y
    plot([(x_c+hsz-grid_hsz_x)*scale,(x_c+ncells_x*psize+hsz-grid_hsz_x)*scale],...
      [ (yi*psize+y_c+hsz-grid_hsz_y)*scale,(yi*psize+y_c+hsz-grid_hsz_y)*scale],...
      'Color',[0.3,0.3,0.3,0.2]);
end
scatter(hsz*scale,hsz*scale,'marker','*');
scatter((x_c+hsz)*scale,(y_c+hsz)*scale,'marker','o');

set(handles.figure1, 'pointer', 'arrow')

% --- Outputs from this function are returned to the command line.
function varargout = spaceTimeOptions_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure

varargout{1} = ~handles.is_canceled;
varargout{2} = handles.params;
figure1_CloseRequestFcn(hObject,eventdata,handles);

function edit_patch_Callback(hObject, eventdata, handles)
% hObject    handle to edit_patch (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_patch as text
%        str2double(get(hObject,'String')) returns contents of edit_patch as a double

patch_sz = str2double(get(hObject,'String'));
prev_patch_sz = handles.params.psize;
if isempty(patch_sz) || isnan(patch_sz) || round(patch_sz)~=patch_sz || patch_sz < 1
    warndlg('Cell size must be a positive integer');
    set(handles.edit_patch,'String',sprintf('%d',prev_patch_sz));
    return;
end
handles.params.psize = patch_sz;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_patch_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_patch (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit_horz_Callback(hObject, eventdata, handles)
% hObject    handle to edit_horz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_horz as text
%        str2double(get(hObject,'String')) returns contents of edit_horz as a double
ncells = str2double(get(hObject,'String'));
prev_cells = handles.params.npatches_x;
if isempty(ncells) || isnan(ncells) || round(ncells)~=ncells || ncells < 1
    warndlg('Cell size must be a positive integer');
    set(handles.edit_patch,'String',sprintf('%d',prev_cells));
    return;
end
handles.params.npatches_x = ncells;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_horz_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_horz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit_frame_Callback(hObject, eventdata, handles)
% hObject    handle to edit_frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_frame as text
%        str2double(get(hObject,'String')) returns contents of edit_frame as a double

frame_num = str2double(get(hObject,'String'));
if isempty(frame_num) || isnan(frame_num) || round(frame_num)~=frame_num || frame_num < 1
    warndlg('Frame number must be a positive integer');
    set(handles.edit_frame,'String',sprintf('%d',handles.frame_num));
    return;
end
min_frame = handles.trx(handles.cur_fly).firstframe;
max_frame = handles.trx(handles.cur_fly).endframe;
if frame_num > max_frame || frame_num < min_frame
    warndlg(sprintf('Frame number must be between %d and %d for the current animal',min_frame,max_frame));
    set(handles.edit_frame,'String',sprintf('%d',handles.frame_num));
    return;
end
handles.frame_num = frame_num;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_frame_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit_trx_Callback(hObject, eventdata, handles)
% hObject    handle to edit_trx (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_trx as text
%        str2double(get(hObject,'String')) returns contents of edit_trx as a double

fly_num = str2double(get(hObject,'String'));
if isempty(fly_num) || isnan(fly_num) || round(fly_num)~=fly_num || fly_num < 1
    warndlg('Animal number must be a positive integer');
    set(handles.edit_trx,'String',sprintf('%d',handles.cur_fly));
    return;
end
if fly_num > handles.n_trx
    warndlg(sprintf('Animal number must be less than %d',handles.n_trx));
    set(handles.edit_trx,'String',sprintf('%d',handles.cur_fly));
    return;
end
handles.cur_fly = fly_num;
if handles.frame_num < handles.trx(fly_num).firstframe
    handles.frame_num = handles.trx(fly_num).firstframe;
end
if handles.frame_num > (handles.trx(fly_num).endframe-1)
    handles.frame_num = handles.trx(fly_num).endframe-1;
end
updateGUI(handles);
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_trx_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_trx (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on button press in pushbutton_cancel.
function pushbutton_cancel_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_cancel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
handles.is_canceled=true;
guidata(hObject,handles);
uiresume(handles.figure1);

% --- Executes on button press in pushbutton_done.
function pushbutton_done_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_done (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
handles.is_canceled=false;
guidata(hObject,handles);
uiresume(handles.figure1);

% --- Executes on button press in toggle_align.
function toggle_align_Callback(hObject, eventdata, handles)
% hObject    handle to toggle_align (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of toggle_align
if get(hObject,'Value')
    handles.params.is_stationary= true;
else
    handles.params.is_stationary= false;
end
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function toggle_align_CreateFcn(hObject, eventdata, handles)
% hObject    handle to toggle_align (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% --- Executes when user attempts to close figure1.
function figure1_CloseRequestFcn(hObject, eventdata, handles)
% hObject    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: delete(hObject) closes the figure
try
    close(handles.fid);
catch ME

end
delete(hObject);

% --- Executes during object deletion, before destroying properties.
function figure1_DeleteFcn(hObject, eventdata, handles)
% hObject    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

function edit_vert_Callback(hObject, eventdata, handles)
% hObject    handle to edit_vert (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_vert as text
%        str2double(get(hObject,'String')) returns contents of edit_vert as a double
ncells = str2double(get(hObject,'String'));
prev_cells = handles.params.npatches_y;
if isempty(ncells) || isnan(ncells) || round(ncells)~=ncells || ncells < 1
    warndlg('Cell size must be a positive integer');
    set(handles.edit_patch,'String',sprintf('%d',prev_cells));
    return;
end
handles.params.npatches_y = ncells;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_vert_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_vert (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on selection change in menu_methods.
function menu_methods_Callback(hObject, eventdata, handles)
% hObject    handle to menu_methods (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns menu_methods contents as cell array
%        contents{get(hObject,'Value')} returns selected item from menu_methods
method_ndx = get(hObject,'Value');
handles.params.cur_method = handles.params.methods{method_ndx};
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function menu_methods_CreateFcn(hObject, eventdata, handles)
% hObject    handle to menu_methods (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit_x_center_Callback(hObject, eventdata, handles)
% hObject    handle to edit_x_center (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_x_center as text
%        str2double(get(hObject,'String')) returns contents of edit_x_center as a double
ncells = str2double(get(hObject,'String'));
prev_cells = handles.params.x_center;
if isempty(ncells) || isnan(ncells) || round(ncells)~=ncells
    warndlg('Cell size must be a integer');
    set(handles.edit_x_center,'String',sprintf('%d',prev_cells));
    return;
end
handles.params.x_center = ncells;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_x_center_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_x_center (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit_y_center_Callback(hObject, eventdata, handles)
% hObject    handle to edit_y_center (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_y_center as text
%        str2double(get(hObject,'String')) returns contents of edit_y_center as a double
ncells = str2double(get(hObject,'String'));
prev_cells = handles.params.y_center;
if isempty(ncells) || isnan(ncells) || round(ncells)~=ncells
    warndlg('Cell size must be a integer');
    set(handles.edit_y_center,'String',sprintf('%d',prev_cells));
    return;
end
handles.params.y_center = ncells;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_y_center_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_y_center (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function edit_imsz_Callback(hObject, eventdata, handles)
% hObject    handle to edit_imsz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_imsz as text
%        str2double(get(hObject,'String')) returns contents of edit_imsz as a double
ncells = str2double(get(hObject,'String'));
prev_cells = handles.params.imsz_display;
if isempty(ncells) || isnan(ncells) || round(ncells)~=ncells || ncells < 1
    warndlg('Cell size must be a positive integer');
    set(handles.edit_imsz,'String',sprintf('%d',prev_cells));
    return;
end
params = handles.params;
grid_sz_x = params.npatches_x*params.psize;
grid_sz_y = params.npatches_y*params.psize;
x_c = params.x_center; y_c = params.y_center;
max_sz_x = max(x_c+grid_sz_x/2,-x_c+grid_sz_x/2);
max_sz_y = max(y_c+grid_sz_y/2,-y_c+grid_sz_y/2);
max_sz = max(max_sz_x,max_sz_y)*2;
prev_sz = params.imsz_display;
if ncells < max_sz
  warndlg('Input size cannot include the whole grid. Select a size large enough to include the whole grid');
  set(handles.edit_imsz,'String',sprintf('%d',prev_sz));
  return;
end

handles.params.imsz_display = ncells;
handles = updatePlots(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_imsz_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_imsz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function dataS = populate_planC_structures_field(fieldname, dcmdir_PATIENT_STUDY_SERIES_RTSTRUCTS, scanOriS, attr, ssObj, cObj, optS)
%"populate_planC_structures_field"
%   Given the name of a child field to planC{indexS.structures}, populates that
%   field based on the data contained in dcmdir.PATIENT.STUDY.SERIES.RTSTRUCTS
%   structure passed in, for structure number structNum.
%
%JRA 07/12/06
%YWU Modified 03/01/08
%DK 04/12/09
%   Fixed Coordinate System
%NAV 07/19/16 updated to dcm4che3
%       replaced dcm2ml_Element with getTagValue
%       replaced "get" with "getValue", and
%       "countItems" with "size()"
%
%Usage:
%   dataS = populate_planC_structures_field(fieldname,dcmdir_PATIENT_STUDY_SERIES_RTSTRUCTS);
%
% 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/>.

%For easier handling
%global xOffset yOffset;

%STRUCTS = dcmdir_PATIENT_STUDY_SERIES_RTSTRUCTS;

%Default value for undefined fields.
dataS = '';

% if ~exist('attr', 'var')
%     %Grab the dicom object representing the structures.
%     attr = scanfile_mldcm(STRUCTS.file);
% end
%
% %Structure Set ROI Sequence
% %SSRS = attr.getValue(hex2dec('30060020'));
% SSRS = attr.getValue(org.dcm4che3.data.Tag.StructureSetROISequence);
%
% %ROI Contour Sequence
% %RCS = attr.getValue(hex2dec('30060039'));
% RCS = attr.getValue(org.dcm4che3.data.Tag.ROIContourSequence);

% %Count em up.
%
% if ~isempty(RCS)
%     nStructs = RCS.size();
% else
%     nStructs = 0;
% end
% %Structure Set item for this structure.
% ssObj = SSRS.get(structNum - 1);
% %ssObj = org.dcm4che3.data.Attributes(SSRS)
% %ROI Number
% %ROINumber = getTagValue(ssObj, '30060022');
% ROINumber = ssObj.getInts(org.dcm4che3.data.Tag.ROINumber); % vr=IS
%
% %Find the contour object for this structure.
% for s=1:nStructs
%     cObj = RCS.get(s - 1);
%
%     %Referenced ROI Number
%     %RRN = getTagValue(cObj, '30060084');
%     RRN = cObj.getInts(org.dcm4che3.data.Tag.ReferencedROINumber); %IS
%
%     if RRN == ROINumber
%         %We found the correct contour for this structure.
%         break;
%     end
%
% end

switch fieldname

    case 'imageNumber'
        %Currently not implemented

    case 'imageType'
        dataS = 'STRUCTURE';

    case 'caseNumber'
        %Currently not implemented

    case 'roiNumber'
        %dataS = ROINumber;
        dataS = ssObj.getInts(805699618); %org.dcm4che3.data.Tag.ROINumber;

    case 'patientName'
        %Patient's Name
        %nameS = getTagValue(attr, '00100010');
        %dataS = [nameS.FamilyName '^' nameS.GivenName '^' nameS.MiddleName];
        nameObj = javaObject('org.dcm4che3.data.PersonName',attr.getString(1048592)); %org.dcm4che3.data.Tag.PatientName;
        compFamilyName = javaMethod('valueOf','org.dcm4che3.data.PersonName$Component','FamilyName');
        compGivenName = javaMethod('valueOf','org.dcm4che3.data.PersonName$Component','GivenName');
        compMiddleName = javaMethod('valueOf','org.dcm4che3.data.PersonName$Component','MiddleName');
        dataS = [char(nameObj.get(compFamilyName)), '^',...
            char(nameObj.get(compGivenName)), '^',...
            char(nameObj.get(compMiddleName))];

    case 'structureName'
        %ROI Name
        %dataS = getTagValue(ssObj, '30060026');
        dataS = char(ssObj.getString(805699622,0)); %org.dcm4che3.data.Tag.ROIName;

    case 'numberRepresentation'
        %Artifact of RTOG field names, not representative of CERR data.
        dataS = 'CHARACTER';

    case 'structureFormat'
        dataS = 'SCAN-BASED';

    case 'structSetSopInstanceUID'
        %dataS = getTagValue(attr, '00080018');
        dataS = char(attr.getString(524312,0)); %org.dcm4che3.data.Tag.SOPInstanceUID;

    case 'numberOfScans' %aka # of CT slices
        %Referenced Frame of Reference Sequence
        %RFRS = attr.getValue(hex2dec('30060010'));
        %RFRS = attr.getValue(org.dcm4che3.data.Tag.ReferencedFrameOfReferenceSequence);

        %Frame of Reference UID
        %FORUID = getTagValue(ssObj, '30060024');
        %FORUID = char(attr.getStrings(org.dcm4che3.data.Tag.ReferencedFrameOfReferenceUID));

        %Find the series referenced by these contours.  See bottom of file.
        %RSS = getReferencedSeriesSequence(RFRS, FORUID);

        %dataS = getTagValue(RSS,hex2dec('30060016')); % check this
        contourSeq = cObj.getValue(805699648); %org.dcm4che3.data.Tag.ContourSequence;
        if ~isempty(contourSeq)
            dataS = contourSeq.size();
        end

%         %Convert to ML structure format.
%         RSSML = getTagStruct(RSS);
%
%         if ~isempty(RSSML) && ~isempty(RSSML.ContourImageSequence)
%             %# slices in this series.
%             dataS = length(fields(RSSML.ContourImageSequence));
%         else
%             dataS = '';
%         end

    case 'maximumNumberScans'
        %Currently not implemented

    case 'maximumPointsPerSegment'
        %Currently not implemented

    case 'maximumSegmentsPerScan'
        %Currently not implemented

    case 'structureEdition'
        %Currently not implemented

    case 'unitNumber'
        %Currently not implemented

    case 'writer'
        %Manufacturer
        %dataS = getTagValue(attr, '00080070');
        dataS = char(attr.getString(524400,0)); %org.dcm4che3.data.Tag.Manufacturer;

    case 'dateWritten'
        %Structure Set Date.
        %dataS = getTagValue(attr, '30060008');
        dataS = char(attr.getString(805699592,0)); %org.dcm4che3.data.Tag.StructureSetDate;

    case 'structureColor'
        %Currently not implemented

    case 'structureDescription'
        dataS = char(attr.getString(528446,0)); % series description

    case 'roiGenerationAlgorithm'
        dataS = char(attr.getString(805699638,0)); %dataS = getTagValue(ssObj, '30060036');

    case 'roiGenerationDescription'
        dataS = char(attr.getString(805699640,0)); %dataS = getTagValue(ssObj, '30060038');

    case 'studyNumberOfOrigin'
        %Currently not implemented

    case 'contour'
        %Contour Sequence
        if ~cObj.contains(805699648) %org.dcm4che3.data.Tag.ContourSequence %hex2dec('30060040')
            return;
        end
        % cSeq = cObj.getValue(hex2dec('30060040'));
        cSeq = cObj.getValue(805699648); %org.dcm4che3.data.Tag.ContourSequence;

        if cSeq.isEmpty
            return;
        end

        nContours = cSeq.size();

        %optS = opts4Exe([getCERRPath,'CERROptions.json']);
        %contourSliceTol = optS.contourToSliceTolerance;

        for c = 1:nContours
            aContour = cSeq.get(c-1);

            % Referenced SOP instance UID
            % refSeq = aContour.getValue(hex2dec('30060016'));
            refSeq = aContour.getValue(805699606); %org.dcm4che3.data.Tag.ContourImageSequence;
            sopInstanceUID = '';
            sopClassUID = '';
            if ~isempty(refSeq)
                % sopInstanceUID = getTagValue(refSeq.get(0), '00081155');
                sopInstanceUID = char(refSeq.get(0).getString(528725,0)); %org.dcm4che3.data.Tag.ReferencedSOPInstanceUID;
                sopClassUID = char(refSeq.get(0).getString(528720,0)); %SOPClassUID
            end

            dataS(c).sopInstanceUID = sopInstanceUID;
            dataS(c).sopClassUID = sopClassUID;

            %Contour Geometric Type
            %geoType = getTagValue(aContour, '30060042');
            geoType = char(aContour.getString(805699650,0)); %org.dcm4che3.data.Tag.ContourGeometricType;

            switch upper(geoType)
                case 'POINT'
                    warning('Single point contour.');
                case 'OPEN_PLANAR'
                    warning('Open planar contours.')
                case 'OPEN_NONPLANAR'
                    warning('Open, non-planar contours.')
                case 'CLOSED_PLANAR'
                    %Great, continue.
            end

            %Number of Contour Points
            %nPoints = getTagValue(aContour, '30060046');
            nPoints = aContour.getInts(805699654); %org.dcm4che3.data.Tag.NumberOfContourPoints;
            if isempty(nPoints)
                nPoints = 0;
            end
            %             if ~(nPoints > 1)
            %                 dataS(i).segments = [];
            %                 continue;
            %             end

            %Contour Data
            try
                %data    = getTagValue(aContour, '30060050');
                data    = aContour.getDoubles(805699664); %org.dcm4che3.data.Tag.ContourData;
            catch
                disp('vacant contour found ...');
                dataS(c).segments = [];
                continue;
            end
            %Reshape Contour Data
            data    = reshape(data, [3 nPoints])';

            %data(:,3) = -data(:,3); %Z is always negative to match RTOG spec

            % Flip based on pt position

            %Get assoc scanUID
            assocUID = getAssocScanUID(attr,ssObj);
            assocScanNum = strcmp(assocUID,{scanOriS.scanUID});
            if ~any(assocScanNum) && length(scanOriS)==1
                assocScanNum = 1;
            end
            if any(assocScanNum)
                imgOriV = scanOriS(assocScanNum).imageOrientationPatient;
            else
                imgOriV = [1,0,0,0,1,0]'; % HFS
            end
%              if ~isempty(imgOri)
                  data = convertCoordinates(data, imgOriV);
%              else
%                     %Default it to HFS
%                 data(:,3) = -data(:,3); %Z is always negative to match RTOG spec
%                 data(:,2) = -data(:,2);
%              end

            %Convert from DICOM mm to CERR cm.
            data    = data / 10;

            %Replicate the last data point: CERR needs first/last identical
            if ~isempty(data)
                data(end+1,:) = data(1,:);
%% REMOVED IN LATEST CERR
%{
                if length(unique(data(:,3))) > 1;

                    a = abs(diff(unique(data(:,3))));

                    if (max(a) > contourSliceTol)
                        %ROI Name
                        name = getTagValue(ssObj, '30060026');

                        warning(['CERR does not support out-of-plane contours. Skipping contour ' num2str(i) ' in structure ' name '.']);
                        continue;
                    end
                end
%}
            end

            dataS(c).segments = data;

        end

        %Segments are inserted in order of storage in the DICOM sequence,
        %meaning segments must be sorted and rearranged to match the slice
        %order of the associated scan.   This is handled by a function that
        %has access to both the scan and contour data, dcmdir2planC.

    case 'rasterSegments'
        %Implementation not necessary

    case 'DSHPoints'
        %Implementation not necessary

    case 'orientationOfStructure'
        %In CERR, all structures are oriented transversely.
        dataS = 'TRANSVERSE';

    case 'transferProtocol'
        dataS = 'DICOM';

    case 'referencedFrameOfReferenceUID'
        %dataS  = getTagValue(attr, '00200052');
        %dataS = char(attr.getStrings(org.dcm4che3.data.Tag.FrameOfReferenceUID));
        %dataS = char(attr.getString(2097234,0));
        dataS = char(ssObj.getString(805699620,0)); %org.dcm4che3.data.Tag.ReferencedFrameOfReferenceUID;

    case 'referencedSeriesUID'

        dataS = '';

        refFrameOfReferenceSeq = attr.getValue(805699600);
        if ~refFrameOfReferenceSeq.isEmpty
            refFrameOfReferenceSeq = refFrameOfReferenceSeq.get(0);
        else
            return
        end

        reRefStudySequence = refFrameOfReferenceSeq.getValue(805699602);
        if ~reRefStudySequence.isEmpty
            reRefStudySequence = reRefStudySequence.get(0);
        else
            return;
        end

        rtRefSeriesSequence = reRefStudySequence.getValue(805699604);
        if ~rtRefSeriesSequence.isEmpty
            rtRefSeriesSequence = rtRefSeriesSequence.get(0);
        else
            return
        end

        dataS = char(rtRefSeriesSequence.getString(2097166,0));

    case 'DICOMHeaders'
        %Currently not implemented
        %Read all the dcm data into a MATLAB struct.
        if strcmpi(optS.saveDICOMheaderInPlanC,'yes')
            dataS = getTagStruct(attr);
        end
%
%         %Remove contoursequence data to avoid storing huge amounts of redundant data.
%         try
%             dataS = rmfield(dataS, 'ROIContourSequence');
%         end

%         dataS = '';

    case 'visible'
        dataS = true; % default to visible

    case 'associatedScan'
        %Currently not implemented

    case 'strUID'
        dataS = createUID('structure');

    case 'assocScanUID'
        %wy, use the Referenced frame of reference UID to associate structures to scan.
        %SSRS = attr.get(org.dcm4che2.data.Tag.StructureSetROISequence);
        %SSRS_1 = SSRS.getDicomObject(0);
        %dataS = char(SSRS_1.getString(org.dcm4che2.data.Tag.ReferencedFrameofReferenceUID));

        %dataS = getTagValue(SSRS.get(hex2dec('00200052')));
        %dataS = ['CT.',dataS];

        dataS = getAssocScanUID(attr,ssObj);

%         %Convert to ML structure format.
%         RSSML = getTagStruct(RSS);
%
%         if ~isempty(RSSML)
%             %UID of series structures were contoured on.
%             dataS = RSSML.SeriesInstanceUID;
%             dataS = ['CT.',dataS];
%         else
%             dataS = '';
%         end

    otherwise
        %         warning(['DICOM Import has no methods defined for import into the planC{indexS.structures}.' fieldname ' field, leaving empty.']);
end

function attr = getReferencedSeriesSequence(Referenced_Frame_Of_Reference_Sequence, Frame_of_Reference_UID);
%"getReferencedSeriesSequence"
%   Searches the RFRS for a match to the FoRUID of a structure and returns
%   the subfield of the RFRS containing information on the series that the
%   contours were defined on.  Not a general function since it is really
%   only used when populating structure fields.

%Easier reading.
referencedSeq = Referenced_Frame_Of_Reference_Sequence;
refUID = Frame_of_Reference_UID;

if ~isempty(referencedSeq)
    nRFRS = referencedSeq.size();
else
    nRFRS = 0;
end
%Search the RFR sequence for the UID matching the FORUID.
for i=1:nRFRS

    myRFR = referencedSeq.get(i-1);

    % RFRUID = getTagValue(myRFR, '00200052');
    RFRUID = char(myRFR.getString(2097234,0)); %org.dcm4che3.data.Tag.FrameOfReferenceUID;

    if isequal(RFRUID,refUID)

        %RT Referenced Study Sequence
        %RTRSS = myRFR.getValue(hex2dec('30060012'));
        RTRSS = myRFR.getValue(805699602); %org.dcm4che3.data.Tag.RTReferencedStudySequence;

        if isempty(RTRSS)
            break;
        end

        RTRSS_1 = RTRSS.get(0);

        %RT Referenced Series Sequence
        %RTRSS = RTRSS_1.getValue(hex2dec('30060014'));
        RTRSS = RTRSS_1.getValue(805699604); %org.dcm4che3.data.Tag.RTReferencedSeriesSequence;

        attr = RTRSS.get(0);

    end

end

if ~exist('attr', 'var')
    warning('No explicit association between structures and a scan could be found.');
    attr = javaObject('org.dcm4che3.data.Attributes');
end

end %End of function

function assocScanUID = getAssocScanUID(attr,ssObj)
%commented by wy
        %Referenced Frame of Reference Sequence
        %referencedSeq = attr.getValue(hex2dec('30060010'));
        referencedSeq = attr.getValue(805699600); %org.dcm4che3.data.Tag.ReferencedFrameOfReferenceSequence;

        %Frame of Reference UID
        %refUID = getTagValue(ssObj, '30060024');
        refUID = char(ssObj.getString(805699620,0)); %org.dcm4che3.data.Tag.ReferencedFrameOfReferenceUID;

        %Find the series referenced by these contours.  See bottom of file.
        refSerSeq = getReferencedSeriesSequence(referencedSeq, refUID);

        %assocScanUID = getTagValue(refSerSeq,'0020000E');
        assocScanUID = char(refSerSeq.getString(2097166,0)); %org.dcm4che3.data.Tag.SeriesInstanceUID;

        assocScanUID = ['CT.',assocScanUID];

end %End of function

end


% Load AOI from file
function ApToolLoadImage(filenames,hObject,handles,varargin)

if isfield(handles,'H')
    delete(handles.H);
end

if ((nargin<1)||isempty(filenames)) % If no filename was give, use dialog box to ask for one
    % Recall last interactively selected path used
    if ispref('matlab_sar_toolbox','last_used_directory')
        pathname = getpref('matlab_sar_toolbox','last_used_directory');
        if ~ischar(pathname)||~exist(pathname,'dir')
            pathname = pwd;
        end
    else
        pathname = pwd;
    end
    [filenames,pathname]=uigetfile(sar_file_extensions('amplitude'),...
        'Open SAR Data File',pathname,'MultiSelect','on');
    if(iscell(filenames)), filenames=sort(filenames); end
    setpref('matlab_sar_toolbox','last_used_directory',pathname);
    if isfield(handles,'phdhandle')
        handles = rmfield(handles,'phdhandle');
    end
else % Path was already passed in with filenames
    pathname='';
end
fullfilename = {}; % Reset if files were previously open
if(iscell(filenames)) % Multiple files requested
    for j=1:length(filenames)
        fullfilename{j}=[pathname filenames{j}];
    end
elseif(filenames)
    fullfilename{1}=[pathname filenames];
else % filename=0.  Cancel was pressed, instead of a file being chosen.
    return;
end
if ~isempty(pathname)
    setpref('matlab_sar_toolbox','last_used_directory',pathname); %store path
end

% Open readers for all files, and build cell array of all readers
reader_obj ={};
sf_ind = []; fn_ind = []; % Indices to filenames and subimages for each reader object
for i = 1:length(fullfilename)
    newreader = open_reader(fullfilename{i});
    fn_ind = [fn_ind i*ones(size(newreader))];
    sf_ind = [sf_ind 1:numel(newreader)];
    reader_obj = [reader_obj newreader];
end
clear newreader;

[readerobj_indices,reader_obj] = group_reader_objs_by_pol(reader_obj); % Consolidate reader objects into polarimetric sets

%get metadata
if (iscell(reader_obj))
    meta=reader_obj{1}.get_meta(); % Assume first frame for getting datasize
    % Adjust later if frame changes
else
    meta=reader_obj.get_meta();
end
nx = double(meta.ImageData.NumCols);
ny = double(meta.ImageData.NumRows);

%set filename or corename as the title of the GUI
if ~iscell(filenames)
    temp = filenames;
    clear filenames;
    filenames{1} = temp;
end
[~,name,ext] = fileparts(filenames{1});
fname = strcat(name,ext);
set(handles.figure1,'Name',['ApertureTool, Filename: ', fname]);

% Select AOI and frame number if necessary
if isempty(varargin) || isempty(varargin{1}) || varargin{1}(1) == 0
    %determine if we'll just load the image or if we'll load an AOI
    %selection MITM_Viewer
    if (nx <= handles.MaxLoadSize && ny <= handles.MaxLoadSize)
        %load image directly into tool
        aoi = [1 1 nx ny];
        FrameNum = 1;
    else
        %launch a MITM viewer with an AOI selection box
         ButtonName = questdlg(sprintf('%s %i x %i\n%s %i x %i\n%s',...
            'Suggested AOI dimensions are less than',handles.MaxLoadSize,handles.MaxLoadSize,...
            'Current AOI dimensions are',nx,ny,...
            'Do you want to redefine the AOI?'), ...
            'AptertureTool AOI', ...
            'Yes', 'No', 'Yes');
        if isempty(ButtonName), return; end %window was closed CANCEL
        if strcmpi(ButtonName,'No')
            aoi = [1 1 nx ny]; %use AOI anyway
            FrameNum = 1;
        else
            [aoi, FrameNum] = mitm_viewer(fullfilename,'mode','aoi','closeAfterSelect',true);
            FrameNum = find(cellfun(@(x) isequal(FrameNum,sf_ind(x)), readerobj_indices));
        end
        if isempty(aoi), return; end % Cancel was selected
    end
else
    aoi = varargin{1};
    if nargin>4 && ~isempty(varargin{2}) && all(varargin{2} ~= 0)
        FrameNum = find(cellfun(@(x) isequal(varargin{2},sf_ind(x)), readerobj_indices));
    else
        FrameNum = 1;
    end
end
if (iscell(reader_obj))
    meta=reader_obj{FrameNum}.get_meta(); % Assume first frame for getting datasize
end

set(handles.nx,'String',nx);
set(handles.ny,'String',ny);

%Draw metaicon
MetaIcon(meta,'handle',handles.metaicon);

% Handle complex data normalization parameters from metadata
% Set data normalization controls based on metadata
if isfield(meta,'Grid') && all(isfield(meta.Grid,{'Row','Col'})) && ...
        isfield(meta.Grid.Row,'WgtType') && ...
        isfield(meta.Grid.Col,'WgtType') && ...
        ((strcmpi(meta.Grid.Col.WgtType,'UNIFORM') && ...
        strcmpi(meta.Grid.Row.WgtType,'UNIFORM')) || ...
        (isfield(meta.Grid.Row.WgtType,'WindowName') && ...
        isfield(meta.Grid.Col.WgtType,'WindowName') && ...
        strcmpi(meta.Grid.Col.WgtType.WindowName,'UNIFORM') && ...
        strcmpi(meta.Grid.Row.WgtType.WindowName,'UNIFORM')))
    % If we already know that data is uniform weighted, this is not an option.
    set(handles.UniformWeighting,'Value',true);
    set(handles.UniformWeighting,'Enable','off');
else
    set(handles.UniformWeighting,'Value',false);
    set(handles.UniformWeighting,'Enable','on');
end
% Set default values for required frequency support deskew values
handles.isvalid_row_deltakcoapoly = isfield(meta,'Grid') && ...
    isfield(meta.Grid,'Row') && isfield(meta.Grid.Row,'DeltaKCOAPoly') && ...
    ~isempty(meta.Grid.Row.DeltaKCOAPoly) && ...
    all(isfinite(meta.Grid.Row.DeltaKCOAPoly(:)));
if handles.isvalid_row_deltakcoapoly && any(meta.Grid.Row.DeltaKCOAPoly(:)~=0)
    set(handles.DeskewFast,'Enable','on');
else
    set(handles.DeskewFast,'Enable','off');
    meta.Grid.Row.DeltaKCOAPoly = 0; % Give default value (no shift)
end
handles.isvalid_col_deltakcoapoly = isfield(meta,'Grid') && ...
    isfield(meta.Grid,'Col') && isfield(meta.Grid.Col,'DeltaKCOAPoly') && ...
    ~isempty(meta.Grid.Col.DeltaKCOAPoly) && ...
    all(isfinite(meta.Grid.Col.DeltaKCOAPoly(:)));
if handles.isvalid_col_deltakcoapoly && any(meta.Grid.Col.DeltaKCOAPoly(:)~=0)
    set(handles.DeskewSlow,'Enable','on');
else
    set(handles.DeskewSlow,'Enable','off');
    meta.Grid.Col.DeltaKCOAPoly = 0; % Give default value (no shift)
end
% Is it possible to deskew frequency support in both dimensions at once?
if isscalar(meta.Grid.Col.DeltaKCOAPoly) && ...
        isscalar(meta.Grid.Row.DeltaKCOAPoly)
    set(handles.DeskewSlow,'Value',handles.isvalid_col_deltakcoapoly); % Center both dimensions
    set(handles.DeskewFast,'Value',handles.isvalid_row_deltakcoapoly);
else % Make user select dimension to deskew
    set(handles.DeskewSlow,'Value',handles.isvalid_col_deltakcoapoly && ...
        all(meta.Grid.Col.DeltaKCOAPoly(:) == 0));
    set(handles.DeskewFast,'Value',handles.isvalid_row_deltakcoapoly && ...
        all(meta.Grid.Row.DeltaKCOAPoly(:) == 0));
end
handles.manual_offset = [0 0];
% Determine weighting function
try
    handles.weight_fun_az = sicdweight2fun(meta.Grid.Col);
    handles.weight_fun_rng = sicdweight2fun(meta.Grid.Row);
    handles.weighting_computed_from_data = false;
catch % Unable to determine weighting functions from metadata.
    % Estimate weighting from complex data instead
    if iscell(reader_obj)
        handles.weight_fun_az = estimate_weighting_file(reader_obj{FrameNum}, 1);
        handles.weight_fun_rng = estimate_weighting_file(reader_obj{FrameNum}, 2);
    else
        handles.weight_fun_az = estimate_weighting_file(reader_obj, 1);
        handles.weight_fun_rng = estimate_weighting_file(reader_obj, 2);
    end
    handles.weighting_computed_from_data = true;
end
% Determine if we have metadata to compute zeropad accurately
if isfield(meta,'Grid') && all(isfield(meta.Grid,{'Row','Col'})) && ...
        all(isfield(meta.Grid.Row,{'SS','ImpRespBW'})) && ...
        all(isfield(meta.Grid.Col,{'SS','ImpRespBW'}))
    handles.AzPad = max(1,1/(meta.Grid.Col.SS*meta.Grid.Col.ImpRespBW));
    handles.RnPad = max(1,1/(meta.Grid.Row.SS*meta.Grid.Row.ImpRespBW));
else % Otherwise, just default to full data.
    handles.AzPad = 1;
    handles.RnPad = 1;
end % User can always manual adjust zeropad area

% Use correct FFT function based on data
% Sgn of -1 means that a forward FFT is the correct transform to move from
% image domain to spatial frequency.  However, we want to flip the
% direction, so that high RF frequencies are at the top (low indices) when
% displayed in MATLAB, and time/angle increases toward the right (higher
% indices).  So we swap the FFT/IFFTs which, in effect, flips left/right
% and up/down in spatial frequency.
if isfield(meta,'Grid') && all(isfield(meta.Grid,{'Row','Col'})) && ...
        isfield(meta.Grid.Row,'Sgn') && isfield(meta.Grid.Col,'Sgn') && ...
        meta.Grid.Row.Sgn > 0 && meta.Grid.Col.Sgn > 0
    handles.fft_sp = @fft2; % Function to transform from image to spatial frequency
    handles.fft_im = @ifft2; % Function to transfrom from spatial frequency to image
elseif isfield(meta,'Grid') && all(isfield(meta.Grid,{'Row','Col'})) && ...
        isfield(meta.Grid.Row,'Sgn') && isfield(meta.Grid.Col,'Sgn') && ...
        meta.Grid.Row.Sgn ~= meta.Grid.Col.Sgn
    error('APERTURETOOL:MIXED_FFT_SGN','Mixed FFT signs not handled.');
else
    handles.fft_sp = @ifft2; % Function to transform from image to spatial frequency
    handles.fft_im = @fft2; % Function to transfrom from spatial frequency to image
end

%get polarization information
if isfield(meta,'ImageFormation') && isfield(meta.ImageFormation,'TxRcvPolarizationProc')
    handles.TxRcvPolarizationProc = meta.ImageFormation.TxRcvPolarizationProc;
    if ~iscell(handles.TxRcvPolarizationProc)
        handles.TxRcvPolarizationProc = {handles.TxRcvPolarizationProc};
        set(handles.image_rep_combo,'value',1);
    else
        set(handles.image_rep_combo,'value',...
            find(strcmpi('Pauli',get(handles.image_rep_combo,'string'))));
    end
    handles.polar_PHD_idx = 1;
    if numel(handles.TxRcvPolarizationProc) == 1
        set(handles.image_rep_combo,'value',1,'visible','off');
        set(handles.PHDPolCombo,'visible','off');
    else
        set(handles.image_rep_combo,'value',2,'visible','on');
        set(handles.PHDPolCombo,'visible','on');
    end
    %set strings in PHDPolCombo
    set(handles.PHDPolCombo,'String',meta.ImageFormation.TxRcvPolarizationProc);
else
    set(handles.image_rep_combo,'value',1,'visible','off');
    set(handles.PHDPolCombo,'visible','off');
end

% Read data
handles.complex_data = double(reader_obj{FrameNum}.read_chip(...
    [aoi(1) aoi(1)+aoi(3)-1],[aoi(2) aoi(2)+aoi(4)-1]));
reader_obj{FrameNum}.close();
% Condition data so that imcontrast control works better
handles.complex_data = 10*handles.complex_data./mean(handles.complex_data(:));
handles.aoi = aoi; % store aoi for later
handles.segment = FrameNum;

% This is derived redundant information, but we compute it once, so we
% don't need to do this in multiple places:
handles.ZpLimsAz = round((aoi(3)/2) * (1 - 1/handles.AzPad)) + 1;
handles.ZpLimsAz(2) = aoi(3) - handles.ZpLimsAz + 1;
handles.ZpWidthAz = diff(handles.ZpLimsAz)+1;
handles.ZpLimsRn = round((aoi(4)/2) * (1 - 1/handles.RnPad)) + 1;
handles.ZpLimsRn(2) = aoi(4) - handles.ZpLimsRn + 1;
handles.ZpWidthRn = diff(handles.ZpLimsRn)+1;

% update metadata
% chip info
meta.ImageData.FirstRow = meta.ImageData.FirstRow + aoi(2) - 1;
meta.ImageData.FirstCol = meta.ImageData.FirstCol + aoi(1) - 1;
meta.ImageData.NumRows = aoi(4);
meta.ImageData.NumCols = aoi(3);
try
    meta = add_sicd_corners(meta);
end
% If ImpRespWid is not defined, but ImpRespBW is, we just use what it would
% be for uniform weighting (since we're probably going to want to apply a
% uniform weighting to the data anyway.)
set(handles.PHDZoomCombo,'value',1); % Reset to "Full"
resolution_defined = true;
if ~isfield(meta.Grid.Col,'ImpRespWid')
    if isfield(meta.Grid.Col,'ImpRespBW')
        meta.Grid.Col.ImpRespWid = .886/meta.Grid.Col.ImpRespBW;
    else % Just set to zero if we don't know it
        meta.Grid.Col.ImpRespWid = 0;
        meta.Grid.Col.SS = 0;
        resolution_defined = false;
    end
end
if ~isfield(meta.Grid.Row,'ImpRespWid')
    if isfield(meta.Grid.Row,'ImpRespBW')
        meta.Grid.Row.ImpRespWid = .886/meta.Grid.Row.ImpRespBW;
    else % Just set to zero if we don't know it
        meta.Grid.Row.ImpRespWid = 0;
        meta.Grid.Row.SS = 0;
        resolution_defined = false;
    end
end

%set initial SS, Ap Time and Ap Bandwidth

handles.meta = meta; % store metadata for later

ApToolprocess_phd(hObject,handles); % Apply PHD options
handles = guidata(hObject); % process_phd change handles (We may fix this later.)

%set initial magnifications to fit
mag = handles.apiSP1.findFitMag();
handles.apiSP1.setMagnification(mag);
set(handles.ImageZoomCombo,'Value',1);
strings = get(handles.ImageZoomCombo,'String');
strings{1} = sprintf('Fit (%d%%)',round(mag*100));
set(handles.ImageZoomCombo,'String',strings);

% Draw imrect on phd
if isfield(handles,'H') && ishandle(handles.H)
    delete(handles.H); % Delete any previous imrects
end
handles.H = imrect(handles.phd, [handles.ZpLimsAz(1), handles.ZpLimsRn(1),...
    handles.ZpWidthAz handles.ZpWidthRn]);
if verLessThan('matlab', '7.6')
    disp('Warning. Matlab version does not support imrect constraint function');
else
    setPositionConstraintFcn(handles.H,...
        makeConstrainToRectFcn('imrect',handles.ZpLimsAz,handles.ZpLimsRn));
end
api_handle = iptgetapi(handles.H);
api_handle.addNewPositionCallback(@(x) ApToolnewpos(x,hObject));

% Now enable relevant controls
set(handles.Play,'enable','on');
set(handles.Last,'enable','on');
set(handles.First,'enable','on');
set(handles.Next,'enable','off'); % Full aperture is not a frame, so
set(handles.Prev,'enable','off'); % no next or previous
set(handles.Recording,'Enable','on');
set(handles.SaveImage,'Enable','on');
set(handles.ManualAdjust,'enable','on');

% Update handles structure
guidata(hObject, handles);

% Update resolution fields
ApToolnewpos(getPosition(handles.H),hObject);


function json=jsonset(fname,mmap,varargin)
%
% json=jsonset(fname,mmap,'$.jsonpath1',newval1,'$.jsonpath2','newval2',...)
%
% Fast writing of JSON data records to stream or disk using memory-map
% (mmap) returned by loadjson/loadbj and JSONPath-like keys
%
% authors:Qianqian Fang (q.fang <at> neu.edu)
% initially created on 2022/02/02
%
% input:
%      fname: a JSON/BJData/UBJSON string or stream, or a file name
%      mmap: memory-map returned by loadjson/loadbj of the same data
%            important: mmap must be produced from the same file/string,
%            otherwise calling this function may cause data corruption
%      '$.jsonpath1,2,3,...':  a series of strings in the form of JSONPath
%            as the key to each of the record to be written
%
% output:
%      json: the modified JSON string or, in the case fname is a filename,
%            the cell string made of jsonpaths that are successfully
%            written
%
% examples:
%      % create test data
%       d.arr={[1,2],'a',struct('c',2)}; d.obj=struct('k','test')
%      % convert to json string
%       str=savejson('',d,'compact',1)
%      % parse and return mmap
%       [dat, mmap]=loadjson(str);
%      % display mmap entries
%       savejson('',mmap)
%      % replace value using mmap
%       json=jsonset(str,mmap,'$.arr.[2].c','5')
%      % save same json string to file (must set savebinary 1)
%       savejson('',d,'filename','file.json','compact',1,'savebinary',1);
%      % fast write to file
%       json=jsonset('file.json',mmap,'$.arr.[2].c','5')
%
% license:
%     BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details
%
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%

if(regexp(fname,'^\s*(?:\[.*\])|(?:\{.*\})\s*$','once'))
    inputstr=fname;
else
    if(~exist('memmapfile','file'))
        fid=fopen(fname,'r+b');
    end
end

mmap=[mmap{:}];
keylist=mmap(1:2:end);

opt=struct;
for i=1:2:length(varargin)
    if(isempty(regexp(varargin{i},'^\$', 'once')))
        opt.(encodevarname(varargin{i}))=varargin{i+1};
    end
end

json={};
for i=1:2:length(varargin)
    if(regexp(varargin{i},'^\$'))
        [tf,loc]=ismember(varargin{i},keylist);
        if(tf)
            bmap=mmap{loc*2};
            if(ischar(varargin{i+1}))
                val=varargin{i+1};
            else
                val=savejson('',varargin{i+1},'compact',1);
            end
            if(length(val)<=bmap(2))
                val=[val repmat(' ',[1,bmap(2)-length(val)])];
                if(exist('inputstr','var'))
                    inputstr(bmap(1):bmap(1)+bmap(2)-1)=val;
                else
                    if(exist('memmapfile','file'))
                        rec={'uint8', [1 bmap(2)],  'x'};
                        fmap=memmapfile(fname,'writable',true,'offset', bmap(1)-1, 'format', rec, 'repeat',1);
                        fmap.Data.x=uint8(val);
                    else
                        fseek(fid,bmap(1)-1,'bof');
                        fwrite(fid,val);
                    end
                    json{end+1}={varargin{i},val};
                end
            end
        end
    end
end

if(exist('fid','var') && fid>=0)
    fclose(fid);
end

if(exist('inputstr','var'))
    json=inputstr;
end


function opt_checkExclusiveProps(opt, proppair, isdefault)

if ischar(opt),
  tag= ['in STRUCT ''' opt ''' '];
  opt= evalin('caller', opt);
else
  tag= '';
end

if nargin<3,
  for p= 1:size(proppair,1),
    for k= 1:length(opt),
      if ~isempty(opt(k).(proppair{p,1})) && ~isempty(opt(k).(proppair{p,2})),
        error('%sonly one of properties ''%s'' and ''%s'' can be nonempty.', ...
              tag, proppair{p,1}, proppair{p,2});
      end
    end
  end
else
  for p= 1:size(proppair,1),
    if ~isdefault.(proppair{p,1}) && ~isdefault.(proppair{p,2}),
      error('%sonly one of properties ''%s'' and ''%s'' can be set.', ...
            tag, proppair{p,1}, proppair{p,2});
    end
  end
end


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

%%
clear; close all; clc;

%% Syntax
% |[P]=tesSmooth_HC(TES,V,IND_V,cPar);|

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


classdef slaveRef < int32

methods

function H = slaveRef(data, hidx)
  global state;

  if (~isfield(state, 'refs'))
    state.refs=cell(1,10);
  end
  if (~isfield(state, 'refs_occupancy'))
    state.refs_occupancy=zeros(1,10);
  end

  if (nargin < 2)
    I=find(state.refs_occupancy == 0);
    if (length(I) == 0)
      hidx = int32(length(state.refs)+1);
    else
      hidx = int32(I(1));
    end
  end

  % generate ref
  H = H@int32(hidx);

  if (nargin < 2)
    % store to global repo
    state.refs{hidx} = data;
    state.refs_occupancy(hidx) = 1;
  end
end

function v = getValue(H)
  global state;
  if (state.refs_occupancy(int32(H)) == 0)
    error('Dereferencing invalid slaveRef.');
  end
  v = state.refs{int32(H)};
end

end % end methods

methods (Static)

function outvar = dereferenceVar(var)

  type=class(var);
  switch (type)
   case 'struct'
    if (length(var) > 1)
      % struct array
      for i=1:length(var)
        outvar(i) = slaveRef.dereferenceVar(var(i));
      end
    else
      % single struct
      fields=fieldnames(var);
      for i=1:length(fields)
        outvar.(fields{i}) = slaveRef.dereferenceVar(var.(fields{i}));
      end
    end

   case 'cell'
    % cell array
    for i=1:length(var)
      outvar{i} = slaveRef.dereferenceVar(var{i});
    end

   case 'slaveRef'
    outvar = var.getValue();

   otherwise
    outvar=var;
  end
end

end % end methods (Static)

end


% MEEG Object
%
% Functionalities of the MEEG object are described in the SPM manual,
% Chapter 13, Section 7.
% _________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: Contents.m 2696 2009-02-05 20:29:48Z guillaume $


function ct = ChannelTag(scnl)
   %ChannelTag Converter from scnlobject to ChannelTag
  ct = reshape([scnl.tag],size(scnl));
end


function [metabolite_structure,IDsAdded] = parseBiggID4VMH(metabolite_structure,startSearch,endSearch,grebMoreIDs)
% the problem is that by chance Bigg and VMH could have the same ID but for
% different metabolites -- I do not do any additional checks right now
% which is dangerous (hence I do not greb more ID's by default)

if ~exist('grebMoreIDs','var')
    grebMoreIDs = 0;
end

% try to guess bigg ID from VMH ID
Mets = fieldnames(metabolite_structure);
if ~exist('startSearch','var')
    startSearch = 1;
end
if ~exist('endSearch','var')
    endSearch = length(Mets);
end

annotationSource = 'Bigg website (matching)';
annotationType = 'automatic';

a = 1;
IDsAdded = '';

mapping = {
    'inchiKey'     'https://identifiers.org/inchikey/'
    'hmdb'  'http://identifiers.org/hmdb'
    'metanetx'  'http://identifiers.org/metanetx.chemical'
    'keggId'    'http://identifiers.org/kegg.compound/'
    'biocyc'    'http://identifiers.org/biocyc/'
    'reactome'  'http://identifiers.org/reactome/' % for the moment I greb only the first entry
    'cheBIId'   'http://identifiers.org/chebi/'
    };
for i = startSearch : endSearch

    %get VMHId
    MetsID= metabolite_structure.(Mets{i}).VMHId;
    % try if a valid website exists
    try

        url = strcat('http://bigg.ucsd.edu/universal/metabolites/',MetsID);
        [syst,success] = urlread(url);
        if ~success %no entry found
            MetsID = regexprep(MetsID,'_','__');
            url  = strcat('http://bigg.ucsd.edu/universal/metabolites/',MetsID);
            [syst,success] = urlread(url);
        end

        if success
            metabolite_structure.(Mets{i}).biggId = MetsID;
            metabolite_structure.(Mets{i}).biggId_source = [annotationSource,':',annotationType,':',datestr(now)];
            if  grebMoreIDs == 1
                for k = 1 : size(mapping,1)
                    [metabolite_structure,idNew] = getData(metabolite_structure,syst,Mets{i}, mapping(k,:),IDsAdded);
                    if ~isempty(idNew) && isempty(find(isnan(idNew),1))
                        if isempty(metabolite_structure.(Mets{i}).(mapping{k,1})) || ~isempty(find(isnan(metabolite_structure.(Mets{i}).(mapping{k,1})),1))
                            metabolite_structure.(Mets{i}).(mapping{k,1}) = (idNew);
                            metabolite_structure.(Mets{i}).(strcat(mapping{k,1},'_source')) = [annotationSource,':',annotationType,':',datestr(now)];
                            IDsAdded{a,1} = Mets{i};
                            IDsAdded{a,2} = mapping{k,1};
                            IDsAdded{a,3} = char(idNew);
                            a = a+1;
                        end
                    end
                end
            end

        end
    catch
        continue;
    end
end

function [metabolite_structure,idNew] = getData(metabolite_structure,syst,met, map,IDsAdded)
a = size(IDsAdded,1)+1;
idNew = '';
try
    startvalue=strfind(syst,map{1,2});
    string = syst(startvalue(1):startvalue(1)+200);
    [tok,rem] = strtok(string,'>');
    [tok2,rem2] = strtok(rem,'<');
    idNew = regexprep(tok2,'>','');
    if contains(idNew,'META:')
        idNew = regexprep(idNew,'META:','');
    end
    if contains(idNew,'CHEBI:')
        idNew = regexprep(idNew,'CHEBI:','');
    end
end


function bbci= bbci_save(bbci, data)
%BBCI_SAVE - Save BBCI classifier
%
%Synopsis:
%  BBCI= bbci_save(BBCI, <DATA>)
%
%To get a description on the structures 'bbci', type
%help bbci_calibrate_structures

% 09-2011 Benjamin Blankertz

bbci= bbci_save_setDefaults(bbci);

if nargin<2,
  data= [];
end
BS= bbci.calibrate.save;

file= fullfile(BS.folder, BS.file);
num= 1;
while ~BS.overwrite && exist([file '.mat'], 'file'),
  num= num + 1;
  file= fullfile(BS.folder, [BS.file, sprintf('%02d',num)]);
end

save(file, '-STRUCT', 'bbci');
msg= sprintf('Classifier saved to <%s.mat>', file);

if ~isempty(data),
  if ~BS.raw_data,
    data= rmfield(data, {'cnt','mrk','mnt'});
  end
  if isequal(BS.data, 'separately'),
    save([file '_data'], '-STRUCT', 'data');
    msg= [msg ' and data separately to *_data.mat'];
  else
    save(file, 'data', '-APPEND');
  end
end

if BS.figures,
  if ~isfield(data, 'figure_handles'),
    data.figure_handles= sort(findobj('Type','figure'));
  end
  fig_folder= strcat(file, '_figures');
  if ~exist(fig_folder, 'dir'),
    mkdir(fig_folder);
  end
  for ff= data.figure_handles(:)',
    figure(ff);
    fig_name= strrep(get(ff,'Name'), ' ', '_');
    if ispc,
      fig_name= strrep(fig_name, '.', '_');
    end
    filename= fullfile(fig_folder, sprintf('Fig-%02d_%s', ff, fig_name));
    util_printFigure(filename, BS.figures_spec{:});
  end
  msg= [msg ' and figures in subfolder _figures'];
end
fprintf('%s.\n', msg);


function applyFcn= misc_getApplyFunc(model)
%applyFcn= misc_getApplyFunc(model)

if isstruct(model),
  func= misc_getFuncParam(model.classy);
else
  func= misc_getFuncParam(model);
end

trainFcnName= func2str(func);
if length(trainFcnName)<7 || ~strncmp('train_', trainFcnName, 6),
  error('names of classifier functions need to be prefixed by ''train_''.');
end

baseName= trainFcnName(7:end);
applyFcnName= ['apply_' baseName];
if exist(applyFcnName, 'file'),
  applyFcn= str2func(applyFcnName);
else
  applyFcn= @apply_separatingHyperplane;
end


function f = resolve_function(expr)
% resolve a scoped function, which is normally not possible in MATLAB

% thus, we have to use a crazy trick: arg_report gives us access to nested functions
ofs = strfind(expr,'::@');
f = arg_report('handle',str2func(expr(1:ofs-1)),{expr(ofs+3:end)});
if iscell(f)
    if isempty(f)
        error(['No function corresponding to the expression ' expr ' was found.']);
    else
        if length(f) > 1
            warnonce(['There are multiple functions with the name ' char(f{1}) '; returning the first one.']); end
        f = f{1};
    end
end


function dx = f_Wrapper(u,x,p,parIdx)
% dx = f(u,x,p)
% parIdx: index of the core (for reentrant purpose)

    if coder.target('MATLAB')
        % Specify your own f(u,x,p) function for normal excution

    else
        % Specify your own f(u,x,p) function for code generation

    end

end