/****************************************************************************
*                triangle.c
*						 
*  This module implements primitives for triangles and smooth triangles.
*
*  from Persistence of Vision Raytracer
*  Copyright 1993 Persistence of Vision Team
*---------------------------------------------------------------------------
*  NOTICE: This source code file is provided so that users may experiment
*  with enhancements to POV-Ray and to port the software to platforms other 
*  than those supported by the POV-Ray Team.  There are strict rules under
*  which you are permitted to use this file.  The rules are in the file
*  named POVLEGAL.DOC which should be distributed with this file. If 
*  POVLEGAL.DOC is not available or for more info please contact the POV-Ray
*  Team Coordinator by leaving a message in CompuServe's Graphics Developer's
*  Forum.  The latest version of POV-Ray may be found there as well.
*
* This program is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
*****************************************************************************/

#include "frame.h"
#include "vector.h"
#include "povproto.h"

METHODS Triangle_Methods = 
  {
  All_Triangle_Intersections,
  Inside_Triangle, Triangle_Normal,
  Copy_Triangle,
  Translate_Triangle, Rotate_Triangle,
  Scale_Triangle, Transform_Triangle, Invert_Triangle, Destroy_Triangle
  };

METHODS Smooth_Triangle_Methods = 
  {
  All_Triangle_Intersections,
  Inside_Triangle, Smooth_Triangle_Normal,
  Copy_Smooth_Triangle,
  Translate_Smooth_Triangle, Rotate_Smooth_Triangle,
  Scale_Smooth_Triangle, Transform_Smooth_Triangle, 
  Invert_Smooth_Triangle, Destroy_Triangle
  };

extern RAY *CM_Ray;
extern long Ray_Triangle_Tests, Ray_Triangle_Tests_Succeeded;

#define max3(x,y,z) ((x>y)?((x>z)?1:3):((y>z)?2:3))

#define MAX3(x,y,z) (((x)>(y))?(((x)>(z))?(x):(z)):(((y)>(z))?(y):(z)))
#define MIN3(x,y,z) (((x)<(y))?(((x)<(z))?(x):(z)):(((y)<(z))?(y):(z)))

void Find_Triangle_Dominant_Axis(Triangle)
TRIANGLE *Triangle;
  {
  DBL x, y, z;

  x = fabs(Triangle->Normal_Vector.x);
  y = fabs (Triangle->Normal_Vector.y);
  z = fabs (Triangle->Normal_Vector.z);
  switch (max3(x, y, z)) 
  {
  case 1: 
    Triangle->Dominant_Axis = X_AXIS;
    break;
  case 2: 
    Triangle->Dominant_Axis = Y_AXIS;
    break;
  case 3: 
    Triangle->Dominant_Axis = Z_AXIS;
    break;
  }
  }

void Compute_Smooth_Triangle (Triangle)
SMOOTH_TRIANGLE *Triangle;
  {
  VECTOR P3MinusP2, VTemp1, VTemp2;
  DBL x, y, z, uDenominator, Proj;

  VSub (P3MinusP2, Triangle->P3, Triangle->P2);
  x = fabs (P3MinusP2.x);
  y = fabs (P3MinusP2.y);
  z = fabs (P3MinusP2.z);

  switch (max3 (x, y, z)) 
  {
  case 1:  
    Triangle->vAxis = X_AXIS;
    Triangle->BaseDelta = P3MinusP2.x;
    break;

  case 2:  
    Triangle->vAxis = Y_AXIS;
    Triangle->BaseDelta = P3MinusP2.y;
    break;

  case 3:  
    Triangle->vAxis = Z_AXIS;
    Triangle->BaseDelta = P3MinusP2.z;
    break;
  }   

  VSub (VTemp1, Triangle->P2, Triangle->P3);
  VNormalize (VTemp1, VTemp1);
  VSub (VTemp2, Triangle->P1, Triangle->P3);
  VDot (Proj, VTemp2, VTemp1);
  VScaleEq (VTemp1, Proj);
  VSub (Triangle->Perp, VTemp1, VTemp2);
  VNormalize (Triangle->Perp, Triangle->Perp);
  VDot (uDenominator, VTemp2, Triangle->Perp);
  VInverseScaleEq (Triangle->Perp, -uDenominator);
  }

