freezeColors Lock colors of plot, enabling multiple colormaps per figure. (v2.3) Problem: There is only one colormap per figure. This function provides an easy solution when plots using different colomaps are desired in the same figure. freezeColors freezes the colors of graphics objects in the current axis so that subsequent changes to the colormap (or caxis) will not change the colors of these objects. freezeColors works on any graphics object with CData in indexed-color mode: surfaces, images, scattergroups, bargroups, patches, etc. It works by converting CData to true-color rgb based on the colormap active at the time freezeColors is called. The original indexed color data is saved, and can be restored using unfreezeColors, making the plot once again subject to the colormap and caxis. Usage: freezeColors applies to all objects in current axis (gca), freezeColors(axh) same, but works on axis axh. Useful for colorbar. Example: subplot(2,1,1); imagesc(X); colormap hot; freezeColors subplot(2,1,2); imagesc(Y); colormap hsv; freezeColors etc... Note: colorbars must also be frozen hc = colorbar; freezeColors(hc), or simply freezeColors(colorbar) For additional examples, see freezeColors_demo. Side effect on render mode: freezeColors does not work with the painters renderer, because Matlab doesn't support rgb color data in painters mode. If the current renderer is painters, freezeColors changes it to zbuffer. See also unfreezeColors, freezeColors_pub.html John Iversen (iversen@nsi.edu) 3/23/05
0001 function freezeColors(varargin) 0002 % freezeColors Lock colors of plot, enabling multiple colormaps per figure. (v2.3) 0003 % 0004 % Problem: There is only one colormap per figure. This function provides 0005 % an easy solution when plots using different colomaps are desired 0006 % in the same figure. 0007 % 0008 % freezeColors freezes the colors of graphics objects in the current axis so 0009 % that subsequent changes to the colormap (or caxis) will not change the 0010 % colors of these objects. freezeColors works on any graphics object 0011 % with CData in indexed-color mode: surfaces, images, scattergroups, 0012 % bargroups, patches, etc. It works by converting CData to true-color rgb 0013 % based on the colormap active at the time freezeColors is called. 0014 % 0015 % The original indexed color data is saved, and can be restored using 0016 % unfreezeColors, making the plot once again subject to the colormap and 0017 % caxis. 0018 % 0019 % 0020 % Usage: 0021 % freezeColors applies to all objects in current axis (gca), 0022 % freezeColors(axh) same, but works on axis axh. Useful for colorbar. 0023 % 0024 % Example: 0025 % subplot(2,1,1); imagesc(X); colormap hot; freezeColors 0026 % subplot(2,1,2); imagesc(Y); colormap hsv; freezeColors etc... 0027 % 0028 % Note: colorbars must also be frozen 0029 % hc = colorbar; freezeColors(hc), or simply freezeColors(colorbar) 0030 % 0031 % For additional examples, see freezeColors_demo. 0032 % 0033 % Side effect on render mode: freezeColors does not work with the painters 0034 % renderer, because Matlab doesn't support rgb color data in 0035 % painters mode. If the current renderer is painters, freezeColors 0036 % changes it to zbuffer. 0037 % 0038 % See also unfreezeColors, freezeColors_pub.html 0039 % 0040 % 0041 % John Iversen (iversen@nsi.edu) 3/23/05 0042 % 0043 0044 % Changes: 0045 % JRI (iversen@nsi.edu) 4/19/06 Correctly handles scaled integer cdata 0046 % JRI 9/1/06 should now handle all objects with cdata: images, surfaces, 0047 % scatterplots. (v 2.1) 0048 % JRI 11/11/06 Preserves NaN colors. Hidden option (v 2.2, not uploaded) 0049 % JRI 3/17/07 Preserve caxis after freezing--maintains colorbar scale (v 2.3) 0050 % JRI 4/12/07 Check for painters mode as Matlab doesn't support rgb in it. 0051 % 0052 0053 % Hidden option for NaN colors: 0054 % Missing data are often represented by NaN in the indexed color 0055 % data, which renders transparently. This transparency will be preserved 0056 % when freezing colors. If instead you wish such gaps to be filled with 0057 % a real color, add 'nancolor',[r g b] to the end of the arguments. E.g. 0058 % freezeColors('nancolor',[r g b]) or freezeColors(axh,'nancolor',[r g b]), 0059 % where [r g b] is a color vector. This works on images & pcolor, but not on 0060 % surfaces. 0061 % Thanks to Fabiano Busdraghi and Jody Klymak for the suggestions. 0062 0063 % Note: Special handling of patches: For some reason, setting 0064 % cdata on patches created by bar() yields an error, 0065 % so instead set facevertexcdata instead for patches. 0066 0067 0068 % Free for all uses, but please retain the following: 0069 % Original Author: 0070 % John Iversen, 2005-7 0071 % john_iversen@post.harvard.edu 0072 0073 appdatacode = 'JRI__freezeColorsData'; 0074 0075 [h, nancolor] = checkArgs(varargin); 0076 0077 %gather all children with scaled or indexed CData 0078 cdatah = getCDataHandles(h); 0079 0080 %current colormap 0081 cmap = colormap; 0082 nColors = size(cmap,1); 0083 cax = caxis; 0084 0085 % convert object color indexes into colormap to true-color data using 0086 % current colormap 0087 for hh = cdatah', 0088 g = get(hh); 0089 0090 %preserve parent axis clim 0091 if strcmp(get(g.Parent,'type'),'axes'), 0092 originalClim = get(g.Parent,'clim'); 0093 else 0094 originalClim = []; 0095 end 0096 0097 %special handling for patch (see note above) 0098 if ~strcmp(g.Type,'patch'), 0099 cdata = g.CData; 0100 else 0101 cdata = g.FaceVertexCData; 0102 end 0103 0104 %get cdata mapping (most objects (except scattergroup) have it) 0105 if isfield(g,'CDataMapping'), 0106 scalemode = g.CDataMapping; 0107 else 0108 scalemode = 'scaled'; 0109 end 0110 0111 %save original indexed data for use with unfreezeColors 0112 siz = size(cdata); 0113 setappdata(hh, appdatacode, {cdata scalemode}); 0114 0115 %convert cdata to indexes into colormap 0116 if strcmp(scalemode,'scaled'), 0117 %4/19/06 JRI, Accommodate scaled display of integer cdata: 0118 % in MATLAB, uint * double = uint, so must coerce cdata to double 0119 % Thanks to O Yamashita for pointing this need out 0120 idx = ceil( (double(cdata) - cax(1)) / (cax(2)-cax(1)) * nColors); 0121 else %direct mapping 0122 idx = cdata; 0123 end 0124 0125 %clamp to [1, nColors] 0126 idx(idx<1) = 1; 0127 idx(idx>nColors) = nColors; 0128 0129 %handle nans in idx 0130 nanmask = isnan(idx); 0131 idx(nanmask)=1; %temporarily replace w/ a valid colormap index 0132 0133 %make true-color data--using current colormap 0134 realcolor = zeros(siz); 0135 for i = 1:3, 0136 c = cmap(idx,i); 0137 c = reshape(c,siz); 0138 c(nanmask) = nancolor(i); %restore Nan (or nancolor if specified) 0139 realcolor(:,:,i) = c; 0140 end 0141 0142 %apply new true-color color data 0143 0144 %true-color is not supported in painters renderer, so switch out of that 0145 if strcmp(get(gcf,'renderer'), 'painters'), 0146 set(gcf,'renderer','zbuffer'); 0147 end 0148 0149 %replace original CData with true-color data 0150 if ~strcmp(g.Type,'patch'), 0151 set(hh,'CData',realcolor); 0152 else 0153 set(hh,'faceVertexCData',permute(realcolor,[1 3 2])) 0154 end 0155 0156 %restore clim (so colorbar will show correct limits) 0157 if ~isempty(originalClim), 0158 set(g.Parent,'clim',originalClim) 0159 end 0160 0161 end %loop on indexed-color objects 0162 0163 0164 % ============================================================================ % 0165 % Local functions 0166 0167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0168 %% getCDataHandles -- get handles of all descendents with indexed CData 0169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0170 0171 function hout = getCDataHandles(h) 0172 % getCDataHandles Find all objects with indexed CData 0173 0174 %recursively descend object tree, finding objects with indexed CData 0175 % An exception: don't include children of objects that themselves have CData: 0176 % for example, scattergroups are non-standard hggroups, with CData. Changing 0177 % such a group's CData automatically changes the CData of its children, 0178 % (as well as the children's handles), so there's no need to act on them. 0179 0180 error(nargchk(1,1,nargin,'struct')) 0181 0182 hout = []; 0183 if isempty(h),return;end 0184 0185 ch = get(h,'children'); 0186 for hh = ch' 0187 g = get(hh); 0188 if isfield(g,'CData'), %does object have CData? 0189 %is it indexed/scaled? 0190 if ~isempty(g.CData) && isnumeric(g.CData) && size(g.CData,3)==1, 0191 hout = [hout; hh]; %#ok<AGROW> %yes, add to list 0192 end 0193 else %no CData, see if object has any interesting children 0194 hout = [hout; getCDataHandles(hh)]; %#ok<AGROW> 0195 end 0196 end 0197 0198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0199 %% checkArgs -- Validate input arguments 0200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0201 0202 function [h, nancolor] = checkArgs(args) 0203 % checkArgs Validate input arguments to freezeColors 0204 0205 nargs = length(args); 0206 error(nargchk(0,3,nargs,'struct')) 0207 0208 %grab handle from first argument if we have an odd number of arguments 0209 if mod(nargs,2), 0210 h = args{1}; 0211 if ~ishandle(h), 0212 error('JRI:freezeColors:checkArgs:invalidHandle',... 0213 'The first argument must be a valid graphics handle (to an axis)') 0214 end 0215 args{1} = []; 0216 nargs = nargs-1; 0217 else 0218 h = gca; 0219 end 0220 0221 %set nancolor if that option was specified 0222 nancolor = [nan nan nan]; 0223 if nargs == 2, 0224 if strcmpi(args{end-1},'nancolor'), 0225 nancolor = args{end}; 0226 if ~all(size(nancolor)==[1 3]), 0227 error('JRI:freezeColors:checkArgs:badColorArgument',... 0228 'nancolor must be [r g b] vector'); 0229 end 0230 nancolor(nancolor>1) = 1; nancolor(nancolor<0) = 0; 0231 else 0232 error('JRI:freezeColors:checkArgs:unrecognizedOption',... 0233 'Unrecognized option (%s). Only ''nancolor'' is valid.',args{end-1}) 0234 end 0235 end 0236 0237