/****************************************************************************
*
*  ATTENTION!!!
*
*  THIS FILE HAS BEEN MODIFIED!!! IT IS NOT PART OF THE OFFICAL
*  POV-RAY 2.2 DISTRIBUTION!!!
*
*  THIS FILE IS PART OF "FASTER THAN POV-RAY" (VERSION 2.2),
*  A SPED-UP VERSION OF POV-RAY 2.2. USE AT YOUR OWN RISK!!!!!!
*
*  New files: addon0.c, addon1.c, addon2.c, addon3.c, addon.h
*
*  The additional modules were written by Dieter Bayer.
*
*  Send comments, suggestions, bugs, ideas ... to:
*
*  e-mail: dieter@cip.e-technik.uni-erlangen.de
*  CIS: 100255.3074
*
*  All changed/added lines are enclosed in #ifdef DB_CODE ... #endif
*
*  The vista projection was taken from:
*
*    A. Hashimoto, T. Akimoto, K. Mase, and Y. Suenaga, 
*    "Vista Ray-Tracing: High Speed Ray Tracing Using Perspective
*    Projection Image", New Advances in Computer Graphics, Proceedings
*    of CG International '89, R. A. Earnshaw, B. Wyvill (Eds.), 
*    Springer, ..., pp. 549-560
*
*  The idea for the light buffer was taken from:
*
*    E. Haines and D. Greenberg, "The Light Buffer: A Shadow-Testing 
*    Accelerator", IEEE CG&A, Vol. 6, No. 9, Sept. 1986, pp. 6-16
*
*****************************************************************************/

/*****************************************************************************
*  addon0.c
*
*  This module was written by Dieter Bayer.
*
*  This module contains functions for initialisation, statistics, previewing,
*  and other gegeneral purposes.
*
*  01.03.1994 : Creation
*
*  29.04.1994 : Version 2.0
*
******************************************************************************/

#include <time.h>
#include "frame.h"
#include "vector.h"
#include "povproto.h"
#include "addon.h"

#ifdef DB_CODE

#define RED 0
#define GREEN 1
#define BLUE 2

#define NUMBER_OF_TYPES      19
#define TYPE_BICUBIC_PATCH    0
#define TYPE_BLOB             1
#define TYPE_BOX              2
#define TYPE_CONE             3
#define TYPE_CSG_INTERSECTION 4
#define TYPE_CSG_MERGE        5
#define TYPE_CSG_UNION        6
#define TYPE_DISC             7
#define TYPE_ELLIPSOID        8
#define TYPE_HEIGHT_FIELD     9
#define TYPE_LIGHT_SOURCE    10
#define TYPE_PLANE           11
#define TYPE_POLY            12
#define TYPE_POLYGON         13
#define TYPE_QUADRIC         14
#define TYPE_SMOOTH_TRIANGLE 15
#define TYPE_SPHERE          16
#define TYPE_TRIANGLE        17
#define TYPE_UNKNOWN         18

#define NUMBER_OF_FLAGS     7
#define FLAG_SUM            0
#define FLAG_INFINITE       1
#define FLAG_USED_IN_CSG    2
#define FLAG_BOUNDED        3
#define FLAG_CLIPPED        4
#define FLAG_BOUND_OBJECT   5
#define FLAG_CLIP_OBJECT    6



/*****************************************************************************
* External variables
******************************************************************************/

extern FRAME Frame;
extern unsigned int Options;
extern METHODS Ellipsoid_Methods;
extern METHODS Sphere_Methods;
extern time_t tstart, tstop;
extern int Use_Slabs;
extern unsigned long Quality_Flags;

extern METHODS Bicubic_Patch_Methods;
extern METHODS Blob_Methods;
extern METHODS Box_Methods;
extern METHODS Cone_Methods;
extern METHODS Csg_Height_Field_Methods;
extern METHODS CSG_Intersection_Methods;
extern METHODS CSG_Merge_Methods;
extern METHODS CSG_Union_Methods;
extern METHODS Disc_Methods;
extern METHODS Ellipsoid_Methods;
extern METHODS Height_Field_Methods;
extern METHODS Light_Source_Methods;
extern METHODS Plane_Methods;
extern METHODS Poly_Methods;
extern METHODS Quadric_Methods;
extern METHODS Smooth_Triangle_Methods;
extern METHODS Sphere_Methods;
extern METHODS Triangle_Methods;

extern PROJECT_TREE_NODE *Root_Vista;

extern size_t Mem_Polygon;



/*****************************************************************************
* Global variables
******************************************************************************/

unsigned int Extended_Options = 0;

/* Amount of memory occupied by the vista buffer and light buffers */

size_t Mem_Vista_Buffer  = 0;
size_t Mem_Light_Buffers = 0;
size_t Allocated_Memory  = 0;



/*****************************************************************************
* Static variables
******************************************************************************/

static int Point_Counter;

static DBL time_used1, time_used2;

static long counter[NUMBER_OF_TYPES][NUMBER_OF_FLAGS];



/*****************************************************************************
* Static functions
******************************************************************************/

