/****************************************************************************
*                objects.c
*
*  This module implements the methods for objects and composite objects.
*
*  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"

extern long Bounding_Region_Tests, Bounding_Region_Tests_Succeeded;
extern long Clipping_Region_Tests, Clipping_Region_Tests_Succeeded;
extern unsigned int Options;
extern long Istack_overflows;
extern int Number_of_istacks;
extern int Max_Intersections;
extern ISTACK *free_istack;

int Intersection (Ray_Intersection, Object, Ray)
INTERSECTION *Ray_Intersection;
OBJECT *Object;
RAY *Ray;
  {
  ISTACK *Depth_Stack;
  INTERSECTION *Local;
  DBL Closest = HUGE_VAL;

  if (Object == NULL)
    return (FALSE);

  if(!Ray_In_Bounds (Ray,Object->Bound))
    return (FALSE);

  Depth_Stack = open_istack ();

  if (All_Intersections (Object, Ray, Depth_Stack))
    {
    while ((Local = pop_entry(Depth_Stack)) != NULL)
      if (Local->Depth < Closest)
        {
        *Ray_Intersection = *Local;
        Closest = Local->Depth;
        }
    close_istack (Depth_Stack);
    return (TRUE);
    }
  else
    {
    close_istack (Depth_Stack);
    return (FALSE);
    }
  }

int Ray_In_Bounds (Ray,Bounds)
RAY *Ray;
OBJECT *Bounds;
  {
  OBJECT *Bound;
  INTERSECTION Local;

  for (Bound = Bounds;
  Bound != NULL;
  Bound = Bound->Sibling)
    {
    Bounding_Region_Tests++;

    if (!Intersection (&Local, Bound, Ray))
      if (!Inside_Object(&Ray->Initial,Bound))
        return (FALSE);

    Bounding_Region_Tests_Succeeded++;
    }

  return (TRUE);
  }

int Point_In_Clip (IPoint, Clip)
VECTOR *IPoint;
OBJECT *Clip;
  {
  OBJECT *Local_Clip;

  for (Local_Clip = Clip;
  Local_Clip != NULL;
  Local_Clip = Local_Clip->Sibling)
    {
    Clipping_Region_Tests++;
    if (!Inside_Object(IPoint, Local_Clip)) 
      return (FALSE);

    Clipping_Region_Tests_Succeeded++;
    }
  return (TRUE);
  }

OBJECT *Copy_Bound_Clip (Old)
OBJECT *Old;
  {
  OBJECT *Current, *New, *Prev, *First;

  First = Prev = NULL;

  for (Current = Old;
  Current != NULL ;
  Current = Current->Sibling) 
    {
    New = Copy_Object (Current);
    if (First == NULL)
      First = New;
    if (Prev != NULL)
      Prev->Sibling = New;
    Prev = New;
    }
  return (First);
  }

OBJECT *Copy_Object (Old)
OBJECT *Old;
  {
  OBJECT *New;

  if (Old == NULL)
    return (NULL);

  New = Copy (Old);

  /* The following copying of OBJECT_FIELDS is redundant if Copy 
   did *New = *Old but we cannot assume it did.
   It is safe for Copy to do *New = *Old but it should not otherwise
   touch OBJECT_FIELDS. */

  New->Methods = Old->Methods;
  New->Type    = Old->Type;
  New->Sibling = Old->Sibling;
  New->Texture = Old->Texture;
  New->Bound   = Old->Bound;
  New->Clip    = Old->Clip;
  New->Bounds  = Old->Bounds;
  New->No_Shadow_Flag = Old->No_Shadow_Flag;

  New->Sibling = NULL;          /* Important */

  New->Texture = Copy_Textures (Old->Texture);
  New->Bound   = Copy_Bound_Clip (Old->Bound);
  if (Old->Bound != Old->Clip)
    New->Clip  = Copy_Bound_Clip (Old->Clip);
  else
    New->Clip  = New->Bound;

  return (New);
  }   

void Translate_Object (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  OBJECT *Sib;

  if (Object == NULL)
    return;

  for (Sib = Object->Bound;
  Sib != NULL;
  Sib = Sib->Sibling)
    Translate_Object (Sib, Vector);

  if (Object->Clip != Object->Bound)
    for (Sib = Object->Clip;
  Sib != NULL;
  Sib = Sib->Sibling)
    Translate_Object (Sib, Vector);

  Translate_Textures (Object->Texture,Vector);

  Translate (Object,Vector);
  }

