// File         : the_triangle.hxx
// Author       : Paul A. Koshevoy
// Created      : Fri Apr 23 18:01:20 MDT 2004
// Copyright    : (C) 2004
// License      : GPL.
// Description  :

#ifndef THE_TRIANGLE_HXX_
#define THE_TRIANGLE_HXX_

// system includes:
#include <cassert>

// local includes:
#include "v3x1p3x1.hxx"
#include "the_ray.hxx"
#include "the_plane.hxx"
#include "the_coord_sys.hxx"


//----------------------------------------------------------------
// the_triangle_t
// 
class the_triangle_t
{
public:
  the_triangle_t() {}
  
  the_triangle_t(const p3x1_t & a, const p3x1_t & b, const p3x1_t & c):
    a_(a),
    b_(b),
    c_(c)
  {
    init_normal();
  }
  
  the_triangle_t(const p3x1_t & a,
		 const p3x1_t & b,
		 const p3x1_t & c,
		 const v3x1_t & n):
    a_(a),
    b_(b),
    c_(c),
    na_(n),
    nb_(n),
    nc_(n)
  {}
  
  the_triangle_t(const p3x1_t & a,
		 const p3x1_t & b,
		 const p3x1_t & c,
		 const v3x1_t & na,
		 const v3x1_t & nb,
		 const v3x1_t & nc):
    a_(a),
    b_(b),
    c_(c),
    na_(na),
    nb_(nb),
    nc_(nc)
  {}
  
  inline void init(const p3x1_t & a, const p3x1_t & b, const p3x1_t & c)
  {
    a_ = a;
    b_ = b;
    c_ = c;
    
    init_normal();
  }
  
  inline void init(const p3x1_t & a,
		   const p3x1_t & b,
		   const p3x1_t & c,
		   const v3x1_t & n)
  {
    a_ = a;
    b_ = b;
    c_ = c;
    na_ = n;
    nb_ = n;
    nc_ = n;
  }
  
  inline void init(const p3x1_t & a,
		   const p3x1_t & b,
		   const p3x1_t & c,
		   const v3x1_t & na,
		   const v3x1_t & nb,
		   const v3x1_t & nc)
  {
    a_ = a;
    b_ = b;
    c_ = c;
    na_ = na;
    nb_ = nb;
    nc_ = nc;
  }
  
  inline void init_normal()
  {
    na_ = calc_normal();
    nb_ = na_;
    nc_ = na_;
  }
  
  inline v3x1_t calc_normal() const
  { return !((b_ - a_) % (c_ - b_)); }
  
  // calculate the surface normal of the point on the triangle at the
  // specified barycentric coordinates:
  inline v3x1_t normal(const p3x1_t & bar_pt) const
  { return !(bar_pt[0] * na_ + bar_pt[1] * nb_ + bar_pt[2] * nc_); }
  
  // transform a point on the triangle expressed in the barycentric
  // coordinate system to world coordinate system:
  inline p3x1_t to_wcs(const p3x1_t & bar_pt) const
  { return bar_pt[0] * a_ + bar_pt[1] * b_ + bar_pt[2] * c_; }
  
  // calculate the intersection point between a ray and the plane in which
  // the triangle rests. If the intersection does not exist, return false.
  // Otherwise store the result as a barycentric point. return true if the
  // intersection is inside the triangle.
  bool intersect(const the_ray_t & ray, p3x1_t & bar_pt) const
  {
    v3x1_t normal(calc_normal());
    
    the_coord_sys_t ref_cs(a_, normal);
    the_plane_t plane(ref_cs);
    
    real_t param = 0;
    if (plane.intersect(ray, param) == false) return false;
    if (param < 0.0) return false;
    
    v3x1_t ab = b_ - a_;
    v3x1_t bc = c_ - b_;
    v3x1_t ca = a_ - c_;
    real_t twice_area_abc = (ab % bc).norm();
    
    p3x1_t p = ray * param;
    v3x1_t ap = p - a_;
    v3x1_t bp = p - b_;
    v3x1_t cp = p - c_;
    
    bar_pt[0] = (bc % cp).norm() / twice_area_abc;
    bar_pt[1] = (ca % ap).norm() / twice_area_abc;
    bar_pt[2] = (ab % bp).norm() / twice_area_abc;
    
    real_t sum_bar_pt = bar_pt[0] + bar_pt[1] + bar_pt[2];
    return fabs(1.0 - sum_bar_pt) <= THE_EPSILON;
  }
  
private:
  // triangle vertices:
  p3x1_t a_;
  p3x1_t b_;
  p3x1_t c_;
  
  // the unit normal vectors at the vertices of the triangle:
  v3x1_t na_;
  v3x1_t nb_;
  v3x1_t nc_;
};


#endif // THE_TRIANGLE_HXX_