static int Get_Object_Type PARAMS((OBJECT *Object));
static void Print_Object_Info PARAMS((int type));
static void Get_Object_Stats PARAMS((OBJECT *Object, int Csg, int Bound_Object, int Clip_Object));
static void Print_Object_Stats PARAMS((void));

static void Draw_Projection PARAMS((PROJECT *Project, int color));
static void Draw_Vista PARAMS((PROJECT_TREE_NODE *Tree));



/*****************************************************************************
*
* FUNCTION      : VB_malloc
*
* ARGUMENTS     : size   - Amount of memory to allocate
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : void * - Pointer at allocated memory
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Allocate memory for the vista buffer and keep track of the memory allocated.
*
* CHANGES
*
*   -
*
******************************************************************************/

void *VB_malloc(size)
size_t size;
{
  Allocated_Memory += size;

  Mem_Vista_Buffer += size;

  return(malloc(size));
}



/*****************************************************************************
*
* FUNCTION      : LB_malloc
*
* ARGUMENTS     : size   - Amount of memory to allocate
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : void * - Pointer at allocated memory
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Allocate memory for the light buffer and keep track of the memory allocated.
*
* CHANGES
*
*   -
*
******************************************************************************/

void *LB_malloc(size)
size_t size;
{
  Allocated_Memory += size;

  Mem_Light_Buffers += size;

  return(malloc(size));
}



/*****************************************************************************
*
* FUNCTION      : new_malloc
*
* ARGUMENTS     : size   - Amount of memory to allocate
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : void * - Pointer at allocated memory
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Allocate memory and keep track of the memory allocated.
*
*   Use instead of malloc when allocating memory!
*
* CHANGES
*
*   -
*
******************************************************************************/

void *new_malloc(size)
size_t size;
{
  Allocated_Memory += size;

  return(malloc(size));
}



/*****************************************************************************
*
* FUNCTION      : new_free
*
* ARGUMENTS     : pointer - Memory to free
*                 size    - Amount of memory that will be freed
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Free memory and keep track of the memory allocated.
*
*   Use instead of free when freeing memory!
*
* CHANGES
*
*   -
*
******************************************************************************/

void new_free(pointer, size)
void *pointer;
size_t size;
{
  Allocated_Memory -= size;

  free(pointer);
}


/*****************************************************************************
*
* FUNCTION      : Init_Additional_1
*
* ARGUMENTS     : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Additional initialisation before boundind slabs are build.
*
*   Split union objects and pass bounding objects to the children if they
*   aren't already bounded (most bounding objects will be removed later).
*
* CHANGES
*
*   -
*
******************************************************************************/

void Init_Additionals_1()
{
  int children_finite;
  long count;
  DBL Volume;
  OBJECT **Object, *Sib, *Last_Sib, *Bound;

  Print_Quadric_Stats();

  if ((Extended_Options & USE_SPLIT_FINITE_UNIONS) ||
      (Extended_Options & USE_SPLIT_INFINITE_UNIONS))
  {
    START_TIME

    Recompute_Bboxes();

    fprintf(stderr, "Splitting union objects");

    Begin_Point();

    count = 0;

    for (Object = &Frame.Objects; (*Object) != NULL; )
    {
      children_finite = FALSE;

      if (Extended_Options & USE_SPLIT_INFINITE_UNIONS)
      {
	/* +usi option is used, i.e. split all unions */

	children_finite = TRUE;
      }
      else
      {
	/* Check, if all children of the union have finite bounding boxes */

	if ((*Object)->Methods == &CSG_Union_Methods)
	{
	  children_finite = TRUE;

	  for (Sib = ((CSG *)(*Object))->Children; Sib != NULL; Sib = Sib->Sibling)
	  {
	    BOUNDS_VOLUME(Volume, Sib->Bounds);
	    if (Volume > INFINITE_VOLUME) children_finite = FALSE;
	  }
	}
      }

      /* Split union object, if all children are finite (or tracing slows
	 down!) and the union isn't clipped */

      if (((*Object)->Methods == &CSG_Union_Methods) &&
	  ((*Object)->Clip == NULL) && (children_finite))
      {
	count++;

	Print_Point(POINT_MOD);

	Bound = (*Object)->Bound;

	if ((Sib = ((CSG *)(*Object))->Children) != NULL)
	{
	  for (; Sib != NULL; Sib = Sib->Sibling)
	  {
	    /* Set child's bound to father's bound (some peolpe use objects
	       that don't fit in the bounding object. Argh!!!
	       The question is: should this be done or not?) */

	    if (Sib->Bound == NULL) Sib->Bound = Bound;

	    Last_Sib = Sib;
	  }

	  Last_Sib->Sibling = (*Object)->Sibling;

	  (*Object) = ((CSG *)(*Object))->Children;
	}
	else
	{
	  (*Object) = (*Object)->Sibling;
	}
      }
      else
      {
	Object = &(*Object)->Sibling;
      }
    }

    fprintf(stderr, "(%ld split)", count);

    End_Point();

    if (count > 0)
    {
      /* Just to be sure */

      Recompute_Bboxes();
    }

    STOP_TIME

    time_used1 = TIME_ELAPSED
  }
  else
  {
    Recompute_Bboxes();
  }
}



