matlab3dbar-chartmatlab-figure

Colors of the bar are not same for the same height in MATLAB 3d bar plot after applying log scale


I'm trying to plot 3D bar chart in MATLAB with colormap. The colormap wasn't showing up. Then I found a work around to apply colormap on bar3:

b = [...] % my data
b = bar3(Z, 1);
for k = 1:length(b)
    zdata = b(k).ZData;
    b(k).CData = zdata;
    b(k).FaceColor = 'interp';
end
colormap('jet')

I'm also applying log scale in Z axis. But it was messing up my plot showing only the top surfaces of the bars. I found a work around for this also.

% Z log fix
llim = .1;
h = get(gca,'Children');
for i = 1:length(h)
       ZData = get(h(i), 'ZData');
       ZData(ZData==0) = llim;
       set(h(i), 'ZData', ZData);
end
set(gca,'ZScale','log')

But I'm getting the following result after the log fix where the bars don't have same color at the same Z value (height).

My Plot

I'm trying to get results like the following plot.

Expected plot

My MATLAB version is R2022a.

Anyone know the solution?


Solution

  • The default CDataMapping is "scaled", by using "direct" mapping you can avoid problems which seem to be arising from dodgy indexing of your colour map in combination with the log scale.

    From the surface property docs:

    'direct' — Interpret the values as indices into the current colormap. Values with a decimal portion are fixed to the nearest lower integer. [...]

    'scaled' — Scale the values to range between the minimum and maximum color limits. The CLim property of the axes contains the color limits.

    The scale you want is 255 * log10(zdata) ./ mx, because

    I suspect, without proof, that the automatic scaling isn't taking the log of zdata when you expect it to, so the indexing is all wrong.

    A full example, including your face colouring and log(0) fixes from your question, would look something like this:

    % Create some random data
    rng(0);
    z = 10.^(rand(1,20)*3 + [20,16,10,5,1,0,0].');
    
    % Make the bar plot
    figure(1); clf;
    b = bar3( z, 1 );
    
    llim = .1; % Min value to avoid log(0) issues
    mx = log10(max(z(:),[],'omitnan')); % log of max value in data
    
    % Loop over all bars to recolour
    for k = 1:length(b)
        % Fix log(0) issue which breaks surfaces
        ZData = get(b(k), 'ZData');
        ZData(ZData==0) = llim;
        set(b(k), 'ZData', ZData);
    
        % Set new CData according to direct scaling between 0 and 255
        b(k).CData = 255 * log10(b(k).ZData) ./ mx;
        b(k).CDataMapping = 'direct';
        % Interp over face for gradient
        b(k).FaceColor = 'interp';
    end
    colormap('jet');         % Set colourmap
    set(gca,'ZScale','log'); % Set log scale
    

    Output:

    bar plot