PageRenderTime 31ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/toolboxes/fieldtrip/compat/ft_databrowser_old.m

http://brainstream.googlecode.com/
MATLAB | 1372 lines | 964 code | 150 blank | 258 comment | 167 complexity | 8c5e20aefc3c84f4c96a1b8277380c48 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.0, GPL-3.0, GPL-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause

Large files files are truncated, but you can click here to view the full file

  1. function [cfg] = ft_databrowser_old(cfg, data)
  2. % FT_DATABROWSER can be used for visual inspection of data. Artifacts that were detected
  3. % by artifact functions (see FT_ARTIFACT_xxx functions where xxx is the type of artifact)
  4. % are marked. Additionally data pieces can be marked and unmarked as artifact by
  5. % manual selection. The output cfg contains the updated artifactdef field.
  6. %
  7. % Use as
  8. % cfg = ft_databrowser(cfg)
  9. % required configuration options:
  10. % cfg.dataset or both cfg.headerfile and cfg.datafile
  11. % or as
  12. % cfg = ft_databrowser(cfg, data)
  13. % with the data as obtained from FT_PREPROCESSING
  14. %
  15. % The following configuration options are supported:
  16. % cfg.trl = structure that defines the data segments of interest. See FT_DEFINETRIAL
  17. % cfg.continuous = 'yes' or 'no' wh ether the file contains continuous data
  18. % cfg.channel = cell-array with channel labels, see FT_CHANNELSELECTION
  19. % cfg.comps = a vector with the components to plot (ex. 1:10) (optional)
  20. % cfg.zscale = [zmin zmax] or 'auto' (default = 'auto')
  21. % cfg.blocksize = number (in seconds), only aplicable if data contains only 1 (long) trial
  22. % cfg.viewmode = string, 'butterfly', 'vertical', 'component' (default = 'butterfly')
  23. % cfg.artfctdef.xxx.artifact = Nx2 matrix with artifact segments see FT_ARTIFACT_xxx functions
  24. % cfg.selectfeature = string, name of feature to be selected/added (default = 'visual')
  25. % cfg.selectmode = string, what to do with a selection, can be 'mark', or 'eval' (default = 'mark')
  26. % 'mark': artfctdef field is updated, 'eval': the function defined in
  27. % cfg.selfun is evaluated f.i. browse_movieplotER calls movieplotER which makes
  28. % a movie of the selected data
  29. % cfg.colorgroups = 'sequential' 'labelcharx' (x = xth character in label), 'chantype' or
  30. % vector with length(data/hdr.label) defining groups (default = 'sequential')
  31. % cfg.channelcolormap = COLORMAP (default = customized lines map with 15 colors)
  32. % cfg.selfun = string, name of function which is evaluated if selectmode is set to 'eval'.
  33. % The selected data and the selcfg are passed on to this function.
  34. % cfg.selcfg = configuration options for selfun
  35. % cfg.eegscale = number, scaling to apply to the EEG channels prior to display
  36. % cfg.eogscale = number, scaling to apply to the EOG channels prior to display
  37. % cfg.ecgscale = number, scaling to apply to the ECG channels prior to display
  38. % cfg.emgscale = number, scaling to apply to the EMG channels prior to display
  39. % cfg.megscale = number, scaling to apply to the MEG channels prior to display
  40. %
  41. % The "artifact" field in the output cfg is a Nx2 matrix comparable to the
  42. % "trl" matrix of FT_DEFINETRIAL. The first column of which specifying the
  43. % beginsamples of an artifact period, the second column contains the
  44. % endsamples of the artifactperiods.
  45. %
  46. % To facilitate data-handling and distributed computing with the peer-to-peer
  47. % module, this function has the following options:
  48. % cfg.inputfile = ...
  49. % cfg.outputfile = ...
  50. % If you specify one of these (or both) the input data will be read from a *.mat
  51. % file on disk and/or the output data will be written to a *.mat file.
  52. % These mat
  53. % files should contain only a single variable, corresponding with the
  54. % input/output structure.
  55. %
  56. % NOTE for debugging: in case the databrowser crashes, use delete(gcf) to kill the figure.
  57. %
  58. % See also FT_PREPROCESSING, FT_REJECTARTIFACT, FT_ARTIFACT_EOG, FT_ARTIFACT_MUSCLE,
  59. % FT_ARTIFACT_JUMP, FT_ARTIFACT_MANUAL, FT_ARTIFACT_THRESHOLD, FT_ARTIFACT_CLIP, FT_ARTIFACT_ECG
  60. % Copyright (C) 2009, Robert Oostenveld, Ingrid Niewenhuis
  61. %
  62. % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
  63. % for the documentation and details.
  64. %
  65. % FieldTrip is free software: you can redistribute it and/or modify
  66. % it under the terms of the GNU General Public License as published by
  67. % the Free Software Foundation, either version 3 of the License, or
  68. % (at your option) any later version.
  69. %
  70. % FieldTrip is distributed in the hope that it will be useful,
  71. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  72. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  73. % GNU General Public License for more details.
  74. %
  75. % You should have received a copy of the GNU General Public License
  76. % along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
  77. %
  78. % $Id: ft_databrowser_old.m 4619 2011-10-28 14:16:06Z roboos $
  79. ft_defaults
  80. % record start time and total processing time
  81. ftFuncTimer = tic();
  82. ftFuncClock = clock();
  83. ftFuncMem = memtic();
  84. % set defaults for optional cfg.input and or cfg.outputfile
  85. if ~isfield(cfg, 'inputfile'), cfg.inputfile = []; end
  86. if ~isfield(cfg, 'outputfile'), cfg.outputfile = []; end
  87. % set the defaults
  88. if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end
  89. if ~isfield(cfg, 'zscale'), cfg.zscale = 'auto'; end
  90. if ~isfield(cfg, 'artfctdef'), cfg.artfctdef = struct; end
  91. if ~isfield(cfg, 'selectfeature'), cfg.selectfeature = 'visual'; end % string or cell-array
  92. if ~isfield(cfg, 'selectmode'), cfg.selectmode = 'mark'; end
  93. if ~isfield(cfg, 'viewmode'), cfg.viewmode = 'butterfly'; end % butterfly, vertical, component, settings
  94. if ~isfield(cfg, 'blocksize'), cfg.blocksize = 1; end % only for segmenting continuous data, i.e. one long trial
  95. if ~isfield(cfg, 'preproc'), cfg.preproc = []; end % see preproc for options
  96. if ~isfield(cfg, 'event'), cfg.event = []; end
  97. if ~isfield(cfg, 'selfun'), cfg.selfun = 'browse_multiplotER'; end
  98. if ~isfield(cfg, 'selcfg'), cfg.selcfg = []; end
  99. if ~isfield(cfg, 'colorgroups'), cfg.colorgroups = 'sequential'; end
  100. if ~isfield(cfg, 'channelcolormap'), cfg.channelcolormap = [0.75 0 0;0 0 1;0 1 0;0.44 0.19 0.63;0 0.13 0.38;0.5 0.5 0.5;1 0.75 0;1 0 0;0.89 0.42 0.04;0.85 0.59 0.58;0.57 0.82 0.31;0 0.69 0.94;1 0 0.4;0 0.69 0.31;0 0.44 0.75]; end
  101. if ~isfield(cfg, 'eegscale'), cfg.eegscale = []; end
  102. if ~isfield(cfg, 'eogscale'), cfg.eogscale = []; end
  103. if ~isfield(cfg, 'ecgscale'), cfg.ecgscale = []; end
  104. if ~isfield(cfg, 'emgscale'), cfg.emgscale = []; end
  105. if ~isfield(cfg, 'megscale'), cfg.megscale = []; end
  106. hasdata = (nargin>1);
  107. if ~isempty(cfg.inputfile)
  108. % the input data should be read from file
  109. if hasdata
  110. error('cfg.inputfile should not be used in conjunction with giving input data to this function');
  111. else
  112. data = loadvar(cfg.inputfile, 'data');
  113. hasdata = true;
  114. end
  115. end
  116. if hasdata
  117. data = ft_checkdata(data, 'datatype', {'raw', 'comp'}, 'feedback', 'yes', 'hassampleinfo', 'yes');
  118. % fetch the header from memory
  119. hdr = ft_fetch_header(data);
  120. if ~isfield(cfg, 'continuous') && length(data.trial) == 1
  121. cfg.continuous = 'yes';
  122. elseif ~isfield(cfg, 'continuous') && length(data.trial) > 1
  123. cfg.continuous = 'no';
  124. end
  125. else
  126. % check if the input cfg is valid for this function
  127. cfg = ft_checkconfig(cfg, 'dataset2files', {'yes'});
  128. cfg = ft_checkconfig(cfg, 'required', {'headerfile', 'datafile'});
  129. cfg = ft_checkconfig(cfg, 'renamed', {'datatype', 'continuous'});
  130. cfg = ft_checkconfig(cfg, 'renamedval', {'continuous', 'continuous', 'yes'});
  131. % read the header from file
  132. hdr = ft_read_header(cfg.headerfile, 'headerformat', cfg.headerformat);
  133. if ~isfield(cfg, 'continuous')
  134. if hdr.nTrials==1
  135. cfg.continuous = 'yes';
  136. else
  137. cfg.continuous = 'no';
  138. end
  139. end
  140. end
  141. if hasdata && isfield(data, 'topo') && strcmp(cfg.viewmode, 'component')
  142. if ~isfield(cfg, 'comp')
  143. cfg.comp = 1:10; % to avoid plotting 274 components topographically
  144. end
  145. cfg.channel = data.label(cfg.comp);
  146. end
  147. if ischar(cfg.selectfeature)
  148. % ensure that it is a cell array
  149. cfg.selectfeature = {cfg.selectfeature};
  150. end
  151. % get some initial parameters from the data
  152. if hasdata
  153. % check whether data has been resampled
  154. if isfield(data, 'cfg') && isempty(ft_findcfg(data.cfg, 'origfs'))
  155. resampled = false;
  156. else
  157. resampled = true;
  158. end
  159. % fetch the events
  160. event = ft_fetch_event(data);
  161. cfg.channel = ft_channelselection(cfg.channel, data.label);
  162. chansel = match_str(data.label, cfg.channel);
  163. fsample = 1/(data.time{1}(2)-data.time{1}(1));
  164. Nchans = length(chansel);
  165. % this is how the input data is segmented
  166. trlorg = zeros(numel(data.trial), 3);
  167. trlorg(:,[1 2]) = data.sampleinfo;
  168. % recreate offset vector (databrowser depends on this for visualisation)
  169. for ntrl = 1:numel(data.trial)
  170. trlorg(ntrl,3) = time2offset(data.time{ntrl}, data.fsample);
  171. end
  172. Ntrials = size(trlorg, 1);
  173. if strcmp(cfg.viewmode, 'component')
  174. if ~isfield(cfg, 'layout')
  175. error('You need to specify a layout-file when browsing through components');
  176. end
  177. % read or create the layout that will be used for the topoplots
  178. cfg.layout = ft_prepare_layout(cfg, data);
  179. if ~isfield(cfg, 'comp')
  180. cfg.comp = 1:10; % to avoid plotting 274 components topographically
  181. end
  182. cfg.channel = data.label(cfg.comp);
  183. end
  184. else
  185. % data has not been resampled
  186. resampled = false;
  187. % read the events
  188. if isempty(cfg.event)
  189. event = ft_read_event(cfg.dataset);
  190. else
  191. event = cfg.event;
  192. end
  193. cfg.channel = ft_channelselection(cfg.channel, hdr.label);
  194. chansel = match_str(hdr.label, cfg.channel);
  195. fsample = hdr.Fs;
  196. Nchans = length(chansel);
  197. Ntrials = hdr.nTrials;
  198. % construct trl-matrix for data from file on disk
  199. trlorg = zeros(Ntrials,3);
  200. for k = 1:Ntrials
  201. trlorg(k,[1 2]) = [1 hdr.nSamples] + [hdr.nSamples hdr.nSamples] .* (k-1);
  202. end
  203. end % if hasdata
  204. if Nchans == 0
  205. error('no channels to display');
  206. end
  207. if Ntrials == 0
  208. error('no trials to display');
  209. end
  210. % determine coloring of channels
  211. if hasdata
  212. labels_all = data.label;
  213. else
  214. labels_all= hdr.label;
  215. end
  216. if size(cfg.channelcolormap,2) ~= 3
  217. error('cfg.channelcolormap is not valid, size should be Nx3')
  218. end
  219. if isnumeric(cfg.colorgroups)
  220. % groups defined by user
  221. if length(labels_all) ~= length(cfg.colorgroups)
  222. error('length(cfg.colorgroups) should be length(data/hdr.label)')
  223. end
  224. R = cfg.channelcolormap(:,1);
  225. G = cfg.channelcolormap(:,2);
  226. B = cfg.channelcolormap(:,3);
  227. chan_colors = [R(cfg.colorgroups(:)) G(cfg.colorgroups(:)) B(cfg.colorgroups(:))];
  228. elseif strcmp(cfg.colorgroups, 'chantype')
  229. type = ft_chantype(labels_all);
  230. [tmp1 tmp2 cfg.colorgroups] = unique(type);
  231. fprintf('%3d colorgroups were identified\n',length(tmp1))
  232. R = cfg.channelcolormap(:,1);
  233. G = cfg.channelcolormap(:,2);
  234. B = cfg.channelcolormap(:,3);
  235. chan_colors = [R(cfg.colorgroups(:)) G(cfg.colorgroups(:)) B(cfg.colorgroups(:))];
  236. elseif strcmp(cfg.colorgroups(1:9), 'labelchar')
  237. % groups determined by xth letter of label
  238. labelchar_num = str2double(cfg.colorgroups(10));
  239. vec_letters = num2str(zeros(length(labels_all),1));
  240. for iChan = 1:length(labels_all)
  241. vec_letters(iChan) = labels_all{iChan}(labelchar_num);
  242. end
  243. [tmp1 tmp2 cfg.colorgroups] = unique(vec_letters);
  244. fprintf('%3d colorgroups were identified\n',length(tmp1))
  245. R = cfg.channelcolormap(:,1);
  246. G = cfg.channelcolormap(:,2);
  247. B = cfg.channelcolormap(:,3);
  248. chan_colors = [R(cfg.colorgroups(:)) G(cfg.colorgroups(:)) B(cfg.colorgroups(:))];
  249. elseif strcmp(cfg.colorgroups, 'sequential')
  250. % no grouping
  251. chan_colors = lines(length(labels_all));
  252. else
  253. error('do not understand cfg.colorgroups')
  254. end
  255. % collect the artifacts that have been detected from cfg.artfctdef.xxx.artifact
  256. artlabel = fieldnames(cfg.artfctdef);
  257. sel = zeros(size(artlabel));
  258. artifact = cell(size(artlabel));
  259. for i=1:length(artlabel)
  260. sel(i) = issubfield(cfg.artfctdef, [artlabel{i} '.artifact']);
  261. if sel(i)
  262. artifact{i} = getsubfield(cfg.artfctdef, [artlabel{i} '.artifact']);
  263. num = size(artifact{i}, 1);
  264. if isempty(num)
  265. num = 0;
  266. end
  267. fprintf('detected %3d %s artifacts\n', num, artlabel{i});
  268. end
  269. end
  270. artifact = artifact(sel==1);
  271. artlabel = artlabel(sel==1);
  272. for i=1:length(cfg.selectfeature)
  273. if ~any(strcmp(cfg.selectfeature{i}, artlabel))
  274. artifact = {[], artifact{:}};
  275. artlabel = {cfg.selectfeature{i}, artlabel{:}};
  276. end
  277. end
  278. if length(artlabel) > 9
  279. error('only upto 9 artifacts groups supported')
  280. end
  281. % make artdata representing all artifacts in a "raw data" format
  282. datendsample = max(trlorg(:,2));
  283. artdat = convert_event(artifact, 'boolvec', 'endsample', datendsample);
  284. artdata = [];
  285. artdata.trial{1} = artdat; % every artifact is a "channel"
  286. artdata.time{1} = offset2time(0, fsample, datendsample);
  287. artdata.label = artlabel;
  288. artdata.fsample = fsample;
  289. artdata.cfg.trl = [1 datendsample 0];
  290. if ischar(cfg.zscale) && strcmp(cfg.zscale, 'auto')
  291. if nargin>1
  292. dat = data.trial{1}(chansel,:);
  293. time = data.time{1};
  294. else
  295. % one second of data is read from file to determine the vertical scaling
  296. dat = ft_read_data(cfg.datafile, 'header', hdr, 'begsample', 1, 'endsample', round(hdr.Fs), 'chanindx', chansel, 'checkboundary', strcmp(cfg.continuous, 'no'), 'dataformat', cfg.dataformat, 'headerformat', cfg.headerformat);
  297. time = (1:hdr.nSamples) / fsample;
  298. end
  299. minval = min(dat(:));
  300. maxval = max(dat(:));
  301. mintime = min(time(:));
  302. maxtime = max(time(:));
  303. cfg.zscale = max(abs(minval), abs(maxval));
  304. cfg.yscale = max(abs(mintime), abs(maxtime));
  305. end
  306. h = figure;
  307. set(h, 'KeyPressFcn', @keyboard_cb);
  308. set(h, 'WindowButtonDownFcn', {@ft_select_range, 'multiple', false, 'xrange', true, 'yrange', false, 'clear', true, 'callback', {@select_range_cb, h}, 'event', 'WindowButtonDownFcn'});
  309. set(h, 'WindowButtonUpFcn', {@ft_select_range, 'multiple', false, 'xrange', true, 'yrange', false, 'clear', true, 'callback', {@select_range_cb, h}, 'event', 'WindowButtonUpFcn'});
  310. set(h, 'WindowButtonMotionFcn', {@ft_select_range, 'multiple', false, 'xrange', true, 'yrange', false, 'clear', true, 'callback', {@select_range_cb, h}, 'event', 'WindowButtonMotionFcn'});
  311. % opt represents the global data/settings, it should contain
  312. % - the original data, epoched or continuous
  313. % - the artifacts represented as continuous data
  314. % - the redraw_cb settings
  315. % - the preproc settings
  316. % - the select_range_cb settings (also used in keyboard_cb)
  317. % these elements are stored inside the figure so that the callback routines can modify them
  318. opt = [];
  319. if hasdata
  320. opt.orgdata = data;
  321. else
  322. opt.orgdata = []; % this means that it will look in opt.cfg.dataset
  323. end
  324. opt.artdata = artdata;
  325. opt.cfg = cfg; % the configuration of this function, not of the preprocessing
  326. opt.hdr = hdr;
  327. opt.event = event;
  328. opt.trlop = 1; % active trial being displayed
  329. opt.ftsel = find(strcmp(artlabel,cfg.selectfeature)); % current artifact/feature being selected
  330. opt.trlorg = trlorg;
  331. opt.fsample = fsample;
  332. opt.artcol = [0.9686 0.7608 0.7686; 0.7529 0.7098 0.9647; 0.7373 0.9725 0.6824;0.8118 0.8118 0.8118; 0.9725 0.6745 0.4784; 0.9765 0.9176 0.5686; 0.6863 1 1; 1 0.6863 1; 0 1 0.6000];
  333. opt.chan_colors = chan_colors;
  334. opt.cleanup = false; % this is needed for a corrent handling if the figure is closed (either in the corner or by "q")
  335. opt.compindx = []; % index of components to be drawn (if viewmode = "component")
  336. opt.resampled = resampled;
  337. if strcmp(cfg.continuous, 'yes')
  338. opt.trialname = 'segment';
  339. else
  340. opt.trialname = 'trial';
  341. end
  342. guidata(h, opt);
  343. % make the user interface elements for the data view
  344. uicontrol('tag', 'group1', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', opt.trialname, 'userdata', 't')
  345. uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'leftarrow')
  346. uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'rightarrow')
  347. uicontrol('tag', 'group1', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'channel','userdata', 'c')
  348. uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', 'uparrow')
  349. uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', 'downarrow')
  350. uicontrol('tag', 'group1a', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'horizontal', 'userdata', 'h')
  351. uicontrol('tag', 'group2a', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'shift+leftarrow')
  352. uicontrol('tag', 'group2a', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+rightarrow')
  353. if strcmp(cfg.continuous, 'no')
  354. ft_uilayout(h, 'tag', 'group1a', 'visible', 'on', 'retag', 'group1');
  355. ft_uilayout(h, 'tag', 'group2a', 'visible', 'on', 'retag', 'group2');
  356. else
  357. ft_uilayout(h, 'tag', 'group1a', 'visible', 'on', 'retag', 'group1');
  358. ft_uilayout(h, 'tag', 'group2a', 'visible', 'on', 'retag', 'group2');
  359. end
  360. uicontrol('tag', 'group1', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'vertical', 'userdata', 'v')
  361. uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '-', 'userdata', 'shift+downarrow')
  362. uicontrol('tag', 'group2', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '+', 'userdata', 'shift+uparrow')
  363. % legend artifacts/features
  364. for iArt = 1:length(artlabel)
  365. uicontrol('tag', 'group3', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', artlabel{iArt}, 'userdata', num2str(iArt), 'position', [0.91, 0.9 - ((iArt-1)*0.09), 0.08, 0.04], 'backgroundcolor', opt.artcol(iArt,:))
  366. uicontrol('tag', 'group3', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '<', 'userdata', ['shift+' num2str(iArt)], 'position', [0.91, 0.85 - ((iArt-1)*0.09), 0.03, 0.04], 'backgroundcolor', opt.artcol(iArt,:))
  367. uicontrol('tag', 'group3', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', '>', 'userdata', ['control+' num2str(iArt)], 'position', [0.96, 0.85 - ((iArt-1)*0.09), 0.03, 0.04], 'backgroundcolor', opt.artcol(iArt,:))
  368. end
  369. if strcmp(cfg.viewmode, 'butterfly')
  370. % button to find label of nearest channel to datapoint
  371. uicontrol('tag', 'group3', 'parent', h, 'units', 'normalized', 'style', 'pushbutton', 'string', 'identify', 'userdata', 'i', 'position', [0.91, 0.1, 0.08, 0.05], 'backgroundcolor', [1 1 1])
  372. end
  373. ft_uilayout(h, 'tag', 'group1', 'width', 0.10, 'height', 0.05);
  374. ft_uilayout(h, 'tag', 'group2', 'width', 0.05, 'height', 0.05);
  375. ft_uilayout(h, 'tag', 'group1', 'style', 'pushbutton', 'callback', @keyboard_cb);
  376. ft_uilayout(h, 'tag', 'group2', 'style', 'pushbutton', 'callback', @keyboard_cb);
  377. ft_uilayout(h, 'tag', 'group3', 'style', 'pushbutton', 'callback', @keyboard_cb);
  378. ft_uilayout(h, 'tag', 'group1', 'retag', 'viewui');
  379. ft_uilayout(h, 'tag', 'group2', 'retag', 'viewui');
  380. ft_uilayout(h, 'tag', 'viewui', 'BackgroundColor', [0.8 0.8 0.8], 'hpos', 'auto', 'vpos', 0);
  381. definetrial_cb(h);
  382. redraw_cb(h);
  383. % %% Scrollbar
  384. %
  385. % % set initial scrollbar value
  386. % dx = maxtime;
  387. %
  388. % % set scrollbar position
  389. % fig_pos=get(gca,'position');
  390. % scroll_pos=[fig_pos(1) fig_pos(2) fig_pos(3) 0.02];
  391. %
  392. % % define callback
  393. % S=['set(gca,''xlim'',get(gcbo,''value'')+[ ' num2str(mintime) ',' num2str(maxtime) '])'];
  394. %
  395. % % Creating Uicontrol
  396. % s=uicontrol('style','slider',...
  397. % 'units','normalized','position',scroll_pos,...
  398. % 'callback',S,'min',0,'max',0, ...
  399. % 'visible', 'off'); %'value', xmin
  400. % set initial scrollbar value
  401. % dx = maxtime;
  402. %
  403. % % set scrollbar position
  404. % fig_pos=get(gca,'position');
  405. % scroll_pos=[fig_pos(1) fig_pos(2) fig_pos(3) 0.02];
  406. %
  407. % % define callback
  408. % S=['set(gca,''xlim'',get(gcbo,''value'')+[ ' num2str(mintime) ',' num2str(maxtime) '])'];
  409. %
  410. % % Creating Uicontrol
  411. % s=uicontrol('style','slider',...
  412. % 'units','normalized','position',scroll_pos,...
  413. % 'callback',S,'min',0,'max',0, ...
  414. % 'visible', 'off'); %'value', xmin
  415. %initialize postion of plot
  416. % set(gca,'xlim',[xmin xmin+dx]);
  417. if nargout
  418. % wait until the user interface is closed, get the user data with the updated artifact details
  419. set(h, 'CloseRequestFcn', @cleanup_cb);
  420. while ishandle(h)
  421. uiwait(h);
  422. opt = guidata(h);
  423. if opt.cleanup
  424. delete(h);
  425. end
  426. end
  427. % add the updated artifact definitions to the output cfg
  428. for i=1:length(opt.artdata.label)
  429. cfg.artfctdef.(opt.artdata.label{i}).artifact = convert_event(opt.artdata.trial{1}(i,:), 'artifact');
  430. end
  431. end % if nargout
  432. % add version information to the configuration
  433. cfg.version.name = mfilename('fullpath');
  434. cfg.version.id = '$Id: ft_databrowser_old.m 4619 2011-10-28 14:16:06Z roboos $';
  435. % add information about the Matlab version used to the configuration
  436. cfg.callinfo.matlab = version();
  437. % add information about the function call to the configuration
  438. cfg.callinfo.proctime = toc(ftFuncTimer);
  439. cfg.callinfo.procmem = memtoc(ftFuncMem);
  440. cfg.callinfo.calltime = ftFuncClock;
  441. cfg.callinfo.user = getusername();
  442. fprintf('the call to "%s" took %d seconds and an estimated %d MB\n', mfilename, round(cfg.callinfo.proctime), round(cfg.callinfo.procmem/(1024*1024)));
  443. % remember the configuration details of the input data
  444. if hasdata && isfield(data, 'cfg')
  445. cfg.previous = data.cfg;
  446. end
  447. % remember the exact configuration details in the output
  448. dataout.cfg = cfg;
  449. % the output data should be saved to a MATLAB file
  450. if ~isempty(cfg.outputfile)
  451. savevar(cfg.outputfile, 'data', dataout); % use the variable name "data" in the output file
  452. end
  453. end % main function
  454. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  455. % SUBFUNCTION
  456. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  457. function cleanup_cb(h, eventdata)
  458. opt = guidata(h);
  459. opt.cleanup = true;
  460. guidata(h, opt);
  461. uiresume
  462. end
  463. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  464. % SUBFUNCTION
  465. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  466. function definetrial_cb(h, eventdata)
  467. opt = guidata(h);
  468. if strcmp(opt.cfg.continuous, 'no')
  469. % keep the original trial definition for visualisation
  470. opt.trlvis = opt.trlorg;
  471. else
  472. % construct a trial definition for visualisation
  473. if isfield(opt, 'trlvis')
  474. thistrlbeg = opt.trlvis(opt.trlop,1);
  475. thistrlend = opt.trlvis(opt.trlop,2);
  476. % remember a representative sample of the current trial
  477. % thissample = round((thistrlbeg+thistrlend)/2);
  478. thissample = thistrlbeg;
  479. end
  480. % look at opt.cfg.blocksize and make opt.trl accordingly
  481. % if original data contains more than one trial, it will fail in ft_fetch_data
  482. datbegsample = min(opt.trlorg(:,1));
  483. datendsample = max(opt.trlorg(:,2));
  484. smppertrl = round(opt.fsample * opt.cfg.blocksize);
  485. begsamples = datbegsample:smppertrl:datendsample;
  486. endsamples = datbegsample+smppertrl-1:smppertrl:datendsample;
  487. if numel(endsamples)<numel(begsamples)
  488. endsamples(end+1) = datendsample;
  489. end
  490. trlvis = [];
  491. trlvis(:,1) = begsamples';
  492. trlvis(:,2) = endsamples';
  493. if size(opt.trlorg,1) > 1 || isempty(opt.orgdata)
  494. % offset is now (re)defined that 1st sample is time 0
  495. trlvis(:,3) = begsamples-1;
  496. else
  497. % offset according to original time axis
  498. trlvis(:,3) = opt.trlorg(3) + begsamples - opt.trlorg(1);
  499. end
  500. if isfield(opt, 'trlvis')
  501. % update the current trial counter and try to keep the current sample the same
  502. % opt.trlop = nearest(round((begsamples+endsamples)/2), thissample);
  503. opt.trlop = nearest(begsamples, thissample);
  504. end
  505. opt.trlvis = trlvis;
  506. end % if continuous
  507. guidata(h, opt);
  508. end
  509. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  510. % SUBFUNCTION
  511. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  512. function help_cb(h, eventdata)
  513. fprintf('------------------------------------------------------------------------------------\n')
  514. fprintf('You can use the following buttons in the data viewer\n')
  515. fprintf('1-9 : select artifact type 1-9\n');
  516. fprintf('shift 1-9 : select previous artifact of type 1-9\n');
  517. fprintf(' (does not work with numpad keys)\n');
  518. fprintf('control 1-9 : select next artifact of type 1-9\n');
  519. fprintf('alt 1-9 : select next artifact of type 1-9\n');
  520. fprintf('arrow-left : previous trial\n');
  521. fprintf('arrow-right : next trial\n');
  522. fprintf('shift arrow-up : increase vertical scaling\n');
  523. fprintf('shift arrow-down : decrease vertical scaling\n');
  524. fprintf('shift arrow-left : increase horizontal scaling\n');
  525. fprintf('shift arrow-down : decrease horizontal scaling\n');
  526. fprintf('q : quit\n');
  527. fprintf('------------------------------------------------------------------------------------\n')
  528. fprintf('\n')
  529. end
  530. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  531. % SUBFUNCTION
  532. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  533. function select_range_cb(range, h) %range 1X4 in sec relative to current trial
  534. opt = guidata(h);
  535. switch opt.cfg.viewmode
  536. case 'butterfly'
  537. begsample = opt.trlvis(opt.trlop,1);
  538. endsample = opt.trlvis(opt.trlop,2);
  539. offset = opt.trlvis(opt.trlop,3);
  540. % determine the selection
  541. if strcmp(opt.trialname, 'trial')
  542. % this is appropriate when the offset is defined according to a
  543. % different trigger in each trial, which is usually the case in trial data
  544. begsel = round(range(1)*opt.fsample+begsample-offset-1);
  545. endsel = round(range(2)*opt.fsample+begsample-offset);
  546. elseif strcmp(opt.trialname, 'segment')
  547. % this is appropriate when the offset is defined according to a
  548. % one trigger, which is always the case in segment data [I think ingnie]
  549. begsel = round(range(1)*opt.fsample+1);
  550. endsel = round(range(2)*opt.fsample+1);
  551. end
  552. % the selection should always be confined to the current trial
  553. begsel = max(begsample, begsel);
  554. endsel = min(endsample, endsel);
  555. case {'vertical', 'component'}
  556. % the range should be in the displayed box
  557. range(1) = max(opt.hpos(1), range(1));
  558. range(2) = max(opt.hpos(1), range(2));
  559. range(1) = min(opt.hpos(2), range(1));
  560. range(2) = min(opt.hpos(2), range(2));
  561. range = (range - opt.hpos(1)) / (opt.hpos(2) - opt.hpos(1)); % left side of the box becomes 0, right side becomes 1
  562. range = range * (opt.hlim(2) - opt.hlim(1)) + opt.hlim(1); % 0 becomes hlim(1), 1 becomes hlim(2)
  563. begsample = opt.trlvis(opt.trlop,1);
  564. endsample = opt.trlvis(opt.trlop,2);
  565. offset = opt.trlvis(opt.trlop,3);
  566. % determine the selection
  567. if strcmp(opt.trialname, 'trial')
  568. % this is appropriate when the offset is defined according to a
  569. % different trigger in each trial, which is usually the case in trial data
  570. begsel = round(range(1)*opt.fsample+begsample-offset-1);
  571. endsel = round(range(2)*opt.fsample+begsample-offset);
  572. elseif strcmp(opt.trialname, 'segment')
  573. % this is appropriate when the offset is defined according to
  574. % one trigger, which is always the case in segment data [I think ingnie]
  575. begsel = round(range(1)*opt.fsample+1);
  576. endsel = round(range(2)*opt.fsample+1);
  577. end
  578. % the selection should always be confined to the current trial
  579. begsel = max(begsample, begsel);
  580. endsel = min(endsample, endsel);
  581. otherwise
  582. error('unknown opt.cfg.viewmode "%s"', opt.cfg.viewmode);
  583. end % switch
  584. if strcmp(opt.cfg.selectmode, 'disp')
  585. % FIXME this is only for debugging
  586. disp([begsel endsel])
  587. elseif strcmp(opt.cfg.selectmode, 'mark')
  588. % mark or unmark artifacts
  589. artval = opt.artdata.trial{1}(opt.ftsel, begsel:endsel);
  590. artval = any(artval,1);
  591. if any(artval)
  592. fprintf('there is overlap with the active artifact (%s), disable this artifact\n',opt.artdata.label{opt.ftsel});
  593. opt.artdata.trial{1}(opt.ftsel, begsel:endsel) = 0;
  594. else
  595. fprintf('there is no overlap with the active artifact (%s), mark this as a new artifact\n',opt.artdata.label{opt.ftsel});
  596. opt.artdata.trial{1}(opt.ftsel, begsel:endsel) = 1;
  597. end
  598. elseif strcmp(opt.cfg.selectmode, 'eval')
  599. % cut out the requested data segment
  600. seldata.label = opt.curdat.label;
  601. seldata.time{1} = offset2time(offset+begsel-begsample, opt.fsample, endsel-begsel+1);
  602. seldata.trial{1} = ft_fetch_data(opt.curdat, 'begsample', begsel, 'endsample', endsel);
  603. seldata.fsample = opt.fsample;
  604. seldata.cfg.trl = [begsel endsel offset];
  605. feval(opt.cfg.selfun, opt.cfg.selcfg, seldata);
  606. else
  607. error('unknown value for cfg.selectmode');
  608. end
  609. guidata(h, opt);
  610. redraw_cb(h);
  611. end % function
  612. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  613. % SUBFUNCTION
  614. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  615. function keyboard_cb(h, eventdata)
  616. opt = guidata(h);
  617. if isempty(eventdata)
  618. % determine the key that corresponds to the uicontrol element that was activated
  619. key = get(h, 'userdata');
  620. h = getparent(h);
  621. else
  622. % determine the key that was pressed on the keyboard
  623. key = parseKeyboardEvent(eventdata);
  624. end
  625. switch key
  626. case {'1' '2' '3' '4' '5' '6' '7' '8' '9'}
  627. % switch to another artifact type
  628. opt.ftsel = str2double(key);
  629. numart = size(opt.artdata.trial{1}, 1);
  630. if opt.ftsel > numart
  631. fprintf('data has no artifact type %i \n', opt.ftsel)
  632. else
  633. guidata(h, opt);
  634. fprintf('switching to the "%s" artifact\n', opt.artdata.label{opt.ftsel});
  635. redraw_cb(h, eventdata);
  636. end
  637. case {'shift+1' 'shift+2' 'shift+3' 'shift+4' 'shift+5' 'shift+6' 'shift+7' 'shift+8' 'shift+9'}
  638. % go to previous artifact
  639. opt.ftsel = str2double(key(end));
  640. numart = size(opt.artdata.trial{1}, 1);
  641. if opt.ftsel > numart
  642. fprintf('data has no artifact type %i \n', opt.ftsel)
  643. else
  644. cursam = opt.trlvis(opt.trlop,1);
  645. artsam = find(opt.artdata.trial{1}(opt.ftsel,1:cursam-1), 1, 'last');
  646. if isempty(artsam)
  647. fprintf('no earlier "%s" artifact found\n', opt.artdata.label{opt.ftsel});
  648. else
  649. fprintf('going to previous "%s" artifact\n', opt.artdata.label{opt.ftsel});
  650. if opt.trlvis(nearest(opt.trlvis(:,1),artsam),1) < artsam
  651. arttrl = nearest(opt.trlvis(:,1),artsam);
  652. else
  653. arttrl = nearest(opt.trlvis(:,1),artsam)-1;
  654. end
  655. opt.trlop = arttrl;
  656. guidata(h, opt);
  657. redraw_cb(h, eventdata);
  658. end
  659. end
  660. case {'control+1' 'control+2' 'control+3' 'control+4' 'control+5' 'control+6' 'control+7' 'control+8' 'control+9' 'alt+1' 'alt+2' 'alt+3' 'alt+4' 'alt+5' 'alt+6' 'alt+7' 'alt+8' 'alt+9'}
  661. % go to next artifact
  662. opt.ftsel = str2double(key(end));
  663. numart = size(opt.artdata.trial{1}, 1);
  664. if opt.ftsel > numart
  665. fprintf('data has no artifact type %i \n', opt.ftsel)
  666. else
  667. cursam = opt.trlvis(opt.trlop,2);
  668. artsam = find(opt.artdata.trial{1}(opt.ftsel,cursam+1:end), 1, 'first') + cursam;
  669. if isempty(artsam)
  670. fprintf('no later "%s" artifact found\n', opt.artdata.label{opt.ftsel});
  671. else
  672. fprintf('going to next "%s" artifact\n', opt.artdata.label{opt.ftsel});
  673. if opt.trlvis(nearest(opt.trlvis(:,1),artsam),1) < artsam
  674. arttrl = nearest(opt.trlvis(:,1),artsam);
  675. else
  676. arttrl = nearest(opt.trlvis(:,1),artsam)-1;
  677. end
  678. opt.trlop = arttrl;
  679. guidata(h, opt);
  680. redraw_cb(h, eventdata);
  681. end
  682. end
  683. case 'leftarrow'
  684. opt.trlop = max(opt.trlop - 1, 1); % should not be smaller than 1
  685. guidata(h, opt);
  686. redraw_cb(h, eventdata);
  687. case 'rightarrow'
  688. opt.trlop = min(opt.trlop + 1, size(opt.trlvis,1)); % should not be larger than the number of trials
  689. guidata(h, opt);
  690. redraw_cb(h, eventdata);
  691. case 'uparrow'
  692. chansel = match_str(opt.hdr.label, opt.cfg.channel);
  693. minchan = min(chansel);
  694. numchan = length(chansel);
  695. chansel = minchan - numchan : minchan - 1;
  696. if min(chansel)<1
  697. chansel = chansel - min(chansel) + 1;
  698. end
  699. % convert numeric array into cell-array with channel labels
  700. opt.cfg.channel = opt.hdr.label(chansel);
  701. disp(opt.cfg.channel);
  702. guidata(h, opt);
  703. redraw_cb(h, eventdata);
  704. case 'downarrow'
  705. chansel = match_str(opt.hdr.label, opt.cfg.channel);
  706. maxchan = max(chansel);
  707. numchan = length(chansel);
  708. chansel = maxchan + 1 : maxchan + numchan;
  709. if max(chansel)>length(opt.hdr.label)
  710. chansel = chansel - (max(chansel) - length(opt.hdr.label));
  711. end
  712. % convert numeric array into cell-array with channel labels
  713. opt.cfg.channel = opt.hdr.label(chansel);
  714. disp(opt.cfg.channel);
  715. guidata(h, opt);
  716. redraw_cb(h, eventdata);
  717. case 'shift+leftarrow'
  718. opt.cfg.blocksize = opt.cfg.blocksize*sqrt(2);
  719. disp(opt.cfg.blocksize);
  720. guidata(h, opt);
  721. definetrial_cb(h, eventdata);
  722. redraw_cb(h, eventdata);
  723. case 'shift+rightarrow'
  724. opt.cfg.blocksize = opt.cfg.blocksize/sqrt(2);
  725. disp(opt.cfg.blocksize);
  726. guidata(h, opt);
  727. definetrial_cb(h, eventdata);
  728. redraw_cb(h, eventdata);
  729. case 'shift+uparrow'
  730. opt.cfg.zscale = opt.cfg.zscale/sqrt(2);
  731. guidata(h, opt);
  732. redraw_cb(h, eventdata);
  733. case 'shift+downarrow'
  734. opt.cfg.zscale = opt.cfg.zscale*sqrt(2);
  735. guidata(h, opt);
  736. redraw_cb(h, eventdata);
  737. case 'q'
  738. guidata(h, opt);
  739. cleanup_cb(h);
  740. case 't'
  741. % select the trial to display
  742. response = inputdlg(sprintf('%s to display', opt.trialname), 'specify', 1, {num2str(opt.trlop)});
  743. if ~isempty(response)
  744. opt.trlop = str2double(response);
  745. opt.trlop = min(opt.trlop, size(opt.trlvis,1)); % should not be larger than the number of trials
  746. opt.trlop = max(opt.trlop, 1); % should not be smaller than 1
  747. end
  748. guidata(h, opt);
  749. redraw_cb(h, eventdata);
  750. case 'h'
  751. % select the horizontal scaling
  752. response = inputdlg('horizontal scale', 'specify', 1, {num2str(opt.cfg.blocksize)});
  753. if ~isempty(response)
  754. opt.cfg.blocksize = str2double(response);
  755. end
  756. guidata(h, opt);
  757. definetrial_cb(h, eventdata);
  758. redraw_cb(h, eventdata);
  759. case 'v'
  760. % select the vertical scaling
  761. response = inputdlg('vertical scale, number or ''maxabs'')', 'specify', 1, {num2str(opt.cfg.zscale)});
  762. if ~isempty(response)
  763. if isnan(str2double(response)) && strcmp(response, 'maxabs')
  764. minval = min(opt.curdat.trial{1}(:));
  765. maxval = max(opt.curdat.trial{1}(:));
  766. opt.cfg.zscale = max(abs([minval maxval]));
  767. else
  768. opt.cfg.zscale = str2double(response);
  769. end
  770. end
  771. guidata(h, opt);
  772. redraw_cb(h, eventdata);
  773. case 'c'
  774. % select channels
  775. select = match_str(opt.hdr.label, opt.cfg.channel);
  776. select = select_channel_list(opt.hdr.label, select);
  777. opt.cfg.channel = opt.hdr.label(select);
  778. guidata(h, opt);
  779. redraw_cb(h, eventdata);
  780. case 'i'
  781. if strcmp(opt.cfg.viewmode, 'butterfly')
  782. % click in data and get name of nearest channel
  783. fprintf('click in the figure to identify the name of the closest channel\n');
  784. val = ginput(1);
  785. channame = val2nearestchan(opt.curdat,val);
  786. channb = match_str(opt.curdat.label,channame);
  787. fprintf('channel name: %s\n',channame);
  788. redraw_cb(h, eventdata);
  789. hold on
  790. xtext = opt.cfg.zscale - 0.1*opt.cfg.zscale;
  791. ft_plot_text(val(1), xtext, channame, 'FontSize', 16);
  792. plot(opt.curdat.time{1}, opt.curdat.trial{1}(channb,:),'k','LineWidth',2)
  793. end
  794. case 'control+control'
  795. % do nothing
  796. case 'shift+shift'
  797. % do nothing
  798. case 'alt+alt'
  799. % do nothing
  800. otherwise
  801. guidata(h, opt);
  802. help_cb(h);
  803. end
  804. uiresume(h);
  805. end
  806. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  807. % SUBFUNCTION
  808. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  809. function toggle_viewmode_cb(h, eventdata, varargin)
  810. opt = guidata(getparent(h));
  811. if ~isempty(varargin) && ischar(varargin{1})
  812. opt.cfg.viewmode = varargin{1};
  813. end
  814. guidata(getparent(h), opt);
  815. redraw_cb(h);
  816. end
  817. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  818. % SUBFUNCTION
  819. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  820. function h = getparent(h)
  821. p = h;
  822. while p~=0
  823. h = p;
  824. p = get(h, 'parent');
  825. end
  826. end
  827. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  828. % SUBFUNCTION
  829. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  830. function redraw_cb(h, eventdata)
  831. h = getparent(h);
  832. opt = guidata(h);
  833. figure(h); % ensure that the calling figure is in the front
  834. fprintf('redrawing with viewmode %s\n', opt.cfg.viewmode);
  835. begsample = opt.trlvis(opt.trlop, 1);
  836. endsample = opt.trlvis(opt.trlop, 2);
  837. offset = opt.trlvis(opt.trlop, 3);
  838. chanindx = match_str(opt.hdr.label, opt.cfg.channel);
  839. if ~isempty(opt.event)
  840. % select only the events in the current time window
  841. event = opt.event;
  842. evtsample = [event(:).sample];
  843. event = event(evtsample>=begsample & evtsample<=endsample);
  844. else
  845. event = [];
  846. end
  847. if isempty(opt.orgdata)
  848. fprintf('reading data... ');
  849. dat = ft_read_data(opt.cfg.datafile, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', strcmp(opt.cfg.continuous, 'no'), 'dataformat', opt.cfg.dataformat, 'headerformat', opt.cfg.headerformat);
  850. else
  851. fprintf('fetching data... ');
  852. dat = ft_fetch_data(opt.orgdata, 'header', opt.hdr, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx);
  853. end
  854. fprintf('done\n');
  855. fprintf('fetching artifacts... ');
  856. art = ft_fetch_data(opt.artdata, 'begsample', begsample, 'endsample', endsample);
  857. fprintf('done\n');
  858. % apply preprocessing and determine the time axis
  859. fprintf('preprocessing data... ');
  860. [dat, lab, tim] = preproc(dat, opt.hdr.label(chanindx), opt.fsample, opt.cfg.preproc, offset);
  861. fprintf('done\n');
  862. opt.curdat.label = lab;
  863. opt.curdat.time{1} = tim;
  864. opt.curdat.trial{1} = dat;
  865. opt.curdat.fsample = opt.fsample;
  866. opt.curdat.cfg.trl = [begsample endsample offset];
  867. % apply scaling to selected channels
  868. % using wildcard to support subselection of channels
  869. if ~isempty(opt.cfg.megscale)
  870. chansel = match_str(lab, ft_channelselection('MEG*', lab));
  871. dat(chansel,:) = dat(chansel,:) .* opt.cfg.megscale;
  872. end
  873. if ~isempty(opt.cfg.eegscale)
  874. chansel = match_str(lab, ft_channelselection('EEG*', lab));
  875. dat(chansel,:) = dat(chansel,:) .* opt.cfg.eegscale;
  876. end
  877. if ~isempty(opt.cfg.eogscale)
  878. chansel = match_str(lab, ft_channelselection('EOG*', lab));
  879. dat(chansel,:) = dat(chansel,:) .* opt.cfg.eogscale;
  880. end
  881. if ~isempty(opt.cfg.ecgscale)
  882. chansel = match_str(lab, ft_channelselection('ECG*', lab));
  883. dat(chansel,:) = dat(chansel,:) .* opt.cfg.ecgscale;
  884. end
  885. if ~isempty(opt.cfg.emgscale)
  886. chansel = match_str(lab, ft_channelselection('EMG*', lab));
  887. dat(chansel,:) = dat(chansel,:) .* opt.cfg.emgscale;
  888. end
  889. fprintf('plotting data... ');
  890. switch opt.cfg.viewmode
  891. case 'butterfly'
  892. cla; % clear the content in the current axis
  893. % to assure current feature is plotted on top
  894. ordervec = 1:length(opt.artdata.label);
  895. ordervec(opt.ftsel) = [];
  896. ordervec(end+1) = opt.ftsel;
  897. for i = ordervec
  898. tmp = diff([0 art(i,:) 0]);
  899. artbeg = find(tmp==+1);
  900. artend = find(tmp==-1) - 1;
  901. for j=1:numel(artbeg)
  902. ft_plot_box([tim(artbeg(j)) tim(artend(j)) -opt.cfg.zscale opt.cfg.zscale], 'facecolor', opt.artcol(i,:), 'edgecolor', 'none');
  903. end
  904. end
  905. h_event = zeros(0, length(event));
  906. h_event_txt = zeros(0, length(event));
  907. if ~opt.resampled
  908. try
  909. % plot a line with text for each event
  910. for k=1:length(event)
  911. try
  912. eventstr = sprintf('%s=%s', event(k).type, num2str(event(k).value)); %value can be both number and string
  913. catch
  914. eventstr = 'unknown';
  915. end
  916. eventtim = (event(k).sample-begsample+offset)/opt.fsample;
  917. eventtim = (eventtim - opt.hlim(1)) / (opt.hlim(2) - opt.hlim(1)); % convert to value relative to box, i.e. from 0 to 1
  918. eventtim = eventtim * (opt.hpos(2) - opt.hpos(1)) + opt.hpos(1); % convert from relative to actual value along the horizontal figure axis
  919. h_event(k) = ft_plot_line([eventtim eventtim], [0 1]);
  920. % h_event(k) = ft_plot_line([eventtim eventtim], [-opt.cfg.zscale opt.cfg.zscale]);
  921. h_event_txt(k) = ft_plot_text(eventtim, ax(4)-0.01, eventstr);
  922. end
  923. end % try
  924. else
  925. if isfield(opt, 'orgdata') && isfield(opt.orgdata, 'sampleinfo') && isfield(opt.orgdata, 'offset')
  926. % find trials within this segment
  927. trlindx = find(((opt.orgdata.sampleinfo(:, 1)-opt.orgdata.offset) >= begsample & (opt.orgdata.sampleinfo(:, 1)-opt.orgdata.offset) <= endsample)==1);
  928. for t = 1:numel(trlindx)
  929. eventtim = (opt.orgdata.sampleinfo(trlindx(t), 1)-opt.orgdata.offset(trlindx(t))-begsample+offset)/opt.fsample;
  930. eventtim = (eventtim - opt.hlim(1)) / (opt.hlim(2) - opt.hlim(1)); % convert to value relative to box, i.e. from 0 to 1
  931. eventtim = eventtim * (opt.hpos(2) - opt.hpos(1)) + opt.hpos(1); % convert from relative to actual value along the horizontal figure axis
  932. h_event(end+1) = ft_plot_line([eventtim eventtim], [-opt.cfg.zscale 1]);
  933. h_event_txt(end+1) = ft_plot_text(eventtim, ax(4)+.01, 'stim');
  934. end
  935. end
  936. end
  937. set(h_event, 'tag', 'events');
  938. set(h_event_txt, 'tag', 'events');
  939. set(gca,'ColorOrder',opt.chan_colors(chanindx,:)) % plot vector does not clear axis, therefore this is possible
  940. % plot the data on top of the box
  941. h_act = ft_plot_vector(tim, dat);
  942. set(h_act, 'tag', 'activations');
  943. ax(1) = tim(1);
  944. ax(2) = tim(end);
  945. ax(3) = -opt.cfg.zscale;
  946. ax(4) = opt.cfg.zscale;
  947. axis(ax);
  948. title(sprintf('%s %d, time from %g to %g s', opt.trialname, opt.trlop, tim(1), tim(end)));
  949. xlabel('time');
  950. % set tags
  951. case 'vertical'
  952. cla; % clear the content in the current axis
  953. tmpcfg = [];
  954. tmpcfg.layout = 'vertical';
  955. tmpcfg.channel = opt.cfg.channel;
  956. tmpcfg.skipcomnt = 'yes';
  957. tmpcfg.skipscale = 'yes';
  958. laytime = ft_prepare_layout(tmpcfg, opt.orgdata);
  959. hlim = [tim(1) tim(end)];
  960. vlim = [-opt.cfg.zscale +opt.cfg.zscale];
  961. % determine the position of each of the labels
  962. labelx = laytime.pos(:,1) - laytime.width/2 - 0.01;
  963. labely = laytime.pos(:,2);
  964. ax(1) = min(laytime.pos(:,1) - laytime.width/2);
  965. ax(2) = max(laytime.pos(:,1) + laytime.width/2);
  966. ax(3) = min(laytime.pos(:,2) - laytime.height/2);
  967. ax(4) = max(laytime.pos(:,2) + laytime.height/2);
  968. axis(ax)
  969. % remember the scaling of the horizontal axis, this is needed for mouse input
  970. hpos(1) = laytime.pos(1,1) - laytime.width(1)/2; % the position of the left side of the timecourse box
  971. hpos(2) = laytime.pos(1,1) + laytime.width(1)/2; % the position of the right side of the timecourse box
  972. opt.hlim = hlim;
  973. opt.hpos = hpos;
  974. % to assure current feature is plotted on top
  975. ordervec = 1:length(opt.artdata.label);
  976. ordervec(opt.ftsel) = [];
  977. ordervec(end+1) = opt.ftsel;
  978. for j = ordervec
  979. tmp = diff([0 art(j,:) 0]);
  980. artbeg = find(tmp==+1);
  981. artend = find(tmp==-1) - 1;
  982. arttim = [tim(artbeg)' tim(artend)']; % convert the artifact sample number to time
  983. arttim = (arttim - opt.hlim(1)) / (opt.hlim(2) - opt.hlim(1)); % convert to value relative to box, i.e. from 0 to 1
  984. arttim = arttim * (opt.hpos(2) - opt.hpos(1)) + opt.hpos(1); % convert from relative to actual value along the horizontal figure axis
  985. for k=1:numel(artbeg)
  986. ft_plot_box([arttim(k,1) arttim(k,2) ax(3) ax(4)], 'facecolor', opt.artcol(j,:), 'edgecolor', 'none');
  987. end
  988. end % for each of the artifact channels
  989. h_event = zeros(0, length(event));
  990. h_event_txt = zeros(0, length(event));
  991. if ~opt.resampled
  992. % plot a line with text for each event
  993. for k=1:length(event)
  994. try
  995. eventstr = sprintf('%s=%s', event(k).type, num2str(event(k).value)); %value can be both number and string
  996. catch
  997. eventstr = 'unknown';
  998. end
  999. eventtim = (event(k).sample-begsample+offset)/opt.fsample;
  1000. eventtim = (eventtim - opt.hlim(1)) / (opt.hlim(2) - opt.hlim(1)); % convert to value relative to box, i.e. from 0 to 1
  1001. eventtim = eventtim * (opt.hpos(2) - opt.hpos(1)) + opt.hpos(1); % convert from relative to actual value along the horizontal figure axis
  1002. h_event(k) = ft_plot_line([eventtim eventtim], [0 1]);
  1003. % h_event(k) = ft_plot_line([eventtim eventtim], [-opt.cfg.zscale opt.cfg.zscale]);
  1004. h_event_txt(k) = ft_plot_text(eventtim, ax(4)-0.01, eventstr);
  1005. end
  1006. else
  1007. if isfield(opt, 'orgdata') && isfield(opt.orgdata, 'sampleinfo') && isfield(opt.orgdata, 'offset')
  1008. % find trials within this segment
  1009. trlindx = find(((opt.orgdata.sampleinfo(:, 1)-opt.orgdata.offset) >= begsample & (opt.orgdata.sampleinfo(:, 1)-opt.orgdata.offset) <= endsample)==1);
  1010. for t = 1:numel(trlindx)
  1011. eventtim = (opt.orgdata.sampleinfo(trlindx(t), 1)-opt.orgdata.offset(trlindx(t))-begsample+offset)/opt.fsample;
  1012. eventtim = (eventtim - opt.hlim(1)) / (opt.hlim(2) - opt.hlim(1)); % convert to value relative to box, i.e. from 0 to 1
  1013. eventtim = eventtim * (opt.hpos(2) - opt.hpos(1)) + opt.hpos(1); % convert from relative to actual value along the horizontal figure axis
  1014. h_event(end+1) = ft_plot_line([eventtim eventtim], [-opt.cfg.zscale 1]);
  1015. h_event_txt(end+1) = ft_plot_text(eventtim, ax(4)+.01, 'stim');
  1016. end
  1017. end
  1018. end
  1019. % set tags
  1020. set(h_event, 'tag', 'events');
  1021. set(h_event_txt, 'tag', 'events');
  1022. for i = 1:length(chanindx)
  1023. datsel = i;
  1024. laysel = match_str(laytime.label, opt.hdr.label(chanindx(i)));
  1025. if ~isempty(datsel)
  1026. h_text = ft_plot_text(labelx(laysel), labely(laysel), opt.hdr.label(chanindx(i)), 'HorizontalAlignment', 'right');
  1027. h_act = ft_plot_vector(tim, dat(datsel, :), 'hpos', laytime.pos(laysel,1), 'vpos', laytime.pos(laysel,2), 'width', laytime.width(laysel), 'height', laytime.height(laysel), 'hlim', hlim, 'vlim', vlim, 'box', false, 'color', opt.chan_colors(chanindx(i),:));
  1028. end
  1029. end
  1030. % set tags
  1031. set(h_text, 'tag', 'activations');
  1032. set(h_act, 'tag', 'activations');
  1033. nticks = 11;
  1034. set(gca, 'xTick', linspace(ax(1), ax(2), nticks))
  1035. xTickLabel = cellstr(num2str( linspace(tim(1), tim(end), nticks)' , '%1.2f'))';
  1036. set(gca, 'xTickLabel', xTickLabel)
  1037. set(gca, 'yTick', [])
  1038. if length(chanindx)<7
  1039. % two ticks per channel
  1040. set(gca, 'yTick', sort([laytime.pos(:,2)+(laytime.height(laysel)/2); laytime.pos(:,2)+(laytime.height(laysel)/4); laytime.pos(:,2)-(laytime.height(laysel)/4); laytime.pos(:,2)-(laytime.height(laysel)/2)]))
  1041. yTickLabel = {num2str(-vlim(2)), num2str(-vlim(2)/2), num2str(vlim(2)/2), num2str(vlim(2))};
  1042. elseif length(chanindx)> 6 && length(chanindx)< 20
  1043. % one tick per channel
  1044. set(gca, 'yTick', sort([laytime.pos(:,2)+(laytime.height(laysel)/4); laytime.pos(:,2)-(laytime.height(laysel)/4)]))
  1045. yTickLabel = {num2str(-vlim(2)/2), num2str(vlim(2)/2)};
  1046. else
  1047. % no space for xticks
  1048. yTickLabel = [];
  1049. end
  1050. tmp = yTickLabel;
  1051. for chanloop = 2:length(chanindx)
  1052. yTickLabel = [yTickLabel tmp];
  1053. end
  1054. set(gca, 'yTickLabel', yTickLabel)
  1055. title(sprintf('%s %d, time from %g to %g s', opt.trialname, opt.trlop, tim(1), tim(end)));
  1056. case 'component'
  1057. % delete time courses
  1058. delete(findobj(h,'tag', 'activations'));
  1059. delete(findobj(h,'tag', 'events'));
  1060. delete(findobj(h,'tag', 'artifacts'));
  1061. compindx = chanindx;

Large files files are truncated, but you can click here to view the full file