Plotting a coloured 3D figure in MATLAB - matlab

I have 3 axes: X of size N, Y of size M and Z of size O, which correspond to the coordinates of my data.
I have a matrix: DATA of size MxNxO, which corresponds to the module for each points.
I would like to plot the MATLAB figure of coordinate X, Y, Z and color the point depending of the value of the matrix DATA of size MxNxO.
I tried lots of functions such as scatter3, surf, plot3, but none is working as I wanted.
This is what I tried:
n = 10;
x = linspace(0,10,n);
y = linspace(0,10,n);
z = linspace(0,10,n);
DATA = randn(n,n,n);
scatter3(x,y,z,DATA);
This code didn't work because DATA is not the same size as x, y, z. I also tried with:
[X,Y,Z] = ndgrid(x,y,z)
scatter3(X,Y,Z,DATA);
but this didn't work either.

The trick with scatter3() is to "unroll" your matrices to a column vector, and don't forget that the fourth argument is size, rather than colour:
n = 10;
x = linspace(0,10,n);
y = linspace(0,10,n);
z = linspace(0,10,n);
[X,Y,Z] = ndgrid(x,y,z);
DATA = randn(n,n,n);
% here 3 is the size. You can set it to a different constant, or vary it as well
scatter3(X(:), Y(:), Z(:), 3, DATA(:));
Results in
You can colour a surface, see its documentation, however, it doesn't seem to make much sense in your case, given you have a full cube of data points. A surface is 2D, whereas your data is 3D. For a 2D surface, simply follow the example in the docs:
n = 10;
x = linspace(0,10,n);
y = linspace(0,10,n);
Z = rand(n);
DATA = randn(n);
surf(x, y, Z, DATA);
Images rendered in R2007b, syntax cross-checked with the documentation.
If you've got a surface defined by an M -by- 4 array containing X, Y, Z and Data, you can use delaunay() to create a Delaunay triangulation of your points and then trisurf() to plot that. Note that this still requires a 2D surface, albeit it can vary in three dimensions. The cube of data in your example still doesn't make sense to plot as a surface, even with this method.

Related

Non-rectangular meshgrids in MATLAB