int Compute_Triangle (Triangle,Smooth)
TRIANGLE *Triangle;
int Smooth;
  {
  VECTOR V1, V2, Temp;
  DBL Length, T1, T2, T3;

  VSub (V1, Triangle->P1, Triangle->P2);
  VSub (V2, Triangle->P3, Triangle->P2);
  VCross (Triangle->Normal_Vector, V1, V2);
  VLength (Length, Triangle->Normal_Vector);
  /* Set up a flag so we can ignore degenerate triangles */
  if (Length < 1.0e-9)
    {
    Triangle->Degenerate_Flag = TRUE;
    return (0);
    }

  /* Normalize the normal vector. */
  VInverseScaleEq (Triangle->Normal_Vector, Length);

  VDot (Triangle->Distance, Triangle->Normal_Vector, Triangle->P1);
  Triangle->Distance *= -1.0;
  Find_Triangle_Dominant_Axis(Triangle);

  switch (Triangle->Dominant_Axis) 
  {
  case X_AXIS:
    if ((Triangle->P2.y - Triangle->P3.y)*(Triangle->P2.z - Triangle->P1.z) <
      (Triangle->P2.z - Triangle->P3.z)*(Triangle->P2.y - Triangle->P1.y)) 
      {
      Temp = Triangle->P2;
      Triangle->P2 = Triangle->P1;
      Triangle->P1 = Temp;
      if (Smooth) 
        {
        Temp = ((SMOOTH_TRIANGLE *) Triangle)->N2;
        ((SMOOTH_TRIANGLE *) Triangle)->N2 = ((SMOOTH_TRIANGLE *) Triangle)->N1;
        ((SMOOTH_TRIANGLE *) Triangle)->N1 = Temp;
        }
      }
    break;

  case Y_AXIS:
    if ((Triangle->P2.x - Triangle->P3.x)*(Triangle->P2.z - Triangle->P1.z) <
      (Triangle->P2.z - Triangle->P3.z)*(Triangle->P2.x - Triangle->P1.x)) 
      {
      Temp = Triangle->P2;
      Triangle->P2 = Triangle->P1;
      Triangle->P1 = Temp;
      if (Smooth) 
        {
        Temp = ((SMOOTH_TRIANGLE *) Triangle)->N2;
        ((SMOOTH_TRIANGLE *) Triangle)->N2 = ((SMOOTH_TRIANGLE *) Triangle)->N1;
        ((SMOOTH_TRIANGLE *) Triangle)->N1 = Temp;
        }
      }
    break;

  case Z_AXIS:
    if ((Triangle->P2.x - Triangle->P3.x)*(Triangle->P2.y - Triangle->P1.y) <
      (Triangle->P2.y - Triangle->P3.y)*(Triangle->P2.x - Triangle->P1.x)) 
      {
      Temp = Triangle->P2;
      Triangle->P2 = Triangle->P1;
      Triangle->P1 = Temp;
      if (Smooth) 
        {
        Temp = ((SMOOTH_TRIANGLE *) Triangle)->N2;
        ((SMOOTH_TRIANGLE *) Triangle)->N2 = ((SMOOTH_TRIANGLE *) Triangle)->N1;
        ((SMOOTH_TRIANGLE *) Triangle)->N1 = Temp;
        }
      }
    break;
  }

  if (Smooth)
    Compute_Smooth_Triangle((SMOOTH_TRIANGLE *) Triangle);

  /* Build the bounding information from the vertices */
  /* Use temps so macro not too big. */
  T1 = MIN3(Triangle->P1.x, Triangle->P2.x, Triangle->P3.x);
  T2 = MIN3(Triangle->P1.y, Triangle->P2.y, Triangle->P3.y);
  T3 = MIN3(Triangle->P1.z, Triangle->P2.z, Triangle->P3.z);
  Make_Vector(&Triangle->Bounds.Lower_Left,T1,T2,T3);

  T1 = MAX3(Triangle->P1.x, Triangle->P2.x, Triangle->P3.x);
  T2 = MAX3(Triangle->P1.y, Triangle->P2.y, Triangle->P3.y);
  T3 = MAX3(Triangle->P1.z, Triangle->P2.z, Triangle->P3.z);
  Make_Vector(&Triangle->Bounds.Lengths,T1,T2,T3);

  VSub(Triangle->Bounds.Lengths, Triangle->Bounds.Lengths,
    Triangle->Bounds.Lower_Left);
  Triangle->Bounds.Lengths.x += EPSILON;
  Triangle->Bounds.Lengths.y += EPSILON;
  Triangle->Bounds.Lengths.z += EPSILON;
  return (1);
  }

