spm_mesh_render.m 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. function varargout = spm_mesh_render(action,varargin)
  2. % Display a surface mesh & various utilities
  3. % FORMAT H = spm_mesh_render('Disp',M,'PropertyName',propertyvalue)
  4. % M - a GIfTI filename/object or patch structure
  5. % H - structure containing handles of various objects
  6. % Opens a new figure unless a 'parent' Property is provided with an axis
  7. % handle.
  8. %
  9. % FORMAT H = spm_mesh_render(M)
  10. % Shortcut to previous call format.
  11. %
  12. % FORMAT H = spm_mesh_render('ContextMenu',AX)
  13. % AX - axis handle or structure returned by spm_mesh_render('Disp',...)
  14. %
  15. % FORMAT H = spm_mesh_render('Overlay',AX,P)
  16. % AX - axis handle or structure given by spm_mesh_render('Disp',...)
  17. % P - data to be overlayed on mesh (see spm_mesh_project)
  18. %
  19. % FORMAT H = spm_mesh_render('ColourBar',AX,MODE)
  20. % AX - axis handle or structure returned by spm_mesh_render('Disp',...)
  21. % MODE - {['on'],'off'}
  22. %
  23. % FORMAT H = spm_mesh_render('ColourMap',AX,MAP)
  24. % AX - axis handle or structure returned by spm_mesh_render('Disp',...)
  25. % MAP - a colour map matrix
  26. %
  27. % FORMAT MAP = spm_mesh_render('ColourMap',AX)
  28. % Retrieves the current colourmap.
  29. %
  30. % FORMAT H = spm_mesh_render('View',AX, V)
  31. % AX - axis handle or structure returned by spm_mesh_render('Disp',...)
  32. % V - viewpoint specification (see view())
  33. %
  34. % FORMAT spm_mesh_render('Register',AX,hReg)
  35. % AX - axis handle or structure returned by spm_mesh_render('Disp',...)
  36. % hReg - Handle of HandleGraphics object to build registry in.
  37. % See spm_XYZreg for more information.
  38. %__________________________________________________________________________
  39. % Copyright (C) 2010-2018 Wellcome Trust Centre for Neuroimaging
  40. % Guillaume Flandin
  41. % $Id: spm_mesh_render.m 7381 2018-07-25 10:27:54Z guillaume $
  42. %-Input parameters
  43. %--------------------------------------------------------------------------
  44. if ~nargin, action = 'Disp'; end
  45. if ~ischar(action)
  46. varargin = {action varargin{:}};
  47. action = 'Disp';
  48. end
  49. varargout = {[]};
  50. %-Action
  51. %--------------------------------------------------------------------------
  52. switch lower(action)
  53. %-Display
  54. %======================================================================
  55. case 'disp'
  56. if isempty(varargin)
  57. [M, sts] = spm_select([1,Inf],'mesh','Select surface mesh file');
  58. if ~sts, return; end
  59. else
  60. M = varargin{1};
  61. end
  62. M = gifti(M);
  63. if numel(M) > 1, M = gifti(spm_mesh_join(M)); end
  64. if ~isfield(M,'vertices')
  65. try
  66. MM = M;
  67. M = gifti(MM.private.metadata(1).value);
  68. try, M.cdata = MM.cdata(); end
  69. catch
  70. error('Cannot find a surface mesh to be displayed.');
  71. end
  72. end
  73. M = export(M,'patch');
  74. O = getOptions(varargin{2:end});
  75. %-Figure & Axis
  76. %------------------------------------------------------------------
  77. if isfield(O,'parent')
  78. H.axis = O.parent;
  79. H.figure = ancestor(H.axis,'figure');
  80. figure(H.figure); axes(H.axis);
  81. else
  82. H.figure = figure('Color',[1 1 1]);
  83. H.axis = axes('Parent',H.figure);
  84. set(H.axis,'Visible','off');
  85. end
  86. renderer = get(H.figure,'Renderer');
  87. set(H.figure,'Renderer','OpenGL');
  88. %-Patch
  89. %------------------------------------------------------------------
  90. P = struct('vertices',M.vertices, 'faces',M.faces);
  91. H.patch = patch(P,...
  92. 'FaceColor', [0.6 0.6 0.6],...
  93. 'EdgeColor', 'none',...
  94. 'FaceLighting', 'gouraud',...
  95. 'SpecularStrength', 0.7,...
  96. 'AmbientStrength', 0.1,...
  97. 'DiffuseStrength', 0.7,...
  98. 'SpecularExponent', 10,...
  99. 'Clipping', 'off',...
  100. 'DeleteFcn', {@myDeleteFcn, renderer},...
  101. 'Visible', 'off',...
  102. 'Tag', 'SPMMeshRender',...
  103. 'Parent', H.axis);
  104. setappdata(H.patch,'patch',P);
  105. %-Label connected components of the mesh
  106. %------------------------------------------------------------------
  107. C = spm_mesh_label(P);
  108. setappdata(H.patch,'cclabel',C);
  109. %-Compute mesh curvature
  110. %------------------------------------------------------------------
  111. curv = spm_mesh_curvature(P) > 0;
  112. setappdata(H.patch,'curvature',curv);
  113. %-Apply texture to mesh
  114. %------------------------------------------------------------------
  115. if isfield(M,'facevertexcdata')
  116. T = M.facevertexcdata;
  117. else
  118. T = [];
  119. end
  120. updateTexture(H,T);
  121. %-Set viewpoint, light and manipulation options
  122. %------------------------------------------------------------------
  123. if isfield(O,'azimuth'), az = O.azimuth; else az = -90; end
  124. if isfield(O,'elevation'), el = O.elevation; else el = 0; end
  125. axis(H.axis,'image');
  126. axis(H.axis,'off');
  127. view(H.axis,[az el]);
  128. material(H.figure,'dull');
  129. H.light = camlight; set(H.light,'Parent',H.axis);
  130. try, H.rotate3d = rotate3d(H.axis); catch, H.rotate3d = []; end % bug #49747
  131. set(H.rotate3d,'Enable','on');
  132. set(H.rotate3d,'ActionPostCallback',{@myPostCallback, H});
  133. %try
  134. % setAllowAxesRotate(H.rotate3d, ...
  135. % setxor(findobj(H.figure,'Type','axes'),H.axis), false);
  136. %end
  137. %-Store handles
  138. %------------------------------------------------------------------
  139. setappdata(H.axis,'handles',H);
  140. set(H.patch,'Visible','on');
  141. camlight(H.light);
  142. %-Add context menu
  143. %------------------------------------------------------------------
  144. spm_mesh_render('ContextMenu',H);
  145. %-Context Menu
  146. %======================================================================
  147. case 'contextmenu'
  148. if isempty(varargin), varargin{1} = gca; end
  149. H = getHandles(varargin{1});
  150. if ~isempty(get(H.patch,'UIContextMenu')), return; end
  151. cmenu = uicontextmenu('Callback',{@myMenuCallback, H});
  152. uimenu(cmenu, 'Label','Inflate', 'Interruptible','off', ...
  153. 'Callback',{@myInflate, H});
  154. uimenu(cmenu, 'Label','Overlay...', 'Interruptible','off', ...
  155. 'Callback',{@myOverlay, H});
  156. uimenu(cmenu, 'Label','Image Sections...', 'Interruptible','off', ...
  157. 'Callback',{@myImageSections, H});
  158. uimenu(cmenu, 'Label','Change geometry...', 'Interruptible','off', ...
  159. 'Callback',{@myChangeGeometry, H});
  160. c = uimenu(cmenu, 'Label', 'Connected Components', 'Interruptible','off');
  161. C = getappdata(H.patch,'cclabel');
  162. for i=1:length(unique(C))
  163. uimenu(c, 'Label',sprintf('Component %d',i), 'Checked','on', ...
  164. 'Callback',{@myCCLabel, H});
  165. end
  166. uimenu(cmenu, 'Label','Rotate', 'Checked','on', 'Separator','on', ...
  167. 'Callback',{@mySwitchRotate, H});
  168. uimenu(cmenu, 'Label','Synchronise Views', 'Visible','off', ...
  169. 'Checked','off', 'Tag','SynchroMenu', 'Callback',{@mySynchroniseViews, H});
  170. c = uimenu(cmenu, 'Label','View');
  171. uimenu(c, 'Label','Go to Y-Z view (right)', 'Callback', {@myView, H, [90 0]});
  172. uimenu(c, 'Label','Go to Y-Z view (left)', 'Callback', {@myView, H, [-90 0]});
  173. uimenu(c, 'Label','Go to X-Y view (top)', 'Callback', {@myView, H, [0 90]});
  174. uimenu(c, 'Label','Go to X-Y view (bottom)', 'Callback', {@myView, H, [-180 -90]});
  175. uimenu(c, 'Label','Go to X-Z view (front)', 'Callback', {@myView, H, [-180 0]});
  176. uimenu(c, 'Label','Go to X-Z view (back)', 'Callback', {@myView, H, [0 0]});
  177. uimenu(cmenu, 'Label','Colorbar', 'Callback', {@myColourbar, H});
  178. c = uimenu(cmenu, 'Label','Colormap');
  179. clrmp = {'hot' 'jet' 'gray' 'hsv' 'bone' 'copper' 'pink' 'white' ...
  180. 'flag' 'lines' 'colorcube' 'prism' 'cool' 'autumn' ...
  181. 'spring' 'winter' 'summer'};
  182. for i=1:numel(clrmp)
  183. uimenu(c, 'Label', clrmp{i}, 'Callback', {@myColourmap, H});
  184. end
  185. c = uimenu(cmenu, 'Label','Transparency');
  186. uimenu(c, 'Label','0%', 'Checked','on', 'Callback', {@myTransparency, H});
  187. uimenu(c, 'Label','20%', 'Checked','off', 'Callback', {@myTransparency, H});
  188. uimenu(c, 'Label','40%', 'Checked','off', 'Callback', {@myTransparency, H});
  189. uimenu(c, 'Label','60%', 'Checked','off', 'Callback', {@myTransparency, H});
  190. uimenu(c, 'Label','80%', 'Checked','off', 'Callback', {@myTransparency, H});
  191. uimenu(cmenu, 'Label','Data Cursor', 'Callback', {@myDataCursor, H});
  192. c = uimenu(cmenu, 'Label','Background Color');
  193. uimenu(c, 'Label','White', 'Callback', {@myBackgroundColor, H, [1 1 1]});
  194. uimenu(c, 'Label','Black', 'Callback', {@myBackgroundColor, H, [0 0 0]});
  195. uimenu(c, 'Label','Custom...', 'Callback', {@myBackgroundColor, H, []});
  196. uimenu(cmenu, 'Label','Save As...', 'Separator', 'on', ...
  197. 'Callback', {@mySave, H});
  198. set(H.rotate3d,'Enable','off');
  199. try, set(H.rotate3d,'UIContextMenu',cmenu); end
  200. try, set(H.patch, 'UIContextMenu',cmenu); end
  201. set(H.rotate3d,'Enable','on');
  202. try
  203. dcm_obj = datacursormode(H.figure);
  204. set(dcm_obj, 'Enable','off', 'SnapToDataVertex','on', ...
  205. 'DisplayStyle','Window', 'Updatefcn',{@myDataCursorUpdate, H});
  206. end
  207. %-Overlay
  208. %======================================================================
  209. case 'overlay'
  210. if isempty(varargin), varargin{1} = gca; end
  211. H = getHandles(varargin{1});
  212. if nargin < 3, varargin{2} = []; end
  213. updateTexture(H,varargin{2:end});
  214. %-Slices
  215. %======================================================================
  216. case 'slices'
  217. if isempty(varargin), varargin{1} = gca; end
  218. H = getHandles(varargin{1});
  219. if nargin < 3, varargin{2} = []; end
  220. renderSlices(H,varargin{2:end});
  221. %-ColourBar
  222. %======================================================================
  223. case {'colourbar', 'colorbar'}
  224. if isempty(varargin), varargin{1} = gca; end
  225. if length(varargin) == 1, varargin{2} = 'on'; end
  226. H = getHandles(varargin{1});
  227. d = getappdata(H.patch,'data');
  228. col = getappdata(H.patch,'colourmap');
  229. if strcmpi(varargin{2},'off')
  230. if isfield(H,'colourbar') && ishandle(H.colourbar)
  231. delete(H.colourbar);
  232. H = rmfield(H,'colourbar');
  233. setappdata(H.axis,'handles',H);
  234. end
  235. return;
  236. end
  237. if isempty(d) || ~any(d(:)), varargout = {H}; return; end
  238. if isempty(col), col = hot(256); end
  239. if ~isfield(H,'colourbar') || ~ishandle(H.colourbar)
  240. H.colourbar = colorbar('peer',H.axis);
  241. set(H.colourbar,'Tag','');
  242. set(get(H.colourbar,'Children'),'Tag','');
  243. end
  244. c(1:size(col,1),1,1:size(col,2)) = col;
  245. ic = findobj(H.colourbar,'Type','image');
  246. if size(d,1) > 1
  247. set(ic,'CData',c(1:size(d,1),:,:));
  248. set(ic,'YData',[1 size(d,1)]);
  249. set(H.colourbar,'YLim',[1 size(d,1)]);
  250. set(H.colourbar,'YTickLabel',[]);
  251. else
  252. set(ic,'CData',c);
  253. clim = getappdata(H.patch,'clim');
  254. if isempty(clim), clim = [false min(d) max(d)]; end
  255. set(ic,'YData',clim(2:3));
  256. set(H.colourbar,'YLim',clim(2:3));
  257. end
  258. setappdata(H.axis,'handles',H);
  259. %-ColourMap
  260. %======================================================================
  261. case {'colourmap', 'colormap'}
  262. if isempty(varargin), varargin{1} = gca; end
  263. H = getHandles(varargin{1});
  264. if length(varargin) == 1
  265. varargout = { getappdata(H.patch,'colourmap') };
  266. return;
  267. else
  268. setappdata(H.patch,'colourmap',varargin{2});
  269. d = getappdata(H.patch,'data');
  270. updateTexture(H,d);
  271. end
  272. %-CLim
  273. %======================================================================
  274. case 'clim'
  275. if isempty(varargin), varargin{1} = gca; end
  276. H = getHandles(varargin{1});
  277. if length(varargin) == 1
  278. c = getappdata(H.patch,'clim');
  279. if ~isempty(c), c = c(2:3); end
  280. varargout = { c };
  281. return;
  282. else
  283. if isempty(varargin{2}) || any(~isfinite(varargin{2}))
  284. setappdata(H.patch,'clim',[false NaN NaN]);
  285. else
  286. setappdata(H.patch,'clim',[true varargin{2}]);
  287. end
  288. d = getappdata(H.patch,'data');
  289. updateTexture(H,d);
  290. end
  291. %-View
  292. %======================================================================
  293. case 'view'
  294. if isempty(varargin), varargin{1} = gca; end
  295. H = getHandles(varargin{1});
  296. if numel(varargin) < 2, v = 'left'; else v = varargin{2}; end
  297. if ischar(v)
  298. switch lower(v)
  299. case 'right'
  300. v = [90 0];
  301. case 'left'
  302. v = [-90 0];
  303. case 'top'
  304. v = [0 90];
  305. case 'bottom'
  306. v = [-180 -90];
  307. case 'front'
  308. v = [-180 0];
  309. case 'back'
  310. v = [0 0];
  311. otherwise
  312. error('Unknown view position.');
  313. end
  314. end
  315. myView([],[],H,v);
  316. %-Register
  317. %======================================================================
  318. case 'register'
  319. if isempty(varargin), varargin{1} = gca; end
  320. H = getHandles(varargin{1});
  321. hReg = varargin{2};
  322. xyz = spm_XYZreg('GetCoords',hReg);
  323. hs = myCrossBar('Create',H,xyz);
  324. set(hs,'UserData',hReg);
  325. spm_XYZreg('Add2Reg',hReg,hs,@myCrossBar);
  326. %-Debug
  327. %======================================================================
  328. case 'debug'
  329. if isempty(varargin), varargin{1} = gca; end
  330. H = getHandles(varargin{1});
  331. set(H.patch,'EdgeColor','r');
  332. M = getappdata(H.patch,'patch');
  333. for i=1:size(M.vertices,1)
  334. text(M.vertices(i,1),M.vertices(i,2),M.vertices(i,3),...
  335. sprintf('%d',i));
  336. end
  337. %-Otherwise...
  338. %======================================================================
  339. otherwise
  340. try
  341. H = spm_mesh_render('Disp',action,varargin{:});
  342. catch
  343. error('Unknown action.');
  344. end
  345. end
  346. varargout = {H};
  347. %==========================================================================
  348. function O = getOptions(varargin)
  349. O = [];
  350. if ~nargin
  351. return;
  352. elseif nargin == 1 && isstruct(varargin{1})
  353. for i=fieldnames(varargin{1})
  354. O.(lower(i{1})) = varargin{1}.(i{1});
  355. end
  356. elseif mod(nargin,2) == 0
  357. for i=1:2:numel(varargin)
  358. O.(lower(varargin{i})) = varargin{i+1};
  359. end
  360. else
  361. error('Invalid list of property/value pairs.');
  362. end
  363. %==========================================================================
  364. function H = getHandles(H)
  365. if ~nargin || isempty(H), H = gca; end
  366. if ishandle(H) && ~isappdata(H,'handles')
  367. a = H; clear H;
  368. H.axis = a;
  369. H.figure = ancestor(H.axis,'figure');
  370. H.patch = findobj(H.axis,'type','patch');
  371. H.light = findobj(H.axis,'type','light');
  372. H.rotate3d = rotate3d(H.figure);
  373. setappdata(H.axis,'handles',H);
  374. elseif ishandle(H)
  375. H = getappdata(H,'handles');
  376. else
  377. H = getappdata(H.axis,'handles');
  378. end
  379. %==========================================================================
  380. function myMenuCallback(obj,evt,H)
  381. H = getHandles(H);
  382. h = findobj(obj,'Label','Rotate');
  383. if strcmpi(get(H.rotate3d,'Enable'),'on')
  384. set(h,'Checked','on');
  385. else
  386. set(h,'Checked','off');
  387. end
  388. if numel(findobj('Tag','SPMMeshRender','Type','Patch')) > 1
  389. h = findobj(obj,'Tag','SynchroMenu');
  390. set(h,'Visible','on');
  391. end
  392. h = findobj(obj,'Label','Colorbar');
  393. d = getappdata(H.patch,'data');
  394. if isempty(d) || ~any(d(:)), set(h,'Enable','off'); else set(h,'Enable','on'); end
  395. if isfield(H,'colourbar')
  396. if ishandle(H.colourbar)
  397. set(h,'Checked','on');
  398. else
  399. H = rmfield(H,'colourbar');
  400. set(h,'Checked','off');
  401. end
  402. else
  403. set(h,'Checked','off');
  404. end
  405. setappdata(H.axis,'handles',H);
  406. %==========================================================================
  407. function myPostCallback(obj,evt,H)
  408. P = findobj('Tag','SPMMeshRender','Type','Patch');
  409. if numel(P) == 1
  410. camlight(H.light);
  411. else
  412. for i=1:numel(P)
  413. H = getappdata(ancestor(P(i),'axes'),'handles');
  414. camlight(H.light);
  415. end
  416. end
  417. %==========================================================================
  418. function varargout = myCrossBar(varargin)
  419. switch lower(varargin{1})
  420. case 'create'
  421. %----------------------------------------------------------------------
  422. % hMe = myCrossBar('Create',H,xyz)
  423. H = varargin{2};
  424. xyz = varargin{3};
  425. hold(H.axis,'on');
  426. hs = plot3(xyz(1),xyz(2),xyz(3),'Marker','+','MarkerSize',60,...
  427. 'parent',H.axis,'Color',[1 1 1],'Tag','CrossBar','ButtonDownFcn',{});
  428. varargout = {hs};
  429. case 'setcoords'
  430. %----------------------------------------------------------------------
  431. % [xyz,d] = myCrossBar('SetCoords',xyz,hMe)
  432. hMe = varargin{3};
  433. xyz = varargin{2};
  434. set(hMe,'XData',xyz(1));
  435. set(hMe,'YData',xyz(2));
  436. set(hMe,'ZData',xyz(3));
  437. varargout = {xyz,[]};
  438. otherwise
  439. %----------------------------------------------------------------------
  440. error('Unknown action string')
  441. end
  442. %==========================================================================
  443. function myInflate(obj,evt,H)
  444. spm_mesh_inflate(H.patch,Inf,1);
  445. axis(H.axis,'image');
  446. %==========================================================================
  447. function myCCLabel(obj,evt,H)
  448. C = getappdata(H.patch,'cclabel');
  449. F = get(H.patch,'Faces');
  450. ind = sscanf(get(obj,'Label'),'Component %d');
  451. V = get(H.patch,'FaceVertexAlphaData');
  452. Fa = get(H.patch,'FaceAlpha');
  453. if ~isnumeric(Fa)
  454. if ~isempty(V), Fa = max(V); else Fa = 1; end
  455. if Fa == 0, Fa = 1; end
  456. end
  457. if isempty(V) || numel(V) == 1
  458. Ve = get(H.patch,'Vertices');
  459. if isempty(V) || V == 1
  460. V = Fa * ones(size(Ve,1),1);
  461. else
  462. V = zeros(size(Ve,1),1);
  463. end
  464. end
  465. if strcmpi(get(obj,'Checked'),'on')
  466. V(reshape(F(C==ind,:),[],1)) = 0;
  467. set(obj,'Checked','off');
  468. else
  469. V(reshape(F(C==ind,:),[],1)) = Fa;
  470. set(obj,'Checked','on');
  471. end
  472. set(H.patch, 'FaceVertexAlphaData', V);
  473. if all(V)
  474. set(H.patch, 'FaceAlpha', Fa);
  475. else
  476. set(H.patch, 'FaceAlpha', 'interp');
  477. end
  478. %==========================================================================
  479. function myTransparency(obj,evt,H)
  480. t = 1 - sscanf(get(obj,'Label'),'%d%%') / 100;
  481. set(H.patch,'FaceAlpha',t);
  482. set(get(get(obj,'parent'),'children'),'Checked','off');
  483. set(obj,'Checked','on');
  484. %==========================================================================
  485. function mySwitchRotate(obj,evt,H)
  486. if strcmpi(get(H.rotate3d,'Enable'),'on')
  487. set(H.rotate3d,'Enable','off');
  488. set(obj,'Checked','off');
  489. else
  490. set(H.rotate3d,'Enable','on');
  491. set(obj,'Checked','on');
  492. end
  493. %==========================================================================
  494. function myView(obj,evt,H,varargin)
  495. view(H.axis,varargin{1});
  496. axis(H.axis,'image');
  497. camlight(H.light);
  498. %==========================================================================
  499. function myColourbar(obj,evt,H)
  500. y = {'on','off'}; toggle = @(x) y{1+strcmpi(x,'on')};
  501. spm_mesh_render('Colourbar',H,toggle(get(obj,'Checked')));
  502. %==========================================================================
  503. function myColourmap(obj,evt,H)
  504. spm_mesh_render('Colourmap',H,feval(get(obj,'Label'),256));
  505. %==========================================================================
  506. function mySynchroniseViews(obj,evt,H)
  507. P = findobj('Tag','SPMMeshRender','Type','Patch');
  508. v = get(H.axis,'cameraposition');
  509. for i=1:numel(P)
  510. H = getappdata(ancestor(P(i),'axes'),'handles');
  511. set(H.axis,'cameraposition',v);
  512. axis(H.axis,'image');
  513. camlight(H.light);
  514. end
  515. %==========================================================================
  516. function myDataCursor(obj,evt,H)
  517. dcm_obj = datacursormode(H.figure);
  518. set(dcm_obj, 'Enable','on', 'SnapToDataVertex','on', ...
  519. 'DisplayStyle','Window', 'Updatefcn',{@myDataCursorUpdate, H});
  520. %==========================================================================
  521. function txt = myDataCursorUpdate(obj,evt,H)
  522. pos = get(evt,'Position');
  523. txt = {['X: ',num2str(pos(1))],...
  524. ['Y: ',num2str(pos(2))],...
  525. ['Z: ',num2str(pos(3))]};
  526. i = ismember(get(H.patch,'vertices'),pos,'rows');
  527. txt = {['Node: ' num2str(find(i))] txt{:}};
  528. d = getappdata(H.patch,'data');
  529. if ~isempty(d) && any(d(:))
  530. if any(i), txt = {txt{:} ['T: ',num2str(d(i))]}; end
  531. end
  532. hMe = findobj(H.axis,'Tag','CrossBar');
  533. if ~isempty(hMe)
  534. ws = warning('off');
  535. spm_XYZreg('SetCoords',pos,get(hMe,'UserData'));
  536. warning(ws);
  537. end
  538. %==========================================================================
  539. function myBackgroundColor(obj,evt,H,varargin)
  540. if isempty(varargin{1})
  541. c = uisetcolor(H.figure, ...
  542. 'Pick a background color...');
  543. if numel(c) == 1, return; end
  544. else
  545. c = varargin{1};
  546. end
  547. h = findobj(H.figure,'Tag','SPMMeshRenderBackground');
  548. if isempty(h)
  549. set(H.figure,'Color',c);
  550. else
  551. set(h,'Color',c);
  552. end
  553. %==========================================================================
  554. function mySave(obj,evt,H)
  555. [filename, pathname, filterindex] = uiputfile({...
  556. '*.gii' 'GIfTI files (*.gii)'; ...
  557. '*.png' 'PNG files (*.png)';...
  558. '*.dae' 'Collada files (*.dae)';...
  559. '*.idtf' 'IDTF files (*.idtf)';...
  560. '*.vtk' 'VTK files (*.vtk)';...
  561. '*.obj' 'OBJ files (*.obj)';...
  562. '*.js' 'JS files (*.js)';...
  563. '*.json' 'JSON files (*.json)'}, 'Save as');
  564. if ~isequal(filename,0) && ~isequal(pathname,0)
  565. [pth,nam,ext] = fileparts(filename);
  566. switch ext
  567. case '.gii'
  568. filterindex = 1;
  569. case '.png'
  570. filterindex = 2;
  571. case '.dae'
  572. filterindex = 3;
  573. case '.idtf'
  574. filterindex = 4;
  575. case {'.vtk','.vtp'}
  576. filterindex = 5;
  577. case '.obj'
  578. filterindex = 6;
  579. case '.js'
  580. filterindex = 7;
  581. case '.json'
  582. filterindex = 8;
  583. otherwise
  584. switch filterindex
  585. case 1
  586. filename = [filename '.gii'];
  587. case 2
  588. filename = [filename '.png'];
  589. case 3
  590. filename = [filename '.dae'];
  591. case 4
  592. filename = [filename '.idtf'];
  593. case 5
  594. filename = [filename '.vtk'];
  595. case 6
  596. filename = [filename '.obj'];
  597. case 7
  598. filename = [filename '.js'];
  599. case 8
  600. filename = [filename '.json'];
  601. end
  602. end
  603. switch filterindex
  604. case 1
  605. G = gifti(H.patch);
  606. [p,n,e] = fileparts(filename);
  607. [p,n,e] = fileparts(n);
  608. switch lower(e)
  609. case '.func'
  610. save(gifti(getappdata(H.patch,'data')),...
  611. fullfile(pathname, filename));
  612. case '.surf'
  613. save(gifti(struct('vertices',G.vertices,'faces',G.faces)),...
  614. fullfile(pathname, filename));
  615. case '.rgba'
  616. save(gifti(G.cdata),fullfile(pathname, filename));
  617. otherwise
  618. save(G,fullfile(pathname, filename));
  619. end
  620. case 2
  621. u = get(H.axis,'units');
  622. set(H.axis,'units','pixels');
  623. p = get(H.axis,'Position');
  624. r = get(H.figure,'Renderer');
  625. hc = findobj(H.figure,'Tag','SPMMeshRenderBackground');
  626. if isempty(hc)
  627. c = get(H.figure,'Color');
  628. else
  629. c = get(hc,'Color');
  630. end
  631. h = figure('Position',p+[0 0 10 10], ...
  632. 'InvertHardcopy','off', ...
  633. 'Color',c, ...
  634. 'Renderer',r);
  635. copyobj(H.axis,h);
  636. set(H.axis,'units',u);
  637. set(get(h,'children'),'visible','off');
  638. %a = get(h,'children');
  639. %set(a,'Position',get(a,'Position').*[0 0 1 1]+[10 10 0 0]);
  640. print(h, '-dpng', '-opengl', fullfile(pathname, filename));
  641. close(h);
  642. set(getappdata(obj,'fig'),'renderer',r);
  643. case 3
  644. saveas(gifti(H.patch),fullfile(pathname, filename),'collada');
  645. case 4
  646. saveas(gifti(H.patch),fullfile(pathname, filename),'idtf');
  647. case 5
  648. saveas(gifti(H.patch),fullfile(pathname, filename),'vtk');
  649. case 6
  650. saveas(gifti(H.patch),fullfile(pathname, filename),'obj');
  651. case 7
  652. saveas(gifti(H.patch),fullfile(pathname, filename),'js');
  653. case 8
  654. saveas(gifti(H.patch),fullfile(pathname, filename),'json');
  655. end
  656. end
  657. %==========================================================================
  658. function myDeleteFcn(obj,evt,renderer)
  659. try, rotate3d(get(obj,'parent'),'off'); end
  660. set(ancestor(obj,'figure'),'Renderer',renderer);
  661. %==========================================================================
  662. function myOverlay(obj,evt,H)
  663. [P, sts] = spm_select(1,'\.img|\.nii|\.gii|\.mat','Select file to overlay');
  664. if ~sts, return; end
  665. spm_mesh_render('Overlay',H,P);
  666. %==========================================================================
  667. function myImageSections(obj,evt,H)
  668. [P, sts] = spm_select(1,'image','Select image to render');
  669. if ~sts, return; end
  670. renderSlices(H,P);
  671. %==========================================================================
  672. function myChangeGeometry(obj,evt,H)
  673. [P, sts] = spm_select([1, Inf],'mesh','Select new geometry mesh');
  674. if ~sts, return; end
  675. G = gifti(P);
  676. if numel(G) > 1, G = gifti(spm_mesh_join(G)); end
  677. if size(H.patch.Vertices,1) ~= size(G.vertices,1)
  678. error('Number of vertices must match.');
  679. end
  680. H.patch.Vertices = G.vertices;
  681. H.patch.Faces = G.faces;
  682. %==========================================================================
  683. function renderSlices(H,P,pls)
  684. if nargin <3
  685. pls = 0.05:0.2:0.9;
  686. end
  687. N = nifti(P);
  688. d = size(N.dat);
  689. pls = round(pls.*d(3));
  690. hold(H.axis,'on');
  691. for i=1:numel(pls)
  692. [x,y,z] = ndgrid(1:d(1),1:d(2),pls(i));
  693. f = N.dat(:,:,pls(i));
  694. x1 = N.mat(1,1)*x + N.mat(1,2)*y + N.mat(1,3)*z + N.mat(1,4);
  695. y1 = N.mat(2,1)*x + N.mat(2,2)*y + N.mat(2,3)*z + N.mat(2,4);
  696. z1 = N.mat(3,1)*x + N.mat(3,2)*y + N.mat(3,3)*z + N.mat(3,4);
  697. surf(x1,y1,z1, repmat(f,[1 1 3]), 'EdgeColor','none', ...
  698. 'Clipping','off', 'Parent',H.axis);
  699. end
  700. hold(H.axis,'off');
  701. axis(H.axis,'image');
  702. %==========================================================================
  703. function C = updateTexture(H,v,col)
  704. %-Get colourmap
  705. %--------------------------------------------------------------------------
  706. if nargin<3, col = getappdata(H.patch,'colourmap'); end
  707. if isempty(col), col = hot(256); end
  708. setappdata(H.patch,'colourmap',col);
  709. %-Get curvature
  710. %--------------------------------------------------------------------------
  711. curv = getappdata(H.patch,'curvature');
  712. if size(curv,2) == 1
  713. curv = 0.5 * repmat(curv,1,3) + 0.3 * repmat(~curv,1,3);
  714. end
  715. %-Project data onto surface mesh
  716. %--------------------------------------------------------------------------
  717. if nargin < 2, v = []; end
  718. if ischar(v)
  719. [p,n,e] = fileparts(v);
  720. if strcmp([n e],'SPM.mat')
  721. swd = pwd;
  722. spm_figure('GetWin','Interactive');
  723. [SPM,v] = spm_getSPM(struct('swd',p));
  724. cd(swd);
  725. else
  726. try, spm_vol(v); catch, v = gifti(v); end
  727. end
  728. end
  729. if isa(v,'gifti'), v = v.cdata; end
  730. if isa(v,'file_array'), v = v(); end
  731. if isempty(v)
  732. v = zeros(size(curv))';
  733. elseif ischar(v) || iscellstr(v) || isstruct(v)
  734. v = spm_mesh_project(H.patch,v);
  735. elseif isnumeric(v) || islogical(v)
  736. if size(v,2) == 1
  737. v = v';
  738. end
  739. else
  740. error('Unknown data type.');
  741. end
  742. v(isinf(v)) = NaN;
  743. setappdata(H.patch,'data',v);
  744. %-Create RGB representation of data according to colourmap
  745. %--------------------------------------------------------------------------
  746. C = zeros(size(v,2),3);
  747. clim = getappdata(H.patch, 'clim');
  748. if isempty(clim), clim = [false NaN NaN]; end
  749. mi = clim(2); ma = clim(3);
  750. if any(v(:))
  751. if size(col,1)>3 && size(col,1) ~= size(v,1)
  752. if size(v,1) == 1
  753. if ~clim(1), mi = min(v(:)); ma = max(v(:)); end
  754. C = squeeze(ind2rgb(floor(((v(:)-mi)/(ma-mi))*size(col,1)),col));
  755. elseif isequal(size(v),[size(curv,1) 3])
  756. C = v; v = v';
  757. else
  758. if ~clim(1), mi = min(v(:)); ma = max(v(:)); end
  759. for i=1:size(v,1)
  760. C = C + squeeze(ind2rgb(floor(((v(i,:)-mi)/(ma-mi))*size(col,1)),col));
  761. end
  762. end
  763. else
  764. if ~clim(1), ma = max(v(:)); end
  765. for i=1:size(v,1)
  766. C = C + v(i,:)'/ma * col(i,:);
  767. end
  768. end
  769. end
  770. setappdata(H.patch, 'clim', [false mi ma]);
  771. %-Build texture by merging curvature and data
  772. %--------------------------------------------------------------------------
  773. C = repmat(~any(v,1),3,1)' .* curv + repmat(any(v,1),3,1)' .* C;
  774. set(H.patch, 'FaceVertexCData',C, 'FaceColor','interp');
  775. %-Update the colourbar
  776. %--------------------------------------------------------------------------
  777. if isfield(H,'colourbar')
  778. spm_mesh_render('Colourbar',H);
  779. end