I want to create a non-rectangular meshgrid in matlab.
Basically I have a polygon shaped feasible set I need to make a grid of in order to interpolate 3D data points in this set. The function for interpolation is given and requires finite (x, y, z) inputs. Where x is nx1, y is 1xm and z is nxm. Right now I have the mesh set up with linspace and set all NaN (infeasible) values to 0 before using my function, which is wrong of course (third figure).
Is there a simple solution for this?
I added a picture illustrating what I'm currently doing: First plot is the feasible set, second plot are solved sample data points in this set and third plot is the interpolation (currently still with rectangular meshgrid and NaN = 0). What I need is a meshgrid looking like the first figure (red polygon) instead of a rectangular one. In the third plot you can see that the rectangular meshgrid in combination with setting NaN to 0 (=infeasible values, not included in the red polygon set) results in a wrong interpolation along the edges, because it includes infeasible regions.
Here is my code using a rectangular meshgrid:
figure (2) %sample data
plot3(X0(1,:), X0(2,:), U, 'x')
%X0(1,:) and X0(2,:) are vectors corresponding to the Z-Values (blue sample data)
%X0 and U are in the feasible set (red polygon)
xv = linspace(xLb(1), xUb(1), 100);
yv = linspace(xLb(2), xUb(2), 100); %xLb and xUb are upper and lower bounds for the rectangle mesh
[x1,x2] = meshgrid(xv, yv);
Z = griddata(X0(1,:), X0(2,:), U, x1, x2);
%This grid obviously includes values that are not in the feasible set (red polygon) by its rectangular nature
Z(isnan(Z))=0; %set infeasible values to 0, wrong of course
testMPC = someInterpolationFunction([0:length(Z)-1]',[0:length(Z)-1],Z);
testMPC.showInterpolation(20,20)
%this shows figure 3 in the attached picture
Try something like this:
nRows = 100;
nCols = 200;
x1 = #(x) max(0, x-50);
x2 = #(x) min(nCols, nCols - 50 + x);
RR = zeros(nRows, nCols);
CC = zeros(nRows, nCols);
for iRow = 1:nRows
c1 = x1(iRow);
c2 = x2(iRow);
colVec = linspace(c1, c2, nCols);
RR(iRow, :) = iRow;
CC(iRow, :) = colVec;
end
mesh(RR, CC, zeros(size(RR)))
You'd need to redefine the functions for x1 and x2 or course as well as the scaling, but this should give you an idea of how to get started.

Plot a surface only for coordinates that satisfy a specific equation in MATLAB

I have two grid coordinates matrices, X and Y, created by calling [X, Y] = meshgrid(x, y), so their elements represent coordinates. How can I plot a surface on the xy-plane, using heights from matrix V, only for coordinates that satisfy a specific equation? For example, my plot extends up to radius a, but I dont want to plot any data to the set of points that satisfy the equation sqrt(x^2 + (y-c)^2) < b, where b, c (a>b) are given constants and x=X(i,j), y=Y(i,j). Is there an easy way to do this, other than creating the two grid coordinates matrices (up to radius a) and then manually removing elements from X, Y, V, using nested for loops? I have not found any way to limit the plotting area I am interested in by changing x, y.
Using Logical Indexing
Just in case you're still looking for any implementation details. Referencing the comment by #Ander Biguri. I have to add that it might be easier to use mesh parameters X and Y directly in the logical indexing. Here is a little playground script that might help future readers. Below Region_Array is a logical array that specifies where the condition in this case sqrt(X.^2 + (Y-c).^2) < b is true. When true Region_Array is indexed with the value "1" and elsewhere with "0". I've split this into two steps just in case the complementary region is quickly wanted. The images/plots below show the resulting surf() and masks/regions. MATLAB has some thorough documentation and examples overviewing logical indexing: Find Array Elements That Meet a Condition
Trivial Surface Plot:
Masks/Regions Not to be Plotted:
Playground Script:
%Random test axes%
x = linspace(0,100,50);
y = linspace(0,100,50);
[X,Y] = meshgrid(x,y);
%Trivial plot of ones%
V = ones(length(x),length(y));
%Constant parameters%
b = 20;
c = 10;
%Eliminating within the curved region%
figure(1)
Region_Array = sqrt(X.^2 + (Y-c).^2) < b;
V(Region_Array) = NaN;
subplot(1,2,1); surf(X,Y,V);
axis([0 100 0 100]);
title("Eliminating Within the Curved Region");
%Eliminating outside the curved region%
V = ones(length(x),length(y));
V(~Region_Array) = NaN;
subplot(1,2,2); surf(X,Y,V);
axis([0 100 0 100]);
title("Eliminating Outside the Curved Region");
figure(2)
subplot(1,2,1); imshow(~Region_Array,'InitialMagnification',200);
title("Region Array Mask/Map (Inside)")
subplot(1,2,2); imshow(Region_Array,'InitialMagnification',200);
title("Region Array Mask/Map (Outside)")
Ran using MATLAB R2019b

How to plot using surf gird in 2D using function value

if the function F is available it is easy to draw surf plot i.e.
x=1:0.1:4;
y=1:0.1:4;
[X,Y]=meshgrid(x,y);
Z=sin(X).^2+cos(Y).^2;
surf(X,Y,Z);
view(2) ;
in my case I calculated F function using least square:
for example I have x and y vectors
x = [0 9.8312 77.1256 117.9810 99.9979];
y = [0 2754.5 4043.3 5376.3 5050.4];
the linear function of these two vector is define by
F= [1149.73 , 37.63];
therefore the estimation is equal to
z= [ones(5,1) x']*F';
which is
z = [1149.73 1519.67 4051.96 5589.35 4912.65];
and if it is plotted
plot(x,y,'b.');
hold on;plot(x,y,'b-');
hold on; plot(x,z,'-r');
The linear z ( red line) is showing correctly. Now I want to plot it for all possible combination of x and y using grid and I need to have a mesh for all inputs
[X,Y] = meshgrid(x,y);
but how to make the Z matrix to show the intensity plot of function Z? The Z suppose to have high intensity close to z value and less value far from it. I should suppose to get something like this
Thanks
P.S: the F is calculated using pinv (least square).
You have to interpolate the scattered data to plot it on grid. Here is a simple example for your x, y and z vectors
xi=linspace(min(x),max(x),100)
yi=linspace(min(y),max(y),100)
[XI YI]=meshgrid(xi,yi);
ZI = griddata(x,y,z,XI,YI);
contourf(XI,YI,ZI)

How do I rotate a cylinder in matlab using matrices?

So, I'm really new to MatLab, and I was trying to make a cylinder using [X Y Z] = cylinder;.Then I got these 3 matrices: X Y and Z that generate an actual cylinder if I mesh them. Now, what I need help to do is rotate this [X Y Z] cylinder 90 degrees clockwise in the y axis. I know there is this command called rotate but my teacher wants me to use rotation and translation matrices explicitly. How could I create these matrices and multiply them to the cylinder?Is there a better way to make the cylinder? I'm really not used to matlab, if you could explain in a detailed form, I would be very thankful.
You should use a rotation matrix for the R^3 which serves as a linear map. There are built in fucntions in MATLAB for that but I guess you are not allowed to use them.
Here is a quick and dirty solution:
[X Y Z] = cylinder;
figure;
surf(X,Y,Z);
% set up rotation matrix:
angle_in_degrees = 90;
angle_in_rad = angle_in_degrees* pi/180;
rotationMatrix = [cos(angle_in_rad) 0 sin(angle_in_rad); 0 1 0; -sin(angle_in_rad) 0 cos(angle_in_rad)];
% get points at the two rings and rotate them separately:
positionOld1 = [X(1,:)',Y(1,:)',Z(1,:)'];
positionOld2 = [X(2,:)',Y(2,:)',Z(2,:)'];
positionNew1 = positionOld1*rotationMatrix;
positionNew2 = positionOld2*rotationMatrix;
% reassemble the two sets of points into X Y Z format:
X = [positionNew1(:,1),positionNew2(:,1)];
Y = [positionNew1(:,2),positionNew2(:,2)];
Z = [positionNew1(:,3),positionNew2(:,3)];
figure;
surf(X,Y,Z);

Set surf minimum for matlab

I have a function which takes a voxel representation of a 3D landscape and can plot a X-Y section to show the middle of the landscape. The voxel representation is stored in a 3 dimensional matrix with a number that represents something important. Obviously the matrix is
1,1,1
2,2,2
in terms of accessing the elements but the actual 3D locations are found in the following method:
(index-1)*resolution+0.5*resolution+minPos;
where resolution is the grid size :
resolution
<-->
__ __ __
|__|__|__|
<- Min pos
and minPos is where the grid starts.
Now in terms of the actual question, i would like to extract a single X-Y section of this voxel representation and display it as a surf. This can be done by just doing this:
surf(voxel(:, :, section))
however then you get this:
The obvious problem is that the grid will start at 0 because that is how the matrix representation is. How can i set the minimum and cell size for surf, ie so that the grid will start at the minimum (shown above) and will have the grid spacing of resolution (shown above).
Read the documentation of surf, you can also provide x and y coordinates corresponding to your data points.
surf(X,Y,Z)
X and Y can be either vectors or matrices:
surf(X,Y,Z) uses Z for the color data and surface height. X and Y are vectors or matrices defining the x and y components of a surface. If X and Y are vectors, length(X) = n and length(Y) = m, where [m,n] = size(Z). In this case, the vertices of the surface faces are (X(j), Y(i), Z(i,j)) triples. To create X and Y matrices for arbitrary domains, use the meshgrid function
Example
Z=[ 0 1 2 3;
7 6 5 4;
8 9 10 11];
x=[-1 0 1 2];
y=[-2 0 2];
surf(x,y,Z);
Of course you have to match Z, x and y matrices/vectors as clearly described in the doc^^
Just remember that elements in columns of Z are surf'ed as values along the y-axis, elements in rows of Z are surf'ed as values along the x-axis. This is clearly to be seen in the example picture.
Solution
I think you switched the x and y-axis around, which you can fix by just transposing z:
s = size(voxel);
xi = (minPosX:resolution:(minPosX+resolution*s(1)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(2)-1));
z = (voxel(:,:,section));
surf(xi, yi, z');
or that you're picking the wrong numbers for constructing xi and yi and it should be this instead:
xi = (minPosX:resolution:(minPosX+resolution*s(2)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(1)-1));
z = (voxel(:,:,section));
surf(xi, yi, z);
So it was easy enough to do:
lets say we have a 3D matrix "voxel";
s = size(voxel);
xi = (minPosX:resolution:(minPosX+resolution*s(1)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(2)-1));
z = (voxel(:,:,section));
[x y] = meshgrid(xi, yi);
x = x';
y = y';
surf(x, y, z);
Provides the following plot:
This is rotated which is annoying, I cant seem to get it to rotate back (I could just visualise around the other way but that's ok)