/*****************************************************************************
*
* FUNCTION      : Init_Additionals_2
*
* ARGUMENTS     : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Additional initialisation after boundind slabs have been build.
*
*   - Project objects onto the screen.
*   - Build the light buffers.
*   - Remove all unnecessary bounding objects.
*   - Print a statistic of the scene.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Init_Additionals_2()
{
  DBL time_used;

  /* If slabs are not used --> vista and light buffer can't be used */

  if (!Use_Slabs)
  {
    Extended_Options &= ~USE_VISTA_BUFFER;
    Extended_Options &= ~USE_LIGHT_BUFFER;
  }

  /* We don't use shadows so we don't need a light buffer */

  if (!(Quality_Flags & Q_SHADOW))
  {
    Extended_Options &= ~USE_LIGHT_BUFFER;
  }

  if ((Extended_Options & USE_VISTA_BUFFER) ||
      (Extended_Options & USE_LIGHT_BUFFER))
  {
    START_TIME

    if (Use_Slabs)
    {
      Init_Project_Tree_Queues();

      if (Extended_Options & USE_VISTA_BUFFER)
      {
	Init_View_Coordinates();

	Build_Vista_Tree();
      }

      if (Extended_Options & USE_LIGHT_BUFFER)
      {
	Build_Light_Buffers();
      }
    }

    Remove_Unnecessary_Bounds();

    Print_Object_Stats();

    STOP_TIME

    time_used2 = TIME_ELAPSED

    time_used = time_used1 + time_used2;

    fprintf(stderr, "Preprocessing time: %.0f seconds\n", time_used);
  }
  else
  {
    Remove_Unnecessary_Bounds();
  }
}



/*****************************************************************************
*
* FUNCTION      : Get_Object_Type
*
* ARGUMENTS     : Object - Point to an object
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : int - Type of object
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Returns the type of an object.
*
* CHANGES
*
*   -
*
******************************************************************************/

static int Get_Object_Type(Object)
OBJECT *Object;
{
  if (Object->Methods == &Bicubic_Patch_Methods)    return(TYPE_BICUBIC_PATCH);

  if (Object->Methods == &Blob_Methods)             return(TYPE_BLOB);

  if (Object->Methods == &Box_Methods)              return(TYPE_BOX);

  if (Object->Methods == &Cone_Methods)             return(TYPE_CONE);

  if (Object->Methods == &CSG_Intersection_Methods) return(TYPE_CSG_INTERSECTION);

  if (Object->Methods == &CSG_Merge_Methods)        return(TYPE_CSG_MERGE);

  if (Object->Methods == &CSG_Union_Methods)        return(TYPE_CSG_UNION);

  if (Object->Methods == &Disc_Methods)             return(TYPE_DISC);

  if (Object->Methods == &Ellipsoid_Methods)        return(TYPE_ELLIPSOID);

  if (Object->Methods == &Height_Field_Methods)     return(TYPE_HEIGHT_FIELD);

  if (Object->Methods == &Csg_Height_Field_Methods) return(TYPE_HEIGHT_FIELD);

  if (Object->Methods == &Light_Source_Methods)     return(TYPE_LIGHT_SOURCE);

  if (Object->Methods == &Plane_Methods)            return(TYPE_PLANE);

  if (Object->Methods == &Poly_Methods)             return(TYPE_POLY);

  if (Object->Methods == &Quadric_Methods)          return(TYPE_QUADRIC);

  if (Object->Methods == &Smooth_Triangle_Methods)  return(TYPE_SMOOTH_TRIANGLE);

  if (Object->Methods == &Sphere_Methods)           return(TYPE_SPHERE);

  if (Object->Methods == &Triangle_Methods)         return(TYPE_TRIANGLE);

  return(TYPE_UNKNOWN);
}



