Plot a 3D-plane in Matlab? - matlab

How can I plot a 3D-plane at specific point in Matlab?
Consider the plane equation
Z=(-a * X - b * Y)/c
with following coefficients:
a=0.01; b=0.03; c= 1; d=0.
I want to plot this plane around point (100,100) not at origin (0,0). How it possible to do that?
The code I used:
[X,Y] = meshgrid(x);
a=0.1;
b=0.2;
c=1;
d=0;
Z=(-a * X - b * Y)/c;
surf(X,Y,Z)
shading flat
xlabel('x')
ylabel('y')
zlabel('z')

surf() just plots whatever set of points you give it. To generate those points, you're evaluating the equation at a specific set of coordinates given by X and Y. Therefore you want those points to be centred around the region of interest:
[X, Y] = meshgrid(95:0.1:105); % e.g. +/-5 at resolution of 0.1
or, say, for arbitrary view coordinates m,n:
[X, Y] = meshgrid(m-20:m+20, n-20:n+20); % e.g. +/-20 at resolution of 1
That gives you the view around 100,100 of a plane centred at the origin, which I think is what you're asking for.
Alternatively if you want the plane itself centred at 100,100, then you need that offset in the equation:
Z=(-a * (X - 100) - b * (Y - 100))/c;
so then a view centred on the origin will be equivalent to viewing the original plane around -100,-100.

Related

matlab: moving circle along a graph and axis equal

Hello and pardon me if my english is a bit rusty. I'm trying to create a circle that moves along a parametric function (coordinates are stored in vectors). I have written a function for drawing the circle and I know that you can use the axis equal command in matlab in order to create a circle shape and avoid an ellipse. My problem is that when I do this the figure window becomes very wide relative to the plotted graph. Any input is appreciated.
MAIN CODE:
t = linspace(0,3);
x = 30*cos(pi/4)/2*(1-exp(-0.5*t));
y = (30*sin(pi/4)/2 + 9.81/0.5^2)*(1-exp(0.5*t)) - 9.81*t/0.5;
for i = 1:length(t)
plot(x,y)
axis equal
hold on
cirkel(x(i),y(i),1,1,'r') % argument #3 is the radius #4 is 1 for fill
hold off
pause(0.01)
end
CIRCLE CODE:
function cirkel(x,y,r,f,c)
angle = linspace(0, 2*pi, 360);
xp = x + r*cos(angle);
yp = y + r*sin(angle);
plot(x,y)
if f == 1 && nargin == 5
fill(xp,yp,c)
end
When you call axis equal it makes one unit of the x axis be the same size as one unit of the y axis. You are seeing what you are because your y values span a much larger range than the x values.
One way to deal with this would be to query the aspect ratio and x/y limits of the current axes as shown in the second part of this answer. However, an easier approach is rather than using fill to plot your circle, you could instead use scatter with a circular marker which will be circular regardless of the aspect ratio of your axes.
t = linspace(0,3);
x = 30*cos(pi/4)/2*(1-exp(-0.5*t));
y = (30*sin(pi/4)/2 + 9.81/0.5^2)*(1-exp(0.5*t)) - 9.81*t/0.5;
% Plot the entire curve
hplot = plot(x, y);
hold on;
% Create a scatter plot where the area of the marker is 50. Store the handle to the plot
% in the variable hscatter so we can update the position inside of the loop
hscatter = scatter(x(1), y(1), 50, 'r', 'MarkerFaceColor', 'r');
for k = 1:length(t)
% Update the location of the scatter plot
set(hscatter, 'XData', x(k), ... % Set the X Position of the circle to x(k)
'YData', y(k)) % Set the Y Position of the circle to y(k)
% Refresh the plot
drawnow
end
As a side note, it is best to update existing plot objects rather than creating new ones.
If you want the small dot to appear circular, and you want to have a reasonable domain (x-axis extent), try this:
function cirkel(x,y,r,f,c)
angle = linspace(0, 2*pi, 360);
xp = x + 0.04*r*cos(angle); %% adding scale factor of 0.04 to make it appear circular
yp = y + r*sin(angle);
plot(x,y)
if f == 1 && nargin == 5
fill(xp,yp,c)
end
Note the addition of the scale factor in the computation of xp. If you want to automate this, you can add another parameter to cirkel(), let's call it s, that contains the scale factor. You can calculate the scale factor in your script by computing the ratio of the range to the domain (y extent divided by x extent).

Matlab - Subtracting two 3D figures with surf and min function

