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
Related
I would like to generate surface of revolution from bezier curve. I have made bezier curve in MATLAB but beyond this point I am stuck and do not know how to proceed. Please help.
Below is the code that I have made.
clc
clear
close all
% Name : Savla Jinesh Shantilal
% Bits ID : 2021HT30609
%% Define inout parameters
B = [1,1; 2,3; 4,3; 3,1]; % Creating matrix for polygon vertices
[r,s] = size(B); % getting size of matrix in terms of rows and columns
n = r-1; % n+1 represents number of vertices of the polygon
np = 20; % represents number of equi-distance points on the bezier curve
t = linspace(0,1,np);
%% Plot polygon
for k = 1:n
plot([B(k,1),B(k+1,1)], [B(k,2),B(k+1,2)], 'r', 'LineWidth', 2)
hold on
grid on
end
%% Generate the points on the bezier curve
for j = 1:np
P = [0,0];
for i = 0:n
M(i+1) = (factorial(n)/(factorial(i)*factorial(n-i)))*((t(j))^i)*((1-t(j))^(n-i));
P = P + B(i+1,:)*M(i+1);
end
Q(j,:) = P;
end
%% Plot the bezier curve from the obtained points
for l = 1:np-1
plot([Q(l,1),Q(l+1,1)],[Q(l,2),Q(l+1,2)], '-- b', 'LineWidth', 2);
hold on
end
Usually one can use the built-in cylinder function for monotonically increasing x-values. Here, the bezier curve has non monotonic values from max(x) so we break it to two parts to parameterize it, and then add an angle rotation.
% first define the x and y coordinate from your Q info:
xx = Q(:,1);
yy = Q(:,2);
N = 1e2;
[~, id] = max(xx); % the position where we split
t = linspace(xx(1),xx(id),N);
% Parameterize the function:
t = linspace(0,2*(xx(id)-xx(1)),N);
x = zeros(1, N);
L = t <= ( xx(id)-xx(1) ); % the "Left" side of the curve
x(L) = t(L)+xx(1);
x(~L) = flip(x(L));
%define the r value
r = x;
r(L) = interp1(xx(1:id) ,yy(1:id) , x(L) ); % Left side
r(~L) = interp1(xx(id:end),yy(id:end), x(~L)); % right side (not left)
% define your x values
x = repmat(x', [1 N]);
% define the theta that will perform the rotation
theta = linspace(0,2*pi, N);
% initialize values for y and z
y = zeros(N);
z = zeros(N);
% calculate the y and z values
for i=1:N
y(i,:) = r(i) *cos(theta);
z(i,:) = r(i) *sin(theta);
end
%plot the surface of revolution and the original curve
s = surf(x,y,z);
alpha 0.4
hold on
plot(xx,yy,'k','LineWidth',3)
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?
I need to extract the isoline coordinates of a 4D variable from a 3D surface defined using a triangulated mesh in MATLAB. I need the isoline coordinates to be a ordered in such a manner that if they were followed in order they would trace the path i.e. the order of the points a 3D printer would follow.
I have found a function that can calculate the coordinates of these isolines (see Isoline function here) but the problem is this function does not consider the isolines to be joined in the correct order and is instead a series of 2 points separated by a Nan value. This makes this function only suitable for visualisation purposes and not the path to follow.
Here is a MWE of the problem of a simplified problem, the surface I'm applying it too is much more complex and I cannot share it. Where x, y and z are nodes, with TRI providing the element connectivity list and v is the variable of which I want the isolines extracted from and is not equal to z.
If anyone has any idea on either.....
A function to extract isoline values in the correct order for a 3D tri mesh.
How to sort the data given by the function Isoline so that they are in the correct order.
.... it would be very much appreciated.
Here is the MWE,
% Create coordinates
[x y] = meshgrid( -10:0.5:10, -10:0.5:10 );
z = (x.^2 + y.^2)/20; % Z height
v = x+y; % 4th dimension value
% Reshape coordinates into list to be converted to tri mesh
x = reshape(x,[],1); y = reshape(y,[],1); z = reshape(z,[],1); v = reshape(v,[],1);
TRI = delaunay(x,y); % Convertion to a tri mesh
% This function calculates the isoline coordinates
[xTows, yTows, zTows] = IsoLine( {TRI,[x, y, z]}, v, -18:2:18);
% Plotting
figure(1); clf(1)
subplot(1,2,1)
trisurf(TRI,x,y,z,v)
hold on
for i = 1:size(xTows,1)
plot3( xTows{i,1}, yTows{i,1}, zTows{i,1}, '-k')
end
hold off
shading interp
xlabel('x'); ylabel('y'); zlabel('z'); title('Isolines'), axis equal
%% This section is solely to show that the isolines are not in order
for i = 1:size(xTows,1)
% Arranging data into colums and getting rid of Nans that appear
xb = xTows{i,1}; yb = yTows{i,1}; zb = zTows{i,1};
xb = reshape(xb, 3, [])'; xb(:,3) = [];
yb = reshape(yb, 3, [])'; yb(:,3) = [];
zb = reshape(zb, 3, [])'; zb(:,3) = [];
subplot(1,2,2)
trisurf(TRI,x,y,z,v)
shading interp
view(2)
xlabel('x'); ylabel('y'); zlabel('z'); title('Plotting Isolines in Order')
axis equal; axis tight; hold on
for i = 1:size(xb,1)
plot3( [xb(i,1) xb(i,2)], [yb(i,1) yb(i,2)], [zb(i,1) zb(i,2)], '-k')
drawnow
end
end
and here is the function Isoline, which I have slightly adpated.
function [xTows, yTows, zTows] = IsoLine(Surf,F,V,Col)
if length(Surf)==3 % convert mesh to triangulation
P = [Surf{1}(:) Surf{2}(:) Surf{3}(:)];
Surf{1}(end,:) = 1i;
Surf{1}(:,end) = 1i;
i = find(~imag(Surf{1}(:)));
n = size(Surf{1},1);
T = [i i+1 i+n; i+1 i+n+1 i+n];
else
T = Surf{1};
P = Surf{2};
end
f = F(T(:));
if nargin==2
V = linspace(min(f),max(f),22);
V = V(2:end-1);
elseif numel(V)==1
V = linspace(min(f),max(f),V+2);
V = V(2:end-1);
end
if nargin<4
Col = 'k';
end
H = NaN + V(:);
q = [1:3 1:3];
% -------------------------------------------------------------------------
% Loop over iso-values ----------------------------------------------------
xTows = [];
yTows = [];
zTows = [];
for k = 1:numel(V)
R = {[],[]};
G = F(T) - V(k);
C = 1./(1-G./G(:,[2 3 1]));
f = unique(T(~isfinite(C))); % remove degeneracies by random perturbation
F(f) = F(f).*(1+1e-12*rand(size(F(f)))) + 1e-12*rand(size(F(f)));
G = F(T) - V(k);
C = 1./(1-G./G(:,[2 3 1]));
C(C<0|C>1) = -1;
% process active triangles
for i = 1:3
f = any(C>=0,2) & C(:,i)<0;
for j = i+1:i+2
w = C(f,q([j j j]));
R{j-i} = [R{j-i}; w.*P(T(f,q(j)),:)+(1-w).*P(T(f,q(j+1)),:)];
end
end
% define isoline
for i = 1:3
X{i} = [R{1}(:,i) R{2}(:,i) nan+R{1}(:,i)]';
% X{i} = [R{1}(:,i) R{2}(:,i)]'; % Changed by Matt
X{i} = X{i}(:)';
end
% plot isoline
if ~isempty(R{1})
% hold on
% H(k) = plot3(X{1},X{2},X{3},Col);
% Added by M.Thomas
xTows{k,1} = X{1};
yTows{k,1} = X{2};
zTows{k,1} = X{3};
end
end
What you will notice is that the isolines (xTows, yTows and zTows) are not in order there "jump around" when plotted sequentially. I need to sort the tows so that they give a smooth plot in order.
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
guys I am trying to calculate 1D power spectrum from 2D FFT of the image. I did it with horizontal averaging but by looking at a graph it's not making me sense. Can you please suggest how to do radial averaging over 2D data set to reach 1D representation of noise power spectrum. Thank you
I will appreciate your help.
Here is my code
$
fid = fopen('C:\Users\3772khobrap\Desktop\project related\NPS_cal_data_UB\100000006.raw','r');
img = fread(fid,[512 512],'uint16');
roi = zeros(64);
avg = zeros(64);
Ux= 0.0075;% Pixel size
Uy = 0.0075;% Pixel size
%% This block of code is subdividing imaage into smaller ROI and averaging purpose
for r = 1:8
r_shift = (r-1)*64;
for c = 1:8
c_shift = (c-1)*64;
for i = 1:64
for j = 1:64
p = img(i+r_shift,j+c_shift);
roi(i,j) = p;
end
end
avg = avg+roi;
end
end
avg = avg./64;
%%Actual process of NPS calculation
scale = (Ux*Uy)./(64*64);%Scaling fator as per NPS calculation formula
f_x = 1/(2*Ux);%Nyquiest frequecy along x direction
f_y = 1/(2*Ux);%Nyquiest frequecy along y direction
FFT_2d = (fftshift(fft2(avg))).^2;% Power spectrum calculation
NPS = abs(FFT_2d).*scale; %% 2D NPS
f = linspace(-f_x,f_y,64);% x-axis vector for 1D NPS
X_img = linspace(-f_x,f_x,512);% X axis of NPS image
Y_img = linspace(-f_x,f_x,512);% Y axis of NPS image
figure(1)
subplot(2,2,1)
imagesc(X_img,Y_img,img)
colormap gray
xlabel('X [cm]'); ylabel('Y [cm]')
title('noise image')
subplot(2,2,2)
imagesc(f,f,log(NPS))
colormap gray
xlabel('frequency [cm^{-1}]'); ylabel('frequency [cm^{-1}]');
title('2D NPS')
subplot(2,2,3)
plot(f_p,NPS(:,32))
xlabel('frequency [cm^{-2}]'); ylabel('NPS [cm^{-2}]')
title('1D NPS from central slice')
subplot(2,2,4)
plot(f_p,mean(NPS,2))
xlabel('frequency [cm^{-2}]'); ylabel('NPS [cm^2]')
title('1D NPS along X direction')
You can program a function like this :
function profile = radialAverage(IMG, cx, cy, w)
% computes the radial average of the image IMG around the cx,cy point
% w is the vector of radii starting from zero
[a,b] = size(IMG);
[X, Y] = meshgrid( (1:a)-cx, (1:b)-cy);
R = sqrt(X.^2 + Y.^2);
profile = [];
for i = w % radius of the circle
mask = (i-1<R & R<i+1); % smooth 1 px around the radius
values = (1-abs(R(mask)-i)) .* double(IMG(mask)); % smooth based on distance to ring
% values = IMG(mask); % without smooth
profile(end+1) = mean( values(:) );
end
end