How to 3D plot a parametric curve WITH orthographic projections? - matlab

How to generate a 3D plot of a parametric curve with the orthographic projections on the inside walls of the 3D plot, as shown in this video?
I can generate a 3D plot of the curve using the plot3 function but I am lost how to create the blue orthographic projections on the inner walls of the cube ...and the red dashed lines.
P.S.
Also, how to generate the 3 separate "Coordinate Functions" plots (not shown on the diagram above but shown in the video) ...and how to synchronize them together during the animation?

Well, this took a lot of time to write.
clear;clc;close all
t = 0:0.01:6;
x = cos(5*t);
y = sin(5*t);
z = t;
%for x,y,z as as general function of t
xmin = min(x);
xmax = max(x);
ymin = min(y);
ymax = max(y);
zmin = min(z);
zmax = max(z);
orangeColorRGB = [0.8500 0.3250 0.0980];
%3D plot
plot3DSubplot = subplot(3,2,[1 3 5]);
view(3)
grid on
title('Parametric curve in R^3')
xlabel('x(t)')
ylabel('y(t)')
zlabel('z(t)')
xyProjectionOffset = -0.5;
xzProjectionOffset = 1.5;
yzProjectionOffset = 1.5;
xlim([xmin max(xmax, yzProjectionOffset)])
ylim([ymin max(ymax, xzProjectionOffset)])
zlim([min(zmin ,xyProjectionOffset) zmax])
curve3D_marker = animatedline(plot3DSubplot,x(1),y(1),z(1),'Marker','o','Color','magenta'); %marker in 3D
curve3D = animatedline(plot3DSubplot, x(1),y(1),z(1),'Color','magenta','LineStyle','-','LineWidth',2); %3D curve
curve3D_projectedToXY = animatedline(plot3DSubplot,x(1),y(1),xyProjectionOffset,'Color','blue','LineStyle','-','LineWidth',2); %projection on xy
curve3D_projectionStraightLineToXY = animatedline(plot3DSubplot,x(1),y(1),z(1),'Color',orangeColorRGB,'LineStyle','--','LineWidth',2); %projection on xy
curve3D_projectedToXZ = animatedline(plot3DSubplot,x(1),xzProjectionOffset,z(1),'Color','blue','LineStyle','-','LineWidth',2); %projection on xz
curve3D_projectionStraightLineToXZ = animatedline(plot3DSubplot,x(1),y(1),z(1),'Color',orangeColorRGB,'LineStyle','--','LineWidth',2); %projection on xy
curve3D_projectedToYZ = animatedline(plot3DSubplot,yzProjectionOffset,y(1),z(1),'Color','blue','LineStyle','-','LineWidth',2); %projection on yz
curve3D_projectionStraightLineToYZ = animatedline(plot3DSubplot,x(1),y(1),z(1),'Color',orangeColorRGB,'LineStyle','--','LineWidth',2); %projection on xy
%x plot
xPlot = subplot(3,2,2);
grid on
title('Coordinates')
ylabel('cos(5t)')
xlim([t(1) t(end)])
ylim([xmin xmax])
xCoordinate = animatedline(xPlot,t(1),x(1),'Color','blue');
xCoordinate_projectedValue = animatedline(xPlot,t(1),x(1),'Color',orangeColorRGB,'LineStyle','--');
xCoordinateMarker = animatedline(xPlot,t(1),x(1),'Color','blue','Marker','o');
%y plot
yPlot = subplot(3,2,4);
grid on
ylabel('sin(5t)')
xlim([t(1) t(end)])
ylim([ymin ymax])
yCoordinate = animatedline(yPlot,t(1),y(1),'Color','blue');
yCoordinate_projectedValue = animatedline(yPlot,t(1),y(1),'Color',orangeColorRGB,'LineStyle','--');
yCoordinateMarker = animatedline(yPlot,t(1),y(1),'Color','blue','Marker','o');
%z plot
zPlot = subplot(3,2,6);
grid on
ylabel('t')
xlim([t(1) t(end)])
ylim([zmin zmax])
zCoordinate = animatedline(zPlot,t(1),z(1),'Color','blue');
zCoordinate_projectedValue = animatedline(zPlot,t(1),z(1),'Color',orangeColorRGB,'LineStyle','--');
zCoordinateMarker = animatedline(zPlot,t(1),z(1),'Color','blue','Marker','o');
for i=2:length(t)
% 3D plot & projections
addpoints(curve3D,x(i),y(i),z(i))
clearpoints(curve3D_marker)
addpoints(curve3D_marker,x(i),y(i),z(i))
%XY projection
addpoints(curve3D_projectedToXY,x(i),y(i),xyProjectionOffset)
clearpoints(curve3D_projectionStraightLineToXY)
addpoints(curve3D_projectionStraightLineToXY,[x(i) x(i)],[y(i) y(i)],[xyProjectionOffset z(i)])
%XZ projection
addpoints(curve3D_projectedToXZ,x(i),xzProjectionOffset,z(i))
clearpoints(curve3D_projectionStraightLineToXZ)
addpoints(curve3D_projectionStraightLineToXZ,[x(i) x(i)],[xzProjectionOffset y(i)],[z(i) z(i)])
%YZ projection
addpoints(curve3D_projectedToYZ,1.5,y(i),z(i))
clearpoints(curve3D_projectionStraightLineToYZ)
addpoints(curve3D_projectionStraightLineToYZ,[yzProjectionOffset x(i)],[y(i) y(i)],[z(i) z(i)])
%2D x plot
addpoints(xCoordinate,t(i),x(i))
clearpoints(xCoordinate_projectedValue)
addpoints(xCoordinate_projectedValue,[t(1) t(i)],[x(i) x(i)])
clearpoints(xCoordinateMarker)
addpoints(xCoordinateMarker,t(i),x(i))
%2D y plot
addpoints(yCoordinate,t(i),y(i))
clearpoints(yCoordinate_projectedValue)
addpoints(yCoordinate_projectedValue,[t(1) t(i)],[y(i) y(i)])
clearpoints(yCoordinateMarker)
addpoints(yCoordinateMarker,t(i),y(i))
%2D z plot
addpoints(zCoordinate,t(i),z(i))
clearpoints(zCoordinate_projectedValue)
addpoints(zCoordinate_projectedValue,[t(1) t(i)],[z(i) z(i)])
clearpoints(zCoordinateMarker)
addpoints(zCoordinateMarker,t(i),z(i))
drawnow
end
Outside the for-loop:
Define your subplots.
Define your lines in a somewhat object-oriented approach. Define lines with animatedline
including their
properties
(color, linewidth etc).
Define lines with animatedline with the intention to use only the marker.
Inside the for-loop:
Add the new points to those lines using addpoints.
Regarding the projections to the xy,yz,xz planes:
The projection of a point (x0,y0,z0) to the xy plane defined by z = -2 is (x0,y0,-2). Therefore when you have a point (x,y,z) you project to the correct plane in the same way.
For the vertical lines that connect the current point to the projection, at every update to the plot remove previous points and redraw them. That's why I first call
clearpoints
to remove any previous points and then addpoints to add 2 points for a straight line from the projection to the actual (x,y,z) point.
For the "marker-lines", again clearpoints and then add the new point.
After adding the points to all the lines, call
drawnow
before the next loop iteration for all lines to be updated at the same time (and for the updates to appear synchronized).

