# Plotting two surface plots in the same figure using two different colormaps

81 views (last 30 days)
Ira on 2 Aug 2024
Commented: Adam Danz on 16 Aug 2024
Hello,
I have been trying to plot two surface plots on top of each other in the same figure - one that is semitransparent, and one that is opaque - using two colormaps to display them. Specifically, I would like surface plot 1 to use the 'turbo' colormap, and surface plot 2 to use the 'gray' colormap and be semitransparent. So far, I have read through a few people with similar problems but their solutions don't help me (most are requesting only 1 specific color per each map and not an entire colormap). How can I do this? Thanks. I cannot provide code due to proprietary information, so any example code given, I will gladly fit to my own.
Umar on 2 Aug 2024
@Walter, thanks for pointing this out.
Umar on 2 Aug 2024
Edited: Umar on 2 Aug 2024

Hi @Ira,

The updated code snippet demonstrates adjusting the colormaps and overlaying the axes,and efficiently presents two distinct surfaces with varying color representations. Here is updated code along with attached plot.

% Define the colormap for the second axis

colormap(gray);

% Generate meshgrid data

[X, Y] = meshgrid(-2:0.1:2);

% Sample data for surface plot 1

Z1 = X.^2 - Y.^2;

% Sample data for surface plot 2

Z2 = sin(X) + cos(Y);

% Create a figure

figure;

% Create subplots for each surface plot

ax1 = subplot(1,2,1); % Axes for surface plot 1

ax2 = subplot(1,2,2); % Axes for surface plot 2

% Plot the first surface on the first axis

surf(ax1, X, Y, Z1, 'FaceColor', 'interp', 'EdgeColor', 'none');

colormap(ax1, turbo);

% Plot the second surface on the second axis

surf(ax2, X, Y, Z2, 'FaceColor', 'interp', 'EdgeColor', 'none', 'FaceAlpha', 0.5);

% Adjust the colormap for the second axis

colormap(ax2, gray);

% Overlay the second axis on top of the first axis

set(ax2, 'Position', get(ax1, 'Position'));

% Hide the second axis

axis(ax2, 'off');

Replacing Z1 & Z2 data with the following data below will display the attached plot below.

Z1 = peaks(41)-5; % Data for the first surface plot with correct dimensions

Z2 = peaks(41)-5; % Data for the second surface plot with correct dimensions

Walter Roberson on 2 Aug 2024
You have two choices:
1. Use seperate plotting axes. Each axes can have its own colormap. Make sure you pass the axes handle into the colormap() calls. You can ax2 = axes('Position', get(ax1, 'Position')) to place the second axes exactly where the first axes is, and then be careful to pass ax1 or ax2 as appropriate to each graphics operation.
2. Alternately, plot normally with one colormap, and then call https://www.mathworks.com/matlabcentral/fileexchange/7943-freezecolors-unfreezecolors to convert the graphics object from indexed to RGB. Once the object is converted to RGB, colormap stops affecting it, so you can then go ahead and colormap the second palette.
Sean Cupitt on 2 Aug 2024
Choice number 1 above would look like this, building on Umar's original data:
[X, Y, Z1] = peaks(30); % Data for the first surface plot
Z2 = peaks(30) - 5; % Data for the second surface plot
figure;
ax1 = axes; % create an axis that will hold the first surface plot
s1 = surf(ax1, X, Y, Z1, 'FaceColor', 'interp', 'EdgeColor', 'none');
ax2 = axes; % create an axis for the second surface plot
s2 = surf(ax2, X, Y, Z2, 'FaceColor', 'interp', 'EdgeColor', 'none', 'FaceAlpha', 0.5);
colormap(ax1,'turbo'); colorbar; % change the color of s1
colormap(ax2,'gray'); % change the color of s2
ax2.Color = 'none'; % set the background color for ax2 to transparent
% make sure they inhabit the same space on the figure
ax2.Position = ax1.Position;
% make the scaling/rotation consistent
Walter Roberson on 2 Aug 2024
That code looks good, @Sean Cupitt

