function varargout = spm_mesh_render(action,varargin) % Display a surface mesh & various utilities % FORMAT H = spm_mesh_render('Disp',M,'PropertyName',propertyvalue) % M - a GIfTI filename/object or patch structure % H - structure containing handles of various objects % Opens a new figure unless a 'parent' Property is provided with an axis % handle. % % FORMAT H = spm_mesh_render(M) % Shortcut to previous call format. % % FORMAT H = spm_mesh_render('ContextMenu',AX) % AX - axis handle or structure returned by spm_mesh_render('Disp',...) % % FORMAT H = spm_mesh_render('Overlay',AX,P) % AX - axis handle or structure given by spm_mesh_render('Disp',...) % P - data to be overlayed on mesh (see spm_mesh_project) % % FORMAT H = spm_mesh_render('ColourBar',AX,MODE) % AX - axis handle or structure returned by spm_mesh_render('Disp',...) % MODE - {['on'],'off'} % % FORMAT H = spm_mesh_render('ColourMap',AX,MAP) % AX - axis handle or structure returned by spm_mesh_render('Disp',...) % MAP - a colour map matrix % % FORMAT MAP = spm_mesh_render('ColourMap',AX) % Retrieves the current colourmap. % % FORMAT H = spm_mesh_render('View',AX, V) % AX - axis handle or structure returned by spm_mesh_render('Disp',...) % V - viewpoint specification (see view()) % % FORMAT spm_mesh_render('Register',AX,hReg) % AX - axis handle or structure returned by spm_mesh_render('Disp',...) % hReg - Handle of HandleGraphics object to build registry in. % See spm_XYZreg for more information. %__________________________________________________________________________ % Copyright (C) 2010-2017 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin % $Id: spm_mesh_render.m 7177 2017-09-28 09:52:56Z guillaume $ %-Input parameters %-------------------------------------------------------------------------- if ~nargin, action = 'Disp'; end if ~ischar(action) varargin = {action varargin{:}}; action = 'Disp'; end varargout = {[]}; %-Action %-------------------------------------------------------------------------- switch lower(action) %-Display %====================================================================== case 'disp' if isempty(varargin) [M, sts] = spm_select([1,Inf],'mesh','Select surface mesh file'); if ~sts, return; end else M = varargin{1}; end M = gifti(M); if numel(M) > 1, M = gifti(spm_mesh_join(M)); end if ~isfield(M,'vertices') try MM = M; M = gifti(MM.private.metadata(1).value); try, M.cdata = MM.cdata(); end catch error('Cannot find a surface mesh to be displayed.'); end end M = export(M,'patch'); O = getOptions(varargin{2:end}); %-Figure & Axis %------------------------------------------------------------------ if isfield(O,'parent') H.axis = O.parent; H.figure = ancestor(H.axis,'figure'); figure(H.figure); axes(H.axis); else H.figure = figure('Color',[1 1 1]); H.axis = axes('Parent',H.figure); set(H.axis,'Visible','off'); end renderer = get(H.figure,'Renderer'); set(H.figure,'Renderer','OpenGL'); %-Patch %------------------------------------------------------------------ P = struct('vertices',M.vertices, 'faces',M.faces); H.patch = patch(P,... 'FaceColor', [0.6 0.6 0.6],... 'EdgeColor', 'none',... 'FaceLighting', 'phong',... 'SpecularStrength', 0.7,... 'AmbientStrength', 0.1,... 'DiffuseStrength', 0.7,... 'SpecularExponent', 10,... 'Clipping', 'off',... 'DeleteFcn', {@myDeleteFcn, renderer},... 'Visible', 'off',... 'Tag', 'SPMMeshRender',... 'Parent', H.axis); setappdata(H.patch,'patch',P); %-Label connected components of the mesh %------------------------------------------------------------------ C = spm_mesh_label(P); setappdata(H.patch,'cclabel',C); %-Compute mesh curvature %------------------------------------------------------------------ curv = spm_mesh_curvature(P) > 0; setappdata(H.patch,'curvature',curv); %-Apply texture to mesh %------------------------------------------------------------------ if isfield(M,'facevertexcdata') T = M.facevertexcdata; else T = []; end updateTexture(H,T); %-Set viewpoint, light and manipulation options %------------------------------------------------------------------ if isfield(O,'azimuth'), az = O.azimuth; else az = -90; end if isfield(O,'elevation'), el = O.elevation; else el = 0; end axis(H.axis,'image'); axis(H.axis,'off'); view(H.axis,[az el]); material(H.figure,'dull'); H.light = camlight; set(H.light,'Parent',H.axis); H.rotate3d = rotate3d(H.axis); set(H.rotate3d,'Enable','on'); set(H.rotate3d,'ActionPostCallback',{@myPostCallback, H}); %try % setAllowAxesRotate(H.rotate3d, ... % setxor(findobj(H.figure,'Type','axes'),H.axis), false); %end %-Store handles %------------------------------------------------------------------ setappdata(H.axis,'handles',H); set(H.patch,'Visible','on'); camlight(H.light); %-Add context menu %------------------------------------------------------------------ spm_mesh_render('ContextMenu',H); %-Context Menu %====================================================================== case 'contextmenu' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); if ~isempty(get(H.patch,'UIContextMenu')), return; end cmenu = uicontextmenu('Callback',{@myMenuCallback, H}); uimenu(cmenu, 'Label','Inflate', 'Interruptible','off', ... 'Callback',{@myInflate, H}); uimenu(cmenu, 'Label','Overlay...', 'Interruptible','off', ... 'Callback',{@myOverlay, H}); uimenu(cmenu, 'Label','Image Sections...', 'Interruptible','off', ... 'Callback',{@myImageSections, H}); uimenu(cmenu, 'Label','Change geometry...', 'Interruptible','off', ... 'Callback',{@myChangeGeometry, H}); c = uimenu(cmenu, 'Label', 'Connected Components', 'Interruptible','off'); C = getappdata(H.patch,'cclabel'); for i=1:length(unique(C)) uimenu(c, 'Label',sprintf('Component %d',i), 'Checked','on', ... 'Callback',{@myCCLabel, H}); end uimenu(cmenu, 'Label','Rotate', 'Checked','on', 'Separator','on', ... 'Callback',{@mySwitchRotate, H}); uimenu(cmenu, 'Label','Synchronise Views', 'Visible','off', ... 'Checked','off', 'Tag','SynchroMenu', 'Callback',{@mySynchroniseViews, H}); c = uimenu(cmenu, 'Label','View'); uimenu(c, 'Label','Go to Y-Z view (right)', 'Callback', {@myView, H, [90 0]}); uimenu(c, 'Label','Go to Y-Z view (left)', 'Callback', {@myView, H, [-90 0]}); uimenu(c, 'Label','Go to X-Y view (top)', 'Callback', {@myView, H, [0 90]}); uimenu(c, 'Label','Go to X-Y view (bottom)', 'Callback', {@myView, H, [-180 -90]}); uimenu(c, 'Label','Go to X-Z view (front)', 'Callback', {@myView, H, [-180 0]}); uimenu(c, 'Label','Go to X-Z view (back)', 'Callback', {@myView, H, [0 0]}); uimenu(cmenu, 'Label','Colorbar', 'Callback', {@myColourbar, H}); c = uimenu(cmenu, 'Label','Colormap'); clrmp = {'hot' 'jet' 'gray' 'hsv' 'bone' 'copper' 'pink' 'white' ... 'flag' 'lines' 'colorcube' 'prism' 'cool' 'autumn' ... 'spring' 'winter' 'summer'}; for i=1:numel(clrmp) uimenu(c, 'Label', clrmp{i}, 'Callback', {@myColourmap, H}); end c = uimenu(cmenu, 'Label','Transparency'); uimenu(c, 'Label','0%', 'Checked','on', 'Callback', {@myTransparency, H}); uimenu(c, 'Label','20%', 'Checked','off', 'Callback', {@myTransparency, H}); uimenu(c, 'Label','40%', 'Checked','off', 'Callback', {@myTransparency, H}); uimenu(c, 'Label','60%', 'Checked','off', 'Callback', {@myTransparency, H}); uimenu(c, 'Label','80%', 'Checked','off', 'Callback', {@myTransparency, H}); uimenu(cmenu, 'Label','Data Cursor', 'Callback', {@myDataCursor, H}); c = uimenu(cmenu, 'Label','Background Color'); uimenu(c, 'Label','White', 'Callback', {@myBackgroundColor, H, [1 1 1]}); uimenu(c, 'Label','Black', 'Callback', {@myBackgroundColor, H, [0 0 0]}); uimenu(c, 'Label','Custom...', 'Callback', {@myBackgroundColor, H, []}); uimenu(cmenu, 'Label','Save As...', 'Separator', 'on', ... 'Callback', {@mySave, H}); set(H.rotate3d,'Enable','off'); try, set(H.rotate3d,'UIContextMenu',cmenu); end try, set(H.patch, 'UIContextMenu',cmenu); end set(H.rotate3d,'Enable','on'); try dcm_obj = datacursormode(H.figure); set(dcm_obj, 'Enable','off', 'SnapToDataVertex','on', ... 'DisplayStyle','Window', 'Updatefcn',{@myDataCursorUpdate, H}); end %-Overlay %====================================================================== case 'overlay' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); if nargin < 3, varargin{2} = []; end updateTexture(H,varargin{2:end}); %-Slices %====================================================================== case 'slices' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); if nargin < 3, varargin{2} = []; end renderSlices(H,varargin{2:end}); %-ColourBar %====================================================================== case {'colourbar', 'colorbar'} if isempty(varargin), varargin{1} = gca; end if length(varargin) == 1, varargin{2} = 'on'; end H = getHandles(varargin{1}); d = getappdata(H.patch,'data'); col = getappdata(H.patch,'colourmap'); if strcmpi(varargin{2},'off') if isfield(H,'colourbar') && ishandle(H.colourbar) delete(H.colourbar); H = rmfield(H,'colourbar'); setappdata(H.axis,'handles',H); end return; end if isempty(d) || ~any(d(:)), varargout = {H}; return; end if isempty(col), col = hot(256); end if ~isfield(H,'colourbar') || ~ishandle(H.colourbar) H.colourbar = colorbar('peer',H.axis); set(H.colourbar,'Tag',''); set(get(H.colourbar,'Children'),'Tag',''); end c(1:size(col,1),1,1:size(col,2)) = col; ic = findobj(H.colourbar,'Type','image'); if size(d,1) > 1 set(ic,'CData',c(1:size(d,1),:,:)); set(ic,'YData',[1 size(d,1)]); set(H.colourbar,'YLim',[1 size(d,1)]); set(H.colourbar,'YTickLabel',[]); else set(ic,'CData',c); clim = getappdata(H.patch,'clim'); if isempty(clim), clim = [false min(d) max(d)]; end set(ic,'YData',clim(2:3)); set(H.colourbar,'YLim',clim(2:3)); end setappdata(H.axis,'handles',H); %-ColourMap %====================================================================== case {'colourmap', 'colormap'} if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); if length(varargin) == 1 varargout = { getappdata(H.patch,'colourmap') }; return; else setappdata(H.patch,'colourmap',varargin{2}); d = getappdata(H.patch,'data'); updateTexture(H,d); end %-CLim %====================================================================== case 'clim' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); if length(varargin) == 1 c = getappdata(H.patch,'clim'); if ~isempty(c), c = c(2:3); end varargout = { c }; return; else if isempty(varargin{2}) || any(~isfinite(varargin{2})) setappdata(H.patch,'clim',[false NaN NaN]); else setappdata(H.patch,'clim',[true varargin{2}]); end d = getappdata(H.patch,'data'); updateTexture(H,d); end %-View %====================================================================== case 'view' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); if numel(varargin) < 2, v = 'left'; else v = varargin{2}; end if ischar(v) switch lower(v) case 'right' v = [90 0]; case 'left' v = [-90 0]; case 'top' v = [0 90]; case 'bottom' v = [-180 -90]; case 'front' v = [-180 0]; case 'back' v = [0 0]; otherwise error('Unknown view position.'); end end myView([],[],H,v); %-Register %====================================================================== case 'register' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); hReg = varargin{2}; xyz = spm_XYZreg('GetCoords',hReg); hs = myCrossBar('Create',H,xyz); set(hs,'UserData',hReg); spm_XYZreg('Add2Reg',hReg,hs,@myCrossBar); %-Debug %====================================================================== case 'debug' if isempty(varargin), varargin{1} = gca; end H = getHandles(varargin{1}); set(H.patch,'EdgeColor','r'); M = getappdata(H.patch,'patch'); for i=1:size(M.vertices,1) text(M.vertices(i,1),M.vertices(i,2),M.vertices(i,3),... sprintf('%d',i)); end %-Otherwise... %====================================================================== otherwise try H = spm_mesh_render('Disp',action,varargin{:}); catch error('Unknown action.'); end end varargout = {H}; %========================================================================== function O = getOptions(varargin) O = []; if ~nargin return; elseif nargin == 1 && isstruct(varargin{1}) for i=fieldnames(varargin{1}) O.(lower(i{1})) = varargin{1}.(i{1}); end elseif mod(nargin,2) == 0 for i=1:2:numel(varargin) O.(lower(varargin{i})) = varargin{i+1}; end else error('Invalid list of property/value pairs.'); end %========================================================================== function H = getHandles(H) if ~nargin || isempty(H), H = gca; end if ishandle(H) && ~isappdata(H,'handles') a = H; clear H; H.axis = a; H.figure = ancestor(H.axis,'figure'); H.patch = findobj(H.axis,'type','patch'); H.light = findobj(H.axis,'type','light'); H.rotate3d = rotate3d(H.figure); setappdata(H.axis,'handles',H); elseif ishandle(H) H = getappdata(H,'handles'); else H = getappdata(H.axis,'handles'); end %========================================================================== function myMenuCallback(obj,evt,H) H = getHandles(H); h = findobj(obj,'Label','Rotate'); if strcmpi(get(H.rotate3d,'Enable'),'on') set(h,'Checked','on'); else set(h,'Checked','off'); end if numel(findobj('Tag','SPMMeshRender','Type','Patch')) > 1 h = findobj(obj,'Tag','SynchroMenu'); set(h,'Visible','on'); end h = findobj(obj,'Label','Colorbar'); d = getappdata(H.patch,'data'); if isempty(d) || ~any(d(:)), set(h,'Enable','off'); else set(h,'Enable','on'); end if isfield(H,'colourbar') if ishandle(H.colourbar) set(h,'Checked','on'); else H = rmfield(H,'colourbar'); set(h,'Checked','off'); end else set(h,'Checked','off'); end setappdata(H.axis,'handles',H); %========================================================================== function myPostCallback(obj,evt,H) P = findobj('Tag','SPMMeshRender','Type','Patch'); if numel(P) == 1 camlight(H.light); else for i=1:numel(P) H = getappdata(ancestor(P(i),'axes'),'handles'); camlight(H.light); end end %========================================================================== function varargout = myCrossBar(varargin) switch lower(varargin{1}) case 'create' %---------------------------------------------------------------------- % hMe = myCrossBar('Create',H,xyz) H = varargin{2}; xyz = varargin{3}; hold(H.axis,'on'); hs = plot3(xyz(1),xyz(2),xyz(3),'Marker','+','MarkerSize',60,... 'parent',H.axis,'Color',[1 1 1],'Tag','CrossBar','ButtonDownFcn',{}); varargout = {hs}; case 'setcoords' %---------------------------------------------------------------------- % [xyz,d] = myCrossBar('SetCoords',xyz,hMe) hMe = varargin{3}; xyz = varargin{2}; set(hMe,'XData',xyz(1)); set(hMe,'YData',xyz(2)); set(hMe,'ZData',xyz(3)); varargout = {xyz,[]}; otherwise %---------------------------------------------------------------------- error('Unknown action string') end %========================================================================== function myInflate(obj,evt,H) spm_mesh_inflate(H.patch,Inf,1); axis(H.axis,'image'); %========================================================================== function myCCLabel(obj,evt,H) C = getappdata(H.patch,'cclabel'); F = get(H.patch,'Faces'); ind = sscanf(get(obj,'Label'),'Component %d'); V = get(H.patch,'FaceVertexAlphaData'); Fa = get(H.patch,'FaceAlpha'); if ~isnumeric(Fa) if ~isempty(V), Fa = max(V); else Fa = 1; end if Fa == 0, Fa = 1; end end if isempty(V) || numel(V) == 1 Ve = get(H.patch,'Vertices'); if isempty(V) || V == 1 V = Fa * ones(size(Ve,1),1); else V = zeros(size(Ve,1),1); end end if strcmpi(get(obj,'Checked'),'on') V(reshape(F(C==ind,:),[],1)) = 0; set(obj,'Checked','off'); else V(reshape(F(C==ind,:),[],1)) = Fa; set(obj,'Checked','on'); end set(H.patch, 'FaceVertexAlphaData', V); if all(V) set(H.patch, 'FaceAlpha', Fa); else set(H.patch, 'FaceAlpha', 'interp'); end %========================================================================== function myTransparency(obj,evt,H) t = 1 - sscanf(get(obj,'Label'),'%d%%') / 100; set(H.patch,'FaceAlpha',t); set(get(get(obj,'parent'),'children'),'Checked','off'); set(obj,'Checked','on'); %========================================================================== function mySwitchRotate(obj,evt,H) if strcmpi(get(H.rotate3d,'Enable'),'on') set(H.rotate3d,'Enable','off'); set(obj,'Checked','off'); else set(H.rotate3d,'Enable','on'); set(obj,'Checked','on'); end %========================================================================== function myView(obj,evt,H,varargin) view(H.axis,varargin{1}); axis(H.axis,'image'); camlight(H.light); %========================================================================== function myColourbar(obj,evt,H) y = {'on','off'}; toggle = @(x) y{1+strcmpi(x,'on')}; spm_mesh_render('Colourbar',H,toggle(get(obj,'Checked'))); %========================================================================== function myColourmap(obj,evt,H) spm_mesh_render('Colourmap',H,feval(get(obj,'Label'),256)); %========================================================================== function mySynchroniseViews(obj,evt,H) P = findobj('Tag','SPMMeshRender','Type','Patch'); v = get(H.axis,'cameraposition'); for i=1:numel(P) H = getappdata(ancestor(P(i),'axes'),'handles'); set(H.axis,'cameraposition',v); axis(H.axis,'image'); camlight(H.light); end %========================================================================== function myDataCursor(obj,evt,H) dcm_obj = datacursormode(H.figure); set(dcm_obj, 'Enable','on', 'SnapToDataVertex','on', ... 'DisplayStyle','Window', 'Updatefcn',{@myDataCursorUpdate, H}); %========================================================================== function txt = myDataCursorUpdate(obj,evt,H) pos = get(evt,'Position'); txt = {['X: ',num2str(pos(1))],... ['Y: ',num2str(pos(2))],... ['Z: ',num2str(pos(3))]}; i = ismember(get(H.patch,'vertices'),pos,'rows'); txt = {['Node: ' num2str(find(i))] txt{:}}; d = getappdata(H.patch,'data'); if ~isempty(d) && any(d(:)) if any(i), txt = {txt{:} ['T: ',num2str(d(i))]}; end end hMe = findobj(H.axis,'Tag','CrossBar'); if ~isempty(hMe) ws = warning('off'); spm_XYZreg('SetCoords',pos,get(hMe,'UserData')); warning(ws); end %========================================================================== function myBackgroundColor(obj,evt,H,varargin) if isempty(varargin{1}) c = uisetcolor(H.figure, ... 'Pick a background color...'); if numel(c) == 1, return; end else c = varargin{1}; end h = findobj(H.figure,'Tag','SPMMeshRenderBackground'); if isempty(h) set(H.figure,'Color',c); else set(h,'Color',c); end %========================================================================== function mySave(obj,evt,H) [filename, pathname, filterindex] = uiputfile({... '*.gii' 'GIfTI files (*.gii)'; ... '*.png' 'PNG files (*.png)';... '*.dae' 'Collada files (*.dae)';... '*.idtf' 'IDTF files (*.idtf)';... '*.vtk' 'VTK files (*.vtk)';... '*.obj' 'OBJ files (*.obj)'}, 'Save as'); if ~isequal(filename,0) && ~isequal(pathname,0) [pth,nam,ext] = fileparts(filename); switch ext case '.gii' filterindex = 1; case '.png' filterindex = 2; case '.dae' filterindex = 3; case '.idtf' filterindex = 4; case {'.vtk','.vtp'} filterindex = 5; case '.obj' filterindex = 6; otherwise switch filterindex case 1 filename = [filename '.gii']; case 2 filename = [filename '.png']; case 3 filename = [filename '.dae']; case 4 filename = [filename '.idtf']; case 5 filename = [filename '.vtk']; case 6 filename = [filename '.obj']; end end switch filterindex case 1 G = gifti(H.patch); [p,n,e] = fileparts(filename); [p,n,e] = fileparts(n); switch lower(e) case '.func' save(gifti(getappdata(H.patch,'data')),... fullfile(pathname, filename)); case '.surf' save(gifti(struct('vertices',G.vertices,'faces',G.faces)),... fullfile(pathname, filename)); case '.rgba' save(gifti(G.cdata),fullfile(pathname, filename)); otherwise save(G,fullfile(pathname, filename)); end case 2 u = get(H.axis,'units'); set(H.axis,'units','pixels'); p = get(H.axis,'Position'); r = get(H.figure,'Renderer'); hc = findobj(H.figure,'Tag','SPMMeshRenderBackground'); if isempty(hc) c = get(H.figure,'Color'); else c = get(hc,'Color'); end h = figure('Position',p+[0 0 10 10], ... 'InvertHardcopy','off', ... 'Color',c, ... 'Renderer',r); copyobj(H.axis,h); set(H.axis,'units',u); set(get(h,'children'),'visible','off'); %a = get(h,'children'); %set(a,'Position',get(a,'Position').*[0 0 1 1]+[10 10 0 0]); print(h, '-dpng', '-opengl', fullfile(pathname, filename)); close(h); set(getappdata(obj,'fig'),'renderer',r); case 3 saveas(gifti(H.patch),fullfile(pathname, filename),'collada'); case 4 saveas(gifti(H.patch),fullfile(pathname, filename),'idtf'); case 5 saveas(gifti(H.patch),fullfile(pathname, filename),'vtk'); case 6 saveas(gifti(H.patch),fullfile(pathname, filename),'obj'); end end %========================================================================== function myDeleteFcn(obj,evt,renderer) try, rotate3d(get(obj,'parent'),'off'); end set(ancestor(obj,'figure'),'Renderer',renderer); %========================================================================== function myOverlay(obj,evt,H) [P, sts] = spm_select(1,'\.img|\.nii|\.gii|\.mat','Select file to overlay'); if ~sts, return; end spm_mesh_render('Overlay',H,P); %========================================================================== function myImageSections(obj,evt,H) [P, sts] = spm_select(1,'image','Select image to render'); if ~sts, return; end renderSlices(H,P); %========================================================================== function myChangeGeometry(obj,evt,H) [P, sts] = spm_select([1, Inf],'mesh','Select new geometry mesh'); if ~sts, return; end G = gifti(P); if numel(G) > 1, G = gifti(spm_mesh_join(G)); end if size(H.patch.Vertices,1) ~= size(G.vertices,1) error('Number of vertices must match.'); end H.patch.Vertices = G.vertices; H.patch.Faces = G.faces; %========================================================================== function renderSlices(H,P,pls) if nargin <3 pls = 0.05:0.2:0.9; end N = nifti(P); d = size(N.dat); pls = round(pls.*d(3)); hold(H.axis,'on'); for i=1:numel(pls) [x,y,z] = ndgrid(1:d(1),1:d(2),pls(i)); f = N.dat(:,:,pls(i)); x1 = N.mat(1,1)*x + N.mat(1,2)*y + N.mat(1,3)*z + N.mat(1,4); y1 = N.mat(2,1)*x + N.mat(2,2)*y + N.mat(2,3)*z + N.mat(2,4); z1 = N.mat(3,1)*x + N.mat(3,2)*y + N.mat(3,3)*z + N.mat(3,4); surf(x1,y1,z1, repmat(f,[1 1 3]), 'EdgeColor','none', ... 'Clipping','off', 'Parent',H.axis); end hold(H.axis,'off'); axis(H.axis,'image'); %========================================================================== function C = updateTexture(H,v,col) %-Get colourmap %-------------------------------------------------------------------------- if nargin<3, col = getappdata(H.patch,'colourmap'); end if isempty(col), col = hot(256); end setappdata(H.patch,'colourmap',col); %-Get curvature %-------------------------------------------------------------------------- curv = getappdata(H.patch,'curvature'); if size(curv,2) == 1 curv = 0.5 * repmat(curv,1,3) + 0.3 * repmat(~curv,1,3); end %-Project data onto surface mesh %-------------------------------------------------------------------------- if nargin < 2, v = []; end if ischar(v) [p,n,e] = fileparts(v); if strcmp([n e],'SPM.mat') swd = pwd; spm_figure('GetWin','Interactive'); [SPM,v] = spm_getSPM(struct('swd',p)); cd(swd); else try, spm_vol(v); catch, v = gifti(v); end end end if isa(v,'gifti'), v = v.cdata; end if isa(v,'file_array'), v = v(); end if isempty(v) v = zeros(size(curv))'; elseif ischar(v) || iscellstr(v) || isstruct(v) v = spm_mesh_project(H.patch,v); elseif isnumeric(v) || islogical(v) if size(v,2) == 1 v = v'; end else error('Unknown data type.'); end v(isinf(v)) = NaN; setappdata(H.patch,'data',v); %-Create RGB representation of data according to colourmap %-------------------------------------------------------------------------- C = zeros(size(v,2),3); clim = getappdata(H.patch, 'clim'); if isempty(clim), clim = [false NaN NaN]; end mi = clim(2); ma = clim(3); if any(v(:)) if size(col,1)>3 && size(col,1) ~= size(v,1) if size(v,1) == 1 if ~clim(1), mi = min(v(:)); ma = max(v(:)); end C = squeeze(ind2rgb(floor(((v(:)-mi)/(ma-mi))*size(col,1)),col)); elseif isequal(size(v),[size(curv,1) 3]) C = v; v = v'; else if ~clim(1), mi = min(v(:)); ma = max(v(:)); end for i=1:size(v,1) C = C + squeeze(ind2rgb(floor(((v(i,:)-mi)/(ma-mi))*size(col,1)),col)); end end else if ~clim(1), ma = max(v(:)); end for i=1:size(v,1) C = C + v(i,:)'/ma * col(i,:); end end end setappdata(H.patch, 'clim', [false mi ma]); %-Build texture by merging curvature and data %-------------------------------------------------------------------------- C = repmat(~any(v,1),3,1)' .* curv + repmat(any(v,1),3,1)' .* C; set(H.patch, 'FaceVertexCData',C, 'FaceColor','interp'); %-Update the colourbar %-------------------------------------------------------------------------- if isfield(H,'colourbar') spm_mesh_render('Colourbar',H); end