Related

How to plot 2 surfaces and their intersection curve in MATLAB?

I have the equations of two surfaces and I want to plot both of them and highlight their intersection
My code looks like this
t=linspace(-1,1,100);
x=t;
y=t;
z=cos(t.^8+12);
plot3(x,y,z,'g-','linewidth',3)
hold on
[x,y]=meshgrid(-2:2,-2:2);
surf(x,y,z)
And that gives me the plot for the surface z = f(x,y) but I can not figure out how to plot the plane x=y
Subject to editing upon further question details.
Here might be an interesting starting point. Below is a script that uses the patch() function to create the rectangle plane x = y. The patch function takes corner coordinates of a rectangle to plot in the 3D case (x,y,z) coordinates. Unfortunately, I was unable to get the surface plot from the above code. So the intersection discussion is a story for another time.
Function Call:
patch(X_Coordinates,Y_Coordinates,Z_Coordinates,Colour);
%***************************************************%
%3D line plot%
%***************************************************%
x = linspace(-1,1,100);
y = linspace(-1,1,100);
z = cos(x.^8+12);
plot3(x,y,z,'g-','linewidth',3)
%***************************************************%
%3D surface plot%
%***************************************************%
hold on
[x,y] = meshgrid(-2:0.01:2,-2:0.01:2);
z = cos(x.^8+12);
% surf(x,y,z);
%***************************************************%
%Plotting the xy-plane%
%***************************************************%
Plane_Top = 255;
Plane_Bottom = -255;
Plane_Width = 4;
%Using plane attributes to set patch points%
X = [-Plane_Width/2 -Plane_Width/2 Plane_Width/2 Plane_Width/2];
Y = [-Plane_Width/2 -Plane_Width/2 Plane_Width/2 Plane_Width/2];
Z = [Plane_Bottom Plane_Top Plane_Top Plane_Bottom];
%Plotting characteristics%
Colour = [252/255 148/255 3/255];
patch(X,Y,Z,Colour);
view(3);
grid on;
xlabel("X-Axis"); ylabel("Y-Axis");
title("Plotting the XY-Plane and Line Function");
Using MATLAB version: R2019b

