Extract cross sections from a plot of multiple spheres in Matlab - matlab

I know the locations of spheres (center and radius) in a box. I want to extract cross sections. I am able to plot the spheres placed in a cube using the following Matlab code:
[X,Y,Z] = sphere;
for SpNum = 1:NumSpheres
surf( X*Radius(SpNum)+Center(SpNum,1), Y*Radius(SpNum)+Center(SpNum,2), Z*Radius(SpNum)+Center(SpNum,3), ...
'FaceColor','r' );
%shading interp;
hold on;
end
axis tight; daspect([1 1 1]);
In the above code, each sphere could have different radius and they do not overlap (so the centers are also different).
The above code does not however generate cross sections. I want to extract cross sections similar to what we get from say X-ray CT data: a series of images in the Z-direction. I think 'interp2/interp3' and 'slice' functions are the relevant functions, but I am not sure how to use them to generate the cross sections. I would appreciate if anyone could give pointers or provide some sample code for my problem?
-- Thanks in advance.
Update:
I tried using meshgrid to generate the grid points followed by the function F(X,Y,Z) as follows:
[X,Y,Z] = meshgrid(1:100,1:100,1:100);
F = zeros(size(X),'uint8');
for SpNum = 1:NumSpheres
F( sqrt((X - Center(SpNum,1)).^2 + (Y - Center(SpNum,2)).^2 + (Z - Center(SpNum,3)).^2) <= Radius(SpNum) ) = 1;
end
surf(F);
followed by:
z = 1;
I = interp3(X, Y, Z, X*Radius(SpNum)+Center(SpNum,1), Y*Radius(SpNum)+Center(SpNum,2), Z*Radius(SpNum)+Center(SpNum,3), z, 'spline');
figure, imshow(I);
I know that interp3 is the function to use since it interpolates the values of the function F(X,Y,Z) which represent the spheres at different location within a bounded box (say 1:100, 1:100, 1:100). The interpolated values at particular 'z' (= 1, 2, 3... 100) should give me 100 cross sections (in the form of 2-D images).
The flaw is in the function F itself, since 'surf' throws an error saying that F should be an array - "CData must be an M-by-N matrix or M-by-N-by-3 array".
Can anyone please help.