I'm trying to make a surf plot that looks like:
So far I have:
x = [-1:1/100:1];
y = [-1:1/100:1];
[X,Y] = meshgrid(x,y);
Triangle1 = -abs(X) + 1.5;
Triangle2 = -abs(Y) + 1.5;
Z = min(Triangle1, Triangle2);
surf(X,Y,Z);
shading flat
colormap winter;
hold on;
[X,Y,Z] = sphere();
Sphere = surf(X, Y, Z + 1.5 );% sphere with radius 1 centred at (0,0,1.5)
hold off;
This code produces a graph that looks like :
A pyramid with square base ([-1,1]x[-1,1]) and vertex at height c = 1.5 above the origin (0,0) is erected.
The top of the pyramid is hollowed out by removing the portion of it that falls within a sphere of radius r=1 centered at the vertex.
So I need to keep the part of the surface of the sphere that is inside the pyramid and delete the rest. Note that the y axis in each plot is different, that's why the second plot looks condensed a bit. Yes there is a pyramid going into the sphere which is hard to see from that angle.
I will use viewing angles of 70 (azimuth) and 35 (elevation). And make sure the axes are properly scaled (as shown). I will use the AXIS TIGHT option to get the proper dimensions after the removal of the appropriate surface of the sphere.
Here is my humble suggestion:
N = 400; % resolution
x = linspace(-1,1,N);
y = linspace(-1,1,N);
[X,Y] = meshgrid(x,y);
Triangle1 = -abs(X)+1.5 ;
Triangle2 = -abs(Y)+1.5 ;
Z = min(Triangle1, Triangle2);
Trig = alphaShape(X(:),Y(:),Z(:),2);
[Xs,Ys,Zs] = sphere(N-1);
Sphere = alphaShape(Xs(:),Ys(:),Zs(:)+2,2);
% get all the points from the pyramid that are within the sphere:
inSphere = inShape(Sphere,X(:),Y(:),Z(:));
Zt = Z;
Zt(inSphere) = nan; % remove the points in the sphere
surf(X,Y,Zt)
shading interp
view(70,35)
axis tight
I use alphaShape object to remove all unwanted points from the pyramid and then plot it without them:
I know, it's not perfect, as you don't see the bottom of the circle within the pyramid, but all my tries to achieve this have failed. My basic idea was plotting them together like this:
hold on;
Zc = Zs;
inTrig = inShape(Trig,Xs(:),Ys(:),Zs(:)+1.5);
Zc(~inTrig) = nan;
surf(Xs,Ys,Zc+1.5)
hold off
But the result is not so good, as you can't really see the circle within the pyramid.
Anyway, I post this here as it might give you a direction to work on.
An alternative to EBH's method.
A general algorithm from subtracting two shapes in 3d is difficult in MATLAB. If instead you remember that the equation for a sphere with radius r centered at (x0,y0,z0) is
r^2 = (x-x0)^2 + (y-y0)^2 + (z-z0)^2
Then solving for z gives z = z0 +/- sqrt(r^2-(x-x0)^2-(y-y0)^2) where using + in front of the square root gives the top of the sphere and - gives the bottom. In this case we are only interested in the bottom of the sphere. To get the final surface we simply take the minimum z between the pyramid and the half-sphere.
Note that the domain of the half-sphere is defined by the filled circle r^2-(x-x0)^2-(y-y0)^2 >= 0. We define any terms outside the domain as infinity so that they are ignored when the minimum is taken.
N = 400; % resolution
z0 = 1.5; % sphere z offset
r = 1; % sphere radius
x = linspace(-1,1,N);
y = linspace(-1,1,N);
[X,Y] = meshgrid(x,y);
% pyramid
Triangle1 = -abs(X)+1.5 ;
Triangle2 = -abs(Y)+1.5 ;
Pyramid = min(Triangle1, Triangle2);
% half-sphere (hemisphere)
sqrt_term = r^2 - X.^2 - Y.^2;
HalfSphere = -sqrt(sqrt_term) + z0;
HalfSphere(sqrt_term < 0) = inf;
Z = min(HalfSphere, Pyramid);
surf(X,Y,Z)
shading interp
view(70,35)
axis tight

Drawing 2D grid in matlab

