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
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 have this piece of MATLAB code that outputs x,y, and z angles and I would like draw a line using them. Can someone point me in the right direction on how to do this?
C = pi;
A = pi;
B = pi;
Z = [cos(C),-sin(C),0; sin(C),cos(C),0; 0,0,1];
X = [1,0,0;0,cos(A),-sin(A);0,sin(A),cos(A)];
Y = [cos(B),0,sin(B);0,1,0;-sin(B),0,cos(B)];
R =(X*Y)*Z;
yaw=atan2(R(2,1),R(1,1))
pitch=atan2(-R(3,1),sqrt(R(3,2)^2+R(3,3)^2))
roll=atan2(R(3,2),R(3,3))
X, Y, and Z are not angles, they are rotation matrices defined by the angles A, B, and C.
it's not clear what's the meaning of "draw a line using them", they are just used to rotate vectors in the 3D space.
here is an example of drawing a rotated vector with them:
% define rotation angles (around the axes)
C = pi/2;
A = pi/4;
B = pi/4;
% generate rotation matrices
Z = [cos(C),-sin(C),0; sin(C),cos(C),0; 0,0,1];
X = [1,0,0;0,cos(A),-sin(A);0,sin(A),cos(A)];
Y = [cos(B),0,sin(B);0,1,0;-sin(B),0,cos(B)];
R =(X*Y)*Z;
% generate a vector and rotate it
v = [1;1;1];
u = R*v;
% plot
quiver3(0,0,0,v(1),v(2),v(3));
hold on
quiver3(0,0,0,u(1),u(2),u(3));
xlim([-1 1]); ylim([-1 1]); zlim([-1 1])
axis square
legend('original','rotated')
I'm looking for a simple way for creating a random unit vector constrained by a conical region. The origin is always the [0,0,0].
My solution up to now:
function v = GetRandomVectorInsideCone(coneDir,coneAngleDegree)
coneDir = normc(coneDir);
ang = coneAngleDegree + 1;
while ang > coneAngleDegree
v = [randn(1); randn(1); randn(1)];
v = v + coneDir;
v = normc(v);
ang = atan2(norm(cross(v,coneDir)), dot(v,coneDir))*180/pi;
end
My code loops until the random generated unit vector is inside the defined cone. Is there a better way to do that?
Resultant image from test code bellow
Resultant frequency distribution using Ahmed Fasih code (in comments).
I wonder how to get a rectangular or normal distribution.
c = [1;1;1]; angs = arrayfun(#(i) subspace(c, GetRandomVectorInsideCone(c, 30)), 1:1e5) * 180/pi; figure(); hist(angs, 50);
Testing code:
clearvars; clc; close all;
coneDir = [randn(1); randn(1); randn(1)];
coneDir = [0 0 1]';
coneDir = normc(coneDir);
coneAngle = 45;
N = 1000;
vAngles = zeros(N,1);
vs = zeros(3,N);
for i=1:N
vs(:,i) = GetRandomVectorInsideCone(coneDir,coneAngle);
vAngles(i) = subspace(vs(:,i),coneDir)*180/pi;
end
maxAngle = max(vAngles);
minAngle = min(vAngles);
meanAngle = mean(vAngles);
AngleStd = std(vAngles);
fprintf('v angle\n');
fprintf('Direction: [%.3f %.3f %.3f]^T. Angle: %.2fº\n',coneDir,coneAngle);
fprintf('Min: %.2fº. Max: %.2fº\n',minAngle,maxAngle);
fprintf('Mean: %.2fº\n',meanAngle);
fprintf('Standard Dev: %.2fº\n',AngleStd);
%% Plot
figure;
grid on;
rotate3d on;
axis equal;
axis vis3d;
axis tight;
hold on;
xlabel('X'); ylabel('Y'); zlabel('Z');
% Plot all vectors
p1 = [0 0 0]';
for i=1:N
p2 = vs(:,i);
plot3ex(p1,p2);
end
% Trying to plot the limiting cone, but no success here :(
% k = [0 1];
% [X,Y,Z] = cylinder([0 1 0]');
% testsubject = surf(X,Y,Z);
% set(testsubject,'FaceAlpha',0.5)
% N = 50;
% r = linspace(0, 1, N);
% [X,Y,Z] = cylinder(r, N);
%
% h = surf(X, Y, Z);
%
% rotate(h, [1 1 0], 90);
plot3ex.m:
function p = plot3ex(varargin)
% Plots a line from each p1 to each p2.
% Inputs:
% p1 3xN
% p2 3xN
% args plot3 configuration string
% NOTE: p1 and p2 number of points can range from 1 to N
% but if the number of points are different, one must be 1!
% PVB 2016
p1 = varargin{1};
p2 = varargin{2};
extraArgs = varargin(3:end);
N1 = size(p1,2);
N2 = size(p2,2);
N = N1;
if N1 == 1 && N2 > 1
N = N2;
elseif N1 > 1 && N2 == 1
N = N1
elseif N1 ~= N2
error('if size(p1,2) ~= size(p1,2): size(p1,2) and/or size(p1,2) must be 1 !');
end
for i=1:N
i1 = i;
i2 = i;
if i > N1
i1 = N1;
end
if i > N2
i2 = N2;
end
x = [p1(1,i1) p2(1,i2)];
y = [p1(2,i1) p2(2,i2)];
z = [p1(3,i1) p2(3,i2)];
p = plot3(x,y,z,extraArgs{:});
end
Here’s the solution. It’s based on the wonderful answer at https://math.stackexchange.com/a/205589/81266. I found this answer by googling “random points on spherical cap”, after I learned on Mathworld that a spherical cap is this cut of a 3-sphere with a plane.
Here’s the function:
function r = randSphericalCap(coneAngleDegree, coneDir, N, RNG)
if ~exist('coneDir', 'var') || isempty(coneDir)
coneDir = [0;0;1];
end
if ~exist('N', 'var') || isempty(N)
N = 1;
end
if ~exist('RNG', 'var') || isempty(RNG)
RNG = RandStream.getGlobalStream();
end
coneAngle = coneAngleDegree * pi/180;
% Generate points on the spherical cap around the north pole [1].
% [1] See https://math.stackexchange.com/a/205589/81266
z = RNG.rand(1, N) * (1 - cos(coneAngle)) + cos(coneAngle);
phi = RNG.rand(1, N) * 2 * pi;
x = sqrt(1-z.^2).*cos(phi);
y = sqrt(1-z.^2).*sin(phi);
% If the spherical cap is centered around the north pole, we're done.
if all(coneDir(:) == [0;0;1])
r = [x; y; z];
return;
end
% Find the rotation axis `u` and rotation angle `rot` [1]
u = normc(cross([0;0;1], normc(coneDir)));
rot = acos(dot(normc(coneDir), [0;0;1]));
% Convert rotation axis and angle to 3x3 rotation matrix [2]
% [2] See https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
crossMatrix = #(x,y,z) [0 -z y; z 0 -x; -y x 0];
R = cos(rot) * eye(3) + sin(rot) * crossMatrix(u(1), u(2), u(3)) + (1-cos(rot))*(u * u');
% Rotate [x; y; z] from north pole to `coneDir`.
r = R * [x; y; z];
end
function y = normc(x)
y = bsxfun(#rdivide, x, sqrt(sum(x.^2)));
end
This code just implements joriki’s answer on math.stackexchange, filling in all the details that joriki omitted.
Here’s a script that shows how to use it.
clearvars
coneDir = [1;1;1];
coneAngleDegree = 30;
N = 1e4;
sol = randSphericalCap(coneAngleDegree, coneDir, N);
figure;plot3(sol(1,:), sol(2,:), sol(3,:), 'b.', 0,0,0,'rx');
grid
xlabel('x'); ylabel('y'); zlabel('z')
legend('random points','origin','location','best')
title('Final random points on spherical cap')
Here is a 3D plot of 10'000 points from the 30° spherical cap centered around the [1; 1; 1] vector:
Here’s 120° spherical cap:
Now, if you look at the histogram of the angles between these random points at the coneDir = [1;1;1], you will see that the distribution is skewed. Here’s the distribution:
Code to generate this:
normc = #(x) bsxfun(#rdivide, x, sqrt(sum(x.^2)));
mysubspace = #(a,b) real(acos(sum(bsxfun(#times, normc(a), normc(b)))));
angs = arrayfun(#(i) mysubspace(coneDir, sol(:,i)), 1:N) * 180/pi;
nBins = 16;
[n, edges] = histcounts(angs, nBins);
centers = diff(edges(1:2))*[0:(length(n)-1)] + mean(edges(1:2));
figure('color','white');
bar(centers, n);
xlabel('Angle (degrees)')
ylabel('Frequency')
title(sprintf('Histogram of angles between coneDir and random points: %d deg', coneAngleDegree))
Well, this makes sense! If you generate points from the 120° spherical cap around coneDir, of course the 1° cap is going to have very few of those samples, whereas the strip between the 10° and 11° caps will have far more points. So what we want to do is normalize the number of points at a given angle by the surface area of the spherical cap at that angle.
Here’s a function that gives us the surface area of the spherical cap with radius R and angle in radians theta (equation 16 on Mathworld’s spherical cap article):
rThetaToH = #(R, theta) R * (1 - cos(theta));
rThetaToS = #(R, theta) 2 * pi * R * rThetaToH(R, theta);
Then, we can normalize the histogram count for each bin (n above) by the difference in surface area of the spherical caps at the bin’s edges:
figure('color','white');
bar(centers, n ./ diff(rThetaToS(1, edges * pi/180)))
The figure:
This tells us “the number of random vectors divided by the surface area of the spherical segment between histogram bin edges”. This is uniform!
(N.B. If you do this normalized histogram for the vectors generated by your original code, using rejection sampling, the same holds: the normalized histogram is uniform. It’s just that rejection sampling is expensive compared to this.)
(N.B. 2: note that the naive way of picking random points on a sphere—by first generating azimuth/elevation angles and then converting these spherical coordinates to Cartesian coordinates—is no good because it bunches points near the poles: Mathworld, example, example 2. One way to pick points on the entire sphere is sampling from the 3D normal distribution: that way you won’t get bunching near poles. So I believe that your original technique is perfectly appropriate, giving you nice, evenly-distributed points on the sphere without any bunching. This algorithm described above also does the “right thing” and should avoid bunching. Carefully evaluate any proposed algorithms to ensure that the bunching-near-poles problem is avoided.)
it is better to use spherical coordinates and convert it to cartesian coordinates:
coneDirtheta = rand(1) * 2 * pi;
coneDirphi = rand(1) * pi;
coneAngle = 45;
N = 1000;
%perfom transformation preventing concentration of points around the pole
rpolar = acos(cos(coneAngle/2*pi/180) + (1-cos(coneAngle/2*pi/180)) * rand(N, 1));
thetapolar = rand(N,1) * 2 * pi;
x0 = rpolar .* cos(thetapolar);
y0 = rpolar .* sin(thetapolar);
theta = coneDirtheta + x0;
phi = coneDirphi + y0;
r = rand(N, 1);
x = r .* cos(theta) .* sin(phi);
y = r .* sin(theta) .* sin(phi);
z = r .* cos(phi);
scatter3(x,y,z)
if all points should be of length 1 set r = ones(N,1);
Edit:
since intersection of cone with sphere forms a circle first we create random points inside a circle with raduis of (45 / 2) in polar coordinates and as #Ahmed Fasih commented to prevent concentration of points near the pole we should first transform this random points, then convert polar to cartesian 2D coordinates to form x0 and y0
we can use x0 and y0 as phi & theta angle of spherical coordinates and add coneDirtheta & coneDirphi as offsets to these coordinates.
then convert spherical to cartesian 3D coordinates
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)));
I need to plot a dome or half a sphere and be able to change the dimensions of the dome. I figured MATLAB would be my best choice.
any suggestions?
THanks
The SPHERE function generates x, y, and z coordinates for a spherical surface. You just have to remove points corresponding to the bottom of the sphere to make a dome. For example:
[x,y,z] = sphere; %# Makes a 21-by-21 point sphere
x = x(11:end,:); %# Keep top 11 x points
y = y(11:end,:); %# Keep top 11 y points
z = z(11:end,:); %# Keep top 11 z points
r = 3; %# A radius value
surf(r.*x,r.*y,r.*z); %# Plot the surface
axis equal; %# Make the scaling on the x, y, and z axes equal
take a look at surf. the formula for a sphere is
x^2+y^2+z^2 = R^2
you may also need meshgrid
Here's a starting point:
R = 7;
[X,Y] = meshgrid(-10:.1:10);
Z = sqrt(R.^2 - X.^2 - Y.^2);
Z(imag(Z) ~= 0) = 0;
mesh(X,Y,Z);
I took a circle graph I made and adapted it to graph a dome. I can't test it but it should work since the circle worked fine.
I made this in JavaScript.
setVox is just a function that will print a voxel on a 3D plane.
var rad = 1; //radius
for (var z = -(rad); z < 0; z++) {
r = Math.round(Math.sqrt(Math.pow(rad,2) - Math.pow(z,2)));
for (var x = -(r); x < r; x++) {
y = Math.round(Math.sqrt(Math.pow(r,2) - Math.pow(x,2)));
setVox(x,y,z);
setVox(x,-(y),z);
}
for (y = -(r); y < r; y++) {
x = Math.round(Math.sqrt(Math.pow(r,2) - Math.pow(y,2)));
setVox(x,y,z);
setVox(-(x),y,z);
}
}
Alternatively, you could download and use this hemisphere function, which is just an adapted version of the default MATLAB sphere function. For example:
[X,Y,Z] = hemisphere;
R = 2;
X1 = R*X + 3;
Y1 = R*Y - 4;
Z1 = R*Z + 2;
surf(X1,Y1,Z1)
axis equal
Resulting plot