/*****************************************************************************
*
* FUNCTION      : Get_Object_Stats
*
* ARGUMENTS     : Object       - object
*                 csg          - Flag if inside a csg object
*                 bound_object - Flag if object is part of a bounding object
*                 clip_object  - Flag if object is part of a clipping object
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Get some information about an object and add it to the scene statistic.
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Get_Object_Stats(Object, csg, bound_object, clip_object)
OBJECT *Object;
int csg, bound_object, clip_object;
{
  int bounded, clipped, infinite, type;
  DBL Volume;

  OBJECT *Sib;

  if (Object == NULL)
    return;

  /* Bounded/Clipped object? */

  bounded = (Object->Bound != NULL);
  clipped = (Object->Clip != NULL);

  /* Don't care about infinite objects if they are inside
     a CSG object or used as clipping/bounding objects */

  if (csg || bound_object || clip_object)
  {
    infinite = FALSE;
  }
  else
  {
    /* Infinte object? */

    BOUNDS_VOLUME(Volume, Object->Bounds);

    infinite = (Volume > INFINITE_VOLUME);
  }

  /* Get object type */

  type = Get_Object_Type(Object);

  /* Count bounding objects */

  if (Object->Bound != NULL)
  {
    for (Sib = Object->Bound; Sib != NULL; Sib = Sib->Sibling)
    {
      Get_Object_Stats(Sib, csg, TRUE, clip_object);
    }
  }

  /* Count clipping objects */

  if (Object->Clip != NULL)
  {
    for (Sib = Object->Clip; Sib != NULL; Sib = Sib->Sibling)
    {
      Get_Object_Stats(Sib, csg, bound_object, TRUE);
    }
  }

  if (Object->Type & COMPOUND_OBJECT)
  {
    if (Object->Type & LIGHT_SOURCE_OBJECT)
    {
      /* Count light source */

      counter[type][FLAG_SUM]++;

      Get_Object_Stats(((LIGHT_SOURCE *)Object)->Children, csg, bound_object, clip_object);
    }
    else
    {
      /* Count CSG object */

      counter[type][FLAG_SUM]++;

      if (csg)            counter[type][FLAG_USED_IN_CSG]++;
      if (infinite)       counter[type][FLAG_INFINITE]++;
      if (bounded)        counter[type][FLAG_BOUNDED]++;
      if (clipped)        counter[type][FLAG_CLIPPED]++;
      if (bound_object)   counter[type][FLAG_BOUND_OBJECT]++;
      if (clip_object)    counter[type][FLAG_CLIP_OBJECT]++;

      for (Sib = ((CSG *)Object)->Children; Sib != NULL; Sib = Sib->Sibling)
      {
	Get_Object_Stats(Sib, TRUE, bound_object, clip_object);
      }
    }
  }
  else
  {
    /* Count the primitive object */

    counter[type][FLAG_SUM]++;

    if (csg)            counter[type][FLAG_USED_IN_CSG]++;
    if (infinite)       counter[type][FLAG_INFINITE]++;
    if (bounded)        counter[type][FLAG_BOUNDED]++;
    if (clipped)        counter[type][FLAG_CLIPPED]++;
    if (bound_object)   counter[type][FLAG_BOUND_OBJECT]++;
    if (clip_object)    counter[type][FLAG_CLIP_OBJECT]++;
  }
}



/*****************************************************************************
*
* FUNCTION      : Print_Object_Info
*
* ARGUMENTS     : type - Object's type
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Print the statistics of one type of object.
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Print_Object_Info(type)
int type;
{
  fprintf(stderr, " : %5ld %5ld %5ld %5ld %5ld   %5ld   %5ld\n",
    counter[type][FLAG_SUM],
    counter[type][FLAG_USED_IN_CSG],
    counter[type][FLAG_INFINITE],
    counter[type][FLAG_BOUND_OBJECT],
    counter[type][FLAG_CLIP_OBJECT],
    counter[type][FLAG_BOUNDED],
    counter[type][FLAG_CLIPPED]);
}



/*****************************************************************************
*
* FUNCTION      : Print_Object_Stats
*
* ARGUMENTS     : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Collect and print a statistic about the scene.
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Print_Object_Stats()
{
  int i, j;
  long sum1, sum2, sum3, sum4, sum5, sum6, sum7, frame_level;
  OBJECT *Sib;

  /* Initialize counters */

  for (i = 0; i < NUMBER_OF_TYPES; i++)
  {
    for (j = 0; j < NUMBER_OF_FLAGS; j++)
    {
      counter[i][j] = 0;
    }
  }

  frame_level = 0;

  for (Sib = Frame.Objects; Sib != NULL; Sib = Sib->Sibling)
  {
    if (Sib->Type & LIGHT_SOURCE_OBJECT)
    {
      if (((LIGHT_SOURCE *)Sib)->Children != NULL)
      {
	frame_level++;
      }
    }
    else
    {
      frame_level++;
    }
    Get_Object_Stats(Sib, FALSE, FALSE, FALSE);
  }

  sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = sum7 = 0;

  for (i = 0; i < NUMBER_OF_TYPES; i++)
  {
    sum1 += counter[i][FLAG_SUM];
    sum2 += counter[i][FLAG_USED_IN_CSG];
    sum3 += counter[i][FLAG_INFINITE];
    sum4 += counter[i][FLAG_BOUND_OBJECT];
    sum5 += counter[i][FLAG_CLIP_OBJECT];
    sum6 += counter[i][FLAG_BOUNDED];
    sum7 += counter[i][FLAG_CLIPPED];
  }

  fprintf(stderr, "                     sum   csg infin bound  clip bounded clipped  KBytes\n");
  fprintf(stderr, "Blob            "); Print_Object_Info(TYPE_BLOB);
  fprintf(stderr, "Box             "); Print_Object_Info(TYPE_BOX);
  fprintf(stderr, "Cone/Cylinder   "); Print_Object_Info(TYPE_CONE);
  fprintf(stderr, "Ellipsoid       "); Print_Object_Info(TYPE_ELLIPSOID);
  fprintf(stderr, "Height Field    "); Print_Object_Info(TYPE_HEIGHT_FIELD);
  fprintf(stderr, "Sphere          "); Print_Object_Info(TYPE_SPHERE);

  fprintf(stderr, "Bicubic Patch   "); Print_Object_Info(TYPE_BICUBIC_PATCH);
  fprintf(stderr, "Disc            "); Print_Object_Info(TYPE_DISC);
  fprintf(stderr, "Smooth Triangle "); Print_Object_Info(TYPE_SMOOTH_TRIANGLE);
  fprintf(stderr, "Triangle        "); Print_Object_Info(TYPE_TRIANGLE);

  fprintf(stderr, "Plane           "); Print_Object_Info(TYPE_PLANE);
  fprintf(stderr, "Poly/Quartic    "); Print_Object_Info(TYPE_POLY);
  fprintf(stderr, "Quadric         "); Print_Object_Info(TYPE_QUADRIC);

  fprintf(stderr, "CSG Intersection"); Print_Object_Info(TYPE_CSG_INTERSECTION);
  fprintf(stderr, "CSG Merge       "); Print_Object_Info(TYPE_CSG_MERGE);
  fprintf(stderr, "CSG Union       "); Print_Object_Info(TYPE_CSG_UNION);

  fprintf(stderr, "Light Source    "); Print_Object_Info(TYPE_LIGHT_SOURCE);

  fprintf(stderr, "TOTAL            : %5ld %5ld %5ld %5ld %5ld   %5ld   %5ld\n", sum1, sum2, sum3, sum4, sum5, sum6, sum7);

  fprintf(stderr, "Scene contains %ld frame level objects.\n", frame_level);

  /* Print amount of allocated memory */

  if (Mem_Vista_Buffer)
    fprintf(stderr, "Vista Buffer : %.2f KBytes\n", (DBL)(Mem_Vista_Buffer) / 1024.0);

  if (Mem_Light_Buffers)
    fprintf(stderr, "Light Buffer : %.2f KBytes\n", (DBL)(Mem_Light_Buffers) / 1024.0);

  if (Allocated_Memory)
    fprintf(stderr, "Total        : %.2f KBytes\n", (DBL)Allocated_Memory / 1024.0);
}