int All_Triangle_Intersections (Object, Ray, Depth_Stack)
OBJECT *Object;
RAY *Ray;
ISTACK *Depth_Stack;
  {
  DBL Depth;
  VECTOR IPoint;

  if (Intersect_Triangle (Ray, (TRIANGLE *)Object, &Depth))
    {
    VScale (IPoint, Ray -> Direction, Depth);
    VAddEq (IPoint, Ray -> Initial);
    if (Point_In_Clip(&IPoint,Object->Clip))
      {
      push_entry(Depth,IPoint,Object,Depth_Stack);
      return (TRUE);
      }
    }
  return (FALSE);
  }

int Intersect_Triangle (Ray, Triangle, Depth)
RAY *Ray;
TRIANGLE *Triangle;
DBL *Depth;
  {
  DBL NormalDotOrigin, NormalDotDirection;
  DBL s, t;

  Ray_Triangle_Tests++;
  if(Triangle->Degenerate_Flag)
    return(FALSE);

  if (Ray == CM_Ray) 
    {
    if (!Triangle->CMCached) 
      {
      VDot (Triangle->CMNormDotOrigin, Triangle->Normal_Vector, Ray->Initial);
      Triangle->CMNormDotOrigin += Triangle->Distance;
      Triangle->CMNormDotOrigin *= -1.0;
      Triangle->CMCached = TRUE;
      }

    VDot (NormalDotDirection, Triangle->Normal_Vector, Ray->Direction);
    if ((NormalDotDirection < Small_Tolerance) &&
      (NormalDotDirection > -Small_Tolerance))
      return (FALSE);

    *Depth = Triangle->CMNormDotOrigin / NormalDotDirection;
    }
  else 
    {
    VDot (NormalDotOrigin, Triangle->Normal_Vector, Ray->Initial);
    NormalDotOrigin += Triangle->Distance;
    NormalDotOrigin *= -1.0;

    VDot (NormalDotDirection, Triangle->Normal_Vector, Ray->Direction);
    if ((NormalDotDirection < Small_Tolerance) &&
      (NormalDotDirection > -Small_Tolerance))
      return (FALSE);

    *Depth = NormalDotOrigin / NormalDotDirection;
    }

  if ((*Depth < Small_Tolerance) || (*Depth > Max_Distance))
    return (FALSE);

  switch (Triangle->Dominant_Axis) 
  {
  case X_AXIS:
    s = Ray->Initial.y + *Depth * Ray->Direction.y;
    t = Ray->Initial.z + *Depth * Ray->Direction.z;

    if ((Triangle->P2.y - s)*(Triangle->P2.z - Triangle->P1.z) <
      (Triangle->P2.z - t)*(Triangle->P2.y - Triangle->P1.y))
      return (FALSE);

    if ((Triangle->P3.y - s)*(Triangle->P3.z - Triangle->P2.z) <
      (Triangle->P3.z - t)*(Triangle->P3.y - Triangle->P2.y))
      return (FALSE);

    if ((Triangle->P1.y - s)*(Triangle->P1.z - Triangle->P3.z) <
      (Triangle->P1.z - t)*(Triangle->P1.y - Triangle->P3.y))
      return (FALSE);

    Ray_Triangle_Tests_Succeeded++;
    return (TRUE);

  case Y_AXIS:
    s = Ray->Initial.x + *Depth * Ray->Direction.x;
    t = Ray->Initial.z + *Depth * Ray->Direction.z;

    if ((Triangle->P2.x - s)*(Triangle->P2.z - Triangle->P1.z) <
      (Triangle->P2.z - t)*(Triangle->P2.x - Triangle->P1.x))
      return (FALSE);

    if ((Triangle->P3.x - s)*(Triangle->P3.z - Triangle->P2.z) <
      (Triangle->P3.z - t)*(Triangle->P3.x - Triangle->P2.x))
      return (FALSE);

    if ((Triangle->P1.x - s)*(Triangle->P1.z - Triangle->P3.z) <
      (Triangle->P1.z - t)*(Triangle->P1.x - Triangle->P3.x))
      return (FALSE);

    Ray_Triangle_Tests_Succeeded++;
    return (TRUE);

  case Z_AXIS:
    s = Ray->Initial.x + *Depth * Ray->Direction.x;
    t = Ray->Initial.y + *Depth * Ray->Direction.y;

    if ((Triangle->P2.x - s)*(Triangle->P2.y - Triangle->P1.y) <
      (Triangle->P2.y - t)*(Triangle->P2.x - Triangle->P1.x))
      return (FALSE);

    if ((Triangle->P3.x - s)*(Triangle->P3.y - Triangle->P2.y) <
      (Triangle->P3.y - t)*(Triangle->P3.x - Triangle->P2.x))
      return (FALSE);

    if ((Triangle->P1.x - s)*(Triangle->P1.y - Triangle->P3.y) <
      (Triangle->P1.y - t)*(Triangle->P1.x - Triangle->P3.x))
      return (FALSE);

    Ray_Triangle_Tests_Succeeded++;
    return (TRUE);
  }
  return (FALSE);
  }