Rotating complex matrix with arbitrary angle in Matlab?

I have implemented the Hermite-Gaussian function in matlab for producing different modes. The light beam along z direction can be seen as a complex matrix in the plane.
The function for HG modes is as below.
%Hermite polynomial
function hk = HermitePoly(n)
if n==0
hk = 1;
elseif n==1
hk = [2 0];
else
hkm2 = zeros(1,n+1);
hkm2(n+1) = 1;
hkm1 = zeros(1,n+1);
hkm1(n) = 2;
for k=2:n
hk = zeros(1,n+1);
for e=n-k+1:2:n
hk(e) = 2*(hkm1(e+1) - (k-1)*hkm2(e));
end
hk(n+1) = -2*(k-1)*hkm2(n+1);
if k<n
hkm2 = hkm1;
hkm1 = hk;
end
end
end
% this is the function of HG modes in z position.
function [HGBeam,X,Y] = Hermite_Gaussian(N,hx,hy,w0,delta,lamda,z)
[X Y]=meshgrid((-N/2:N/2-1)*delta);
[theta,rad] = cart2pol(X,Y);
k=2*pi/lamda;
zr=pi*w0^2/lamda;
wz=w0*sqrt(1+(z/zr)^2);
qz=z+1i*zr;
q0=1i*zr;
if z==0
rz=Inf;
else
rz=z*(1+(zr/z)^2);
end
AmpLGB=sqrt(2/(2^(hx+hy)*pi*factorial(hx)*factorial(hy)*w0^2)).*(q0/qz).*(-conj(qz)/qz)^((hx+hy)/2).*exp(-(rad.*rad)/(wz)^2).*polyval(HermitePoly(hx),sqrt(2)*X/wz).*polyval(HermitePoly(hy),sqrt(2)*Y/wz);
PsLGB=exp(-1i*(k*(rad.*rad)/(2*rz)+k*z-(hx+hy+1)*atan(z/zr)));
HGBeam=AmpLGB.*PsLGB;
end
Now I plot one example for HG(2,0) as the following (example1):
clc
clear all;
close all;
lambda=809e-9; % optical wavelength
w0=0.025; %optical beam waist 15mm
k=2*pi/lambda; % optical wavenumber
Zr=pi*w0^2/lambda; % Rayleigh range
z0=0; % start position z=0; but careful 0*Inf is undefined, here 0*Inf=NAN
N=1024; % samples/side length at source plane
D1=0.25; % side length [m] at source plane
delta1=D1/N; % grid spacing [m]
x1=-D1/2:delta1:D1/2-delta1; % source plane x and y coordinates
y1=x1;
%% HG modes
HGx=2;
HGy=0;
HGintheory=Hermite_Gaussian(N,HGx,HGy,w0,delta1,lambda,z0);
h7=figure(7);
imagesc(x1,y1,abs(HGintheory).^2);
title(sprintf('z=%d; HG(%d,%d)',z0,HGx,HGy))
xlabel('x (m)'); ylabel('y (m)');
The plot of the light field will be as the following picture in the left side (its intensity):
We can use rot90() function to rotate matrix HGintheory (which is add one line code: HGintheory=rot90(HGintheory);) and then the field will rotate 90 degree (right side of the intensity plot).
Because I want to work with the light field. So the question is how can I rotate the complex matrix HGintheory in
arbitrary angle? For example 45 degree?
Does anyone knows how to rotate a complex matrix with big size? If something is wrong or unclear, please pointing out and Thank you in advance!
You can decompose your complex field into two real fields (amplitude and phase), rotate both with imrotate, and combine them afterwards pixel-wise
Hamp=abs(HGintheory);
Hphase=angle(HGintheory);
RotAngle=45;
HampRot=imrotate(Hamp,RotAngle,'bilinear','crop');
HphaseRot=imrotate(Hphase,RotAngle,'bilinear','crop');
HfullRot=HampRot.*exp(1i*HphaseRot);
figure(1);
imagesc(x1,y1,abs(HGintheory).^2);
figure(2);
imagesc(x1,y1,abs(HfullRot).^2);
You can rotate your initial meshgrid using:
[X,Y] = meshgrid(x1,y1)
xyc = [mean(x1), mean(y1)];
angel = 45;
R = [cosd(angel), -sind(angel); sind(angel), cosd(angel)];
XY = xyc' + R * ([X(:) Y(:)]-xyc)';
XR = reshape(XY(1,:),size(X));
YR = reshape(XY(2,:),size(Y));
and then use those transformed coordinates to plot:
imagesc(XR,YR,IntensityHGin);

