Matlab fit 3D data points with implicit function - matlab

I am trying to fit 3D data points (>1000) with the implicit function of a cylinder (5 parameters : a,b,c,d,r) :
-r + sqrt((x-(x+a*(-b+y)+c*(-d+z))/(1+a^2+c^2))^2+(-b+y-(a*(x+a*(-b+y)+c*(-d+z)))/(1+a^2+c^2))^2+(-d+z-(c*(x+a*(-b+y)+c*(-d+z)))/(1+a^2+c^2))^2) == 0
I can't find a good way to implement that with Matlab (my knowledge is till very shallow in Matlab syntax). It would be much easier with a explicit function for sure. I have looked extensively on the net and I haven't found any specific answer.
I have also the parametric function of the cylinder using the same parameters if you know a way to fit the parametric equation directly?
x = v-(c*r*cos(u))/(sqrt(1+c^2))-(a*r*sin(u))/((1+c^2)*sqrt(1+(a^2)/(1+c^2)));
y = b+a*v+(r*sin(u))/(sqrt(1+(a^2)/(1+c^2)));
z = d+c*v+(r*cos(u))/(sqrt(1+c^2))-(a*c*r*sin(u))/((1+c^2)*sqrt(1+(a^2)/(1+c^2)));
Thanks a lot in advance.

You can use cftool with a user define function of the cylinder form you specify above. From the docs,
cftool( x, y, z ) creates a surface fit to x and y inputs and z output. x, y, and z must be numeric, have two or more elements, and have compatible sizes. Sizes are compatible if x, y, and z all have the same number of elements or x and y are vectors, z is a 2D matrix, length(x ) = n, and length(y) = m where [m,n] = size(z). cftool opens Curve Fitting app if necessary.
If you just type cftool it will open an interactive session where you can try some fits (assuming you have the toolbox)...
EDIT: You can use fsolve in this case. Save the cylinder function to a separate file called cylinder, where the code is something like:
function F = cylinder(P, X)
a = P(1);
b = P(2);
c = P(3);
d = P(4);
r = P(5);
x = X(1,:);
y = X(2,:);
z = X(3,:);
% f(x,y,z) - r = 0
F = sqrt( ( x-( (x+a*(-b+y)+c*(-d+z)))/(1+a^2+c^2)).^2 ...
+(-b+y-(a*(x+a*(-b+y)+c*(-d+z)))/(1+a^2+c^2)).^2 ...
+(-d+z-(c*(x+a*(-b+y)+c*(-d+z)))/(1+a^2+c^2)).^2 ) -r;
end
I've assumed you have the correct form of cylinder here. You would call this with you N data points, X in form (3,N),
startparams = [0,0,0,0,1]
coeff=fsolve('cylinder',startparams,[],X)

Related

MATLAB "gradient" function swaps x and y dimensions?

I am trying to compute the gradient of a 3-D matrix using MATLAB (version 2016a). If I type "help gradient" it says the following:
"HX and HY can either be scalars to specify
the spacing between coordinates or vectors to specify the
coordinates of the points. If HX and HY are vectors, their length
must match the corresponding dimension of F." (emphasis mine).
Here is a sample code:
x = 1:30; nx = length(x);
y = 1:62; ny = length(y);
z = 1:23; nz = length(z);
F = rand(nx,ny,nz);
[FX,FY,FZ] = gradient(F,x,y,z);
Here, I am inputting vectors x, y, and z to compute the gradient. These are vectors and it says in the help that HX and HY must have a length matching the corresponding dimension of F. The x-dimension of F has length nx. So the x vector also has length nx. It should work, according to the help.
However, I get an error:
Index exceeds matrix dimensions.
Error in gradient (line 112)
h = h(3:n) - h(1:n-2);
When I dig a little deeper into the "gradient" function I come across this line in the "parse_inputs" embedded function:
% Swap 1 and 2 since x is the second dimension and y is the first.
loc = v;
if ndim > 1
loc(2:-1:1) = loc(1:2);
end
What is going on here?
Why does MATLAB swap the x and y dimensions?
If I do the following code and swap the x and y vectors, then the code works.
x = 1:30; nx = length(x);
y = 1:62; ny = length(y);
z = 1:23; nz = length(z);
F = rand(nx,ny,nz);
[FX,FY,FZ] = gradient(F,y,x,z);
I just don't understand why. I've looked around on stack overflow but can't find any answer to the question.
In any case, it seems that the help is somewhat misleading because you actually need to swap x and y to make the function work...
It is not gradient that swaps dimensions, it's everything else...
MATLAB indexes arrays as (row,column), and array sizes are given in the same order, as [height,width].
However, whenever the documentation to any function mentions x and y, the x is always horizontal and y vertical. So in a way MATLAB indexes as (y,x).
The right way to fix your code is:
x = 1:30; nx = length(x);
y = 1:62; ny = length(y);
z = 1:23; nz = length(z);
F = rand(ny,nx,nz); % <<< Note the order here!
[FX,FY,FZ] = gradient(F,x,y,z);
Personal opinion: This is terribly confusing, I have seen lots of people making mistakes because of this, and have made quite a few mistakes myself as well. But they are very consistent with this in the MATLAB documentation, using (i,j) or (x,y) depending on the required order -- with the exception of ndgrid, where the documentation uses x1,x2,x3,... but should really have used a different letter.