int Inside_Triangle (IPoint, Object)
VECTOR *IPoint;
OBJECT *Object;
  {
  return (FALSE);
  }

void Triangle_Normal (Result, Object, IPoint)
OBJECT *Object;
VECTOR *Result, *IPoint;
  {
  *Result = ((TRIANGLE *)Object)->Normal_Vector;
  }

void *Copy_Triangle (Object)
OBJECT *Object;
  {
  TRIANGLE *New;

  New = Create_Triangle ();
  *New = * ((TRIANGLE *)Object);

  return (New);
  }

void Translate_Triangle (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  TRIANGLE *Triangle = (TRIANGLE *) Object;
  VECTOR Translation;

  VEvaluate (Translation, Triangle->Normal_Vector, *Vector);
  Triangle->Distance -= Translation.x + Translation.y + Translation.z;
  VAddEq (Triangle->P1, *Vector)
    VAddEq (Triangle->P2, *Vector)
      VAddEq (Triangle->P3, *Vector)

        /* Recalculate the bounds */
        VAddEq(Object->Bounds.Lower_Left, *Vector);
  }

void Rotate_Triangle (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  TRANSFORM Trans;

  Compute_Rotation_Transform (&Trans, Vector);
  Transform_Triangle (Object, &Trans);
  }

void Scale_Triangle (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  TRIANGLE *Triangle = (TRIANGLE *) Object;
  DBL Length,T1,T2,T3;

  Triangle->Normal_Vector.x = Triangle->Normal_Vector.x / Vector->x;
  Triangle->Normal_Vector.y = Triangle->Normal_Vector.y / Vector->y;
  Triangle->Normal_Vector.z = Triangle->Normal_Vector.z / Vector->z;

  VLength(Length, Triangle->Normal_Vector);
  VInverseScaleEq (Triangle->Normal_Vector, Length);
  Triangle->Distance /= Length;

  VEvaluateEq (Triangle->P1, *Vector);
  VEvaluateEq (Triangle->P2, *Vector);
  VEvaluateEq (Triangle->P3, *Vector);

  /* Recompute the bounds */
  /* Use temps so macro not too big. */
  T1 = MIN3(Triangle->P1.x, Triangle->P2.x, Triangle->P3.x);
  T2 = MIN3(Triangle->P1.y, Triangle->P2.y, Triangle->P3.y);
  T3 = MIN3(Triangle->P1.z, Triangle->P2.z, Triangle->P3.z);
  Make_Vector(&Triangle->Bounds.Lower_Left,T1,T2,T3);

  T1 = MAX3(Triangle->P1.x, Triangle->P2.x, Triangle->P3.x);
  T2 = MAX3(Triangle->P1.y, Triangle->P2.y, Triangle->P3.y);
  T3 = MAX3(Triangle->P1.z, Triangle->P2.z, Triangle->P3.z);
  Make_Vector(&Triangle->Bounds.Lengths,T1,T2,T3);

  VSub(Triangle->Bounds.Lengths, Triangle->Bounds.Lengths,
    Triangle->Bounds.Lower_Left);
  Triangle->Bounds.Lengths.x += EPSILON;
  Triangle->Bounds.Lengths.y += EPSILON;
  Triangle->Bounds.Lengths.z += EPSILON;
  }

void Transform_Triangle (Object, Trans)
OBJECT *Object;
TRANSFORM *Trans;
  {
  TRIANGLE *Triangle = (TRIANGLE *) Object;

  MTransPoint (&Triangle->Normal_Vector,
    &Triangle->Normal_Vector, Trans);
  MTransPoint (&Triangle->P1, &Triangle->P1, Trans);
  MTransPoint (&Triangle->P2, &Triangle->P2, Trans);
  MTransPoint (&Triangle->P3, &Triangle->P3, Trans);
  Compute_Triangle (Triangle,FALSE);
  }

