How do I set individual face colors from a patch while leaving the rest interpolated by a colormap?

9 次查看(过去 30 天)
Hi,
I am trying to patch a surface and control colors of each face. I have an mx3 array of faces (fvc.faces), and an nx3 array of vertices (fvc.vertices) and an mx1 array of numbers which represents an intensity for every face (fvc.facevertexcdata). I can set a colormap and map the surface as with a jet colormap as follows:
g = colormap(jet(128)); h = patch(fvc) shading flat
This gives me the surface with colors of each face mapped to this jet colormap. However, I would like to manually change some of the faces, for example, I want to make the color of face 10 black, while not changing anything else. I am not having success doing this. Since fvc.facevertexcdata is a 1D array, I cannot change a particular face to an RGB vector. What I was thinking is somewhere matlab must store the rbg values for each face when it applies the jet colormap. If I can get to this I think I could change a face color individually and set this mx3 array as fvc.facevertexcdata. I just don't know where to find where matlab stores this data when matlab applies the colormap to the facevertexcdata I have.
If anyone has experience with this I would greatly appreciate any help.
Thanks

采纳的回答

Walter Roberson
Walter Roberson 2014-4-22
Your fvc.facevertexcdata corresponds to the FaceVertexCData property, and your "one dimensional" specification there corresponds to the "indexed color" case,
An n-by-1 matrix, where n is the number of rows in the Faces property, which specifies one color per face.
In turn, the handling of CData and FaceVertexCData is controlled by the property
CDataMapping
scaled — Transform the color data to span the portion of the colormap indicated by the axes CLim property, linearly mapping data values to colors. See the caxis command for more information on this mapping.
direct — Use the color data as indices directly into the colormap. When not scaled, the data are usually integer values ranging from 1 to length(colormap). MATLAB maps values less than 1 to the first color in the colormap, and values greater than length(colormap) to the last color in the colormap. Values with a decimal portion are fixed to the nearest lower integer.
"scaled" is the default and is what you are using. The lowest value in your FaceVertexCData is being mapped to the lowest color index and the highest value in your FaceVertexCData is being mapped to the highest color index and everything else is being linearly transformed inbetween.
Thus to use a specific color for a particular face, you should find the desired color in the colormap, calculate its position as a fraction of the number of colors in the map, then multiply that fraction by the range of intensities being considered, (max(FaceVertexCData)-min(FaceVertexCData)), and add min(FaceVertexCData) to that. Now set the face's entry in the FaceVertexCData vector to that back-calculated value.
If, however, the desired color is not normally in the colormap, then you have a bit more work to o. You need to calculate the color number that each intensity maps to, replace the FaceVertexCData values by the appropriate color number, and change the CDataMapping property to 'direct':
colormap(jet);
numcol = size(colormap,1);
min_inten = min(FaceVertexCData);
cfrac = (FaceVertexCData - min_inten) ./ (max(FaceVertexCData) - min_inten));
cnum = 1 + floor(numcol * (cfrac * (1-eps)));
and set the FaceVertexCData property to cnum.
Multiplying cfrac by (1-eps) is correcting for a boundary problem. The fraction will be exactly 0 at the minimum intensity, and could potentially be exactly 1 at the maximum intensity (round-off error permitting.) Unless limited precision dictates otherwise, only the very maximum intensity can map to exactly 1, with everything else mapping to at least slightly below 1. If you multiply this by the number of colors you would get a value from exactly 0 to (potentially) the number of colors. But you want 1 to the number of colors. If add 1 then the highest intensity could potentially end up mapping to one more than the number of colors. To avoid this possibility, we multiply the color fraction by just slightly less than 1, so that the maximum value is now slightly less than 1; multiply that by the number of colors and the maximum is now just slightly less than the number of colors, with the minimum mapping to exactly 0. Now take the floor() and you will get 0 to (one less than the number of colors). Add 1 and you get 1 to the number of colors.
Now that we have the vector of color indices into the color map, to set a face to a color outside the color map, we pull a trick: we add another color to the color map, and we set the FaceVertexCData to be the number of the new entry. Because we already switched to 'direct' instead of 'scaled', the entries we already calculated indices for still refer to the same color, but now we can also reference colors that were not there before. For example,
colormap([colormap;0 0 0])
and now the last entry of the colormap is [0 0 0], pure black. You can do the same thing for pinning the color of other faces.

更多回答(1 个)

Iman
Iman 2014-4-22
Thanks! That is exactly what I needed. The only issue now is that the colormap changes and thus the colorbar changes with it which I didn't want. However, I used a the function, cbfreeze to freeze the colorbar before changing particular face colors.

类别

Help CenterFile Exchange 中查找有关 Colormaps 的更多信息

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by