Additional axis on 3D surface in MATLAB - matlab

Suppose I plot a 3d graph using
[X,Y,Z] = peaks(25);
figure
surf(X,Y,Z);
How can I add addtional x and y axis with labels to the z=0 plane like the red ones shown in this picture? I would like to keep the original axis as is.

I would just draw my own fake axis. I'm not sure there is a better solution, but this one works.
[X,Y,Z] = peaks(25);
figure
surf(X,Y,Z);
hold on;
%define the plot limits
xlim_arr = [-4 4];
ylim_arr = [-5 5];
zlim_arr = [-10 10];
%fix the limits of the real axes
xlim(xlim_arr);
ylim(ylim_arr);
zlim(zlim_arr);
%add grid and labels through all x-points
for i=xlim_arr(1):xlim_arr(2)
plot3([i i],[ylim_arr(1) ylim_arr(2)], [0 0], 'Color', [0.7 0.7 0.7], 'LineWidth',0.4);
text(i, ylim_arr(1)-0.4, 0, num2str(i));
text(i, ylim_arr(2)+0.4, 0, num2str(i));
end
%add grid and labels through all y-points
for i=ylim_arr(1):ylim_arr(2)
plot3([xlim_arr(1) xlim_arr(2)], [i i], [0 0], 'Color', [0.7 0.7 0.7], 'LineWidth',0.4);
text(xlim_arr(1)-0.4, i, 0, num2str(i));
text(xlim_arr(2)+0.4, i, 0, num2str(i));
end
%add the bold frame to highlight the fake axes
px = [xlim_arr(1) xlim_arr(1) xlim_arr(2) xlim_arr(2) xlim_arr(1)];
py = [ylim_arr(1) ylim_arr(2) ylim_arr(2) ylim_arr(1) ylim_arr(1)];
pz = [0 0 0 0 0];
plot3(px,py,pz, 'k', 'LineWidth', 0.5);

Related

Plotting bar and reverse bar into the Y axis

I am trying to plot two sequences of bars, using the same y-axis. One of the sequences goes on the bottom, the other goes on top (from top to bottom), and the y axis is split into two (half for the bottom bars and half for the top bars). The bottom bars use one color, like green, and the top bars use another color, red, and the y axis uses the respective colors on each half on the axis.
Example:
Question: How can I split the y-axis in the way represented in the figure above?
Thanks for helping! =D
You could create this effect by layering three axes and adjusting their properties accordingly. For example:
% The larger left axes:
hLeft = axes('Position', [0.1 0.1 0.8 0.8], ... % Normalized position
'XLim', [0 11], 'YLim', [0 3], ... % Set desired limits
'Box', 'off'); % Turn off outline
hLeft.XLabel.String = 'Time'; % Add an x label
hLeft.YLabel.String = 'Line'; % Add a y label
hLeft.Title.String = 'Bar-bar-plot'; % Add a title
hLine = line(hLeft, 1:10, rand(1,10)+1, 'Color', 'k'); % Plot a sample line
% The lower right axes:
hLower = axes('Position', [0.1 0.1 0.8 0.4], ... % Position over bottom half
'XLim', [0 11], 'YLim', [0 1.5], ... % Set desired limits
'YColor', [0 0.5 0], 'Color', 'none', ... % Change colors
'YAxisLocation', 'right'); % Position y axis
hLower.XAxis.Visible = 'off'; % Turn of x axis
hLower.YLabel.String = 'Bar 1'; % Add a y label
hold on;
hBarLower = bar(hLower, 1:10, rand(1,10), ... % A sample bar plot
'FaceColor', 'none', 'EdgeColor', [0 0.5 0]);
% The upper right axes:
hUpper = axes('Position', [0.1 0.5 0.8 0.4], ... % Position over top half
'XLim', [0 11], 'YLim', [0 1.5], ... % Set desired limits
'YColor', [1 0 0], 'Color', 'none', ... % Change colors
'YAxisLocation', 'right', ... % Position y axis
'YDir', 'reverse'); % Reverse y axis
hUpper.XAxis.Visible = 'off'; % Turn off x axis
hUpper.YLabel.String = 'Bar 2'; % Add a y label
hold on;
hBarUpper = bar(hUpper, 1:10, rand(1,10), ... % A sample bar plot
'FaceColor', 'none', 'EdgeColor', [1 0 0]);
% Add a legend:
hLegend = legend(hUpper, [hLine hBarLower hBarUpper], ...
{'line', 'bar 1', 'bar 2'}, 'Color', 'w');
And here's the plot:
You can reposition the legend as needed by left clicking on it and dragging it to the best spot.