TRIANGLE *Create_Triangle()
  {
  TRIANGLE *New;

  if ((New = (TRIANGLE *) malloc (sizeof (TRIANGLE))) == NULL)
    MAError ("triangle");

  INIT_OBJECT_FIELDS(New,TRIANGLE_OBJECT,&Triangle_Methods)

    Make_Vector (&(New -> Normal_Vector), 0.0, 1.0, 0.0);
  New -> Distance = 0.0;
  New -> CMNormDotOrigin = 0.0;
  New -> CMCached = FALSE;
  Make_Vector (&(New -> P1), 0.0, 0.0, 0.0);
  Make_Vector (&(New -> P2), 1.0, 0.0, 0.0);
  Make_Vector (&(New -> P3), 0.0, 1.0, 0.0);
  New -> Degenerate_Flag = FALSE;

  /* NOTE: Dominant_Axis is computed when Parse_Triangle calls
   Compute_Triangle.  vAxis is used only for smooth triangles */

  return (New);
  }

void Invert_Triangle (Object)
OBJECT *Object;
  {
  return;
  }

/* Calculate the Phong-interpolated vector within the triangle
   at the given intersection point. The math for this is a bit
   bizarre:

    -         P1
    |        /|\ \
    |       / |Perp\
    |      /  V  \   \
    |     /   |    \   \
  u |    /____|_____PI___\
    |   /     |       \    \
    -  P2-----|--------|----P3
              Pbase    PIntersect
        |-------------------|
                       v

   Triangle->Perp is a unit vector from P1 to Pbase. We calculate

   u = (PI - P1) DOT Perp / ((P3 - P1) DOT Perp).

   We then calculate where the line from P1 to PI intersects the line P2 to P3:
   PIntersect = (PI - P1)/u.

   We really only need one coordinate of PIntersect.  We then calculate v as:

      v = PIntersect.x / (P3.x - P2.x)
 or   v = PIntersect.y / (P3.y - P2.y)
 or   v = PIntersect.z / (P3.z - P2.z)

   depending on which calculation will give us the best answers.

   Once we have u and v, we can perform the normal interpolation as:

     NTemp1 = N1 + u(N2 - N1);
     NTemp2 = N1 + u(N3 - N1);
     Result = normalize (NTemp1 + v(NTemp2 - NTemp1))

   As always, any values which are constant for the triangle are cached
   in the triangle.
*/

void Smooth_Triangle_Normal (Result, Object, IPoint)
OBJECT *Object;
VECTOR *Result, *IPoint;
  {
  SMOOTH_TRIANGLE *Triangle = (SMOOTH_TRIANGLE *) Object;
  VECTOR PIMinusP1, NTemp1, NTemp2;
  DBL u = 0.0, v = 0.0;

  VSub (PIMinusP1, *IPoint, Triangle->P1);
  VDot (u, PIMinusP1, Triangle->Perp);
  if (u < 1.0e-9) 
    {
    *Result = Triangle->N1;
    return;
    }

  /* BaseDelta contains P3.x-P2.x,  P3.y-P2.y, or P3.z-P2.z depending on the
      value of vAxis. */

  switch (Triangle->vAxis) 
  {
  case X_AXIS:  
    v = (PIMinusP1.x/u + Triangle->P1.x - Triangle->P2.x) / Triangle->BaseDelta;
    break;

  case Y_AXIS:  
    v = (PIMinusP1.y/u + Triangle->P1.y - Triangle->P2.y) / Triangle->BaseDelta;
    break;

  case Z_AXIS:  
    v = (PIMinusP1.z/u + Triangle->P1.z - Triangle->P2.z)/ Triangle->BaseDelta;
    break;
  }

  VSub (NTemp1, Triangle->N2, Triangle->N1);
  VScaleEq (NTemp1, u);
  VAddEq (NTemp1, Triangle->N1);
  VSub (NTemp2, Triangle->N3, Triangle->N1);
  VScaleEq (NTemp2, u);
  VAddEq (NTemp2, Triangle->N1);
  VSub (*Result, NTemp2, NTemp1);
  VScaleEq (*Result, v);
  VAddEq (*Result, NTemp1); 
  VNormalize (*Result, *Result);
  }

