MATLAB: fitting a 3d surface to a bar graph - matlab

I have a matrix (about 400x400) of numbers between 0 and 1. Here is a plot of the 3D bar graph
I want to fit a 3D surface to the bar graph. Any ideas? The elements of the matrix (the numbers between 0 and 1) should give the height of the surface at each index. I would like the surface to just give us the general shape of the bar graph, and not go through every point.

% init
x = randn(1000,1);
y = randn(1000,1);
nbins = [10 20];
% make histogram
h = histogram2(x,y,nbins)
% set limits and steps
min_x = min(x); max_x = max(x); step_x = (max_x - min_x)/nbins(1);
min_y = min(y); max_y = max(y); step_y = (max_y - min_y)/nbins(2);
% make grid
surf_z = h.Values;
surf_x = [min_x + step_x/2 : step_x : max_x - step_x/2];
surf_y = [min_y + step_y/2 : step_y : max_y - step_y/2];
[xx, yy] = meshgrid(surf_x, surf_y)
% plot 3D surface
surf(xx',yy',surf_z)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% other variant
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% make grid and interpolant
[xx, yy] = ndgrid(surf_x, surf_y)
F = griddedInterpolant(xx,yy,surf_z,'spline');
% make 3D surface with a some step value
step = 0.01;
[Xq,Yq] = ndgrid(min(surf_x):step:max(surf_x), min(surf_y):step:max(surf_y));
Zq = F(Xq,Yq);
% plot 3D surface
mesh(Xq,Yq,Zq);

Related

How do I label lines in a MatLab plot?

What my plot looks like
What the plot should look like
The code is working like it should but im trying to get the labels to show up on each line from (1-8). Just like the picture above.
I have read a bunch of posts and tried to search Matlab but i havent been able to figure it out.
clc;clear;close all;
V_inf = 20; % freestream velocity
R = 1; % cylinder radius
n = 8; % number of panels
d_theta = 2*pi/n; % resolution of angles
alpha = 0; % angle of attack
theta = pi+pi/n:-d_theta:-pi+pi/n; % angles of boundary points of panels
X = R*cos(theta); % X coordinates of boundary points of panels
Y = R*sin(theta); % Y coordinates of boundary points of panels
Phi = zeros(n,1); % angle from Vinf to bottom of panel
beta = zeros(n,1); % angle from Vinf to outward normal of panel
conX = zeros(n,1); % X coordinates of control points
conY = zeros(n,1); % Y coordinates of control points
S = zeros(n,1); % panel length
for i = 1:n
Phi(i) = -alpha + atan2((Y(i+1)-Y(i)),(X(i+1)-X(i)));
beta(i) = Phi(i)+pi/2;
if beta(i)>2*pi, beta(i)=beta(i)-2*pi;
elseif beta(i)<0, beta(i)=beta(i)+2*pi; end
conX(i) = (X(i+1)+X(i))/2;
conY(i) = (Y(i+1)+Y(i))/2;
S(i) = sqrt((X(i+1)-X(i))^2 + (Y(i+1)-Y(i))^2);
end
close all
plot(R*cos(0:0.01:2*pi),R*sin(0:0.01:2*pi),'b', X,Y,'r',conX,conY,'g^');
axis equal; legend('Exact shape','Panel approximation','Control points')
xlabel('x, m'); ylabel('y, m'); title('Fig. 1 Panel layout (n = 8, R = 1m)');
Possibly plotting the labels along the points of a circle using the text() function may suffice. There's some shifting of points and flipping that needs to be done to get the order you wish but otherwise it's just 8 points taken along a circle that is smaller in diameter in comparison to the octagon. An alternative would be using the green triangles as reference instead but that involves more math. As long as your octagon is expected to be symmetrical vertically and horizontally this should work alright.
clc;clear;close all;
V_inf = 20; % freestream velocity
R = 1; % cylinder radius
n = 8; % number of panels
d_theta = 2*pi/n; % resolution of angles
alpha = 0; % angle of attack
theta = pi+pi/n:-d_theta:-pi+pi/n; % angles of boundary points of panels
X = R*cos(theta); % X coordinates of boundary points of panels
Y = R*sin(theta); % Y coordinates of boundary points of panels
Phi = zeros(n,1); % angle from Vinf to bottom of panel
beta = zeros(n,1); % angle from Vinf to outward normal of panel
conX = zeros(n,1); % X coordinates of control points
conY = zeros(n,1); % Y coordinates of control points
S = zeros(n,1); % panel length
for i = 1:n
Phi(i) = -alpha + atan2((Y(i+1)-Y(i)),(X(i+1)-X(i)));
beta(i) = Phi(i)+pi/2;
if beta(i)>2*pi, beta(i)=beta(i)-2*pi;
elseif beta(i)<0, beta(i)=beta(i)+2*pi; end
conX(i) = (X(i+1)+X(i))/2;
conY(i) = (Y(i+1)+Y(i))/2;
S(i) = sqrt((X(i+1)-X(i))^2 + (Y(i+1)-Y(i))^2);
end
close all
plot(R*cos(0:0.01:2*pi),R*sin(0:0.01:2*pi),'b', X,Y,'r',conX,conY,'g^');
axis equal; legend('Exact shape','Panel approximation','Control points')
xlabel('x, m'); ylabel('y, m'); title('Fig. 1 Panel layout (n = 8, R = 1m)');
%*************************************************************************%
%ADDING LABELS BY PLOTTING LABELS ALONG CIRCLE%
%*************************************************************************%
Radius = 0.8;
Number_Of_Data_Points = 9;
theta = linspace(0,2*pi,Number_Of_Data_Points);
X_Circle = Radius*cos(theta);
X_Circle = X_Circle(1:end-1);
Y_Circle = Radius*sin(theta);
Y_Circle = Y_Circle(1:end-1);
X_Circle = flip(circshift(X_Circle,3));
Y_Circle = flip(circshift(Y_Circle,3));
for Point_Index = 1: numel(conX)
X_Displacement = X_Circle(Point_Index);
Y_Displacement = Y_Circle(Point_Index);
text(X_Displacement,Y_Displacement,num2str(Point_Index),'HorizontalAlignment','center','fontsize',20);
end
To Plot on Control Points:
%*************************************************************************%
%ADDING LABELS BY PLOTTING LABELS ALONG CONTROL POINTS%
%*************************************************************************%
for Point_Index = 1: numel(conX)
text(conX(Point_Index),conY(Point_Index),num2str(Point_Index),'HorizontalAlignment','center','fontsize',20);
end

