I am trying to make a cut out of a pipe and I want to make a curved surface to represent the outside of the pipe. However when I plot the surface I only get the diagonal of the surface instead of the surface itself. How can I fix this?
MWE:
r = 0:0.1:3;
z = 0:0.1:10;
[rr, zz] = meshgrid(r,z);
% set cut planes angles
theta1 = 0;
theta2 = pi*135/180;
nt = 101; % angle resolution
figure(1);
clf;
t3 = linspace(theta1, (theta2 - 2*pi), nt);
[rr3, tt3] = meshgrid(r,t3);
% Create curved surface
xx5 = r(end) * cos(tt3);
yy5 = r(end) * sin(tt3);
h5 = surface(xx5, yy5,zz)
The mesh-grid you created is based on theta and the radius. However, the radius is constant for the outside of the pipe so instead it should be based on theta and z since those are the two independent variables defining the grid. Based on this reasoning I believe the following is what you're after.
r = 0:0.1:3;
z = 0:0.1:10;
% set cut planes angles
theta1 = 0;
theta2 = pi*135/180;
nt = 101; % angle resolution
figure(1);
clf;
% create a grid over theta and z
t3 = linspace(theta1, (theta2 - 2*pi), nt);
[tt3, zz3] = meshgrid(t3, z);
% convert from cylindical to Cartesian coordinates
xx5 = r(end) * cos(tt3);
yy5 = r(end) * sin(tt3);
% plot surface
h5 = surface(xx5, yy5, zz3, 'EdgeColor', 'none');
% extra stuff to make plot prettier
axis vis3d
axis equal
view(3)
camzoom(0.7);
Try with surf with surf(xx5, yy5, zz). Is this what you are looking for?
Related
I am trying to generate a semi ellipsoidal dome shape by using x, y, z values. In my code below, I define the x,y,z values but I am unable to assign those values to sphere.
How do I resolve this?
clc
x = [65 55.2125 50.8267 46.7398 42.9232 39.3476 35.9815 32.7882 29.7175 26.6833 23.4690 18.7605];
y = x;
z = [0.0,0.9,2.7,5.2,8.2,11.8,15.8,20.3,25.2,30.7,37.1,47.5]; % max height of dome is 47.5
[x,y,z] = sphere(20);
x = x(12:end,:);
y = y(12:end,:);
z = z(12:end,:);
r = 65; % radius of the dome
surf(r.*x,r.*y,r.*z);
axis equal;
It will be simpler or at least more elegant to use the parametric equations of an ellipsoide.
% semi axis parameters
a = 65; % x-axis
b = 65; % y-axis
c = 47.5; % z-axis
%% Parametrisation
%
% To reach each point of the ellipsoide we need two angle:
% phi ∈ [0,2𝜋]
% theta ∈ [0, 𝜋]
%
% But since we only need half of an ellipsoide we can set
% theta ∈ [0,𝜋/2]
[theta,phi] = ndgrid(linspace(0,pi/2,50),linspace(0,2*pi,50));
x = a*sin(theta).*cos(phi);
y = b*sin(theta).*sin(phi);
z = c*cos(theta);
%plot
surf(x,y,z)
axis equal
Result:
You can remove the bottom half of the sphere by assigning NaNs to the approptiate elements of x and y:
[x,y,z] = sphere(20);
I = (z<0);
x(I) = NaN;
y(I) = NaN;
surf(x,y,z)
I have an axisymmetric flow with m x n grid points in r and z direction and I want to plot the temperature that is stored in a matrix with size mxn in a 3D cylindrical plot as visualized in the link below (My reputation is not high enough to include it as a picture).
I have managed to plot it in 2D (r,z plane) using contour but I would like to add the theta plane for visualization. How can I do this?
You can roll your own with multiple calls to surface().
Key idea is: for each surface: (1) theta=theta1, (2) theta=theta2, (3) z=zmax, (4) z=0, (5) r=rmax, generate a 3D mesh (xx,yy,zz) and the temperature map on that mesh. So you have to think about how to construct each surface mesh.
Edit: completed code is now provided. All magic number and fake data are put at (almost) the top of the code so it's easy to convert it into a general purpose Matlab function. Good luck!
% I have adjusted the range values to show the curved cylinder wall
% display a variable temperature
r = 0:0.1:2.6; % you can also try r = 0:0.1:3.0
z = 0:0.1:10; % you can also try z = 0:0.1:15;
[rr, zz] = meshgrid(r,z);
% fake temperature data
temp = 100 + (10* (3-rr).^0.6) .* (1-((zz - 7.5)/7.5).^6) ;
% visualize in 2D
figure(1);
clf;
imagesc(r,z,temp);
colorbar;
% set cut planes angles
theta1 = 0;
theta2 = pi*135/180;
nt = 40; % angle resolution
figure(2);
clf;
xx1 = rr * cos(theta1);
yy1 = rr * sin(theta1);
h1 = surface(xx1,yy1,zz,temp,'EdgeColor', 'none');
xx2 = rr * cos(theta2);
yy2 = rr * sin(theta2);
h2 = surface(xx2,yy2,zz,temp,'EdgeColor', 'none');
% polar meshgrid for the top end-cap
t3 = linspace(theta1, (theta2 - 2*pi), nt);
[rr3, tt3] = meshgrid(r,t3);
xx3 = rr3 .* cos(tt3);
yy3 = rr3 .* sin(tt3);
zz3 = ones(size(rr3)) * max(z);
temp3 = zeros(size(rr3));
for k = 1:length(r)
temp3(:,k) = temp(end,k);
end
h3 = surface(xx3,yy3,zz3,temp3,'EdgeColor', 'none');
% polar meshgrid for the bottom end-cap
zz4 = ones(size(rr3)) * min(z);
temp4 = zeros(size(rr3));
for k = 1:length(r)
temp4(:,k) = temp(1,k);
end
h4 = surface(xx3,yy3,zz4,temp4,'EdgeColor', 'none');
% generate a curved meshgrid
[tt5, zz5] = meshgrid(t3,z);
xx5 = r(end) * cos(tt5);
yy5 = r(end) * sin(tt5);
temp5 = zeros(size(xx5));
for k = 1:length(z)
temp5(k,:) = temp(k,end);
end
h5 = surface(xx5, yy5, zz5,temp5,'EdgeColor', 'none');
axis equal
colorbar
view(125,25); % viewing angles
I'm trying to calculate the surface between two circular curves (yellow surface in this picture as simplification) but I'm somehow stuck since I don't have datapoints at the same angular values of the two curves. Any ideas?
Thanks for your help!
Picture:
I assume you have the x,y coordinates which you used to the plot. I obtained them here using imfreehand. I used inpolygon to generate a binary mask for each curve and then apply xor on them to get a mask of the desired area:
% x,y were obtained using imfreehand on 100x100 image and getPosition()
x = [21;22;22;22;22;22;22;23;23;23;23;23;23;24;25;25;26;26;27;28;29;30;30;31;32;32;33;34;35;36;37;38;39;40;41;42;43;44;45;46;47;48;49;50;51;52;53;54;55;56;57;58;59;60;61;62;63;64;65;66;67;68;69;70;71;72;73;74;75;76;77;78;79;79;80;80;81;81;81;82;82;82;82;83;83;83;84;84;85;85;86;86;86;86;86;86;85;84;84;83;82;81;80;79;78;77;76;75;74;73;72;71;70;69;68;67;66;65;64;63;62;61;60;59;58;57;56;55;54;53;52;51;50;49;48;47;46;45;44;43;42;41;40;39;38;37;36;35;34;33;32;31;30;29;28;27;26;25;25;24;24;23;22;21;21;21;21;21;21;21;21;21;21;21;21;21];
y = [44;43;42;41;40;39;38;37;36;35;34;33;32;31;30;29;28;27;26;25;24;23;22;21;20;19;18;18;17;17;17;17;17;16;16;16;16;16;16;15;15;14;14;14;14;14;14;15;15;15;16;16;17;17;17;17;18;18;18;19;20;20;21;22;23;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40;41;42;43;44;45;46;47;48;49;50;51;52;53;54;55;56;56;57;57;58;59;59;60;61;61;61;61;61;60;60;60;59;58;57;56;56;55;55;54;54;54;54;54;54;54;54;54;55;55;55;55;56;57;58;59;60;61;61;62;63;63;64;64;65;65;66;66;66;66;66;66;65;64;63;62;61;60;59;58;57;56;55;54;53;52;51;50;49;48;47;46;45;44];
% generate arbitrary xy
x1 = (x - 50)./10; y1 = (y - 50)./10;
x2 = (x - 50)./10; y2 = (y - 40)./10;
% generate binary masks using poly2mask
pixelSize = 0.01; % resolution
xx = min([x1(:);x2(:)]):pixelSize:max([x1(:);x2(:)]);
yy = min([y1(:);y2(:)]):pixelSize:max([y1(:);y2(:)]);
[xg,yg] = meshgrid(xx,yy);
mask1 = inpolygon(xg,yg,x1,y1);
mask2 = inpolygon(xg,yg,x2,y2);
% add both masks (now their common area pixels equal 2)
combinedMask = mask1 + mask2;
% XOR on both of them
xorMask = xor(mask1,mask2);
% compute mask area in units (rather than pixels)
Area = bwarea(xorMask)*pixelSize^2;
% plot
subplot(131);
plot(x1,y1,x2,y2,'LineWidth',2);
title('Curves');
axis square
set(gca,'YDir','reverse');
subplot(132);
imshow(combinedMask,[]);
title('Combined Mask');
subplot(133);
imshow(xorMask,[]);
title(['XNOR Mask, Area = ' num2str(Area)]);
function area = area_between_curves(initial,corrected)
interval = 0.1;
x = -80:interval:80;
y = -80:interval:80;
[X,Y] = meshgrid(x,y);
in_initial = inpolygon(X,Y,initial(:,1),initial(:,2));
in_corrected = inpolygon(X,Y,corrected(:,1),corrected(:,2));
in_area = xor(in_initial,in_corrected);
area = interval^2*nnz(in_area);
% visualization
figure
hold on
plot(X(in_area),Y(in_area),'r.')
plot(X(~in_area),Y(~in_area),'b.')
end
If I use the lines of the question, this is the result:
area = 1.989710000000001e+03
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
I am trying to produce this graph using Matlab. The built-in ellipsoid function is confusing. For this problem I have two variables ( width and length ) and a constant height.
to make it very simple I want to show that the width is changing while we approach the tip but height is constant. w,x,h are the variables shown in the graph.
I would really appreciate it if someone can help.
The following code gets you a long way, I think. See example output:
I added enought comments that you should be able to take it from here...
% plot ellipsoid in 3D
% height and width of ellipsoid:
e_h = 10;
e_w = 3;
% position where the "quivers" (arrows) go:
q_offset = 2; % distance from axis
q_scale = 0.5; % multiplier to give parabola some size
q_spacing = 0.5; % distance between arrows
q_height = 2.5; % height above XY plane where arrows are drawn
N = 1000; % number of points for drawing
theta = linspace(0, 2*pi, N); % parameter to help drawing ellipse
zerov = zeros(1, N); % array of zeros that I will need
% coordinates of main ellipse:
x = e_w * sin(theta);
y = zeros(size(x));
z = e_h * cos(theta);
% plot main ellipse:
figure;
plot3(x, y, z)
% secondary plot
y2 = q_scale*(e_w.^2 - x.^2) + 2; % offset parabola - what your plot looked like...
hold on
plot3(x, y2, zerov+q_height); % plotting the parabola in the XY plane at height
axis equal % make the plot dimensions isotropic
% add quivers
q_base = -e_w:q_spacing:e_w; % x coordinate; y and z are fixed
q_length = (e_w.^2 - q_base.^2)*q_scale; % length of quiver - just an equation I chose
q0 = zeros(size(q_base)); % another vector I will need multiple times
q1 = ones(size(q_base)); % ditto
% plot the arrows: the "-1" argument means "don't scale"
quiver3(q_base, q0+q_offset, q_height*q1, q0, q_length, q0, -1)