void *Copy_Smooth_Triangle (Object)
OBJECT *Object;
  {
  SMOOTH_TRIANGLE *New;

  New = Create_Smooth_Triangle ();
  *New = * ((SMOOTH_TRIANGLE *)Object);

  return (New);
  }

void Translate_Smooth_Triangle (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  SMOOTH_TRIANGLE *Triangle = (SMOOTH_TRIANGLE *) Object;
  VECTOR Translation;

  VEvaluate (Translation, Triangle->Normal_Vector, *Vector);
  Triangle->Distance -= Translation.x + Translation.y + Translation.z;
  VAddEq (Triangle->P1, *Vector)
    VAddEq (Triangle->P2, *Vector)
      VAddEq (Triangle->P3, *Vector)
        Compute_Triangle ((TRIANGLE *) Triangle,TRUE);
  }

void Rotate_Smooth_Triangle (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  TRANSFORM Trans;

  Compute_Rotation_Transform (&Trans, Vector);
  Transform_Smooth_Triangle (Object, &Trans);
  }

void Scale_Smooth_Triangle (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  SMOOTH_TRIANGLE *Triangle = (SMOOTH_TRIANGLE *) Object;
  DBL Length;

  Triangle->Normal_Vector.x = Triangle->Normal_Vector.x / Vector->x;
  Triangle->Normal_Vector.y = Triangle->Normal_Vector.y / Vector->y;
  Triangle->Normal_Vector.z = Triangle->Normal_Vector.z / Vector->z;

  VLength(Length, Triangle->Normal_Vector);
  VScaleEq (Triangle->Normal_Vector, 1.0 / Length);
  Triangle->Distance /= Length;

  VEvaluateEq (Triangle->P1, *Vector);
  VEvaluateEq (Triangle->P2, *Vector);
  VEvaluateEq (Triangle->P3, *Vector);
  Compute_Triangle ((TRIANGLE *) Triangle,TRUE);
  }

void Transform_Smooth_Triangle (Object, Trans)
OBJECT *Object;
TRANSFORM *Trans;
  {
  SMOOTH_TRIANGLE *Triangle = (SMOOTH_TRIANGLE *) Object;

  MTransPoint (&Triangle->Normal_Vector,
    &Triangle->Normal_Vector, Trans);
  MTransPoint (&Triangle->P1, &Triangle->P1, Trans);
  MTransPoint (&Triangle->P2, &Triangle->P2, Trans);
  MTransPoint (&Triangle->P3, &Triangle->P3, Trans);
  MTransPoint (&Triangle->N1, &Triangle->N1, Trans);
  MTransPoint (&Triangle->N2, &Triangle->N2, Trans);
  MTransPoint (&Triangle->N3, &Triangle->N3, Trans);
  Compute_Triangle ((TRIANGLE *) Triangle,TRUE);
  }

void Invert_Smooth_Triangle (Object)
OBJECT *Object;
  {
  return;
  }

SMOOTH_TRIANGLE *Create_Smooth_Triangle()
  {
  SMOOTH_TRIANGLE *New;

  if ((New = (SMOOTH_TRIANGLE *) malloc (sizeof (SMOOTH_TRIANGLE))) == NULL)
    MAError ("smooth triangle");

  INIT_OBJECT_FIELDS(New,SMOOTH_TRIANGLE_OBJECT,&Smooth_Triangle_Methods)

    Make_Vector (&(New->Normal_Vector), 0.0, 1.0, 0.0);
  New->Distance = 0.0;
  New -> CMNormDotOrigin = 0.0;
  New -> CMCached = FALSE;
  Make_Vector (&(New -> P1), 0.0, 0.0, 0.0);
  Make_Vector (&(New -> P2), 1.0, 0.0, 0.0);
  Make_Vector (&(New -> P3), 0.0, 1.0, 0.0);
  Make_Vector (&(New -> N1), 0.0, 1.0, 0.0);
  Make_Vector (&(New -> N2), 0.0, 1.0, 0.0);
  Make_Vector (&(New -> N3), 0.0, 1.0, 0.0);
  New -> BaseDelta = 0.0;
  New -> Degenerate_Flag = FALSE;

  /* NOTE: Dominant_Axis and vAxis are computed when Parse_Triangle calls
   Compute_Triangle.  */

  return (New);
  }

void Destroy_Triangle (Object)
OBJECT *Object;
  {
  free (Object);
  }
