I'm trying to graph a simple 2D Gaussian in MATLAB using the surf function, but I'm getting an error saying that the last value in surf must be a matrix and not a vector. I don't understand how to graph the function then as every other example I've found while searching has had the third value as a vector. I feel as if I'm completely off base here with the surf function. Any ideas?
amp = 10;
x0 = 0;
y0 = 0;
sigmaX = 10
sigmaY = 10
X = 1:1:100;
Y = 1:1:100;
Z = amp*exp(-(X-x0).^2/(2*sigmaX^2)+(Y-y0).^2/(2*sigmaY^2));
disp(size(Z))
surf(X, Y, Z);
Edit
When I plot this using #Suever's answer, I get something that doesn't look like a Gaussian at all.
Here's the plot
amp = 1;
x0 = 0;
y0 = 0;
sigmaX = 1;
sigmaY = 1;
%X = 1:1:100;
%Y = 1:1:100;
[X,Y] = meshgrid(-3:.1:3);
%Z = X .* exp(-X.^2 - Y.^2);
Z = amp*exp(-(X-x0).^2/(2*sigmaX^2)+(Y-y0).^2/(2*sigmaY^2));
surf(X, Y, Z);
You have used X and Y to define a 2D domain over which you would like to compute your gaussian. If you want Z to be a function of X and Y, you need to define Z for all permutations of X and Y. If you don't provide a matrix of Z values, MATLAB has no idea how to create a surface over the X Y ranges you've provided.
You can create all permutations of X and Y using meshgrid and then compute Z over this entire domain. Then you will be able to display the result with surf.
amp = 10; x0 = 50; y0 = 50; sigmaX = 10; sigmaY = 10;
[X, Y] = meshgrid(1:100, 1:100);
% Z as you had it written (see correct version below)
Z = amp*exp(-(X-x0).^2./(2*sigmaX^2)+(Y-y0).^2./(2*sigmaY^2));
surf(X, Y, Z);
Update
Your equation for the 2D Gaussian is wrong. The - sign should be outside of the addition of the two components. The way that you had it written, you negated the X-component and then added to the Y component.
Z = amp*exp(-((X-x0).^2./(2*sigmaX^2)+(Y-y0).^2./(2*sigmaY^2)));
Related
I am trying to use the spherical harmonics to represent a perturbation of a spherical object in an acoustic and fluid flow in 3D.
So far, I have been able to use a 2D perturbation on a 3D spherical object, however, I would like to extend that.
The current code represents a 3D sphere with a 2D perturbation, so the perturbation is only in x and y:
clearvars; clc; close all;
Nx = 128;
Ny = 128;
Nz = 128;
Lx =128;
Ly = 128;
Lz = 128;
xi = (0:Nx-1)/Nx*2*pi;
xi_x = 2*pi/Lx;
x = xi/xi_x;
yi = (0:Ny-1)/Ny*2*pi;
yi_y = 2*pi/Ly;
y = yi/yi_y;
zi = (0:Nz-1)/Nz*2*pi;
zi_z = 2*pi/Lz;
z = zi/zi_z;
[X,Y,Z] = meshgrid(x,y,z);
A = 2*pi / Lx;
B = 2*pi / Ly;
C = 2*pi / Lz;
x0 = 64;
y0 = 64;
z0 = 64;
rx0 = 20;
ry0 = 20;
rz0 = 20;
p = 3;
b = 0.1; % pert amplitude
c = 12;
d = 1;
a = 4;
theta = atan2(Y -y0, X-x0) - (pi/c);
p0 = ((X-x0) .* (X-x0)) /(rx0 * rx0) + ((Y-y0) .* (Y-y0))/(ry0 * ry0) + ((Z-z0) .* (Z-z0))/(rz0 * rz0);
Test =d + a * exp((-1. * p0 .* (1 - b .* cos(c * theta))).^p) ;
figure
isosurface(X,Y,Z,Test);
shading flat;
grid on;
Which returns the isosurface:
However, I would like to achieve something similar to this plot (perturbation in z as well):
This is my attempt using spherical harmonics to reproduce the above picture:
clearvars; clc; close all;
%in spherical coord
%calculate r
Nx = 128;
Ny = 128;
Nz = 128;
Lx =128;
Ly = 128;
Lz = 128;
xi = (0:Nx-1)/Nx*2*pi;
xi_x = 2*pi/Lx;
x = xi/xi_x;
yi = (0:Ny-1)/Ny*2*pi;
yi_y = 2*pi/Ly;
y = yi/yi_y;
zi = (0:Nz-1)/Nz*2*pi;
zi_z = 2*pi/Lz;
z = zi/zi_z;
r = sqrt(x.^2 + y.^2 + z.^2);
% Create the grid
delta = pi/127;
%Taking for instance l=1, m=-1 you can generate this harmonic on a (azimuth, elevation) grid like this:
azimuths = 0 : delta : pi;
elevations = 0 : 2*delta : 2*pi;
[R, A, E] = ndgrid(r, azimuths, elevations); %A is phi and E is theta
H = 0.25 * sqrt(3/(2*pi)) .* exp(-1j*A) .* sin(E) .* cos(E);
%transform the grid back to cartesian grid like this:
%can also add some radial distortion to make things look nicer:
%the radial part depends on your domain
X = r .* cos(A) .* sin(E);
Y = r .* sin(A) .* sin(E);
Z = r .* cos(E);
%parameters
x0 = 64;
y0 = 64;
z0 = 64;
rx0 = 20;
ry0 = 20;
rz0 = 20;
p = 3;
b = 0.1; % pert amplitude
%c = 12;
d = 1;
a = 4;
p0 = ((X-x0) .* (X-x0)) /(rx0 * rx0) + ((Y-y0) .* (Y-y0))/(ry0 * ry0) + ((Z-z0) .* (Z-z0))/(rz0 * rz0);
Test1 =d + a * exp((-1. * p0 .*H).^p) ;
figure
isosurface(X,Y,Z,real(Test1)); %ERROR
This gives me the following error:
Error using griddedInterpolant
Grid arrays must have NDGRID structure.
Is the issue in the way I am setting up the spherical harmonics? or the functional form of Test1?? Thanks
I think the problem is that you've created a NDGrid in the second code. In the first code that worked you created a meshgrid.
isosurface expects a grid in meshgrid format and transforms it later into an NDGrid format with permute just for the usage of the griddedInterpolant function. By the input of an NDGrid this operation will fail.
Why did you switch to creating a NDGrid? Did you get the same error when using meshgrid?
EDIT
OK, new theory: I think the problem is the grid itsself. Read the documentation on ndgrid for more information but for short: A NDGrid format is a completely rectangular grid where all nodes are exclusivly surrounded by 90° angles. By spanning up a grid with ndgrid(r, azimuths, elevations) or meshgrid(r, azimuths, elevations) you are getting this rectangular grid but of course this grid is meaningless because r, azimuths and elevations represent spherical coordinates. By later converting R, A and E into the carthesian coordinates X, Y and Z you get a propper spherical grid but the grid isn't a NDGrid structure anymore since it's not rectangular anymore.
So you have to find a workaround to calculate with your spherical grid.
Maybe you can use another function to visualize your data that dosn't take any grid format as input
Or maybe you can try working with a rectangular cartesian grid by setting all values outside the sphere to 0. (You have to refine your grid accordingly to achieve a good estimation of the sphere)
I am trying to plot a cake piece like surface in matlab but I have no idea how to define z to make it look like a cake pie. The shape I need is this:
This is the code I wrote so far:
th = linspace(0, pi/3);
r = linspace(0, pi/3);
% z = linspace(0, 10);
[R, TH] = meshgrid(r, th);
x = R.*cos(TH);
y = R.*sin(TH);
z = R;
% z = 10 * ones(size(x));
ss = surf(x, y, z, 'FaceAlpha',0.3);
ss.EdgeAlpha = 0.6;
ss.FaceAlpha = 0.1;
it will look similar to that shape, but not exactly, I mean the shape will be rough:
z = rand(size(x));
another option with smoother surface:
z = zeros(size(x));
[m,n]=size(x);
for i=1 : n
for j=1 : m
if(mod(i+j,2)==0)
z(i,j) = 1;
end
end
end
I've been trying to evaluate a function in matlab. I want my x vector to go from 0 to 1000 and my y vector to go from 0 to 125. They should both have a length of 101.
The equation to be evaluated is z(x,y) = ay + bx, with a=10 and b=20.
a = 10;
b = 20;
n = 101;
dx = 10; % Interval length
dy = 1.25;
x = zeros(1,n);
y = zeros(1,n);
z = zeros(n,n);
for i = 1:n;
x(i) = dx*(i-1);
y(i) = dy*(i-1);
for j = 1:n;
z(i,j) = a*dy*(j-1) + b*dx*(j-1);
end
end
I get an answer, but I don't know if I did it correctly with the indices in the nested for loop?
See MATLAB's linspace function.
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
z=a*y+b*x;
This is easier and takes care of the interval spacing for you. From the linspace documentation,
y = linspace(x1,x2,n) generates n points. The spacing between the points is (x2-x1)/(n-1).
Edit:
As others have pointed out, my solution above makes a vector, not a matrix which the OP seems to want. As #obchardon pointed out, you can use meshgrid to make that 2D grid of x and y points to generate a matrix of z. Updated approached would be:
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
[X,Y] = meshgrid(x,y);
z=a*Y+b*X;
(you may swap the order of x and y depending on if you want each variable along the row or column of z.)
I could use this method to draw a ellipsoid in Matlab:
Xc is x-coordinate of the center, Xr is half the length of ellipsoid in x direction
Xc = 0; Xr = 1;
Yc = 0; Yr = 2;
Zc = 0; Zr = 3;
[X Y Z] = ellipsoid(Xc, Yc, Zc, Xr, Yr, Zr);
surf(X,Y,Z);
This can give me a 3-D image of the ellipsoid but if I do not know things like Xc or Xr ..., all I know is a function:
A = 5; B = 0; C =2;
D = 2; E = 1;
P = [A B;
B C];
q = [D E];
syms x y;
f = [x y] * P * [x y]' + q * [x y] + 1;
But it seems that surf won't allow to use syms variables.
ezplot(f) give only a 2-D ellipse.
How can I draw a 3-D ellipsoid with this function in Matlab using surf, mesh or anything else?
The reason I write this function f is that I want to see how the positive definiteness and negative definiteness of matrix P influence the image of function f.
Thanks!
You can draw an ellipsoid (or any other implicit surface) using isosurface. For example, the ellipsoid
2*x^2+2xy+3y^2+2yz+4z^2+4z-1=0 can be drawn using the following code:
[x,y,z]= meshgrid(-2:0.05:2); % the surface should fit in the meshgrid area
v= 2*x.^2+2*x.*y+3*y.^2+2*y.*z+4*z.^2+4*z-1; % the left side of the equation
isosurface(x,y,z,v,0); % the value 0 is the right hand side of the equation
axis tight
I'm trying to practice curve fitting on a 2D Gaussian, but in order to do that I need to add random noise to my predefined Gaussian. My first instinct was to cycle through two for loops and create two matrices X and Y with random numbers, but when I tried that (I don't have the code anymore) Matlab wouldn't let me plot the Gaussian because I didn't generate my X and Y values using the meshgrid function. Since I seem to need to use meshgrid, can anyone help me figure out how to generate a random meshgrid so I can add some noise to my Gaussian?
amp = 1;
x0 = 0;
y0 = 0;
sigmaX = 1;
sigmaY = 1;
%X = 1:1:100;
%Y = 1:1:100;
[X,Y] = meshgrid(-3:.1:3);
%Z = X .* exp(-X.^2 - Y.^2);
Z = amp*exp(-((X-x0).^2/(2*sigmaX^2)+(Y-y0).^2/(2*sigmaY^2)));
surf(X, Y, Z);
%Add noise now
EDIT: So I found out that rand can return a random matrix which will work with the surf function (for some reason it wasn't working for me earlier though). The result looks something like this: noisy 2D gaussian
amp = 1;
x0 = 0;
y0 = 0;
sigmaX = 1;
sigmaY = 1;
[X,Y] = meshgrid(-3:.1:3);
%Z = X .* exp(-X.^2 - Y.^2);
Z = amp*exp(-((X-x0).^2/(2*sigmaX^2)+(Y-y0).^2/(2*sigmaY^2)));
surf(X, Y, Z);
%Make some noise
[xRows, xColumns] = size(X);
[yRows, yColumns] = size(Y);
figure(2)
X = -.1 + (.1+.1)*rand(61,61);
Y = -.1 + (.1+.1)*rand(61,61);
Z = amp*exp(-((X-x0).^2/(2*sigmaX^2)+(Y-y0).^2/(2*sigmaY^2)));
surf(X, Y, Z)
But I feel like the Gaussian has largely lost it's typical bell shape and looks more like a slope field than anything. I'm going to try and refine it but I would love any input.
That's what i would do.
amp=1;
x0=0;
y0=0;
sigmaX=1;
sigmaY=1;
noiseAmp=.1;
x=[-2:.1:2];
y=[-2:.1:2];
%Create two Noise Vectors
noisez1=noiseAmp.*rand(1,length(x));
noisez2=noiseAmp.*rand(1,length(x));
% Make an meshgrid out of the two Vectors
[noiseZ1,noiseZ2]=meshgrid(noisez1,noisez2);
% Add the Meshgrids togehter
Noise=noiseZ1+noiseZ2;
[X,Y]=meshgrid(x,y);
% Add the Noise to the result of Z
Z=amp*exp(-((X-x0).^2/(2*sigmaX^2)+(Y-y0).^2/(2*sigmaY^2)))+Noise;
surf(X,Y,Z);
if you just want a 2D plot you can try this
amp=1;
noiseAmp=0.01;
x0=0;
y0=0;
sigmaX=1;
sigmaY=1;
x=[-5:.01:5];
noiseY=noiseAmp*rand(1,length(x));
y=noiseY+amp*exp(-((x-x0).^2/(2*sigmaX^2)));
plot(x,y);
where noiseAmp is the Amplitude of the noise.
But if you still want to create a 3D plot with the surf() function, you have to add a random meshgrid to the Z result.