I am trying to graphically find the intersections between two surfaces and the x-y plane. (Intersection of surface z1 with the x-y plane and intersection z2 with the x-y plane)
I have created arrays representing the surfaces z1 = 3+x+y and z2 = 4-2x-4y and the z3 for the x-y plane using meshgrid. Looking everywhere, the only command that seems I can use to find the intersections between arrays is the intersect(A,B) command where A and B are arrays. When I enter intersect(z1,z3) however, I get the error "A and B must be vectors, or 'rows' must be specified." When I try intersect (z1,z2,'rows'), I am returned a 0-by-21 empty matrix. What am I doing wrong here?
My code:
x = -10:10;
y = -10:10;
[X,Y] = meshgrid(x,y);
z1 = 3+X+Y;
z2 = 4-2.*X-4.*Y;
z3 = 0.*X+0.*Y; %x-y plane
surf(X,Y,z1)
hold on
surf(X,Y,z2)
surf(X,Y,z3)
int1 = intersect(z1,z3,'rows');
int2 = intersect(z2,z3,'rows');
It sounds like you want the points where z1 = z2. To numerically find these, you have a couple options.
1) Numerical rootfinding: fsolve is capable of solving systems of equations. You can formulate the surfaces as functions of one vector, [x;y] and solve for the vector that makes the two surfaces equal. An example using the initial guess x=1, y=1 follows:
z1 = #(x) 3 + x(1) + x(2);
z2 = #(x) 4 - 2*x(1) - 4*x(2);
f = #(x) z1(x) - z2(x);
x0 = [1;1]
intersect = fsolve(#(x) f(x), x0);
2) Minimizing the error: If you are stuck with discrete data (arrays instead of functions) you can simply find the points where z1 - z2 is closest to zero. An easy starting point is to take the arrays Z1 and Z2 and find all points where the difference nears zero:
tol = 1e-3;
near_zero = abs(Z1 - Z2) < tol;
near_zero is going to be a logical array that is true whenever the difference between Z1 and Z2 is small relative to tol. You can use this to index into corresponding meshgrid arrays for X and Y to find the coordinates of intersection.
a simple way (no major function calls) to solve this is as follows:
x = -10:.1:10;
y = -10:.1:10;
[X,Y] = meshgrid(x,y);
z1 = 3+X+Y;
z2 = 4-2.*X-4.*Y;
z3 = z1 - z2;
[~,mn] = min(abs(z3));
the intersection is defined as (x, y(mn)).
This, of course is a numerical approximation (since you wanted a numerical method), subject to boundary condition which I haven't explored (you'll need to disregard values far from zero when performing the minimum function)
Note: if you're looking for an equation, consider performing a least squares approximation on the resulting data.
Related
[X,Y] = meshgrid(-8:.5:8);
R = sqrt(X.^2 + Y.^2) + eps;
Z = sin(R)./R;
scatter3(X,Y,Z)
Error using scatter3 (line 64)
X, Y and Z must be vectors of the same length.
Matlab R2018b windows x64
As shown in the documentation, X, Y, Z must be vectors. (When you enter an article on mathworks from Googling, say, "matlab scatter3", you will first see the syntax for the function. Blue text means hyperlink. All the inputs are linked to the bottom of the page where their exact typing is defined.)
The reason is (probably) as follows.
As stated in the documentation, scatter3 puts circles (or other symbols of your choice if you modify the graphic object) on 3D coordinates of your choice. The coordinates are the ith element of X, Y, Z respectively. For example, the x-coordinate of the 10th point you wish to plot in 3D is X(10).
Thus it is not natural to input matrices into scatter3. If you know X(i), Y(i), Z(i) are indeed the coordinates you want to plot for all i, even though your X, Y, Z are not vectors for some reason, you need to reshape X, Y, Z.
In order to reshape, you can simply do scatter3(X(:), Y(:), Z(:)) which tells Matlab to read your arrays as a vectors. (You should look up in what order this is done. But it is in the intuitive way.) Or you can use reshape. Chances are: reshape is faster for large data set. But ofc (:) is more convenient.
The following should work:
[X,Y] = meshgrid(-8:.5:8);
R = sqrt(X.^2 + Y.^2) + eps;
Z = sin(R)./R;
X = X(:);
Y = Y(:);
Z = Z(:);
scatter3(X,Y,Z)
scatter3 needs vectors, not matrices as far as I can see here
this is my result:
If you want to use meshgrid without reshaping the matrices you have to use plot3 and the 'o' symbol. So you can get a similar result with:
plot3(X,Y,Z,'o')
EDIT:
A question that arose in association with this post was, which of the following methods is more efficient in terms of computation speed: The function reshape(X,[],1), suggested by me, or the simpler colon version X(:), suggested by #Argyll.
After timing the reshape function versus the : method, I have to admit that the latter is more efficient.
I added my results and the code I used to time both functions:
sizes = linspace(100,10000,100);
time_reshape = [];
time_col = [];
for i=1:length(sizes)
X = rand(sizes(i)); % Create random squared matrix
r = #() ResFcn(X);
c = #() ColFcn(X);
time_reshape = [time_reshape timeit(r)/1000] % Take average of 1000 measurements
time_col = [time_col timeit(c)/1000] % Take average of 1000 measurements
end
figure()
hold on
grid on
plot(sizes(2:end), time_col(2:end))
plot(sizes(2:end), time_reshape(2:end))
legend("Colon","Reshape","Location","northwest")
title("Comparison: Reshape vs. Colon Method")
xlabel("Length of squared matrix")
ylabel("Average execution time [s]")
hold off
function res = ResFcn(X)
for i = 1:1000 % Repeat 1000 times
res = reshape(X,[],1);
end
end
function res = ColFcn(X)
for i = 1:1000 % Repeat 1000 times
res = X(:);
end
end
I want to plot a two-dimensional function of the polar coordinates r and theta in three-dimensional cartesian coordinates. I have that (sorry about bad maths formatting, LaTeX not compatible, it seems)
f(r,theta) = r/2 * (cos(theta - pi/4) + sqrt(1 + 1/2 * cos(2*theta)))
Converting r and theta to cartesian coordinates
x = r * cos(theta), y = r * sin(theta)
Further, the domain is -1<r<1 and 0<theta<2 * pi, which I define by
r = -1:2/50:1;
and
theta = 0:2*pi/50:2*pi;
giving me two vectors of the same dimensions.
I can define the x and y values used for plotting as row vectors by
x = r. * cos(theta);
and
y = r. * sin(theta);
So now I need to define the z values, which will depend on the values of x and y. I thought I should make a 101x101 where each matrix element contains a data point of the final surface. But how should I do this? I thought about using a double for loop:
for i=1:numel(r)
for j=1:numel(theta)
z(i,j) = r(i)/2 .* cos(theta(j) - pi/4) + r(i).*sqrt(1 + 1/2 * cos(2.*theta(j)));
end
end
Then simply surf(z)
While this definitely gives me a surface, it gives me the incorrect surface! I don't know what is happening here. The incorrect surface is given in Figure 1, while the correct one is given in Figure 2. Can anyone help me out? For reference, the correct surface was plotted with GeoGebra, using
A = Function[<expression 1>, <Expresison 2>, <Expression 3>, <var 1>, <start>, <stop>, <var 2>, <start>, <stop>]
Figure 1. Incorrect surface.
Figure 2. Correct surface.
As others have said, you can use meshgrid to make this work.
Here's your example using gridded r and theta and an anonymous function to replace the double loop:
r = -1:2/50:1;
theta = 0:2*pi/50:2*pi;
% define anonymous function f(r,theta)
f = #(r,theta) r/2 .* (cos(theta - pi/4) + sqrt(1 + 1/2 .* cos(2.*theta)));
% generate grids for r and theta
[r, theta] = meshgrid(r,theta);
% calculate z from gridded r and theta
z = f(r,theta);
% convert r,theta to x,y and plot with surf
x = r.*cos(theta);
y = r.*sin(theta);
surf(x,y,z);
You need to use meshgrid to get matrix coordinates if you want to use surf. Taking your x and y (lower case), call
[X,Y] = meshgrid(x,y);
Then X and Y (upper case) will have the same values as you gave it, but laid out in the two-dimensional array as expected by surf. Loop over the indices here and compute your Z, which should have all(size(Z) == size(X)).
https://www.mathworks.com/help/matlab/ref/meshgrid.html
I need to plot level surfaces of a 3 variable function. The variables are in a column vector X = [x, y, z]^t. The function is f(X) = X^t * A * X. Where ^t means transpose and A is a 3x3 constant matrix. I know for a fact that A is symmetric and therefore diagonalizable, i.e. A = V * D * V^t. Just in case it turns out to be useful.
I intend to use isosurface to get the points of the function where it equals a certain level and then use patch to plot.
However I can't figure out how to compute the value of the function for every point in the grid. Ideally I'd like to do
x = linspace(-1, 1,10); y=x; z = x;
[XX,YY,ZZ]=meshgrid(x,y,z);
f = [XX YY ZZ]'*A*[XX YY ZZ];
level = 1;
s = isosurface(XX,YY,ZZ,f,level);
patch(s, 'EdgeColor','none','FaceColor','blue');
but this won't work obviously because of the sizes of XX and A. What I've done so far is do the math myself to obtain the function as a polynomial of XX, YY and ZZ but it's incredibly ugly and not practical.
Anybody knows how to do this? Thanks!
If I understand correctly, this does that you want. In the following explanation I will use 10x10x10 points as given in your example (although the code works for any number of points). Also, I define a random 3x3 matrix A.
Once XX, YY and ZZ have been generated as 10x10x10 arrays (step 1), the key is to build a 1000x3 matrix in which the first column is x coordinate, the second is y and the third is z. This is variable XYZ in the code below (step 2).
Since XYZ is a matrix, not a vector, the function f can't be computed using matrix multiplication. But it can be obtained efficiently with bsxfun. First compute an intermediate 1000x3x3 variable (XYZ2) with all 3x3 products of coordinates for each of the 1000 points (step 3). Then reshape it into a 1000x9 matrix and multiply by the 9x1 vector obtained from linearizing A (step 4).
The f thus obtained has size 1000x1. You need to reshape it into a 10x10x10 array to match XX, YY and ZZ (step 5). Then you can use isosurface as per your code (step 6).
A = rand(3);
x = linspace(-1, 1,10); y = x; z = x;
[XX,YY,ZZ] = meshgrid(x,y,z); %// step 1
XYZ = [XX(:) YY(:) ZZ(:)]; %// step 2
XYZ2 = bsxfun(#times, XYZ, permute(XYZ, [1 3 2])); %// step 3
f = reshape(XYZ2,[],numel(A))*A(:); %// step 4
f = reshape(f, size(XX)); %// step 5
level = 1;
s = isosurface(XX,YY,ZZ,f,level);
patch(s, 'EdgeColor','none','FaceColor','blue'); %// step 6
I suspect that you could explicitly define your function as
ffun = #(x,y,z) [x y z]*A*[x; y; z];
then use arrayfun to apply this function to each element of your coordinate vectors:
f = arrayfun(ffun,XX,YY,ZZ);
This will return f(i)=ffun(XX(i),YY(i),ZZ(i)) for each i in 1:numel(XX), which I think is what you are after. The output f will have the same shape as your input arrays, which seems perfectly fine to use with isosurface later.
I've some x.mat and y.mat.
And I would like to find the polynomial equation from this.
I've tried
p = polyfit(x,y,3);
with y2 = p(1)*x.^3 + p(2)*x.^2 + p(3)*x
but my y2 is not equal to the original y . What is wrong ?
Thanks you
As radarhead wrote in his comment, you forgot the coefficient of zero degree (p(4) here).
Assuming x and y are vectors of same length n, polyfit(x,y,n-1) will return a vector containing the coefficients of the interpolating polynomial (of degree n-1) in descending order.
Then, the value of the interpolating polynomial at a point z will be given by:
p(1)*z^3 + p(2)*z^2 + p(3)*z + p(4)
Don't forget p(4)! As Bas suggested, you can use the polyval function to easily compute the value of a polynomial at a a given point:
polyval(p,z);
To illustrate this, see the code below, which generates 4 data points, plots those points and the polynomial interpolating them:
n = 4;
x = sort(rand(n,1));
y = rand(n,1);
p = polyfit(x,y,n-1);
figure
hold on
plot(x,y,'bo');
xx=linspace(x(1),x(end),100);
plot(xx,polyval(p,xx),'r');
hold off
I am trying to plot the following equation in MATLAB:
ratio = sqrt(1+1/(kr)^2)
With k and r on the x and y axes, and ratio on the z axis. I used meshgrid to create a matrix with values for x and y varying from 1 to 10:
[x,y] = meshgrid([1:1:10],[1:1:10]);
The problem now is to create values for z. I've tried to just type the whole equation in, but that gives this result:
>> Z = sqrt(1+1/(x .* y)^2)???
Error using ==> mldivide
Matrix dimensions must agree.
So what I did is go to through the whole process manually, which produces the right graph in the end:
z = z^2;
z = 1 ./ z;
z = 1 + z;
z = sqrt(z);
mesh(x,y,z)
Is there a more elegant way to do this? Or a way to type in the equation and let MATLAB handle the rest?
Try this:
Z = sqrt(1+1./(x .* y).^2);
surf(Z);
The problem that you had is related to using / instead of ./ and ^2 instead of .^2