void Rotate_Object (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  OBJECT *Sib;

  if (Object == NULL)
    return;

  for (Sib = Object->Bound;
  Sib != NULL;
  Sib = Sib->Sibling)
    Rotate_Object (Sib, Vector);

  if (Object->Clip != Object->Bound)
    for (Sib = Object->Clip;
  Sib != NULL;
  Sib = Sib->Sibling)
    Rotate_Object (Sib, Vector);

  Rotate_Textures (Object->Texture,Vector);

  Rotate (Object,Vector);
  }

void Scale_Object (Object, Vector)
OBJECT *Object;
VECTOR *Vector;
  {
  OBJECT *Sib;

  if (Object == NULL)
    return;

  for (Sib = Object->Bound;
  Sib != NULL;
  Sib = Sib->Sibling)
    Scale_Object (Sib, Vector);

  if (Object->Clip != Object->Bound)
    for (Sib = Object->Clip;
  Sib != NULL;
  Sib = Sib->Sibling)
    Scale_Object (Sib, Vector);

  Scale_Textures (Object->Texture,Vector);

  Scale (Object,Vector);
  }

int Inside_Object (IPoint, Object)
VECTOR *IPoint;
OBJECT *Object;
  {
  OBJECT *Sib;

  /* removed 7/19/92 CEY   
   for (Sib = Object->Bound;
        Sib != NULL;
        Sib = Sib->Sibling)
     if (!Inside_Object(IPoint, Sib))
       return(FALSE);
*/
  for (Sib = Object->Clip;
  Sib != NULL;
  Sib = Sib->Sibling)
    if (!Inside_Object(IPoint, Sib))
      return(FALSE);

  return (Inside(IPoint,Object));
  }

void Invert_Object (Object)
OBJECT *Object;
  {
  /*   OBJECT *Sib; */

  if (Object == NULL)
    return;

  /* removed 3/29/93 CEY
   for (Sib = Object->Clip;
        Sib != NULL;
        Sib = Sib->Sibling)
     Invert_Object (Sib);
*/
  Invert (Object);
  }

void Destroy_Object (Object)
OBJECT *Object;
  {
  OBJECT *Sib;

  while (Object != NULL)
    {
    Destroy_Textures (Object->Texture);
    Destroy_Object (Object->Bound);
    if (Object->Bound != Object->Clip)
      Destroy_Object (Object->Clip);
    Sib = Object->Sibling;
    Destroy(Object);
    Object = Sib;
    }
  }   

void Transform_Object (Object, Trans)
OBJECT *Object;
TRANSFORM *Trans;
  {
  OBJECT *Sib;

  if (Object == NULL)
    return;

  for (Sib = Object->Bound;
  Sib != NULL;
  Sib = Sib->Sibling)
    Transform_Object (Sib, Trans);

  if (Object->Clip != Object->Bound)
    for (Sib = Object->Clip;
  Sib != NULL;
  Sib = Sib->Sibling)
    Transform_Object (Sib, Trans);

  Transform_Textures (Object->Texture,Trans);

  Transform (Object,Trans);
  }

void create_istack ()
  {
  ISTACK *New;

  if ((New = (ISTACK *) malloc (sizeof (ISTACK))) == NULL) 
    {
    fprintf (stderr, "\nOut of memory. Cannot allocate istack");
    close_all();
    exit(1);
    }

  New->next = free_istack;
  free_istack = New;

  if ((New->istack = (INTERSECTION *)
    malloc (Max_Intersections * sizeof (INTERSECTION))) == NULL) 
    {
    fprintf (stderr, "\nOut of memory. Cannot allocate istack entries");
    close_all();
    exit(1);
    }
  Number_of_istacks++;
  }

ISTACK *open_istack()
  {
  ISTACK *istk;

  if (free_istack == NULL) 
    create_istack ();

  istk = free_istack;
  free_istack = istk->next;
  istk->top_entry = 0;

  return (istk);
  }

void close_istack (istk)
ISTACK *istk;
  {
  istk->next = free_istack;
  free_istack = istk;
  }

void incstack(istk)
ISTACK *istk;
  {
  if (++istk->top_entry >= (unsigned int)Max_Intersections)
    {
    istk->top_entry--;
    Istack_overflows++;
    }
  }