/*****************************************************************************
*
* FUNCTION      : Clip_Polygon
*
* ARGUMENTS     : Points             - polygon's points
*                 PointCnt           - Number of points in polygon
*                 VX1, VY1, VX2, VY1 - Normal vectors of the clipping planes
*                 DX1, DY1, DX2, DY2 - Distances of the clipping planes from
*                                      the origin
*
* MODIFIED ARGS : Points, PointCnt
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Clip polygon at the viewing pyramid define by the normal vectors
*   VX1, VX2, VY1, VY2 and the distances DX1, DX2, DY1, DY2.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Clip_Polygon (Points, PointCnt, VX1, VX2, VY1, VY2, DX1, DX2, DY1, DY2)
VECTOR *Points;
int *PointCnt;
VECTOR *VX1, *VX2, *VY1, *VY2;
DBL DX1, DX2, DY1, DY2;
{
  DBL aktd, pred, fird, k;
  VECTOR aktP, intP, preP, firP, d;
  int i, pc;
  VECTOR ClipPoints[MAX_CLIP_POINTS];

  /********** clip polygon at "left" plane **********/

  pc = 0;

  firP = Points[0];
  fird = VX1->x * firP.x + VX1->y * firP.y + VX1->z * firP.z - DX1;

  if (fird <= 0.0)
    ClipPoints[pc++] = firP;

  aktP = preP = firP;
  aktd = pred = fird;

  for (i = 1; i < *PointCnt; i++)
  {
    aktP = Points[i];
    aktd = VX1->x * aktP.x + VX1->y * aktP.y + VX1->z * aktP.z - DX1;

    if (((aktd < 0.0) && (pred > 0.0)) || ((aktd > 0.0) && (pred < 0.0)))
    {
      d.x = preP.x - aktP.x;
      d.y = preP.y - aktP.y;
      d.z = preP.z - aktP.z;

      k = -aktd / (VX1->x * d.x + VX1->y * d.y + VX1->z * d.z);

      intP.x = aktP.x + k * d.x;
      intP.y = aktP.y + k * d.y;
      intP.z = aktP.z + k * d.z;

      ClipPoints[pc++] = intP;
    }

    if (aktd <= 0.0)
      ClipPoints[pc++] = aktP;

    preP = aktP;
    pred = aktd;
  }

  if (((fird < 0.0) && (aktd > 0.0)) || ((fird > 0.0) && (aktd < 0.0)))
  {
    d.x = firP.x - aktP.x;
    d.y = firP.y - aktP.y;
    d.z = firP.z - aktP.z;

    k = -aktd / (VX1->x * d.x + VX1->y * d.y + VX1->z * d.z);

    intP.x = aktP.x + k * d.x;
    intP.y = aktP.y + k * d.y;
    intP.z = aktP.z + k * d.z;

    ClipPoints[pc++] = intP;
  }

  for (i = 0; i < pc; i++)
    Points[i] = ClipPoints[i];

  if ((*PointCnt = pc) == 0)
    return;

  /********** clip polygon at "right" plane **********/

  pc = 0;

  firP = Points[0];
  fird = VX2->x * firP.x + VX2->y * firP.y + VX2->z * firP.z - DX2;

  if (fird <= 0.0)
    ClipPoints[pc++] = firP;

  aktP = preP = firP;
  aktd = pred = fird;

  for (i = 1; i < *PointCnt; i++)
  {
    aktP = Points[i];
    aktd = VX2->x * aktP.x + VX2->y * aktP.y + VX2->z * aktP.z - DX2;

    if (((aktd < 0.0) && (pred > 0.0)) || ((aktd > 0.0) && (pred < 0.0)))
    {
      d.x = preP.x - aktP.x;
      d.y = preP.y - aktP.y;
      d.z = preP.z - aktP.z;
      
      k = -aktd / (VX2->x * d.x + VX2->y * d.y + VX2->z * d.z);
      
      intP.x = aktP.x + k * d.x;
      intP.y = aktP.y + k * d.y;
      intP.z = aktP.z + k * d.z;

      ClipPoints[pc++] = intP;
    }

    if (aktd <= 0.0)
      ClipPoints[pc++] = aktP;

    preP = aktP;
    pred = aktd;
  }

  if (((fird < 0.0) && (aktd > 0.0)) || ((fird > 0.0) && (aktd < 0.0)))
  {
    d.x = firP.x - aktP.x;
    d.y = firP.y - aktP.y;
    d.z = firP.z - aktP.z;

    k = -aktd / (VX2->x * d.x + VX2->y * d.y + VX2->z * d.z);

    intP.x = aktP.x + k * d.x;
    intP.y = aktP.y + k * d.y;
    intP.z = aktP.z + k * d.z;

    ClipPoints[pc++] = intP;
  }

  for (i = 0; i < pc; i++)
    Points[i] = ClipPoints[i];

  if ((*PointCnt = pc) == 0)
    return;

  /********** clip polygon at "bottom" plane **********/

  pc = 0;

  firP = Points[0];
  fird = VY1->x * firP.x + VY1->y * firP.y + VY1->z * firP.z - DY1;

  if (fird <= 0.0)
    ClipPoints[pc++] = firP;

  aktP = preP = firP;
  aktd = pred = fird;

  for (i = 1; i < *PointCnt; i++)
  {
    aktP = Points[i];
    aktd = VY1->x * aktP.x + VY1->y * aktP.y + VY1->z * aktP.z - DY1;

    if (((aktd < 0.0) && (pred > 0.0)) || ((aktd > 0.0) && (pred < 0.0)))
    {
      d.x = preP.x - aktP.x;
      d.y = preP.y - aktP.y;
      d.z = preP.z - aktP.z;
      
      k = -aktd / (VY1->x * d.x + VY1->y * d.y + VY1->z * d.z);
      
      intP.x = aktP.x + k * d.x;
      intP.y = aktP.y + k * d.y;
      intP.z = aktP.z + k * d.z;

      ClipPoints[pc++] = intP;
    }

    if (aktd <= 0.0)
      ClipPoints[pc++] = aktP;

    preP = aktP;
    pred = aktd;
  }

  if (((fird < 0.0) && (aktd > 0.0)) || ((fird > 0.0) && (aktd < 0.0)))
  {
    d.x = firP.x - aktP.x;
    d.y = firP.y - aktP.y;
    d.z = firP.z - aktP.z;

    k = -aktd / (VY1->x * d.x + VY1->y * d.y + VY1->z * d.z);

    intP.x = aktP.x + k * d.x;
    intP.y = aktP.y + k * d.y;
    intP.z = aktP.z + k * d.z;

    ClipPoints[pc++] = intP;
  }

  for (i = 0; i < pc; i++)
    Points[i] = ClipPoints[i];

  if ((*PointCnt = pc) == 0)
    return;

  /********** clip polygon at "top" plane **********/

  pc = 0;

  firP = Points[0];
  fird = VY2->x * firP.x + VY2->y * firP.y + VY2->z * firP.z - DY2;

  if (fird <= 0.0)
    ClipPoints[pc++] = firP;

  aktP = preP = firP;
  aktd = pred = fird;

  for (i = pc = 0; i < *PointCnt; i++)
  {
    aktP = Points[i];
    aktd = VY2->x * aktP.x + VY2->y * aktP.y + VY2->z * aktP.z - DY2;

    if (((aktd < 0.0) && (pred > 0.0)) || ((aktd > 0.0) && (pred < 0.0)))
    {
      d.x = preP.x - aktP.x;
      d.y = preP.y - aktP.y;
      d.z = preP.z - aktP.z;
      
      k = -aktd / (VY2->x * d.x + VY2->y * d.y + VY2->z * d.z);
     
      intP.x = aktP.x + k * d.x;
      intP.y = aktP.y + k * d.y;
      intP.z = aktP.z + k * d.z;
      
      ClipPoints[pc++] = intP;
    }

    if (aktd <= 0.0)
      ClipPoints[pc++] = aktP;

    preP = aktP;
    pred = aktd;
  }

  if (((fird < 0.0) && (aktd > 0.0)) || ((fird > 0.0) && (aktd < 0.0)))
  {
    d.x = firP.x - aktP.x;
    d.y = firP.y - aktP.y;
    d.z = firP.z - aktP.z;

    k = -aktd / (VY2->x * d.x + VY2->y * d.y + VY2->z * d.z);

    intP.x = aktP.x + k * d.x;
    intP.y = aktP.y + k * d.y;
    intP.z = aktP.z + k * d.z;

    ClipPoints[pc++] = intP;
  }

  for (i = 0; i < pc; i++)
    Points[i] = ClipPoints[i];

  *PointCnt = pc;
}



