function [center, radii, evecs, p] = ellipsoidfit_leastsquares(x,y,z)
% Fit an ellipsoid to a set of 3D data points.
%
% Input arguments:
% x, y, z
%    cartesian coordinates of noisy data points
%
% Output arguments:
% center:
%    ellispoid center coordinates [xc; yc; zc]
% radii:
%    ellipsoid radii [a; b; c]
% evecs:
%    ellipsoid radii directions as columns of the 3x3 matrix
% p:
%    the 10 parameters describing the ellipsoid algebraically
%    Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx + 2Hy + 2Iz + J = 0
%    where norm([A,B,C,D,E,F,G,H,I,J]) == 1
%
% Examples:
% [center, radii, evecs] = ellipsoid_leastsquares(x,y,z)

% Copyright 2011 Levente Hunyadi

narginchk(3, 3);  % check input arguments
validateattributes(x, {'numeric'}, {'real','nonempty','vector'});
validateattributes(y, {'numeric'}, {'real','nonempty','vector'});
validateattributes(z, {'numeric'}, {'real','nonempty','vector'});
x = x(:);
y = y(:);
z = z(:);

assert(numel(x) >= 9, ...
    'At least 9 points are required to fit a unique ellipsoid.');

% fit ellipsoid in the form Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx + 2Hy + 2Iz = 1
% use singular value decomposition (unconstrained problem)
D = [ x .* x, ...  % size = (number of data points) x (9 ellipsoid parameters + 1 constant term)
      y .* y, ...
      z .* z, ...
      2 .* x .* y, ...
      2 .* x .* z, ...
      2 .* y .* z, ...
      2 .* x, ...
      2 .* y, ... 
      2 .* z, ...
      ones(size(x)) ];
[~,~,V] = svd(D,0);
p = V(:,end);  % smallest singular value

[center, radii, evecs, p] = ellipsoid_im2ex(p);