MATLab, plotting a graph with a function

I'm having trouble writing this code. So, I'm trying to make a code for
func = y*x(n) + z * x(n)
All the values are arbitrary and x(n) is the value at the position n. I need to plot a graph at each nth position. So if x(1) = 5 I plot a point at when x=1 and y=5. The issue is that I can't figure out how to make an arbitrary array and don't know how to get the answer for func when I add x(n) value at the nth position. I also am having trouble plotting a graph, but think this is because I can't figure out to use the array yet.
I'm new to MatLab.
So if I am following y & z are just constants? I think the confusion is typically this would be written something like "y = ax +bx"
Like Cris Luengo mentioned in the comments above, you should should go over some basic Matlab tutorials as this is very basic.
% y and Z are constants
y = 1;
z = 2;
%this makes x = [0,1,2,...10];
x = 0:1:10;
func = y.*x + z.*x;
plot(func)
This should do the trick:
% Define X as a range between -10 to 10 (+1 on every step)...
x = -10:10;
% Define your constants...
y = 3;
z = -1;
% Define the function...
fun = #(x) (y .* x) + (z .* x);
% Plot X on the x-axis and fun(x) on the y-axis...
% fun(x) numerically evaluates fun for the given x
plot(x,fun(x));
Refer to this page for more information about anonymous functions.

3d-plotting surface 2x^2 + 3y^2 + z^2 = 6