/*****************************************************************************
*
* FUNCTION      : MInvers
*
* ARGUMENTS     : m - matrix to invert
*                 r - inverted matrix
*
* MODIFIED ARGS : r
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Invert a 4x4 matrix.
*
* CHANGES
*
*   -
*
******************************************************************************/

void MInvers(r, m)
MATRIX *r, *m;
{
  DBL d00, d01, d02, d03;
  DBL d10, d11, d12, d13;
  DBL d20, d21, d22, d23;
  DBL d30, d31, d32, d33;
  DBL m00, m01, m02, m03;
  DBL m10, m11, m12, m13;
  DBL m20, m21, m22, m23;
  DBL m30, m31, m32, m33;
  DBL D;

  m00 = (*m)[0][0];  m01 = (*m)[0][1];  m02 = (*m)[0][2];  m03 = (*m)[0][3];
  m10 = (*m)[1][0];  m11 = (*m)[1][1];  m12 = (*m)[1][2];  m13 = (*m)[1][3];
  m20 = (*m)[2][0];  m21 = (*m)[2][1];  m22 = (*m)[2][2];  m23 = (*m)[2][3];
  m30 = (*m)[3][0];  m31 = (*m)[3][1];  m32 = (*m)[3][2];  m33 = (*m)[3][3];

  d00 = m11*m22*m33 + m12*m23*m31 + m13*m21*m32 - m31*m22*m13 - m32*m23*m11 - m33*m21*m12;
  d01 = m10*m22*m33 + m12*m23*m30 + m13*m20*m32 - m30*m22*m13 - m32*m23*m10 - m33*m20*m12;
  d02 = m10*m21*m33 + m11*m23*m30 + m13*m20*m31 - m30*m21*m13 - m31*m23*m10 - m33*m20*m11;
  d03 = m10*m21*m32 + m11*m22*m30 + m12*m20*m31 - m30*m21*m12 - m31*m22*m10 - m32*m20*m11;

  d10 = m01*m22*m33 + m02*m23*m31 + m03*m21*m32 - m31*m22*m03 - m32*m23*m01 - m33*m21*m02;
  d11 = m00*m22*m33 + m02*m23*m30 + m03*m20*m32 - m30*m22*m03 - m32*m23*m00 - m33*m20*m02;
  d12 = m00*m21*m33 + m01*m23*m30 + m03*m20*m31 - m30*m21*m03 - m31*m23*m00 - m33*m20*m01;
  d13 = m00*m21*m32 + m01*m22*m30 + m02*m20*m31 - m30*m21*m02 - m31*m22*m00 - m32*m20*m01;

  d20 = m01*m12*m33 + m02*m13*m31 + m03*m11*m32 - m31*m12*m03 - m32*m13*m01 - m33*m11*m02;
  d21 = m00*m12*m33 + m02*m13*m30 + m03*m10*m32 - m30*m12*m03 - m32*m13*m00 - m33*m10*m02;
  d22 = m00*m11*m33 + m01*m13*m30 + m03*m10*m31 - m30*m11*m03 - m31*m13*m00 - m33*m10*m01;
  d23 = m00*m11*m32 + m01*m12*m30 + m02*m10*m31 - m30*m11*m02 - m31*m12*m00 - m32*m10*m01;

  d30 = m01*m12*m23 + m02*m13*m21 + m03*m11*m22 - m21*m12*m03 - m22*m13*m01 - m23*m11*m02;
  d31 = m00*m12*m23 + m02*m13*m20 + m03*m10*m22 - m20*m12*m03 - m22*m13*m00 - m23*m10*m02;
  d32 = m00*m11*m23 + m01*m13*m20 + m03*m10*m21 - m20*m11*m03 - m21*m13*m00 - m23*m10*m01;
  d33 = m00*m11*m22 + m01*m12*m20 + m02*m10*m21 - m20*m11*m02 - m21*m12*m00 - m22*m10*m01;

  D = m00*d00 - m01*d01 + m02*d02 - m03*d03;

  if (fabs(D) < EPSILON)
    Fatal_Error("Singular matrix in MInvers.\n");

  (*r)[0][0] = +d00/D; (*r)[0][1] = -d10/D;  (*r)[0][2] = +d20/D; (*r)[0][3] = -d30/D;
  (*r)[1][0] = -d01/D; (*r)[1][1] = +d11/D;  (*r)[1][2] = -d21/D; (*r)[1][3] = +d31/D;
  (*r)[2][0] = +d02/D; (*r)[2][1] = -d12/D;  (*r)[2][2] = +d22/D; (*r)[2][3] = -d32/D;
  (*r)[3][0] = -d03/D; (*r)[3][1] = +d13/D;  (*r)[3][2] = -d23/D; (*r)[3][3] = +d33/D;
}