I finally figured it. For the benefit of others, here is the code.
% A 3-D matrix 'F' which has its value at particular coordinate set to 255 if it belongs to any one of the spheres and 0 otherwise.
[X,Y,Z] = meshgrid(1:100,1:100,1:100);
F = zeros(size(X));
for SpNum = 1:NumSpheres
F( sqrt((X - Center(SpNum,1)).^2 + (Y - Center(SpNum,2)).^2 + (Z - Center(SpNum,3)).^2) <= Radius(SpNum) ) = 255;
end
% Extract cross sections from F using interp3 function along the z-axis.
I = zeros(size(X));
for z = 1:100
I(:,:,z) = interp3(X, Y, Z, F, 1:100, (1:100)', z, 'spline');
end
implay(I,4);
You could test and visualize the output by setting Center (a 3-D vector) and Radius of each sphere (some arbitrary NumSpheres) to some random values. The above code will display a window with cross-sections.
Previously, I was trying to use 'surf' to render the spheres which is not right. To render, you have to use the first code snippet. Another mistake I made was using a row vector for the 6th argument instead of column vector.
Hope this helps.
--
Cheers,
Ram.

Related

how to plot graphs above each other in matlab [duplicate]

I wanted to generate a plot (X vs Y), and Z values depend on the Y. The example is shown in the figure below. The matrix size of X is same with Z but not Y. I can plot Z against X, but I wanted to combine all the plot into a single plot and become Y against X. I can plot multiple plots into a single plot but the plot is overlapping each other.
My question is there any method I can merge multiple plots into a single plot without overlapping each plot as the difference between each plot is very small (e.g Z1=1,2,3,4,5 and Z2=1.0001,2.0002,3.0001,4.0002,5.0001). So, I wanted to set each Z plot at different Y axis. (e.g Z1 at Y=0, Z2 at Y=2 ...)
Does anyone have any suggestions or idea?
Thank You
I'll clarify the ideas I wrote in a comment.
First, let's get some data:
x = 470:0.1:484;
z1 = cos(x)/2;
z2 = sin(x)/3;
z3 = cos(x+0.2)/2.3;
I'll plot just three data sets, all of this is trivial to extend to any number of data sets.
Idea 1: multiple axes
The idea here is simply to use subplot to create a small-multiple type plot:
ytick = [-0.5,0.0,0.5];
ylim = [-0.9,0.9]);
figure
h1 = subplot(3,1,1);
plot(x,z1);
set(h1,'ylim',ylim,'ytick',ytick);
title('z1')
h2 = subplot(3,1,2);
plot(x,z2);
set(h2,'ylim',ylim,'ytick',ytick);
title('z2')
h3 = subplot(3,1,3);
plot(x,z3);
set(h3,'ylim',ylim,'ytick',ytick);
title('z3')
Note that it is possible to, e.g., remove the tick labels from the top two plot, leaving only labels on the bottom one. You can then also move the axes so that they are closer together (which might be necessary if there are lots of these lines in the same plot):
set(h1,'xticklabel',[],'box','off')
set(h2,'xticklabel',[],'box','off')
set(h3,'box','off')
set(h1,'position',[0.13,0.71,0.8,0.24])
set(h2,'position',[0.13,0.41,0.8,0.24])
set(h3,'position',[0.13,0.11,0.8,0.24])
axes(h1)
title('')
ylabel('z1')
axes(h2)
title('')
ylabel('z2')
axes(h3)
title('')
ylabel('z3')
Idea 2: same axes, plot with offset
This is the simpler approach, as you're dealing only with a single axis. #Zizy Archer already showed how easy it is to shift data if they're all in a single 2D matrix Z. Here I'll just plot z1, z2+2, and z3+4. Adjust the offsets to your liking. Next, I set the 'ytick' property to create the illusion of separate graphs, and set the 'yticklabel' property so that the numbers along the y-axis match the actual data plotted. The end result is similar to the multiple axes plots above, but they're all in a single axes:
figure
plot(x,z1);
hold on
plot(x,z2+2);
plot(x,z3+4);
ytick = [-0.5,0.0,0.5];
set(gca,'ytick',[ytick,ytick+2,ytick+4]);
set(gca,'yticklabel',[ytick,ytick,ytick]);
text(484.5,0,'z1')
text(484.5,2,'z2')
text(484.5,4,'z3')
The simplest would be to shift Z data. But note that Z2 would look like to be oscillating around 1 - so this is a neat visual representation, but might mislead.
% Simple version - shift Z curves by 0, 1, ... (as recommended by #Cris Luengo)
shiftMat = repmat(0 : size(Z, 2)-1, size(Z,1), 1);
Z = Z + shiftMat;
%Min shift up to have non-overlapping - curves touching
for i = 2 : size(Z, 2)
Zdif = (Z(:, i-1) - Z(:, i));
Z(:, i) = Z(:, i) + max(Zdif); % + 0.01 to separate them a little bit.
end
%Bigger shift up, to have all points of Z(2) equal or above all points of z1.
for i = 2 : numZ
Zdif = max(Z(:, i-1))-min(Z(:, i));
Z(:, i) = Z(:, i) + Zdif;
end
Another possibility is to have multiple Y axis and each Z curve plotted against its own Y axis. This is likely fancier and shouldn't mislead, but it is way more work, even after you grab the function, as you still need to position all those axes. MATLAB by default lets you use only 2 axes, so grab a function from fileexchange to add more: https://www.mathworks.com/matlabcentral/fileexchange/9016-addaxis

MATLAB: meshgrid pairs whoe sums<=1

I need some help with MATLAB coding. I have two variables x=0:0.1:1 and y=0:0.1:1. I want to generate the meshgrid only for those points which satisfy the condition x+y<=1. Please help me.
[X,Y] = meshgrid(x,y) returns two matrixes, representing points from (0,0) to (1,1). The requirement to remove values where x + y >= 1 works well on a graph - basically just draw a diagonal line, creating a triangle. However, it doesn't work very well for matrices - who's ever heard of a triangle matrix?
What you can do is set the excluded values to some 'bad' value, and then ignore them. I chose to set them to NaN, because functions like surf won't plot nan's:
x = 0:.1:1;
y = 0:.1:1;
[X,Y] = surf(x,y);
X(X+Y>=1) = nan;
Y(X+Y>=1) = nan;
surf(X,Y,X.*Y)

Draw a line with non-Cartesian coordinates in MATLAB

MATLAB's surf command allows you to pass it optional X and Y data that specify non-cartesian x-y components. (they essentially change the basis vectors). I desire to pass similar arguments to a function that will draw a line.
How do I plot a line using a non-cartesian coordinate system?
My apologies if my terminology is a little off. This still might technically be a cartesian space but it wouldn't be square in the sense that one unit in the x-direction is orthogonal to one unit in the y-direction. If you can correct my terminology, I would really appreciate it!
EDIT:
Below better demonstrates what I mean:
The commands:
datA=1:10;
datB=1:10;
X=cosd(8*datA)'*datB;
Y=datA'*log10(datB*3);
Z=ones(size(datA'))*cosd(datB);
XX=X./(1+Z);
YY=Y./(1+Z);
surf(XX,YY,eye(10)); view([0 0 1])
produces the following graph:
Here, the X and Y dimensions are not orthogonal nor equi-spaced. One unit in x could correspond to 5 cm in the x direction but the next one unit in x could correspond to 2 cm in the x direction + 1 cm in the y direction. I desire to replicate this functionality but drawing a line instead of a surf For instance, I'm looking for a function where:
straightLine=[(1:10)' (1:10)'];
my_line(XX,YY,straightLine(:,1),straightLine(:,2))
would produce a line that traced the red squares on the surf graph.
I'm still not certain of what your input data are about, and what you want to plot. However, from how you want to plot it, I can help.
When you call
surf(XX,YY,eye(10)); view([0 0 1]);
and want to get only the "red parts", i.e. the maxima of the function, you are essentially selecting a subset of the XX, YY matrices using the diagonal matrix as indicator. So you could select those points manually, and use plot to plot them as a line:
Xplot = diag(XX);
Yplot = diag(YY);
plot(Xplot,Yplot,'r.-');
The call to diag(XX) will take the diagonal elements of the matrix XX, which is exactly where you'll get the red patches when you use surf with the z data according to eye().
Result:
Also, if you're just trying to do what your example states, then there's no need to use matrices just to take out the diagonal eventually. Here's the same result, using elementwise operations on your input vectors:
datA = 1:10;
datB = 1:10;
X2 = cosd(8*datA).*datB;
Y2 = datA.*log10(datB*3);
Z2 = cosd(datB);
XX2 = X2./(1+Z2);
YY2 = Y2./(1+Z2);
plot(Xplot,Yplot,'rs-',XX2,YY2,'bo--','linewidth',2,'markersize',10);
legend('original','vector')
Result:
Matlab has many built-in function to assist you.
In 2D the easiest way to do this is polar that allows you to make a graph using theta and rho vectors:
theta = linspace(0,2*pi,100);
r = sin(2*theta);
figure(1)
polar(theta, r), grid on
So, you would get this.
There also is pol2cart function that would convert your data into x and y format:
[x,y] = pol2cart(theta,r);
figure(2)
plot(x, y), grid on
This would look slightly different
Then, if we extend this to 3D, you are only left with plot3. So, If you have data like:
theta = linspace(0,10*pi,500);
r = ones(size(theta));
z = linspace(-10,10,500);
you need to use pol2cart with 3 arguments to produce this:
[x,y,z] = pol2cart(theta,r,z);
figure(3)
plot3(x,y,z),grid on
Finally, if you have spherical data, you have sph2cart:
theta = linspace(0,2*pi,100);
phi = linspace(-pi/2,pi/2,100);
rho = sin(2*theta - phi);
[x,y,z] = sph2cart(theta, phi, rho);
figure(4)
plot3(x,y,z),grid on
view([-150 70])
That would look this way

Sketch f(x,y)=(21/4)x^2y over the region x^2 <= y <= 1

Can someone share a technique using MATLAB to plot the surface f(x,y)=(21/4)x^2y over the region x^2 <= y <= 1?
Also, if anyone is aware of some tutorials or links that would help with this type of problem, could you please share them?
Thanks.
Here is another approach:
%%
close all
x=linspace(-1,1,40);
g1=x.^2;
g2=ones(1,40);
y=[];
n=20;
for k=0:n
y=[y;g1+(g2-g1)*k/n];
end
x=x(ones(1,n+1),:);
z=21/4*x.^2.*y;
meshz(x,y,z)
axis tight
xlabel('x-axis')
ylabel('y-axis')
view(136,42)
And the result:
And finally, you can map the region (-1,1)x(0,1) in the uv-plane into the region bounded by $y=x^2 and y=1 in the xy-plane with the parametrization:
f(u,v) = (u\sqrt{v},v)
Capture from: https://math.stackexchange.com/questions/823168/transform-rectangular-region-to-region-bounded-by-y-1-and-y-x2
This code produces the same image shown above:
close all
[u,v]=meshgrid(linspace(-1,1,40),linspace(0,1,20));
x=u.*sqrt(v);
y=v;
z=21/4*x.^2.*y;
meshz(x,y,z)
axis tight
xlabel('x-axis')
ylabel('y-axis')
view(136,42)
First off, let's look at your valid region of values. This is telling us that y >= x^2 and also y <= 1. This means that your y values need to be on the positive plane bounded by the parabola x^2 and they also must be less than or equal to 1. In other words, your y values must be bound within the area dictated from y = x^2 to y = 1. Pictorially, your y values are bounded within this shape:
As such, your x values must also be bound between -1 and 1. Therefore, your actual boundaries are: -1 <= x <= 1 and 0 <= y <= 1. However, this only locates our boundaries for x and y but it doesn't handle where the plot has valid values. We'll tackle that later.
Now that we have that established, you can use ezsurf to plot surface plots in MATLAB that are dictated by a 2D equation.
You call ezsurf like so:
ezsurf(FUN, [XMIN,XMAX,YMIN,YMAX]);
FUN is a function or a string that contains the equation you want, and XMIN,XMAX,YMIN,YMAX contain the lowest and highest x and y values you want to plot. Plotting without these values assumes a span from -2*pi to 2*pi in both dimensions. As such, let's create a new function that will handle when we have valid values, and when we don't. Use this code, and save it to a new file called myfun.m. Make sure you save this to your current Working Directory.
function z = myfun(x,y)
z = (21/4)*x.^2.*y;
z(~(x.^2 <= y & y <= 1)) = nan;
end
This will allow you to take a series of x and y values and output values that are dictated by the 2D equation that you have given us. Any values that don't satisfy the condition of x^2 <= y <= 1, you set them to NaN. ezsurf will not plot NaN values.
Now, call ezsurf like so:
ezsurf(#myfun, [-1,1,0,1]);
You thus get:
This will spawn a new figure for you, and there are some tools at the top that will allow you interact with your 3D plot. For instance, you can use the rotation tool that's at the top bar beside the hand to rotate your figure around and see what this looks like. Click on this tool, then left click your mouse and hold the left mouse button anywhere within the surface plot. You can drag around, changing the azimuth and the latitude to get the perspective that you want.
Edit: June 4th, 2014
Noting your comments, we can decrease the jagged edges by increasing the number of points in the plot. As such, you can append a final parameter to ezsurf which is N, the number of points to add in each dimension. Increasing the number of points will decrease the width in between each point and so the plot will look smoother. The default value of N is 60 in both dimensions. Let's try increasing the amount of points in each dimension to 100.
ezsurf(#myfun, [-1,1,0,1], 100);
Your plot will look like:
Hope this helps!
Try the following to make the required function, compute the values, and plot only the region that is desired:
% Make the function. You could put this in a file by itself, if you wanted.
f = #(x,y) (21/4)*x.^2.*y;
[X Y] = meshgrid(linspace(0,1));
Z = f(X,Y);
% compute the values we want to plot:
valsToPlot = (X.^2 <= Y) & (Y <= 1);
% remove the values that we don't want to plot:
X(~valsToPlot) = nan;
Y(~valsToPlot) = nan;
Z(~valsToPlot) = nan;
% And... plot.
figure(59382);
clf;
surf(X,Y,Z);

MATLAB - Intersect between a surface and a plane

I have a 3D mesh like in this picture.
Now what I want to do is create a plane that will intersect the surface at a certain Z value. I would then want to get the x and y coordinates of this intersection and have matlab output them.
What I'm planning on doing is that this picture is a model of a lake. This lake will have water evaporating that will be removing a certain z value of water. I would then like to see what the new shoreline would look like by obtaining the x and y coordinates of this intersection.
This is my code for plotting that picture.
function plot(x,y,z)
Contour = xlsread('C:\Users\Joel\Copy\Contour','A2:D4757');
x = Contour(:,1);
y = Contour(:, 2);
z = Contour(:,3);
F = TriScatteredInterp(x,y,z);
[X,Y]=meshgrid(linspace(-600,600,300));
Z=F(X,Y);
surf(X,Y,Z);
end
You can do this by simply thresholding the interpolated Z values
inside = Z < seaLevel; % binary mask of points inside water
shoreline = bwmorph( inside, 'remove' ); % a mask with only shoreline pixels eq to 1
figure;
surf( X, Y, Z, 'EdgeColor', 'none', 'FaceColor', [210,180,140]/255, 'FaceAlpha', .5 );
hold on;
ii = find( shoreline );
plot3( X(ii), Y(ii), seaLevel*ones(size(ii)), 'b', 'LineWidth', 2 );
The contour3 will give nicer boundaries:
[h,c]=contour3(X,Y,Z,[seaLevel seaLevel]);
seaLevel is given twice: otherwise contour3 thinks seaLevel is the number of levels to automatically calibrate. And to nicely annotate with the numeric height of your seaLevel:
clabel(h,c);
you may modify c to print "sea level" instead of num2str(seaLevel).
If you don't have the MATLAB toolbox for image processing you can't use the function "bwmorph". As a solution you can replace line 2 of Shai's code with the following code which reimplements the bwmorph function for parameter 'remove'. (The reimplementation is probably neither very perfomrant nor an exact reimplementatin (the borders of the matrix aren't used) - but the solution should work as a first step. Feel free to improve).
shoreline= zeros(size(inside));
for i_row = 2:size(inside,1)-1
for i_col = 2:size(inside,2)-1
if(inside(i_row,i_col))
if (~( inside(i_row+1,i_col) && ...
inside(i_row-1,i_col) && ...
inside(i_row,i_col+1) && ...
inside(i_row,i_col-1)))
inside2(i_row,i_col) = 1;
end
end
end
end