Matlab: patch area between two curves which depend on the curves values

I'm trying to fill an area between two curves with respect to a function which depends on the values of the curves.
Here is the code of what I've managed to do so far
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
N=[n_vec,fliplr(n_vec)];
X=[x_vec,fliplr(y_vec)];
figure(1)
subplot(2,1,1)
hold on
plot(n_vec,x_vec,n_vec,y_vec)
hp = patch(N,X,'b')
plot([n_vec(i) n_vec(i)],[x_vec(i),y_vec(i)],'linewidth',5)
xlabel('n'); ylabel('x')
subplot(2,1,2)
xx = linspace(y_vec(i),x_vec(i),100);
plot(xx,cc(xx,y_vec(i),x_vec(i)))
xlabel('x'); ylabel('c(x)')
This code produces the following graph
The color code which I've added represent the color coding that each line (along the y axis at a point on the x axis) from the area between the two curves should be.
Overall, the entire area should be filled with a gradient color which depends on the values of the curves.
I've assisted the following previous questions but could not resolve a solution
MATLAB fill area between lines
Patch circle by a color gradient
Filling between two curves, according to a colormap given by a function MATLAB
NOTE: there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
The surf plot method
The same as the scatter plot method, i.e. generate a point grid.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
Generate a logical array indicating whether the points are inside the polygon, but no need to extract the points:
in = inpolygon(px, py, N, X);
Generate Z. The value of Z indicates the color to use for the surface plot. Hence, it is generated using the your function cc.
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
Set Z values for points outside the area of interest to NaN so MATLAB won't plot them.
pz(~in) = nan;
Generate a bounded colourmap (delete if you want to use full colour range)
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
Finally, plot.
figure;
colormap(jet)
surf(px,py,pz,'edgecolor','none');
view(2) % x-y view
Feel free to turn the image arround to see how it looks like in the Z-dimention - beautiful :)
Full code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
% generate z
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
pz(~in) = nan;
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
% plot
figure;
colormap(c)
surf(px,py,pz,'edgecolor','none');
view(2)
You can use imagesc and meshgrids. See comments in the code to understand what's going on.
Downsample your data
% your initial upper and lower boundaries
n_vec_long = linspace(2,10,1000000);
f_ub_vec_long = linspace(2, 10, length(n_vec_long));
f_lb_vec_long = abs(sin(n_vec_long));
% downsample
n_vec = linspace(n_vec_long(1), n_vec_long(end), 1000); % for example, only 1000 points
% get upper and lower boundary values for n_vec
f_ub_vec = interp1(n_vec_long, f_ub_vec_long, n_vec);
f_lb_vec = interp1(n_vec_long, f_lb_vec_long, n_vec);
% x_vec for the color function
x_vec = 0:0.01:10;
Plot the data
% create a 2D matrix with N and X position
[N, X] = meshgrid(n_vec, x_vec);
% evaluate the upper and lower boundary functions at n_vec
% can be any function at n you want (not tested for crossing boundaries though...)
f_ub_vec = linspace(2, 10, length(n_vec));
f_lb_vec = abs(sin(n_vec));
% make these row vectors into matrices, to create a boolean mask
F_UB = repmat(f_ub_vec, [size(N, 1) 1]);
F_LB = repmat(f_lb_vec, [size(N, 1) 1]);
% create a mask based on the upper and lower boundary functions
mask = true(size(N));
mask(X > F_UB | X < F_LB) = false;
% create data matrix
Z = NaN(size(N));
% create function that evaluates the color profile for each defined value
% in the vectors with the lower and upper bounds
zc = #(X, ub, lb) 1 ./ (1 + (exp(-X) ./ (exp(-ub) - exp(-lb))));
CData = zc(X, f_lb_vec, f_ub_vec); % create the c(x) at all X
% put the CData in Z, but only between the lower and upper bound.
Z(mask) = CData(mask);
% normalize Z along 1st dim
Z = normalize(Z, 1, 'range'); % get all values between 0 and 1 for colorbar
% draw a figure!
figure(1); clf;
ax = axes; % create some axes
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
xlabel('n')
ylabel('x')
This already looks kinda like what you want, but let's get rid of the blue area outside the boundaries. This can be done by creating an 'alpha mask', i.e. set the alpha value for all pixels outside the previously defined mask to 0:
figure(2); clf;
ax = axes; % create some axes
hold on;
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
% set a colormap
colormap(flip(hsv(100)))
% set alpha for points outside mask
Calpha = ones(size(N));
Calpha(~mask) = 0;
sc.AlphaData = Calpha;
% plot the other lines
plot(n_vec, f_ub_vec, 'k', n_vec, f_lb_vec, 'k' ,'linewidth', 1)
% set axis limits
xlim([min(n_vec), max(n_vec)])
ylim([min(x_vec), max(x_vec)])
there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
It is difficult to achieve this using patch.
However, you may use scatter plots to "fill" the area with coloured dots. Alternatively, and probably better, use surf plot and generate z coordinates using your cc function (See my seperate solution).
The scatter plot method
First, make a grid of points (resolution 500*500) inside the rectangular space bounding the two curves.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
figure;
scatter(px(:), py(:), 1, 'r');
The not-interesting figure of the point grid:
Next, extract the points inside the polygon defined by the two curves.
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
hold on;
scatter(px, py, 1, 'k');
Black points are inside the area:
Finally, create color and plot the nice looking gradient colour figure.
% create color for the points
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s'); % use size 16, filled square markers.
Note that you may need a fairly dense grid of points to make sure the white background won't show up. You may also change the point size to a bigger value (won't impact performance).
Of cause, you may use patch to replace scatter but you will need to work out the vertices and face ids, then you may patch each faces separately with patch('Faces',F,'Vertices',V). Using patch this way may impact performance.
Complete code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate point grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
% generate color
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s');

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