/*****************************************************************************
*
* FUNCTION      : Fatal_Error
*
* ARGUMENTS     : str - String to print to stderr
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Print an error message and leave program.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Fatal_Error(str)
char *str;
{
  fputs(str, stderr);
  exit(1);
}



/*****************************************************************************
*
* FUNCTION      : Fatal_MAError
*
* ARGUMENTS     : str - String to print to stderr after out of memory message
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Print an error message and leave program. Call if memory
*   allocation fails.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Fatal_MAError(str)
char *str;
{
  fprintf(stderr, "Out of memory. Cannot allocate %s.\n", str);
  exit (1);
}



/*****************************************************************************
*
* FUNCTION      : Begin_Point
*
* ARGUMENTS     : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Print points to stderr and initialize point counter.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Begin_Point()
{
  fprintf(stderr, "...");
  Point_Counter = 0;
}



/*****************************************************************************
*
* FUNCTION      : Print_Point
*
* ARGUMENTS     : Repeat - Determines after how many calls a point will be printed
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Print points to stderr.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Print_Point(Repeat)
int Repeat;
{
  if ((Point_Counter % Repeat) == 0)
    fprintf(stderr, ".");
  Point_Counter++;
}



/*****************************************************************************
*
* FUNCTION      : End_Point
*
* ARGUMENTS     : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Print newline to stderr.
*
* CHANGES
*
*   -
*
******************************************************************************/