I am trying to get a 2D grid using matlab with x >= -1 and y <= 1 with step size of 0.1
But I'm getting 3D grid with no proper step sizes. Any ideas?
My code:
[x, y] = meshgrid(-1:0.1:5, 0:0.1:1);
surf(x,y)
Do you just want to plot a bunch of 2D points? You use plot. Using your example, you would take your x,y points and simply put dot markers for each point. Convert them into 1D arrays first before you do this:
[X,Y] = meshgrid(-1:0.1:5, 0:0.1:1);
X = X(:);
Y = Y(:);
plot(X,Y,'b.');
xlabel('X'); % // Label the X and Y axes
ylabel('Y');
This is what I get:
Edit based on comments
If you want to rotate this grid by an angle, you would use a rotation matrix and multiply this with each pair of (x,y) co-ordinates. If you recall from linear algebra, to rotate a point counter-clockwise, you would perform the following matrix multiplication:
[x'] = [cos(theta) -sin(theta)][x]
[y'] [sin(theta) cos(theta)][y]
x,y are the original co-ordinates while x',y' are the output co-ordinates after rotation of an angle theta. If you want to rotate -30 degrees (which is 30 degrees clockwise), you would just specify theta = -30 degrees. Bear in mind that cos and sin take in their angles as radians, so this is actually -pi/6 in radians. What you need to do is place each of your points into a 2D matrix. You would then use the rotation matrix and apply it to each point. This way, you're vectorizing the solution instead of... say... using a for loop. Therefore, you would do this:
theta = -pi/6; % // Define rotation angle
rot = [cos(theta) -sin(theta); sin(theta) cos(theta)]; %// Define rotation matrix
rotate_val = rot*[X Y].'; %// Rotate each of the points
X_rotate = rotate_val(1,:); %// Separate each rotated dimension
Y_rotate = rotate_val(2,:);
plot(X_rotate, Y_rotate, 'b.'); %// Show the plot
xlabel('X');
ylabel('Y');
This is what I get:
If you wanted to perform other transformations, like scaling each axis, you would just multiply either the X or Y co-ordinates by an appropriate scale:
X_scaled = scale_x*X;
Y_scaled = scale_y*Y;
X_scaled and Y_scaled are the scaled versions of your co-ordinates, with scale_x and scale_y are the scales in each dimension you want. If you wanted to translate the co-ordinates, you would add or subtract each of the dimensions by some number:
X_translate = X + X_shift; %// Or -
Y_translate = Y + Y_shift; %// Or -
X_translate and Y_translate are the translated co-ordinates, while X_shift and Y_shift are the amount of shifts you want per dimension. Note that you either do + or -, depending on what you want.

Shade a subset of a sphere's surface in MATLAB

I'm attempting to generate a plot of a hemisphere with a shaded area on the surface bound by max/min values of elevation and azimuth. Essentially I'm trying to reproduce this:
Generating the hemisphere is easy enough, but past that I'm stumped. Any ideas?
Here's the code I used to generate this sphere:
[x,y,z] = sphere;
x = x(11:end,:);
y = y(11:end,:);
z = z(11:end,:);
r = 90;
surf(r.*x,r.*y,r.*z,'FaceColor','yellow','FaceAlpha',0.5);
axis equal;
If you want to highlight a certain area of your hemisphere, first you decide the minimum and maximum azimuth (horizontal sweep) and elevation (vertical sweep) angles. Once you do this, take your x,y,z co-ordinates and convert them into their corresponding angles in spherical co-ordinates. Once you do that, you can then subset your x,y,z co-ordinates based on these angles. To convert from Cartesian to spherical, you would thus do:
Source: Wikipedia
theta is your elevation while phi is your azimuth. r would be the radius of the sphere. Because sphere generates co-ordinates for a unit sphere, r = 1. Therefore, to calculate the angles, we simply need to do:
theta = acosd(z);
phi = atan2d(y, x);
Take note that the elevation / theta is restricted 0 to 180 degrees, while the azimuth / phi is restricted between -180 to 180 degrees. Because you're only creating half of a sphere, the elevation should simply vary from 0 to 90 degrees. Also note that acosd and atan2d return the result in degrees. Now that we're here, you just have to subset what part of the sphere you want to draw. For example, let's say we wanted to restrict the sphere such that the min. and max. azimuth span from -90 to 90 degrees while the elevation only spans from 0 to 45 degrees. As such, let's find those x,y,z co-ordinates that satisfy these constraints, and ensure that anything outside of this range is set to NaN so that these points aren't drawn on the sphere. As such:
%// Change your ranges here
minAzimuth = -90;
maxAzimuth = 90;
minElevation = 0;
maxElevation = 45;
%// Compute angles - assuming that you have already run the code for sphere
%// [x,y,z] = sphere;
%// x = x(11:end,:);
%// y = y(11:end,:);
%// z = z(11:end,:);
theta = acosd(z);
phi = atan2d(y, x);
%%%%%// Begin highlighting logic
ind = (phi >= minAzimuth & phi <= maxAzimuth) & ...
(theta >= minElevation & theta <= maxElevation); % // Find those indices
x2 = x; y2 = y; z2 = z; %// Make a copy of the sphere co-ordinates
x2(~ind) = NaN; y2(~ind) = NaN; z2(~ind) = NaN; %// Set those out of bounds to NaN
%%%%%// Draw our original sphere and then the region we want on top
r = 90;
surf(r.*x,r.*y,r.*z,'FaceColor','white','FaceAlpha',0.5); %// Base sphere
hold on;
surf(r.*x2,r.*y2,r.*z2,'FaceColor','red'); %// Highlighted portion
axis equal;
view(40,40); %// Adjust viewing angle for better view
... and this is what I get:
I've made the code modular so that all you have to do is change the four variables that are defined at the beginning of the code, and the output will highlight that desired part of the hemisphere that are bounded by those min and max ranges.
Hope this helps!
One option is to create an array with the corresponding color you want to attribute to each point.
A minimal example (use trigonometry to convert your azimuth and elevation to logical conditions on x, y, and z):
c=(y>0).*(x>0).*(z>0.1).*(z<0.5);
c(c==0)=NaN;
surf(r.*x,r.*y,r.*z,c ,'FaceAlpha',0.5); axis equal;
yields this:
Note: this only works with the resolution of the grid. (i.e each 'patch' of the surface can have a different color). To exactly reproduce your plot, you might want to superpose the grid sphere with another one that has a much larger number of grid points on which you apply the above code.

Visualizing a toroidal surface in Matlab

I have a problem which is twofold:
How do I plot a toroidal surface in MATLAB, given a major radius R and a minor radius a? To avoid confusion, it is the toroidal/poloidal coordinate system, illustrated in the picture below, that I'm talking about.
Now, in any point (phi, theta) on this surface, the minor radius will be distorted by some value that I have stored in a matrix. How do I plot this distorted surface? This might be easy once I have the answer to part 1, but this is my actual goal, so any solution to part 1 that cannot handle this is pretty useless to me.
If anyone can tell me how to make the image appear smaller here, please do =)
Addressing your first question: first you will need to define the coordinates of your torus in toroidal coordinates (seems natural!) and then convert to Cartesian coordinates, which is how MATLAB expects all plots to be constructed (unless you are making a polar plot, of course). So we start of by defining our toroidal coordinates:
aminor = 1.; % Torus minor radius
Rmajor = 3.; % Torus major radius
theta = linspace(-pi, pi, 64) ; % Poloidal angle
phi = linspace(0., 2.*pi, 64) ; % Toroidal angle
We just want a single surface of the torus, so the minor radius is a scalar. We now make a 2D mesh of these coordinates:
[t, p] = meshgrid(phi, theta);
and convert to 3D Cartesian coordinates using the formulas given on the Wikipedia page linked to in the question:
x = (Rmajor + aminor.*cos(p)) .* cos(t);
y = (Rmajor + aminor.*cos(p)) .* sin(t);
z = aminor.*sin(p);
Now we have our torus defined in 3D, we can plot it using surf:
surf(x, y, z)
axis equal
Edit: To address your second question, it depends how you have your distortions stored, but say you have a matrix defined at each toroidal and poloidal point, you will just have to multiply the constant that is the minor radius by the distortion. In the following I create a distortion matrix that is the same dimensions as my toroidal and poloidal angle coordinate matrices and that is normally distributed about a mean of 1 with a FWHM of 0.1:
distortion = 1. + 0.1 * randn(64, 64);
x = (Rmajor + aminor .* distortion .*cos(p)) .* cos(t);
y = (Rmajor + aminor .* distortion .* cos(p)) .* sin(t);
z = aminor.* distortion .* sin(p);
surf(x, y, z)
The result of which is:
You can do this with surf - just create matrices with the x,y,z coordinates. The page you linked to has the trig equations to do this (they are exactly what you'd come up with on your own - I wrote the code below before checking your link).
R = 10;
a = 3;
tx=nan(41,21);
ty=nan(41,21);
tz=nan(41,21);
for j=1:21
for i=1:41
phi = (i-1)*2*pi/40;
theta = (j-1)*2*pi/20;
tx(i,j)= cos(phi) * (R+cos(theta)*a);
ty(i,j)= sin(phi) * (R+cos(theta)*a);
tz(i,j)= sin(theta)*a;
end
end
figure
surf(tx,ty,tz)
axis equal
To distort the surface, replace the constant a with a matrix of the desired minor radius values, and index into that matrix - i.e. tz(i,j) = sin(theta)*distortion(i,j) (but in all dimensions, obviously).