Adjusting the transparency of a contour plot using a gradient of alpha values

118 views (last 30 days)
Hi,
I'm drawing a contourplot .
hLines = 5;
[~,h] = contour(X, Y, Z,hLines,'Fill','on');
For the plot I would like to adjust the alpha values decreasingly. Meaning, the face of the lower Z-Values is almost transparent, whereas the peak should be completely opaque.
If found a possible solution here submitted. And so far it works fine. But here comes the problem. If I run the code, everythings looks fine, but if I adjust the contour plot e.g. by moving it etc. it goes back to it's default alpha values! It seems as if matlab sort of 'overwrites' my changes the second it refreshes the plot.
Does anyone know any way to adjust the alpha values of the faces and make them keep the transparencies?
Here is the entire section that I'm using.
hLines = 5;
[~,h] = contour(X, Y, Z,hLines,'Fill','on');
drawnow;
title('Inverse 2D Laplace Spectrum','FontSize',20,'FontWeight','bold','Interpreter','none');
ylh = ylabel('T1-Values [s]','FontSize',12,'FontWeight','bold');
xlh = xlabel('T2-Values [s]','FontSize',12,'FontWeight','bold');
set(gca,'XScale', 'log');
set(gca,'YScale', 'log');
grid on;
grid minor;
colorbar;
hFills = h.FacePrims;
[hFills.ColorType] = deal('truecoloralpha');
AlphaGradient=exp(linspace(log(10),log(200),hLines)); %The actual values that I want to use for the alpha values
for idx = 1:numel(hFills)
hFills(idx).ColorData(4) = AlphaGradient(idx);
end
So this is how it looks like directely after the adjustments and the way it actually want it to look...
Transparent plot
And this is how it looks like if I move it just a little bit...

Answers (2)

Will Grant
Will Grant on 31 Aug 2021
Edited: Will Grant on 31 Aug 2021
Here is a simplified version of what works for me in R2020a to keep the transparency you want even after moving / zooming / etc.
% Create the contour object.
ax = uiaxes();
[~, contourObj] = contourf(ax, peaks);
% This is the secret that 'keeps' the transparency.
eventFcn = @(srcObj, e) updateTransparency(srcObj);
addlistener(contourObj, 'MarkedClean', eventFcn);
% Elsewhere in script, a separate file, or another method of your class.
function updateTransparency(contourObj)
contourFillObjs = contourObj.FacePrims;
for i = 1:length(contourFillObjs)
% Have to set this. The default is 'truecolor' which ignores alpha.
contourFillObjs(i).ColorType = 'truecoloralpha';
% The 4th element is the 'alpha' value. First 3 are RGB. Note, the
% values expected are in range 0-255.
contourFillObjs(i).ColorData(4) = 180;
end
end
It appears that matlab always sets the ColorType property of those FacePrims back to truecolor whenever it updates the object. So the trick to keeping the transparency as you want is to put the transparency adjusting code in a function, and set that function as a callback to the MarkedClean event on the contour object.
The beauty of this is that the addlistener() version of creating a listener bundles the function handle and listener object into the contour itself. So you don't need to worry about the lifetime of the listener object (which can be a pain). Whenever you call a cla() or close the axes, the axes, contour, and all children are deleted, including the listener object.
Note that my eventFcn sends in the srcObj to updateTransparency(). Matlab graphics events always pass in the originating object of the event. Since we added the event right to the contour object, the contour is the object the event handler will get. It just ignores the 'event object' property. Event callbacks are required to accept those two parameters, or else you'll get a "not enough input arguments" error.

darova
darova on 2 Apr 2020
Here is my today achievement
clc,clear
opengl software
n = 1000;
[X,Y,Z] = peaks(20);
% surf(X,Y,Z,'facecolor','none')
hold on
for lev = 1:2:7
[~,h] = contourf(X,Y,Z,[1 1]*lev); % create contour at some level
h1 = get(h,'children');
h2 = copyobj(h1,gca); % create copy for white surface
set(h2,'facecolor','w') % copy object - white
set(h1,'facealpha',lev/7) % facealpha
set(h1,'zdata',lev*ones(n,1)) % assign some height for surface
set(h2,'zdata',lev*ones(n,1)-0.5) % bottom white surface
end
hold off
axis vis3d
  12 Comments
Alan Meier
Alan Meier on 16 Jul 2020
I'm using R2018 a and when I run your answer code snippet, I get the same result as TheoBoveri. When I change the code as in the second comment I get the same error as TheoBoveri again.
Using the code from the next comment and the provided .fig file I get the following result because the length(h1) is empty for me as well:
Anf finally the result for the code in the last comment:
f1 =
Figure (1) with properties:
Number: 1
Name: ''
Color: [0.9400 0.9400 0.9400]
Position: [704 518 560 420]
Units: 'pixels'
Show all properties
ans =
'figure'
h0 =
Axes with properties:
XLim: [-3 3]
YLim: [-3 3]
XScale: 'linear'
YScale: 'linear'
GridLineStyle: '-'
Position: [0.1300 0.1100 0.7750 0.8150]
Units: 'normalized'
Show all properties
ans =
'axes'
h0 =
Contour with properties:
LineColor: 'flat'
LineStyle: '-'
LineWidth: 0.5000
Fill: 'off'
LevelList: [-6 -4 -2 0 2 4 6]
XData: [20×20 double]
YData: [20×20 double]
ZData: [20×20 double]
Show all properties
ans =
'contour'
h1 =
0×0 empty GraphicsPlaceholder array.
ans =
[]
DGM
DGM on 18 Aug 2021
Edited: DGM on 19 Aug 2021
I'm pretty sure this hasn't worked since R2014b. It works in R2009b and not in R2015b. Contour objects don't have patch children anymore, and consequently, they don't have FaceAlpha.
You could use the x,y data encoded in the 'ContourMatrix' property to build a legacy-style contour using patch objects, but you'll have to bear in mind that 'ContourMatrix' is a sequential description of all the regions in the object. You'd have to loop through the vertex lists in ContourMatrix and create patch objects for each list of vertices.
EDIT:
okay here. This should work for R2014b+ (tested in R2015b and R2019b):
n = 1000;
[X,Y,Z] = peaks(20);
L = 1:2:7;
ct = jet(numel(L));
hold on
for k = 1:numel(L)
[~,h1] = contourf(X,Y,Z,[1 1]*L(k)); % create contour at some level
cm = get(h1,'contourmatrix').';
delete(h1);
while ~isempty(cm)
numvertx = cm(1,2);
vtx = cm(2:numvertx+1,:);
cm = cm((numvertx+2):end,:);
p1 = patch(vtx(:,1),vtx(:,2),ct(k,:),'facealpha',L(k)/7);
p2 = patch(vtx(:,1),vtx(:,2),'w');
set(p1,'zdata',L(k)*ones(n,1)) % assign some height for surface
set(p2,'zdata',L(k)*ones(n,1)-0.5) % bottom white surface
end
end
hold off
view(3)
grid on
axis equal
xlim([-3 3])
ylim([-3 3])
zlim([0 8])
You can of course get a 2D view by doing view(2)
That said, I don't know that this really answers the question. The end result is a gradient, but the white padding means it's not transparent overall. The same result can be had without the white padding, by simply doing a weighted average of each patch color and [1 1 1].
Simply omitting the white padding and stacking the transparent patches doesn't really work, as they occlude each other. The effective color and transparency of each region would be affected by all the patches below it.
It might be possible to do it with polyshape by making holes in the objects, but it might not be fun determining which objects need to be inset into which.

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!