void End_Point()
{
  fprintf(stderr, "\n");
}



/*****************************************************************************
*
* FUNCTION      : Draw_Projection
*
* ARGUMENTS     : Project - projection to draw
*                 color   - Color to be used
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Draws a projection in the specified color.
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Draw_Projection(Project, color)
PROJECT *Project;
int color;
{
  int x, y, x1, x2, y1, y2;
  unsigned short int r, g, b;

  switch (color)
  {
    case RED   : r = 255; g = b = 0; break;
    case GREEN : g = 255; r = b = 0; break;
    case BLUE  : b = 255; r = g = 0; break;
    default    : r = g = b = 255;
  }

  x1 = Project->x1;
  x2 = Project->x2;
  y1 = Project->y1;
  y2 = Project->y2;

  if ((x1 <= x2) && (y1 <= y2))
  {
    if (x1 < 0) x1 = 0;
    if (x2 < 0) x2 = 0;
    if (y1 < 0) y1 = 0;
    if (y2 < 0) y2 = 0;

    if (x1 >= Frame.Screen_Width)  x1 = Frame.Screen_Width - 1;
    if (x2 >= Frame.Screen_Width)  x2 = Frame.Screen_Width - 1;
    if (y1 >= Frame.Screen_Height) y1 = Frame.Screen_Height - 1;
    if (y2 >= Frame.Screen_Height) y2 = Frame.Screen_Height - 1;

    for (x = x1; x <= x2; x++)
    {
      display_plot (x, y1, r, g, b);
      display_plot (x, y2, r, g, b);
    }
    for (y = y1; y <= y2; y++)
    {
      display_plot (x1, y, r, g, b);
      display_plot (x2, y, r, g, b);
    }
  }
}



/*****************************************************************************
*
* FUNCTION      : Draw_Vista
*
* ARGUMENTS     : Tree - current node/leaf in the vista tree
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Draws recursively all projections of subnodes in the current node.
*
* CHANGES
*
*   -
*
******************************************************************************/

static void Draw_Vista(Tree)
PROJECT_TREE_NODE *Tree;
{
  unsigned short i;
  PROJECT_TREE_LEAF *Leaf;

  if (Tree->is_leaf)
  {
    Leaf = (PROJECT_TREE_LEAF *)Tree;

    if (Leaf->Object->Type & COMPOUND_OBJECT)
    {
      Draw_Projection(&Leaf->Project, BLUE);
    }
    else
    {
      Draw_Projection(&Leaf->Project, RED);
    }
  }
  else
  {
    for (i = 0; i < Tree->Entries; i++)
    {
      Draw_Vista(Tree->Entry[i]);
    }
  }

  /* draw bounding object's vista */

/*
  Draw_Projection(&Tree->Project, GREEN);
*/
}



/*****************************************************************************
*
* FUNCTION      : Draw_Vista_Tree
*
* ARGUMENTS     : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE  : none
*
* AUTHOR        : Dieter Bayer, May, 1994
*
* DESCRIPTION
*
*   Draw the vista tree.
*
* CHANGES
*
*   -
*
******************************************************************************/

void Draw_Vista_Tree()
{
  if (Extended_Options & USE_VISTA_BUFFER)
  {
    Draw_Vista(Root_Vista);
  }
}



#endif