I was trying to plot the above surface in octave/matlab and ran into the this problem.
My code is as follows:
x = linspace(-sqrt(3),sqrt(3),1000);
y = linspace(-sqrt(2),sqrt(2),1000);
z = sqrt(6-2*x.^2-3*y.^2);
surf(x,y,z)
I got the error:
error: mesh: X, Y, Z arguments must be real.
I understand this was because some (x,y)s would result in negative 6-2*x.^2-3*y.*2, but I don't know how to tackle this because I can't trim either part of x or y. Any one can help? Thanks
It depends what you want to do with the non-real values of z.
One thing you could do is to set all these values to zero or NaN (as per #hbaderts' comment):
z = sqrt(6-2*x.^2-3*y.^2);
z( imag(z)~=0 ) = NaN;
One more thing though: your code might have a problem because z is a length-1000 vector, and you want it to be a 1000x1000 matrix. You should use meshgrid() on x and y to get two-dimensional matrices everywhere:
x = linspace(-sqrt(3),sqrt(3),1000);
y = linspace(-sqrt(2),sqrt(2),1000);
[xx,yy] = meshgrid(x,y);
z = sqrt(6-2*xx.^2-3*yy.^2);
z( imag(z)~=0 ) = NaN;
surf(xx,yy,z,'edgecolor','none')
(thanks #LuisMendo for the 'edgecolor','none' suggestion for better visualization.)
Running the above piece of code on octave gives this plot:

Get coefficients of symbolic polynomial in Matlab

I have a Matlab function that returns a polynomial of the form:
poly = ax^2 + bx*y + cy^2
where a, b, and c are constants, and x and y are symbolic (class sym).
I want to get the coefficients of the polynomial in the form [a b c], but I'm running into the following problem. If the function returns poly = y^2, then coeffs(poly) = 1. I don't want this – I want it to return [0 0 1].
How can I create a function that will give me the coefficients of a symbolic polynomial in the form that I want?
You can use sym2poly if your polynomial is a function of a single variable like your example y^2:
syms y
p = 2*y^2+3*y+4;
c = sym2poly(p)
which returns
c =
2 3 4
Use fliplr(c) if you really want the coefficients in the other order. If you're going to be working with polynomials it would probably also be a good idea not to create a variable called poly, which is the name of a function you might want to use.
If you actually need to handle polynomials in multiple variables, you can use MuPAD functions from within Matlab. Here is how you can use MuPAD's coeff to get the coefficients in terms of the order of variable they precede (x or y):
syms x y
p = 2*x^2+3*x*y+4*y;
v = symvar(p);
c = eval(feval(symengine,'coeff',p,v))
If you want to extract all of the information from the polynomial, the poly2list function is quite helpful:
syms x y
p = 2*x^2+3*x*y+4*y;
v = symvar(p);
m = eval(feval(symengine,'poly2list',p,v));
c = m(:,1); % Coefficients
degs = m(:,2:end); % Degree of each variable in each term
The polynomial can then be reconstructed via:
sum(c.*prod(repmat(v,[size(m,1) 1]).^degs,2))
By the way, good choice on where you go to school. :-)
There is also an alternative to this problem. For a given degree, this function returns a polynomial of that degree and its coefficients altogether.
function [polynomial, coefficeint] = makePoly(degree)
syms x y
previous = 0 ;
for i=0:degree
current = expand((x+y)^i);
previous= current + previous ;
end
[~,poly] = coeffs(previous);
for j= 1:length(poly)
coefficeint(j) = sym(strcat('a', int2str(j)) );
end
polynomial = fliplr(coefficeint)* poly.' ;
end
Hope this helps.

Plotting an ellipse in MATLAB given in matrix form

I have an ellipse in 2 dimensions, defined by a positive definite matrix X as follows: a point x is in the ellipse if x'*X*x <= 1. How can I plot this ellipse in matlab? I've done a bit of searching while finding surprisingly little.
Figured out the answer actually: I'd post this as an answer, but it won't let me (new user):
Figured it out after a bit of tinkering. Basically, we express the points on the ellipse border (x'*X*x = 1) as a weighted combination of the eigenvectors of X, which makes some of the math to find the points easier. We can just write (au+bv)'X(au+bv)=1 and work out the relationship between a,b. Matlab code follows (sorry it's messy, just used the same notation that I was using with pen/paper):
function plot_ellipse(X, varargin)
% Plots an ellipse of the form x'*X*x <= 1
% plot vectors of the form a*u + b*v where u,v are eigenvectors of X
[V,D] = eig(X);
u = V(:,1);
v = V(:,2);
l1 = D(1,1);
l2 = D(2,2);
pts = [];
delta = .1;
for alpha = -1/sqrt(l1)-delta:delta:1/sqrt(l1)+delta
beta = sqrt((1 - alpha^2 * l1)/l2);
pts(:,end+1) = alpha*u + beta*v;
end
for alpha = 1/sqrt(l1)+delta:-delta:-1/sqrt(l1)-delta
beta = -sqrt((1 - alpha^2 * l1)/l2);
pts(:,end+1) = alpha*u + beta*v;
end
plot(pts(1,:), pts(2,:), varargin{:})
I stumbled across this post while searching for this topic, and even though it's settled, I thought I might provide another simpler solution, if the matrix is symmetric.
Another way of doing this is to use the Cholesky decomposition of the semi-definite positive matrix E implemented in Matlab as the chol function. It computes an upper triangular matrix R such that X = R' * R. Using this, x'*X*x = (R*x)'*(R*x) = z'*z, if we define z as R*x.
The curve to plot thus becomes such that z'*z=1, and that's a circle. A simple solution is thus z = (cos(t), sin(t)), for 0<=t<=2 pi. You then multiply by the inverse of R to get the ellipse.
This is pretty straightforward to translate into the following code:
function plot_ellipse(E)
% plots an ellipse of the form xEx = 1
R = chol(E);
t = linspace(0, 2*pi, 100); % or any high number to make curve smooth
z = [cos(t); sin(t)];
ellipse = inv(R) * z;
plot(ellipse(1,:), ellipse(2,:))
end
Hope this might help!