Plotting a 2D array of values as discrete squares without interpolation

10 visualizzazioni (ultimi 30 giorni)
I believe my question is very similar to this one that never got answered:
I have a set of data that were measured by a 24 x 24 array of pressure sensors. The sensors measure pressure in the X, Y, and Z directions. Each sensor makes a discrete measurement of the average value of pressure over its surface. I want to plot these values as discrete colored squares.
The code below uses Surf:
load("StressData.mat");
figure;
subplot(1, 3, 1);
surf(z, "edgecolor", "none", "facecolor", "interp", "facealpha", 1);
title("Contact Pressure"); xlabel("X"); ylabel("Y");
axis equal; xlim([1, 24]); ylim([1, 24]); view([90, -90]);
colormap("Jet"); shading("interp");
subplot(1, 3, 2);
surf(x, "edgecolor", "none", "facecolor", "interp", "facealpha", 1);
title("X pressure"); xlabel("X"); ylabel("Y");
axis equal; xlim([1, 24]); ylim([1, 24]); view([90, -90]);
colormap("Jet"); shading("interp");
subplot(1, 3, 3);
surf(y, "edgecolor", "none", "facecolor", "interp", "facealpha", 1);
title("Y pressure"); xlabel("X"); ylabel("Y");
axis equal; xlim([1, 24]); ylim([1, 24]); view([90, -90]);
colormap("Jet"); shading("interp");
This interpolates between the values. I want to display each value discretely without interpolating. Note that the X and Y data have both positive and negative values. So when you change the viewpoint, the image looks like this:
An alternative is to use bar3. This plots the values as squares, but it has its own set of problems:
%% Plot data as individual pixels
figure;
subplot(1, 3, 1);
b = bar3(z, 1);
title("Contact Pressure"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k = 1 : length(b)
b(k).FaceColor = 'flat';
b(k).CData(:, :) = repmat(b(k).ZData(:, 2), 1, 4);
end
subplot(1, 3, 2);
b = bar3(x, 1);
title("X pressure"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k = 1 : length(b)
b(k).FaceColor = 'flat';
b(k).CData(:, :) = repmat(b(k).ZData(:, 2), 1, 4);
end
subplot(1, 3, 3);
b = bar3(y, 1);
title("Y pressure"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k = 1 : length(b)
b(k).FaceColor = 'flat';
b(k).CData(:, :) = repmat(b(k).ZData(:, 2), 1, 4);
end
The main problem is that the bars have tops, sides, and bottoms and while I've figured out how to color the tops and the sides, I haven't been able to figure out how to color the bottoms, so the negative values get hidden. This is particularly apparent in the X and Y pressures (see below).
Am I on the right track? How do I color the bottoms of the bars?
  4 Commenti
dpb
dpb il 5 Set 2025
Modificato: dpb il 5 Set 2025
My bad on the latter above; I forgot to delete the clean-up code...got interrupted and forgot where was when got back.
Probably, except they then would still be there, just with the colors instead of default black/near black; I guess on reflection they aren't anything except the bar outlines for the low-height bars. (<Doh!!>)
Unfortunately, I think those are handles only by the bar object and not settable in finer detail.
load StressData y
y(1:5,:);
isZ=(abs(y)<1); nnz(isZ);
h=bar3(y);
size(h);
for i=1:numel(h)
for j=1:size(y,1);
if abs(y(j,i))<1
h(i).CData((j-1)*6+[1:6],:)=nan;
end
end
h(i).EdgeColor=0.9*[1 1 1];
end
hAx=gca;
hF=figure;
copyobj(hAx,hF)
m=[h.CData]; [min(m,[],'all') max(m,[],'all')]
ans = 1×2
1 20
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
c=colormap;
maxH=max(m,[],'all');
ix=round(linspace(1,height(c),maxH))
ix = 1×20
1 14 28 41 55 68 82 95 108 122 135 149 162 175 189 202 216 229 243 256
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
for i=1:maxH
h(i).EdgeColor=c(ix(i),:);
end
matches the bar color so the edges disappear but, of course, puts the mesh back in darker so still can't really see the negative data.
Being unable to set the edges on a per individual bar basis pretty-much means this is a dead end and there's no 'none' property value for the edges.
dpb
dpb il 6 Set 2025
Modificato: dpb il 6 Set 2025
load StressData y
subplot(1,2,1);
hb = bar3(y);
title("P Y Top View"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k=1:numel(hb)
hb(k).CData(:, :) = repmat(hb(k).ZData(:, 2), 1, 4);
end
ylim([1 inf])
subplot(1,2,2);
hb = bar3(y);
title("P Y Bottom View"); xlabel("X"); ylabel("Y"); colormap("jet");
for k=1:numel(hb)
hb(k).CData(:, :) = repmat(hb(k).ZData(:, 2), 1, 4);
end
hAx=gca;
view([270, -90]);
hAx.XDir='reverse';
ylim([1 inf])
figure
hb = bar3(y);
title("P Y 3D View"); xlabel("X"); ylabel("Y"); colormap("jet");
for k=1:numel(hb)
hb(k).CData(:, :) = repmat(hb(k).ZData(:, 2), 1, 4);
end
set(hb,{'FaceAlpha'},{0.5});
view(-37.5,15)
I was intrigued by the question about coloring the bottoms of the bars -- the bottoms are colored the same as the lowest bar sides as the bottom view above shows. The confusing factor here alone seems to be that there is no mechanism to not have a bar drawn for every element of the array whereas it appears what would really be desired would be to be able to draw only bars with values greater than some threshold? In my experiments above about setting the "zero" plane to transparent, it looks like almost all of those are abs(P)<1 and then there is a very sizable jump to the next smallest values.
I haven't experimented, but I think one would have to draw each bar individually as a totally separate object to be able to do that. MATLAB graphics functions, like array operations require regular grids with very few exceptions.

Accedi per commentare.

Risposta accettata

Jim McIntyre
Jim McIntyre il 30 Lug 2025
So, a somewhat less-than-satisfying workaround is to plot the bar3, set the bar colors, and then force the bar heights to a small positive value. This shows all the colors (compares to the surf plots at the top of my question), but loses the ability to orbit the plot to see the bar heights as magnitudes. I'd still like to find a way to turn off the "floor."
load('https://www.mathworks.com/matlabcentral/answers/uploaded_files/1838126/StressData.mat');
%% Plot data as individual pixels
figure;
subplot(1, 3, 1);
b = bar3(z, 1);
title("Contact Pressure"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k = 1 : length(b)
b(k).FaceColor = 'flat';
b(k).CData(:, :) = repmat(b(k).ZData(:, 2), 1, 4);
b(k).ZData(abs(b(k).ZData) > 0) = 0.01;
end
zlim([0 1]);
subplot(1, 3, 2);
b = bar3(x, 1);
title("X pressure"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k = 1 : length(b)
b(k).FaceColor = 'flat';
b(k).CData(:, :) = repmat(b(k).ZData(:, 2), 1, 4);
b(k).ZData(abs(b(k).ZData) > 0) = 0.01;
end
zlim([0 1]);
subplot(1, 3, 3);
b = bar3(y, 1);
title("Y pressure"); xlabel("X"); ylabel("Y"); colormap("jet"); view([270, 90]);
for k = 1 : length(b)
b(k).FaceColor = 'flat';
b(k).CData(:, :) = repmat(b(k).ZData(:, 2), 1, 4);
b(k).ZData(abs(b(k).ZData) > 0) = 0.01;
end
zlim([0 1]);

Più risposte (2)

dpb
dpb il 7 Set 2025
Modificato: dpb il 7 Set 2025
OK, I finally went and looked at File Exchange because I figured somebody else had already gotten fed up with bar3 issues and it turns out there was a very good start in <scatterbar3> of a 3D bar plot that puts individual bars at any location wanted.
It didn't have a way to deal with NaN data, but it didn't take much to make at least a first pass to treat it as do other MATLAB graphics functions...
function scatterbar3(X,Y,Z,width)
%SCATTERBAR3 3-D scatter bar graph.
% SCATTERBAR3(X,Y,Z,WIDTH) draws 3-D bars of height Z at locations X and Y with width WIDTH.
%
% X, Y and Z must be of equal size. If they are vectors, than bars are placed
% in the same fashion as the SCATTER3 or PLOT3 functions.
%
% If they are matrices, then bars are placed in the same fashion as the SURF
% and MESH functions.
%
% The colors of each bar read from the figure's colormap according to the bar's height.
% By Mickey Stahl - 2/25/02, Engineering Development Group
% Revised Duane Bozarth - 09/07/25, DB Technical Services
[r,c]=size(Z);
for j=1:r
for k=1:c
if isfinite(Z(j,k))
drawbar(X(j,k),Y(j,k),Z(j,k),width/2)
end
end
end
view(3) % dpb set default 3D perspective
grid on % dpb to add grid by default, too
minZ=min(Z,[],'all','omitnan'); maxZ=max(Z,[],'all','omitnan'); % dpb to handle NaN (gracefully?)
zlim=[minZ maxZ];
if zlim(1)>0,zlim(1)=0;end
if zlim(2)<0,zlim(2)=0;end
minX=min(X,[],'all','omitnan'); maxX=max(X,[],'all','omitnan'); % original didn't handle nan -- dpb
minY=min(Y,[],'all','omitnan'); maxY=max(Y,[],'all','omitnan');
% add if hold on to set additional points but don't make axis range smaller from existing -- dpb
if ishold(gca)
lim=axis();
minX=min(minX,lim(1)); maxX=max(maxX,lim(2));
minY=min(minY,lim(3)); maxY=max(maxY,lim(4));
zlim=[min(minX,lim(5)) max(maxZ,lim(6))];
axis([minX maxX minY maxY zlim])
caxis(zlim)
else
axis([minX-width maxX+width minY-width maxY+width zlim])
caxis([minZ maxZ])
end
function drawbar(x,y,z,width)
h(1)=patch([-width -width width width]+x,[-width width width -width]+y,[0 0 0 0],'b');
h(2)=patch(width.*[-1 -1 1 1]+x,width.*[-1 -1 -1 -1]+y,z.*[0 1 1 0],'b');
h(3)=patch(width.*[-1 -1 -1 -1]+x,width.*[-1 -1 1 1]+y,z.*[0 1 1 0],'b');
h(4)=patch([-width -width width width]+x,[-width width width -width]+y,[z z z z],'b');
h(5)=patch(width.*[-1 -1 1 1]+x,width.*[1 1 1 1]+y,z.*[0 1 1 0],'b');
h(6)=patch(width.*[1 1 1 1]+x,width.*[-1 -1 1 1]+y,z.*[0 1 1 0],'b');
set(h,'facecolor','flat','FaceVertexCData',z)
end
end
load StressData y
z=y; % rename height variable
[x,y]=meshgrid(1:height(y),1:width(y)); % doesn't implement the Z only input option
colormap(jet)
%subplot(1,2,1)
scatterbar3(x,y,z,1)
title('Include all data -- looks like bar3')
%subplot(1,2,2)
figure
z(abs(z)<=1)=nan; % remove the very small (relatively speaking) "baseline" data
scatterbar3(x,y,z,1)
colormap(jet)
colormap(jet)
title('Exclude baseline data -- no baseline mesh occlusion')
I think this is more like what you were hoping to be able to produce? There is another that also has high marks that says the individual bars are addressable, but I didn't look at it. The big disadvantage of this one is that each bar consists of six patch objects and the author didn't make any attempt to organize and save them but reuses the handles. This means it isn't convenient to get to any specific bar to make any other changes as is. But, it wouldn't take too much to save the handles in their groups of six and then by bar location which would make it possible to address each bar face.
I attached the updated version; the pasted copy has the additional help text expunged to shorten it...I'm not sure what the protocol is on FEX about putting up the derivative; it's unfortunate there's no way to make make contributions to existing posts.

Steven Lord
Steven Lord il 29 Lug 2025
load('https://www.mathworks.com/matlabcentral/answers/uploaded_files/1838126/StressData.mat')
whos
Name Size Bytes Class Attributes x 24x24 4608 double y 24x24 4608 double z 24x24 4608 double
If I understand what you're trying to do, you want to use the Z data for both the elevation and the color of the face?
surf(z, z);
colormap('jet')
colorbar
Let's look at it from above.
figure
surf(z, z);
colormap('jet')
colorbar
view(2)
What if your data included both positive and negative values? I'll also move the "outer ring" so that some of the values in your data are above that ring and some are below.
figure
zm = z - 150;
zm(z < 10) = -50; % move the "bottom" up so the outer ring is not on the floor
surf(zm, zm);
colormap('jet')
colorbar
And if you were to view it from below?
figure
surf(zm, zm);
colormap('jet')
colorbar
view([0 0 -1])
If that's not what you're looking for, please provide more details (in word rather than in code or pictures) of what you'd like to see to help me better understand your goal.
  1 Commento
Jim McIntyre
Jim McIntyre il 30 Lug 2025
Modificato: Jim McIntyre il 30 Lug 2025
You say: What if your data included both positive and negative values? I'll also move the "outer ring" so that some of the values in your data are above that ring and some are below.
That is exactly the case for the x and y datasets.
Yes, I want to use the value data (x, y, and z for the different plots) for both the color and the elevation. The problem is that bar3 seems to include a "floor" color in the zero plane. Is there a way to turn that off so that I can if I set the color of all faces of the bar to the same, I can make the color of the bottom of each bar the same as the top?
Alternatively, if there's a way to plot just the colored squares without the elevation, I'd live with that.

Accedi per commentare.

Categorie

Scopri di più su Data Distribution Plots in Help Center e File Exchange

Prodotti


Release

R2025a

Community Treasure Hunt

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

Start Hunting!

Translated by