function M = volume_intersect_sphere_analytical(varargin)
% Compute the overlap volume between 2 spheres defined in an array.
% Computation is vectorized, and intersection volume are computed an
% analytical way.
%
% Input: spheres data presented in an array G of four columns.
%        G contains parameters of the n spheres
%           . G(1:n,1) - x-coordinate of the center of spheres,
%           . G(1:n,2) - y-coordinate of the center of spheres,
%           . G(1:n,3) - z-coordinate of the center of spheres,
%           . G(1:n,4) - radii of the spheres
%        Each row of the array contains the information for one sphere.
%
%        Input can also be provided in three different vectors. These
%        vectors can be row or column vectors. The 1st one corresponds to
%        x-coordinate of the center of spheres, the 2nd one to the
%        y-coordinate, the 3rd one to the z-coordinate and the 4th one to
%        the radii of the spheres.
%        An optional binary argument can be provided to display or not the
%        result.
%
% Output: Square matrix M(n,n) containing intersection volumes between
% spheres
%         M(i,j) contains the intersection volume between spheres i & j
%         By definition, M(i,i) corresponds to the volume of sphere i.
%
%
% Examples
%
%         x = [0,1,5,3,-5];
%         y = [0,4,3,7,0];
%         z = [0,4,3,7,0];
%         r = [1,5,3,2,2];
%         Display_solution = true;
%         disp('First call')
%         M = volume_intersect_sphere_analytical(x,y,z,r,Display_solution);
%         disp('Second call')
%         M = volume_intersect_sphere_analytical([x',y',z',r'],false);
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% For 2 spheres i & j, three cases are possible depending on the distance
% d(i,j) of the centers of the spheres i and j.
%   Case 1: spheres i & j do not overlap, there is no overlap volume
%           M(i,j)=0;
%             Condition: d(i,j)>= ri+rj
%             M(i,j) = 0;
%   Case 2: spheres i & j fully overlap, the overlap volume has to be
%           computed.
%             Condition: d(i,j)<= abs(ri-rj)
%             M(i,j) = 4/3*pi*min(ri,rj).^3
%   Case 3: spheres i & j partially overlap, the overlap volume has to be
%           computed decomposing the overlap volume.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Guillaume JACQUENOT
% guillaume dot jacquenot at gmail dot com
% 2008 01 31
% 2009 09 10
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if nargin==0
    % Performs an example
    % Creation of 5 spheres
    x = [0,1,5,3,-5];
    y = [0,4,3,7,0];
    z = [0,4,3,7,0];
    r = [1,5,3,2,2];
    Display_solution = true;
elseif nargin==1 || nargin==2
    temp = varargin{1};
    x = temp(:,1);
    y = temp(:,2);
    z = temp(:,3);
    r = temp(:,4);
    if nargin == 2
        Display_solution = varargin{2};
    else
        Display_solution = false;
    end
elseif nargin==4 || nargin==5
    x = varargin{1};
    y = varargin{2};
    z = varargin{3};
    r = varargin{4};
    if nargin == 5
        Display_solution = varargin{5};
    else
        Display_solution = false;
    end
else
    help volume_intersect_sphere_analytical
    error('volume_intersect_sphere_analytical:e0',...
          'The number of arguments must 0, 1, 2, 4 or 5');
end

% Checking input argument
if ~islogical(Display_solution)
    error('volume_intersect_sphere_analytical:e1',...
          'Display_solution should be logical variable')
end

% Inputs are reshaped
size_x = numel(x);
size_y = numel(y);
size_z = numel(z);
size_r = numel(r);

x = reshape(x,size_x,1);
y = reshape(y,size_y,1);
z = reshape(z,size_z,1);
r = reshape(r,size_r,1);

% Checking if the three input vectors have the same length
if (size_x~=size_y)||(size_x~=size_z)||(size_x~=size_r)
    error('volume_intersect_sphere_analytical:e2',...
          'Input of function must be the same length');
end

% Checking if there is any negative or null radius
if any(r<=0)
    disp(['spheres with null or negative radius'...
          ' won''t be taken into account in the computation.'])
    temp = (r>0);
    x = x(temp);
    y = y(temp);
    z = z(temp);
    r = r(temp);
end

% Checking the size of the input argument
if size_x==1
    M = 4/3*pi*r.^3;
    return
end

% Computation of distance between all spheres, which will allow to
% determine which cases to use.
X = meshgrid(x);
Y = meshgrid(y);
Z = meshgrid(z);
D = sqrt((X-X').^2+(Y-Y').^2+(Z-Z').^2);


% Since the resulting matrix M is symmetric M(i,j)=M(j,i), computations are
% performed only on the upper part of the matrix
D = triu(D,1);

[R1,R2] = meshgrid(r);
sumR    = triu(R1+R2,1);
difR    = triu(abs(R1-R2),1);

% Creating the resulting vector
M = zeros(size_x*size_x,1);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% C2: Case 2: spheres i & j fully overlap
% One of the spheres is inside the other one.
C2    = triu(D<=difR);
M(C2) = 4/3*pi*min(R1(C2),R2(C2)).^3;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Case 3: spheres i & j partially overlap
% Partial intersection between spheres i & j
C3 = (D>difR)&(D<sumR);
% Computation of the coordinates of one of the intersection points of the
% spheres i & j
Xi(C3,1) = (R1(C3).^2-R2(C3).^2+D(C3).^2)./(2*D(C3));

H1(C3,1) = R1(C3)-Xi(C3,1);
H2(C3,1) = R2(C3)+Xi(C3,1)-D(C3);

% Computation of the partial intersection volume between spheres i & j
M(C3,1) = pi/3*(H1(C3,1).^2.*(3*R1(C3)-H1(C3,1))+...
                H2(C3,1).^2.*(3*R2(C3)-H2(C3,1)));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Compute the volume of each sphere.
% Assign the results to the diagonal of M
M(1:size_x+1:size_x*size_x) = 4/3*pi.*r.^3;

% Conversion of vector M to matrix M
M = reshape(M,size_x,size_x);

% Creating the lower part of the matrix
M = M + tril(M',-1);

% Display results if no argument is provided
if Display_solution
    f = figure;
    hAxs = axes('Parent',f);
    hold on, box on, axis equal
    xlabel('x')
    ylabel('y','Rotation',0)
    zlabel('z','Rotation',0)
    title(['Compute the intersection volume between 2 '...
           ' spheres a vectorized way for several spheres'])
    colour = rand(size_x,3);
    [X,Y,Z]=sphere(20);
    for t = 1: size_x
        surf(x(t)+r(t)*X,y(t)+r(t)*Y,z(t)+r(t)*Z,...
            'FaceColor',colour(t,:),'EdgeColor','none','parent',hAxs)

%         surf(x(t)+r(t)*X,y(t)+r(t)*Y,z(t)+r(t)*Z,...
%             'FaceColor','none','parent',hAxs)
        for u = t+1:size_x
            if M(t,u)~=0
              plot3([x(t),x(u)],[y(t),y(u)],[z(t),z(u)],'k-','parent',hAxs)
              text((x(t)+x(u))/2,(y(t)+y(u))/2,(z(t)+z(u))/2,...
                    num2str(M(t,u)),...
                    'HorizontalAlignment','center',...
                    'BackgroundColor',[1 1 1],'parent',hAxs)
            end
        end
    end
    axis tight
    view([  0.8765    0.7660   -0.0000   -0.8213
           -0.6140    0.3778    0.8090   -0.2864
           -0.8451    0.5200   -0.5878   10.2792
                 0         0         0    1.0000])
end