3 x-axis in matlab plot?

I need to plot a figure with 3 x-axes. Each axis is linked to the other by a mathematical formula. I want to do this because the x value can be seen as wavelength [nm], velocity [m/s] or energy [eV] and I want the reader to not have to convert it themselves on each graph.
I searched online and only found something for 2 x-axes, but no more.
Edit: I am using version R2011a.
So it should look like this, which I (obviously) didn't create in MATLAB:
Thanks in advance!
As shown in this answer, you can create a new axes object with near-zero height, so that it is essentially just the x-axis. Be aware that all actual plots must be done on the first axes as this is the area you can see!
Demo code:
% Create some plotting data and plot
x = 0:0.1:2*pi; y = sin(x);
% Plot, can specify line attributes (like LineWidth) either
% - inline: plot(x,y,'linewidth',2)
% - after: p1 = plot(x,y); p1.LineWidth = 2;
plot(x,y);
% Get current axes object (just plotted on) and its position
ax1 = gca;
axPos = ax1.Position;
% Change the position of ax1 to make room for extra axes
% format is [left bottom width height], so moving up and making shorter here...
ax1.Position = axPos + [0 0.3 0 -0.3];
% Exactly the same as for plots (above), axes LineWidth can be changed inline or after
ax1.LineWidth = 2;
% Add two more axes objects, with small multiplier for height, and offset for bottom
ax2 = axes('position', (axPos .* [1 1 1 1e-3]) + [0 0.15 0 0], 'color', 'none', 'linewidth', 2);
ax3 = axes('position', (axPos .* [1 1 1 1e-3]) + [0 0.00 0 0], 'color', 'none', 'linewidth', 2);
% You can change the limits of the new axes using XLim
ax2.XLim = [0 10];
ax3.XLim = [100 157];
% You can label the axes using XLabel.String
ax1.XLabel.String = 'Lambda [nm]';
ax2.XLabel.String = 'Velocity [m/s]';
ax3.XLabel.String = 'Energy [eV]';
Output:
Edit:
Before the 2014b graphics changes you will need to make a couple of tweaks for getting and setting axes properties. The equivalent code would more heavily use the set command, and look something like this:
x = 0:0.1:2*pi; y = sin(x);
plot(x,y);
ax1 = findobj(gca, 'type', 'axes')
axPos = get(ax1, 'Position');
set(ax1, 'Position', axPos + [0 0.3 0 -0.3]);
set(ax1, 'LineWidth', 2);
ax2 = axes('position', (axPos .* [1 1 1 1e-3]) + [0 0.15 0 0], 'color', 'none', 'linewidth', 2);
ax3 = axes('position', (axPos .* [1 1 1 1e-3]) + [0 0.00 0 0], 'color', 'none', 'linewidth', 2);
set(ax2, 'xlim', [0 10]);
set(ax3, 'xlim', [100 157]);
axes(ax1); xlabel('Lambda [nm]');
axes(ax2); xlabel('Velocity [m/s]');
axes(ax3); xlabel('Energy [eV]');
Here's an example of how you can do this:
msx = [1 50 60 90];
msy = [0 1 3 8];
lx = 90/4*[1 2 3 4]; % Scale the data with respect to the data that will use the "primary" X-axis
ly = [0 2 8 10];
evx = 90/19*[1 7 10 19]; % Scale the data with respect to the data that will use the "primary" X-axis
evy = [0 8 16 20];
figure
a=axes('units','normalized','position',[.1 .35 .7 .6],'xlim',[0 100],'xtick',0:10:100);
plot(lx, ly)
hold on
plot(msx, msy)
hold on
plot(evx, evy)
xlabel(a,'velocity m/s')
b=axes('units','normalized','position',[.1 .21 .7 0.000001],'xlim',[0 4],'color','none', 'xtick',0:1:10);
xlabel(b,'lambda nm');
c=axes('units','normalized','position',[.1 .10 .7 0.000001],'xlim',[0 19],'color','none', 'xtick',0:1:19);
xlabel(c,'energy eV');
For the position: specified as a four-element vector of the form [left bottom width height]. The default value of [0 0 1 1] includes the whole interior of the container. (see https://de.mathworks.com/help/matlab/ref/axes-properties.html)
Output figure:

How do I resolve this issue with 3D image visualization?

I have this 3D image data that I need to visualize. I have been able to visualize it with 2D slices using imshow3D, but I would like to see the image data in 3D space.
The code I used is as follows (courtesy: How do i create a rectangular mask at known angles?), but I can't tell why it isn't displaying:
% create input image
imageSizeX = 120;
imageSizeY = 200;
imageSizeZ = 50
% generate 3D grid
[columnsInImage, rowsInImage, pagesInImage] = meshgrid(1:imageSizeX, 1:imageSizeY, 1:imageSizeZ);
% create the sphere in the image.
centerY = imageSizeY/2;
centerX = imageSizeX/2;
centerZ = imageSizeZ/2;
diameter = 56;
radius = diameter/2;
sphereVoxels = (rowsInImage - centerY).^2 ...
+ (columnsInImage - centerX).^2 + (pagesInImage - centerZ).^2 <= radius.^2;
% change image from logical to numeric labels.
Img = double(sphereVoxels);
for ii = 1:numel(Img)
if Img(ii) == 0
Img(ii) = 2; % intermediate phase voxels
end
end
% specify the desired angle
angle = 60;
% specify desired pixel height and width of solid
width = imageSizeX;
height = imageSizeY;
page = imageSizeZ;
% Find the row point at which theta will be created
y = centerY - ( radius*cos(angle * pi/180) )
% determine top of the solid bar
y0 = max(1, y-height);
% label everything from y0 to y to be = 3 (solid)
Img(y0:y, 1:width, 1:page)=3;
% figure, imshow3D(Img);
% axis on;
% grid on;
% display it using an isosurface
fv = isosurface(Img, 0);
patch(fv,'FaceColor',[0 0 .7],'EdgeColor',[0 0 1]); title('Binary volume of a sphere');
view(45,45);
axis tight;
grid on;
xlabel('x-axis [pixels]'); ylabel('y-axis [pixels]'); zlabel('z-axis [pixels]')
Although, the solid bar is not diagonal as the figure attached below, I would expect the image to be something similar to this:
I do not know exactly what I am doing wrong here.
With regard to the problem in your code, it appears that you set points inside the sphere to 1, then set all the remaining points outside the sphere to 2, then a section through the y plane to 3. There is no value of 0 in the volume in this case, so trying to get an isosurface at the value of 0 isn't going to find anything.
However, if you'd rather create a "voxelated" Minecraft-like surface, like in your sample image showing the facets of your voxels, then I have another option for you...
First, I created a set of volume data as you did in your example, with the exception that I omitted the for loop that sets values to 2, and instead set the values of the solid bar to 2.
Next, I made use of a function build_voxels that I've used in a few 3D projects of mine:
function [X, Y, Z, C] = build_voxels(roiMask)
maskSize = size(roiMask);
% Create the ROI surface patches pointing toward -x:
index = find(diff(padarray(roiMask, [1 0 0], 'pre'), 1, 1) > 0);
[X1, Y1, Z1, C1] = make_patches([-1 -1 -1 -1], [1 1 -1 -1], [-1 1 1 -1]);
% Create the ROI surface patches pointing toward +x:
index = find(diff(padarray(roiMask, [1 0 0], 'post'), 1, 1) < 0);
[X2, Y2, Z2, C2] = make_patches([1 1 1 1], [-1 -1 1 1], [-1 1 1 -1]);
% Create the ROI surface patches pointing toward -y:
index = find(diff(padarray(roiMask, [0 1 0], 'pre'), 1, 2) > 0);
[X3, Y3, Z3, C3] = make_patches([-1 -1 1 1], [-1 -1 -1 -1], [-1 1 1 -1]);
% Create the ROI surface patches pointing toward +y:
index = find(diff(padarray(roiMask, [0 1 0], 'post'), 1, 2) < 0);
[X4, Y4, Z4, C4] = make_patches([1 1 -1 -1], [1 1 1 1], [-1 1 1 -1]);
% Create the ROI surface patches pointing toward -z:
index = find(diff(padarray(roiMask, [0 0 1], 'pre'), 1, 3) > 0);
[X5, Y5, Z5, C5] = make_patches([1 1 -1 -1], [-1 1 1 -1], [-1 -1 -1 -1]);
% Create the ROI surface patches pointing toward +z:
index = find(diff(padarray(roiMask, [0 0 1], 'post'), 1, 3) < 0);
[X6, Y6, Z6, C6] = make_patches([-1 -1 1 1], [-1 1 1 -1], [1 1 1 1]);
% Collect patch data:
X = [X1 X2 X3 X4 X5 X6];
Y = [Y1 Y2 Y3 Y4 Y5 Y6];
Z = [Z1 Z2 Z3 Z4 Z5 Z6];
C = [C1 C2 C3 C4 C5 C6];
function [Xp, Yp, Zp, Cp] = make_patches(Xo, Yo, Zo)
[Xp, Yp, Zp] = ind2sub(maskSize, index);
Xp = bsxfun(#plus, Xp, Xo./2).';
Yp = bsxfun(#plus, Yp, Yo./2).';
Zp = bsxfun(#plus, Zp, Zo./2).';
Cp = index(:).';
end
end
This function accepts a 3D matrix, ideally a logical mask of the volume region(s) to create a surface for, and returns 4 4-by-N matrices: X/Y/Z matrices for the voxel face patches and an index matrix C that can be used to get values from the volume data matrix for use in coloring each surface.
Here's the code to render the surfaces:
[X, Y, Z, C] = build_voxels(Img > 0);
rgbData = reshape([1 0 0; 1 1 0], [2 1 3]);
hSurface = patch(X, Y, Z, rgbData(Img(C), :, :), ...
'AmbientStrength', 0.5, ...
'BackFaceLighting', 'unlit', ...
'EdgeColor', 'none', ...
'FaceLighting', 'flat');
axis equal;
axis tight;
view(45, 45);
grid on;
xlabel('x-axis (voxels)');
ylabel('y-axis (voxels)');
zlabel('z-axis (voxels)');
light('Position', get(gca, 'CameraPosition'), 'Style', 'local');
And here's the plot:
Note that the sphere and bar surfaces are colored differently since they are labeled with values 1 and 2, respectively, in the volume data Img. These values are extracted from Img using C and then used as an index into rgbData, which contains red (first row) and yellow (second row) RGB triplets. This will create an N-by-1-by-3 matrix of polygon face colors.

Filling an area above a curve with many colors (matlab, surf)

I'm trying to create a figure in matlab that looks like this:
desired figure
I am doing so by: (i) assigning value points to each x,y coordinate, (ii) plotting a surf, and (iii) change the view point so the third axis is not seen. Here is the code:
x = linspace(0, 1, 10);
y = linspace(0, 1, 10);
z = linspace(0, 1, 10);
z = repmat(z, 10, 1);
z = flipud(triu(z));
z(z==0) = nan;
hold off
surf(x, y, z, 'linestyle', 'none')
colormap([linspace(0.39, 1, 20)',linspace(0.58, 0.25, 20)', linspace(0.93, 0.25, 20)']);
colorbar
xlim([x(1) x(end)])
shading interp
view([90 -90])
hold on
plot(x, 1-y, 'linewidth', 2)
I get the following figure: matlab figure I get
As you can see, there a lot of white spaces above the line which I would like to be in color as well. Unfortunately, I cannot add any more grid points as calculating the actual value of the points takes a lot of time (unlike the example above).
Is there a way to have matlab draw colors in those white spaces as well?
Thanks!
You can try to use patch function to create filled polygon.
See http://www.mathworks.com/help/matlab/ref/patch.html
Try the following code:
vert = [0 1;1 1;1 0]; % x and y vertex coordinates
fac = [1 2 3]; % vertices to connect to make triangle
fvc = [1 0 0; 1 1 1; 0 0 1];
patch('Faces',fac,'Vertices',vert,'FaceVertexCData',fvc,'FaceColor','interp');
Result is close:
I was managed to get closer to the desired figure:
close all
x = linspace(0, 1, 10);
y = linspace(0, 1, 10);
%colorbar
xlim([x(1) x(end)])
%Fill rectangle.
vert = [0 0; 1 0; 1 1; 0 1]; % x and y vertex coordinates
fac = [1 2 3 4]; % vertices to connect to make squares
%patch('Faces',fac,'Vertices',vert,'FaceColor','red')
fvc = [1 0 0; 0.6 0.7 1; 0.6 0.7 1; 1 0 0]; %Color of vertices (selected to be close to example image).
patch('Faces',fac,'Vertices',vert,'FaceVertexCData',fvc,'FaceColor','interp')
hold on
%Fill lower triangle with white color.
vert = [0 0;0 1;1 0]; % x and y vertex coordinates
fac = [1 2 3]; % vertices to connect to make triangle
fvc = [1 1 1; 1, 1, 1; 1, 1, 1]; %White color
patch('Faces',fac,'Vertices',vert,'FaceVertexCData',fvc,'FaceColor','interp');
plot(x, 1-y, 'linewidth', 2)
set(gca,'Xtick',[],'Ytick',[]); %Remove tick marks
Result:
Thank you Rotem! I wasn't aware of the patch function and indeed it solved the issue!
The colors on the actual figure I'm trying to achieve are not linear, so I just used patch for all the empty triangles. Here is the adjusted code I use for the simple example (again, this is just a bit more general just to be able to have non linear colors in the area above the curve):
x = linspace(0, 1, 10);
y = linspace(0, 1, 10);
z = linspace(0, 1, 10);
z = repmat(z, 10, 1)+0.1;
z = flipud(triu(z));
z(z==0) = nan;
z = z-0.1;
hold off
surf(x, y, z, 'linestyle', 'none')
colormap([linspace(0.39, 1, 20)',linspace(0.58, 0.25, 20)', linspace(0.93, 0.25, 20)']);
colorbar
xlim([x(1) x(end)])
shading interp
view([90 -90])
hold on
patch_cor_y = kron((length(y):-1:1)', ones(3, 1));
patch_cor_x = kron((1:length(x))', ones(3, 1));
patch_cor = [y(patch_cor_y(2:end-2))', x(patch_cor_x(3:end-1))'];
patch_path = reshape(1:length(patch_cor),3, length(patch_cor)/3)';
patch_col = z(sub2ind(size(z), patch_cor_x(3:end-1), patch_cor_y(2:end-2)));
patch('Faces',patch_path,'Vertices',patch_cor,'FaceVertexCData',patch_col,'FaceColor','interp', 'EdgeColor', 'none');
plot(x, 1-y, 'linewidth', 2)
The figure achieved: figure

MATLAB: Scatterplot - points with different shapes according to position

The properties of my figure I want to build are the following:
The figure shows 200 points. The points above the diagonal should be shown with red stars and the one below the diagonal, with blue triangles.
This is what I've managed to do so far
x=[0 1];
y=[0 1];
line(x,y, 'linewidth', 1);
hggroup = scatter(rand(100,1),rand(100,1));
axis tight;
axis square;
title('Scatterplot')
Could you help me with that? Thanks in advance.
How about this:
line([0 1],[0 1], 'linewidth', 1);
hold on
x = rand(100,1);
y = rand(100,1);
idx = y>x;
scatter(x(idx),y(idx),'r*');
scatter(x(~idx),y(~idx),'b^');
axis tight;
axis square;
title('Scatterplot')