Interpolate surface of 3D cylinder in Matlab

I have a dataset that describes a point cloud of a 3D cylinder (xx,yy,zz,C):
and I would like to make a surface plot from this dataset, similar to this
In order to do this I thought I could interpolate my scattered data using TriScatteredInterp onto a regular grid and then plot it using surf:
F = TriScatteredInterp(xx,yy,zz);
max_x = max(xx); min_x = min(xx);
max_y = max(yy); min_y = min(yy);
max_z = max(zz); min_z = min(zz);
xi = min_x:abs(stepSize):max_x;
yi = min_y:abs(stepSize):max_y;
zi = min_z:abs(stepSize):max_z;
[qx,qy] = meshgrid(xi,yi);
qz = F(qx,qy);
F = TriScatteredInterp(xx,yy,C);
qc = F(qx,qy);
figure
surf(qx,qy,qz,qc);
axis image
This works really well for convex and concave objects but ends in this for the cylinder:
Can anybody help me as to how to achieve a nicer plot?
Have you tried Delaunay triangulation?
http://www.mathworks.com/help/matlab/ref/delaunay.html
load seamount
tri = delaunay(x,y);
trisurf(tri,x,y,z);
There is also TriScatteredInterp
http://www.mathworks.com/help/matlab/ref/triscatteredinterp.html
ti = -2:.25:2;
[qx,qy] = meshgrid(ti,ti);
qz = F(qx,qy);
mesh(qx,qy,qz);
hold on;
plot3(x,y,z,'o');
I think what you are loking for is the Convex hull function. See its documentation.
K = convhull(X,Y,Z) returns the 3-D convex hull of the points (X,Y,Z),
where X, Y, and Z are column vectors. K is a triangulation
representing the boundary of the convex hull. K is of size mtri-by-3,
where mtri is the number of triangular facets. That is, each row of K
is a triangle defined in terms of the point indices.
Example in 2D
xx = -1:.05:1; yy = abs(sqrt(xx));
[x,y] = pol2cart(xx,yy);
k = convhull(x,y);
plot(x(k),y(k),'r-',x,y,'b+')
Use plot to plot the output of convhull in 2-D. Use trisurf or trimesh to plot the output of convhull in 3-D.
A cylinder is the collection of all points equidistant to a line. So you know that your xx, yy and zz data have one thing in common, and that is that they all should lie at an equal distance to the line of symmetry. You can use that to generate a new cylinder (line of symmetry taken to be z-axis in this example):
% best-fitting radius
% NOTE: only works if z-axis is cylinder's line of symmetry
R = mean( sqrt(xx.^2+yy.^2) );
% generate some cylinder
[x y z] = cylinder(ones(numel(xx),1));
% adjust z-range and set best-fitting radius
z = z * (max(zz(:))-min(zz(:))) + min(zz(:));
x=x*R;
y=y*R;
% plot cylinder
surf(x,y,z)
TriScatteredInterp is good for fitting 2D surfaces of the form z = f(x,y), where f is a single-valued function. It won't work to fit a point cloud like you have.
Since you're dealing with a cylinder, which is, in essence, a 2D surface, you can still use TriScatterdInterp if you convert to polar coordinates, and, say, fit radius as a function of angle and height--something like:
% convert to polar coordinates:
theta = atan2(yy,xx);
h = zz;
r = sqrt(xx.^2+yy.^2);
% fit radius as a function of theta and h
RFit = TriScatteredInterp(theta(:),h(:),r(:));
% define interpolation points
stepSize = 0.1;
ti = min(theta):abs(stepSize):max(theta);
hi = min(h):abs(stepSize):max(h);
[qx,qy] = meshgrid(ti,hi);
% find r values at points:
rfit = reshape(RFit(qx(:),qy(:)),size(qx));
% plot
surf(rfit.*cos(qx),rfit.*sin(qx),qy)