function points = intersectLineCylinder(line, cylinder, varargin)
%INTERSECTLINECYLINDER Compute intersection points between a line and a cylinder
%
%   POINTS = intersectLineCylinder(LINE, CYLINDER)
%   Returns intersection points between a line and a cylinder.
%
%   Input parameters:
%   LINE     = [x0 y0 z0  dx dy dz]
%   CYLINDER = [x1 y1 z1 x2 y2 z2 R]
%
%   Output:
%   POINTS   = [x1 y1 z1 ; x2 y2 z2]
%
%   POINTS = intersectLineCylinder(LINE, CYLINDER, 'checkBounds', B)
%   Where B is a boolean (TRUE by default), check if the points are within
%   the bounds defined by the two extreme points. If B is false, the
%   cylinder is considered as infinite.
%
%   Example
%     % Compute intersection between simple cylinder and line
%     line = [60 60 60 1 2 3];
%     cylinder = [20 50 50 80 50 50 30];
%     points = intersectLineCylinder(line, cylinder);
%     % Display the different shapes
%     figure;
%     drawCylinder(cylinder);
%     hold on; light;
%     axis([0 100 0 100 0 100]);
%     drawLine3d(line);
%     drawPoint3d(points, 'ko');
%     
%
%     % Compute intersections when one of the points is outside the
%     % cylinder
%     line = [80 60 60 1 2 3];
%     cylinder = [20 50 50 80 50 50 30];
%     intersectLineCylinder(line, cylinder)
%     ans = 
%           67.8690   35.7380   23.6069
%
%   
%   See also
%   lines3d, intersectLinePlane
%
%   References
%   See the link:
%   http://www.gamedev.net/community/forums/topic.asp?topic_id=467789
%
% ---
% Author: David Legland, from a file written by Daniel Trauth (RWTH)
% e-mail: david.legland@grignon.inra.fr
% Created: 2007-01-27

% HISTORY
% 2010-10-21 change cylinder argument convention, add bounds check and doc
% 2010-10-21 add check for points on cylinders, update doc


%% Parse input arguments

% default arguments
checkBounds = true;

% parse inputs
while length(varargin)>1
    var = varargin{1};
    if strcmpi(var, 'checkbounds')
        checkBounds = varargin{2};
    else
        error(['Unkown argument: ' var]);
    end
    varargin(1:2) = [];
end


%% Parse cylinder parameters

% Starting point of the line
l0 = line(1:3)';

% Direction vector of the line
dl = line(4:6)';

% Starting position of the cylinder
c0 = cylinder(1:3)';

% Direction vector of the cylinder
dc = cylinder(4:6)' - c0;

% Radius of the cylinder
r = cylinder(7);


%% Resolution of a quadratic equation to find the increment

% Substitution of parameters
e = dl - (dot(dl,dc)/dot(dc,dc))*dc;
f = (l0-c0) - (dot(l0-c0,dc)/dot(dc,dc))*dc;

% Coefficients of 2-nd order equation
A = dot(e, e);
B = 2*dot(e,f);
C = dot(f,f) - r^2;

% compute discriminant
delta = B^2 - 4*A*C;

% check existence of solution(s)
if delta<0
    points = zeros(0, 3);
    return;
end

% extract roots
x1 = (-B + sqrt(delta))/(2*A);
x2 = (-B - sqrt(delta))/(2*A);
x = [x1;x2];


%% Estimation of points position

% process the smallest position
x1 = min((x));

% Point on the line: l0 + x*dl = p
point1 = l0 + x1*dl;

% process the greatest position
x2 = max((x));

% Point on the line: l0 + x*dl = p
point2 = l0 + x2*dl;

% Format result
points = [point1' ; point2'];


%% Check if points are located between bounds

if checkBounds
    % cylinder axis
    axis = [c0' dc'];
    
    % compute position on axis
    ts = linePosition3d(points, axis);
    
    % check bounds
    ind = ts>=0 & ts<=1;
    points = points(ind, :);
end