Adam Danz on 2 Aug 2024
Edited: Adam Danz on 2 Aug 2024
Assign two surface objects two different colormaps using Truecolor arrays
The recommended approach is to create an overlayed axes so that each surface gets its own axes and a colormap can be applied to each axes (doc link).
But let's explore a different solution. Instead of mapping colors, set the surface's color data using a Truecolor array. I've included a little helper function colormapToTruecolor to convert color map values into Truecolor values.
% Create surface data
R=20;
r=5;
[a, b] = meshgrid(0:20/180*pi:2*pi);
X=((R-r)+r*cos(a)).*cos(b);
Y=((R-r)+r*cos(a)).*sin(b);
Z=r.*sin(a);
% create the first surface using colors from the winter colormap
fig = figure();
tcl = tiledlayout(fig,1,1);
ax = nexttile(tcl);
surf1Colormap = winter();
tc = colormapToTruecolor(surf1Colormap,Z);
hsurf1 = surf(ax,X,Y,Z,tc,'FaceColor','interp','EdgeAlpha',0.3);
% create second surface using colors from the autumn colormap
hold on
surf2Colormap = spring();
tc = colormapToTruecolor(surf2Colormap,Z);
hsurf2 = surf(ax,X,Y+20,Z,tc,'FaceColor','interp','EdgeAlpha',0.3);
rotate(hsurf2,[0 1 0],45)
axis equal
view([28,24])
% Assign colormaps (which wont' affect the surfaces) and add colorbars
% A second, hidden axes is needed to host the second colormap.
axHidden = axes(tcl,'visible','off','HandleVisibility','off');
colormap(ax,surf1Colormap)
colormap(axHidden,surf2Colormap)
cb1 = colorbar(ax);
cb1.Layout.Tile = 'east';
cb1.Label.String = 'Surface 1';
cb2 = colorbar(axHidden);
cb2.Layout.Tile = 'east';
cb2.Label.String = 'Surface 2';
function tc = colormapToTruecolor(map,ZData)
% map is a n-by-3 colormap matrix
% ZData is a k-by-w matrix of ZData (or CData, I suppose)
% tc is a kxwx3 Truecolor array based on map and ZData values.
tcIdx = round(rescale(ZData,1,height(map)));
tc = reshape(map(tcIdx,:),[size(ZData),3]);
end
Why this is better than using axes overlays
Sometimes this problem is solved by overlaying two axes, assinging a surface and colormap to each axes, and making the top axes invisible. A lot of care must be taken to get this right. Several properties must match between the two sets of axes, the most important being axis position, axis limits, and axis direction, and DataAspectRatio, but other property mismatches between the two sets of axes can thrown off the correspondance. Then there's axes interactions. If you pan or zoom, the axes should move together. linkaxes along with linkprop can help with that but neither respond to pressing the restore button on the axes toolbar.
The approach above using Truecolor arrays requires a second invisible axes but its only purpose is to host the second colormap so the figure can have two colorbars. Importantly, the two objects are in the same axes so no wizardry is needed to share the same data space.
Also, the single-axes Truecolor approach allows for surfaces to intersect! The two figure below contain the same surfaces and the same viewing angles but the one on the left uses axes overlays which prevents the surfaces from intersecting!
OST on 16 Aug 2024
Adam Danz on 16 Aug 2024
It's at the end of the code block in my answer. Here's a copy:
function tc = colormapToTruecolor(map,ZData)
% map is a n-by-3 colormap matrix
% ZData is a k-by-w matrix of ZData (or CData, I suppose)
% tc is a kxwx3 Truecolor array based on map and ZData values.
tcIdx = round(rescale(ZData,1,height(map)));
tc = reshape(map(tcIdx,:),[size(ZData),3]);
end

### Categories

Find more on Lighting, Transparency, and Shading in Help Center and File Exchange

R2024a

### Community Treasure Hunt

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

Start Hunting!