how to plot a sphere on top of Gaussian 3D?

I want to create a shape that is a sphere on top of the 3D Gaussian.
something like this:
for plotting Gaussian I wrote tihs:
% isotropic Gaussian parameters
n = 100; % resolution
s = 2; % width
x = linspace(-5,5,n);
[X,Y] = meshgrid(x);
gaus2d = exp( -(X.^2 + Y.^2 )/(2*s^2));
figure(1), clf
surf(x,x,gaus2d)
and for sphere:
rotate3d on
hold on
[x1,y1,z1] = sphere;
% adjusting the radius of sphere
x1 = x1*s;
y1 = y1*s;
z1 = z1*s;
surf(x1,y1,z1)
The problem is: I don't know how to shift the sphere on top of the Gaussian. How to transfer sphere on top of the Gaussain?
You can add a constant to the sphere's z-values in ordner to 'lift' it up:
% isotropic Gaussian parameters
n = 100; % resolution
s = 2; % width
x = linspace(-5,5,n);
[X,Y] = meshgrid(x);
gaus2d = exp( -(X.^2 + Y.^2 )/(2*s^2));
figure(1), clf
surf(x,x,gaus2d)
rotate3d on
hold on
[x1,y1,z1] = sphere;
% adjusting the radius of sphere
x1 = x1*s;
y1 = y1*s;
z1 = z1;
% add a constant to sphere, so that it is on top of gauss
addi = max(gaus2d(:)) - min(z1(:));
z1 = z1 + addi;
surf(x1,y1,z1)

How can I make a cylindrical 3D contour plot in Matlab?

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

How to plot a point in some color based on the result of a comparaison, not using a loop?

I'm using random points to determine the area below a curve (Monte-Carlo):
X: 1xn vector of x values for the function
Y: 1xn vector of y = f(x)
RP: mxn matrix of m random y for each x
I would like to split RP into RPA and RPB depending on it being above or below the curve. The idea is then to plot RPA and RPB against X, in different colors. This code doesn't work because RPA and RPB number of columns is not the same than X:
clf
f = #(x) sin(x/10) + cos(x/60); % Function
xMin = 1; xMax = 100; % x interval
X = [xMin:xMax];
Y = f(X);
plot(X,Y), hold on % Plot function
yMin = min(Y); yMax = max(Y); % Axes limits
set(gca, 'xlim', [xMin, xMax], 'ylim', [yMin, yMax])
m = 20; % Random points per x value
RP = rand(m, columns(X)) .* (yMax-yMin) .+ yMin;
% Split points (doesn't work)
RPA = RP(RP>Y);
RPB = RP(RP<=Y);
br = size(RPB) / size(RP) % Ratio of points below
a = (xMax - xMin) * (yMax - yMin) * br % Area below
% Plot points
plot(X, RPA, 'r.') % Above
plot(X, RPB, 'g.') % Below
Is there a possibility to build RPA and RPB so that they are the same size than RP, with the excluded points y being NaN or something similar, which can be counted and plotted?
You gave a good answer yourself. You can build RPA and RPB with strategic NaNs:
% Split points (works!)
RPA = RP;
RPA(RP<=Y) = NaN;
RPB = RP;
RPB(RPB > Y) = NaN;
And than calculating the ration as the not-NaN:
br = sum(~isnan(RPB)) / sum(~isnan(RP)) % Ratio of points below
I get this nice image:

Area between circular curves

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