/*----------------------------------------------------------------------------*
 | sourcefile Line3D.cpp                                                      |
 |                                                                            |
 | version 1.00                                                               |
 |                                                                            |
 |                                                                            |
 | This file is part of the 3D Geometry Primer, found at:                     |
 | http://www.flipcode.com/geometry/                                          |        
 |                                                                            |
 | It contains an example implementation of a 3D line.                        |
 | You can find more info in the 3D Geometry Primer on www.flipcode.com.      |
 |                                                                            |
 |                                                                            |
 | Copyright (C) 2002  Bram de Greve                                          |
 |                                                                            |
 | This library is free software; you can redistribute it and/or              |
 | modify it under the terms of the GNU Lesser General Public                 |
 | License as published by the Free Software Foundation; either               |
 | version 2.1 of the License, or (at your option) any later version.         |
 |                                                                            |
 | This library is distributed in the hope that it will be useful,            |
 | but WITHOUT ANY WARRANTY; without even the implied warranty of             |
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU          |
 | Lesser General Public License for more details.                            |
 |                                                                            |
 | You should have received a copy of the GNU Lesser General Public           |
 | License along with this library; if not, write to the Free Software        |
 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  |
 |                                                                            |
 | You can contact me by e-mail on bdegreve@mail.be                           |
 *----------------------------------------------------------------------------*/

#include "Line3D.h"



// -----------------------------------------------------------------------------
// DEFAULT CONSTRUCTOR
//
// this constructor will create a line with the origin as support point and
// a direction vector that'll point along the positive z-axis.  
// i.e. this line will lie along the z-axis.                       
// -----------------------------------------------------------------------------

Line3D::Line3D():
    m_support(0.0f, 0.0f, 0.0f),
    m_direction(0.0f, 0.0f, 1.0f)
{
}



// -----------------------------------------------------------------------------
// CONSTRUCTOR point vector
//
// this conststruct will create a line with a given support point and
// direction vector.  the direction vector will be normalized
// -----------------------------------------------------------------------------

Line3D::Line3D(const Point3D& a_support, const Vector3D& a_direction):
    m_support(a_support),
    m_direction(a_direction)
{
    m_direction.normalize();
}



// -----------------------------------------------------------------------------
// CONSTRUCTOR point point
//
// this constructor will create a line through both points.  First point
// will serve as support point and the direction vector will point from
// the support point to the second point.  the direction vector will
// be normalized
// -----------------------------------------------------------------------------

Line3D::Line3D(const Point3D& a_support, const Point3D& a_secondPoint):
    m_support(a_support),
    m_direction(a_secondPoint - a_support)
{
    m_direction.normalize();
}



// -----------------------------------------------------------------------------
// SET point vector
//
// this accessor will set this line to have a given support point and
// direction vector.  the direction vector will be normalized
// -----------------------------------------------------------------------------

void Line3D::set(const Point3D& a_support, const Vector3D& a_direction)
{
    m_support = a_support;
    m_direction = a_direction;
    m_direction.normalize();
}



// -----------------------------------------------------------------------------
// SET point point
//
// this accessor will set this line to go through two given points.  the first
// one will serve as support point and the direction vector will point from
// this support point to the second point.  the direction vector will be
// normalized
// -----------------------------------------------------------------------------

void Line3D::set(const Point3D& a_support, const Point3D& a_secondPoint)
{
    m_support = a_support;
    m_direction = a_secondPoint - a_support;
    m_direction.normalize();
}



// -----------------------------------------------------------------------------
// PROJECT point
//
// return the orthogonal projection of a point onto this line
// -----------------------------------------------------------------------------

Point3D Line3D::project(const Point3D& a_point)
{
    const Vector3D sp = a_point - m_support;
    const float t = dot(sp, m_direction);
    return getPoint(t);
}



// -----------------------------------------------------------------------------
// CONTAINS point, tolerance
//
// return true if point is point of line 
// -----------------------------------------------------------------------------

bool Line3D::contains(const Point3D& a_point, float a_tolerance)
{
    return dist(*this, a_point) <= a_tolerance;
}



// -----------------------------------------------------------------------------
// DIST line point
//
// this method will calculate the (absolute) distance from a point to a line
// -----------------------------------------------------------------------------

float dist(const Line3D& a_line, const Point3D& a_point)
{
    const Vector3D sp = a_point - a_line.getSupport();
    const float t = dot(sp, a_line.getDirection());
    const Vector3D v = sp - t * a_line.getDirection();

    return v.getNorm();
}



// -----------------------------------------------------------------------------
// DIST line line
//
// this method will calculate the (absolute) distance between two lines
// -----------------------------------------------------------------------------

float dist(const Line3D& a_line1, const Line3D& a_line2)
{
    const Vector3D sr = a_line2.getSupport() - a_line1.getSupport();
    const Vector3D d = a_line1.getDirection();
    const Vector3D e = a_line2.getDirection();
    const Vector3D n = cross(d, e);

    if (n.isNull())
    {
        const Vector3D v = sr - dot(sr, d) * d;
        return v.getNorm();
    }
    else
    {
        return fabsf(dot(sr, n));
    }
}



// -----------------------------------------------------------------------------
// INTERSECT line line
//
// this method will calculate the intersection between two lines
//
// takes: const Line3D& line1
//        const Line3D& line2
//        float& t1: will return parameter of intersection point on line1
//        float& t2: will return parameter of intersection point on line2
// returns: 0: no intersection points found (crossing or parallel lines)
//          1: exactly one intersection point found (intersection lines)
//         -1: a infinite number of intersection points (coincident lines)
// -----------------------------------------------------------------------------

int intersect(const Line3D& a_line1, const Line3D& a_line2, 
              float& a_t1, float& a_t2)
{
    const Vector3D sr = a_line2.getSupport() - a_line1.getSupport();
    const Vector3D d = a_line1.getDirection();
    const Vector3D e = a_line2.getDirection();
    const Vector3D n = cross(d, e);
    
    // only return intersection points if distance between lines is not null
    // hardcode distance between lines to reuse normalvector
    
    if (n.isNull())
    {
        const Vector3D v = sr - dot(sr, d) * d;
        if (v.isNull())
        {
            // coincident lines, infinite many intersection points
            return -1;
        }
        else
        {
            // parallel lines, no intersection points
            return 0;
        }
    }
    else
    {
        if (dot(sr, n) == 0.0f)
        {
            // crossing lines, no intersection points
            return 0;
        }
    }


    // now we're sure we have exactly one intersection point
    // use Cramer to find it

    if (fabsf(n.z) > fabsf(n.x) && fabsf(n.z) > fabsf(n.y))
    {
        const float invNz = 1.0f / n.z;
        a_t1 = (sr.x * e.y - sr.y * e.x) * invNz;
        a_t2 = (sr.x * d.y - sr.y * d.x) * invNz;
    }
    else if (fabsf(n.x) > fabsf(n.y))
    {
        const float invNx = 1.0f / n.x;
        a_t1 = (sr.y * e.z - sr.z * e.y) * invNx;
        a_t2 = (sr.y * d.z - sr.z * d.y) * invNx;
    }
    else
    {
        const float invNy = 1.0f / n.y;
        a_t1 = (sr.z * e.x - sr.x * e.z) * invNy;
        a_t2 = (sr.z * d.x - sr.x * d.z) * invNy;
    }

    return 1;
}



// --- END OF FILE Line3D.cpp --------------------------------------------------
