/******************************************************************************\

  Copyright 1995 The University of North Carolina at Chapel Hill.
  All Rights Reserved.

  Permission to use, copy, modify and distribute this software and its
  documentation for educational, research and non-profit purposes, without
  fee, and without a written agreement is hereby granted, provided that the
  above copyright notice and the following three paragraphs appear in all
  copies.

  IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA
  AT CHAPEL HILL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
  OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS
  SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
  OF NORTH CAROLINA HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.


  Permission to use, copy, modify and distribute this software and its
  documentation for educational, research and non-profit purposes, without
  fee, and without a written agreement is hereby granted, provided that the
  above copyright notice and the following three paragraphs appear in all
  copies.

  THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY
  DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED
  HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION
S
  TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

  The authors may be contacted via:

  US Mail:             J. Cohen/M. Lin/D. Manocha/K. Ponamgi
                       Department of Computer Science
                      Sitterson Hall, CB #3175
                       University of N. Carolina
                       Chapel Hill, NC 27599-3175

  Phone:               (919)962-1749

  EMail:              {cohenj,lin,manocha,ponamgi}@cs.unc.edu




\*****************************************************************************/


/*****************************************************************************\
  collision.c
  --
  Description :  This file contains the top-level routines for the collision
                 detection library. 

\*****************************************************************************/


/*----------------------------- Local Includes -----------------------------*/

#include <matrix.h>
#include <dist.h>
#include <collision.h>
#include <collision_types.h>
#include <math.h>
#include <stdio.h>
#include <options.h>

/*----------------------------- Local Constants -----------------------------*/


/*------------------------------ Local Macros -------------------------------*/

#define featTag(featurePtr) (*((int *) (featurePtr)))

/*------------------------------- Local Types -------------------------------*/


/*------------------------ Local Function Prototypes ------------------------*/

static int col_test_pair(__col_PairNode *pair);
static int InitPair(int id1, int id2);

static __col_Vertex *RandomVertex(__col_Polyhedron *polytope);

/*------------------------------ Local Globals ------------------------------*/

__col_State state;

/*---------------------------------Functions-------------------------------- */

__col_Polytope *col_get_polytope(int id)
{
    __col_Polytope *pt;

    pt = (__col_Polytope *) calloc(1, sizeof(__col_Polytope));
    pt = &(state.polytopes[id]);
    return pt;
}


/*****************************************************************************\
 @ col_open()
 -----------------------------------------------------------------------------
 description : Call to activate collision detection library
 input       : Maximum number of polytopes to be instanced, name of polytope
               data file, scale factor (1.0 to use the polytopes' original
	       scale), and an (int *) to get the number of polytopes loaded 
 output      : COL_OK or COL_ERROR is returned, and num_polytopes indicates
               the number of polytopes loaded from the polytope file 
 notes       : passing in -1 for max_polytopes uses the number of objects in
               the library as max_polytopes, indicating that the user plans to
	       instance each polytope no more than once.
\*****************************************************************************/
int col_open(int max_polytopes, char *filename,
	     double scale, int *num_polytopes)
{
    int i;
    int index, row, col, row2, col2;
    __col_PairNode *pair;


    if ( (*num_polytopes = __col_loadPolyhedronLibrary(filename, scale)) <= 0)
	return COL_ERROR;
    
    if (max_polytopes == -1)
	max_polytopes = *num_polytopes;
    
    /* max_polytopes must be even */
    max_polytopes += max_polytopes % 2;

    state.max_polytopes = max_polytopes;

    
    state.polytopes = (__col_Polytope *)calloc(max_polytopes,
					    sizeof(__col_Polytope));
    if (state.polytopes == COL_NULL)
    {
	fprintf(stderr, "col_open: couldn't allocate polytopes\n");
	return COL_ERROR;
    }
    
#ifdef COL_DENSE_MATRIX
    state.pairs = (__col_PairNode *)calloc(max_polytopes*(max_polytopes-1)/2,
				     sizeof(__col_PairNode));
#endif /* COL_DENSE_MATRIX */

#ifdef COL_SPARSE_MATRIX
    state.pairs = (__col_PairNode **)calloc(max_polytopes*(max_polytopes-1)/2,
				      sizeof(__col_PairNode *));
#endif /* COL_SPARSE_MATRIX */
    if (state.pairs == COL_NULL)
    {
	fprintf(stderr, "col_open: couldn't allocate matrix\n");
	return COL_ERROR;
    }
    state.matrix_rows = max_polytopes-1;
    state.matrix_cols = max_polytopes/2;

    state.overlap_status = (char *)calloc(max_polytopes*(max_polytopes-1)/2,
					  sizeof(char));
    if (state.overlap_status == COL_NULL)
    {
	fprintf(stderr, "col_open: couldn't allocate overlap status\n");
	return COL_ERROR;
    }
    
    state.num_polytopes = COL_NULL;
    state.active_list = COL_NULL;
    state.box_sort_list[__COL_X] = COL_NULL;
    state.box_sort_list[__COL_Y] = COL_NULL;
    state.box_sort_list[__COL_Z] = COL_NULL;
    state.nbody_active_list = COL_NULL;
    state.colliding_list = COL_NULL;
    state.using_nbody = COL_TRUE;

    return COL_OK;
} /** End of col_open() **/


/*****************************************************************************\
 @ col_enable_nbody()
 -----------------------------------------------------------------------------
 description : Enable the nbody algorithm
 input       : 
 output      : 
 notes       : The nbody algorithm is enabled by default
\*****************************************************************************/
int col_enable_nbody()
{
    int id;
    int row, col;
    
    if (state.using_nbody == COL_TRUE)
	return COL_OK;
    for (id = 0; id < state.num_polytopes; id++)
    {
	__col_update_bounding_box(&state.polytopes[id]);
#ifdef COL_LOCAL_SORT
	__col_local_sort(&state, &(state.polytopes[id]));
#endif
    }
    
    state.using_nbody = COL_TRUE;
    return COL_OK;
} /** End of col_enable_nbody() **/


/*****************************************************************************\
 @ col_disable_nbody()
 -----------------------------------------------------------------------------
 description : Disable the nbody algorithm - track the distance of all active
               pairs 
 input       : 
 output      : 
 notes       : The nbody algorithm is enabled by default
\*****************************************************************************/
int col_disable_nbody()
{
    int id;
    int row, col;
    __col_PairNode *pair;
    
    if (state.using_nbody == COL_FALSE)
	return COL_OK;

    state.using_nbody = COL_FALSE;
    return COL_OK;
} /** End of col_disable_nbody() **/



/*****************************************************************************\
 @ col_use_cuboid()
 -----------------------------------------------------------------------------
 description : Use a fixed bounding cube for a polytope rather than a
               dynamically resized bounding box
 input       : Polytope ID
 output      : 
 notes       : The default setting is dynamically resized bounding boxes
\*****************************************************************************/
int col_use_cuboid(int id)
{
    state.polytopes[id].box.use_cuboid = COL_TRUE;
    return COL_OK;
} /** End of col_use_cuboid() **/

/*****************************************************************************\
 @ col_use_dynamic_bounding_box()
 -----------------------------------------------------------------------------
 description : Use dynamic bounding boxes that change shape to fit the object
               as it moves around.
 input       : Polytope id number.
 output      : 
 notes       : These is the default setting for each instanced polytope
\*****************************************************************************/
int col_use_dynamic_bounding_box(int id)
{
    state.polytopes[id].box.use_cuboid = COL_FALSE;
    return COL_OK;
} /** End of col_use_dynamic_bounding_box() **/



/*****************************************************************************\
 @ RandomVertex()
 -----------------------------------------------------------------------------
 description : Choose a random vertex for a polytope
 input       : Polytope
 output      : A vertex of the polytope
 notes       : Used to choose an initial vertex for bounding box extrema
\*****************************************************************************/
static __col_Vertex *RandomVertex(__col_Polyhedron *polytope)
{
    col_Fnode *fn;
    int i, n;
    
    /* count number of vertices */
    for (fn = polytope->verts, n = 0; fn; n++, fn = fn->next);
    
    /* pick a random vertex */
    n = __col_randomInt(n);
    fn = polytope->verts;
    for (i = 0; i < n; i++) fn = fn->next;
    return fn->f.v;
    
} /** End of RandomVertex() **/


/*****************************************************************************\
 @ col_instance_polytope()
 -----------------------------------------------------------------------------
 description : Instance a polyhedron from the library.
 input       : name of library object to instance
 output      : id number to use when referencing the polytope
 notes       : 
\*****************************************************************************/
int col_instance_polytope(char *lib_object, int *id)
{
    char instance_name[64];
    int polynum;
    int i;
    __col_PairNode *pair;
    int axis;
    
    polynum = state.num_polytopes;
    
    if (polynum >= state.max_polytopes)
	return COL_ERROR;

    sprintf(instance_name, "%s_%d", lib_object, polynum);
    
    state.polytopes[polynum].id = polynum;
    state.polytopes[polynum].polytope =
	__col_createPolyhedron(lib_object, instance_name);
/*    dumpCones(state.polytopes[polynum].polytope);    */


    state.polytopes[polynum].box.use_cuboid = COL_FALSE;
    col_calc_cuboid(polynum, state.polytopes[polynum].box.center,
		    &(state.polytopes[polynum].box.radius));
    for (axis=0; axis<3; axis++)
    {
	state.polytopes[polynum].box.min[axis].id = polynum;
	state.polytopes[polynum].box.min[axis].min_max = 0;
	state.polytopes[polynum].box.min[axis].value = 0.0; /*bogus value*/
	state.polytopes[polynum].box.min[axis].vert =
	    RandomVertex(state.polytopes[polynum].polytope);	    
	
	state.polytopes[polynum].box.max[axis].id = polynum;
	state.polytopes[polynum].box.max[axis].min_max = 1;
	state.polytopes[polynum].box.max[axis].value = 0.0; /*bogus value*/
	state.polytopes[polynum].box.max[axis].vert =
	    RandomVertex(state.polytopes[polynum].polytope);

	
	state.polytopes[polynum].box.min[axis].prev = COL_NULL;
	state.polytopes[polynum].box.min[axis].next =
	    &state.polytopes[polynum].box.max[axis];
	state.polytopes[polynum].box.max[axis].prev =
	    &state.polytopes[polynum].box.min[axis];
	state.polytopes[polynum].box.max[axis].next =
	    state.box_sort_list[axis];
	if (state.box_sort_list[axis] != COL_NULL)
	    state.box_sort_list[axis]->prev =
		&state.polytopes[polynum].box.max[axis];
	state.box_sort_list[axis] = &state.polytopes[polynum].box.min[axis];
    }

    
#ifdef COL_DENSE_MATRIX    
    for (i=0; i < polynum; i++)
	if (InitPair(i, polynum) == COL_ERROR)
	    return COL_ERROR;
#endif /* COL_DENSE_MATRIX */
    
    state.num_polytopes++;
    *id = polynum;

#ifdef COL_LOCAL_SORT
    __col_local_sort(&state, &state.polytopes[polynum]);
#endif
    
    return COL_OK;
} /** End of col_instance_polytope() **/

/*****************************************************************************\
 @ InitPair()
 -----------------------------------------------------------------------------
 description : 
 input       : 
 output      : 
 notes       : for the moment, the assignment of initial closest features
               assumes that id1 and id2 are in the correct order (id1 < id2)
\*****************************************************************************/
static int InitPair(int id1, int id2)
{
    __col_PairNode *pair;

    __COL_ALLOCATE_PAIR(state, id1, id2, pair);
    
    pair->distance = __COL_INFINITY;
    pair->active = COL_FALSE;
    pair->colliding = COL_FALSE;
    pair->next_active = COL_NULL;
    pair->prev_nbody_active = COL_NULL;
    pair->next_nbody_active = COL_NULL;
    pair->prev_colliding = COL_NULL;
    pair->next_colliding = COL_NULL;
    
    pair->closest_features[0] =
	__col_randFeat(state.polytopes[id1].polytope);
    pair->closest_features[1] =
	__col_randFeat(state.polytopes[id2].polytope);
    return COL_OK;
} /** End of InitPair() **/




/*****************************************************************************\
 @ col_instance_polytope_by_num()
 -----------------------------------------------------------------------------
 description : Instance a polytope by index within the library rather than by
               the object's name 
 input       : 
 output      : 
 notes       :
\*****************************************************************************/
int col_instance_polytope_by_num(int poly_num, int *id)
{
  extern __col_Polyhedron  __col_polyhedronLibrary[];

  return col_instance_polytope(__col_polyhedronLibrary[poly_num].name,
				       id);
} /** End of col_instance_polytope_by_num() **/




/*****************************************************************************\
 @ col_calc_cuboid()
 -----------------------------------------------------------------------------
 description : Calculates the center of the instantiated polytopes.  This is
	       used for determining a collision reaction (for example with a
	       boundry box).
 input       : 
 output      : A point inside the polytope and the maximum distance from
               this point to any vertex.
 notes       : It currently computes a point inside the polytope -- the
               unweighted average of the vertices.  This isn't the
               center of mass.
\*****************************************************************************/
int col_calc_cuboid(int poly_id, double *box_center, double *box_radius)
{
  int           counter=0;
  __col_Polyhedron    *p;
  col_Fnode         *verts;
  double        length_squared, max_length_squared;
  double        vec[3];
  
  box_center[__COL_X] = 0.;
  box_center[__COL_Y] = 0.;
  box_center[__COL_Z] = 0.;
  p = state.polytopes[poly_id].polytope;

  /*
     Calculate the center for a bounding cube - the center of mass might be a
     better choice, but for now we take the unweighted average of vertex
     coordinates, which is at least guaranteed to be inside the convex
     polytope.
  */
  
  for (verts= p->verts; verts; verts= verts->next)
  {
      box_center[__COL_X] += verts->f.v->coords[__COL_X];
      box_center[__COL_Y] += verts->f.v->coords[__COL_Y];
      box_center[__COL_Z] += verts->f.v->coords[__COL_Z];
      ++counter;
  }

  box_center[__COL_X] /= counter;
  box_center[__COL_Y] /= counter;
  box_center[__COL_Z] /= counter;

  /*
     Now find the box "radius", which will be the maximum distance from the box
     center to any vertex.  This "radius" will be used as the half of the box
     width, so the object should fit into this box at any orientation.
  */
  
  max_length_squared = 0.0;
  for (verts = p->verts; verts; verts = verts->next)
  {
      vec[__COL_X] = box_center[__COL_X] - verts->f.v->coords[__COL_X];
      vec[__COL_Y] = box_center[__COL_Y] - verts->f.v->coords[__COL_Y];
      vec[__COL_Z] = box_center[__COL_Z] - verts->f.v->coords[__COL_Z];
      length_squared = vec[__COL_X]*vec[__COL_X] + vec[__COL_Y]*vec[__COL_Y] +
	  vec[__COL_Z]+vec[__COL_Z] ;
      if (length_squared > max_length_squared)
	  max_length_squared = length_squared;
  }
  *box_radius = (float)sqrt(max_length_squared);
  *box_radius *= (COL_CUBOID_BUFFER_PERCENT + 100.0)/100.0;

  return COL_OK;
  
} /** End of col_calc_cuboid() **/



/*****************************************************************************\
 @ col_set_transform
 -----------------------------------------------------------------------------
 description : Set the current transformation for a polytope
 input       : Polytope id, 4x4 transformation matrix
 output      : 
 notes       : The matrix should be a 4x4 used to premultiply a
               column vector. 
\*****************************************************************************/
int col_set_transform(int id, col_Mat4 matrix)
{
    col_Mat4 pose;
    int i, j;
    col_Mat4 inv_pose;

#ifdef ALLOW_SCALING_IN_POSE
    __col_matInvertArbitraryXform(matrix,
				  state.polytopes[id].polytope->inv_pose);
#else
    __col_matInvertXform(matrix, state.polytopes[id].polytope->inv_pose);   
#endif
    __col_mat4Copy(matrix, state.polytopes[id].polytope->pose);
    if (state.using_nbody == COL_TRUE)
    {
	__col_update_bounding_box(&state.polytopes[id]);
#ifdef COL_LOCAL_SORT
	__col_local_sort(&state, &(state.polytopes[id]));
#endif
    }
    
    return COL_OK;
} /** End of col_set_transform() **/



/*****************************************************************************\
 @ col_activate_pair()
 -----------------------------------------------------------------------------
 description : Activate a pair of polytopes for collision detection
 input       : IDs of the polytopes to be activated
 output      : 
 notes       :
\*****************************************************************************/
int col_activate_pair(int id1, int id2)
{
    int first_index, second_index;
    __col_PairNode *pair;
    char overlapping;
    
    __COL_GET_PAIR(state, id1, id2, pair);

#ifdef COL_SPARSE_MATRIX
    if (!(pair))
	if (InitPair(id1, id2) == COL_ERROR)
	    return COL_ERROR;
    __COL_GET_PAIR(state, id1, id2, pair);
#endif /* COL_SPARSE_MATRIX */

    if (__COL_PAIR_ACTIVE(pair) == COL_TRUE)
	return COL_OK;
    pair->active = COL_TRUE;
    pair->next_active = state.active_list;
    state.active_list = pair;

    __COL_OVERLAPPING(state, id1, id2, overlapping);
    if ((state.using_nbody == COL_TRUE) && overlapping)
	__col_AddPair(&state, pair);

    return COL_OK;
} /** End of col_activate_pair() **/

/*****************************************************************************\
 @ col_activate_full()
 -----------------------------------------------------------------------------
 description : Activate collision detection for one polytope with all the
               other polytopes
 input       : ID of polytope to activate with all others
 output      : 
 notes       :
\*****************************************************************************/
int col_activate_full(int id)
{
    int count;

    for (count=0; count < state.num_polytopes; count++)
	if (count != id)
	    col_activate_pair(id, count);
    return COL_OK;
} /** End of col_activate_full() **/

/*****************************************************************************\
 @ col_activate_all()
 -----------------------------------------------------------------------------
 description : Activate all pairs of polytopes for collision detection
 input       : 
 output      : 
 notes       :
\*****************************************************************************/
int col_activate_all()
{
    int row, col;

    for (row=0; row < state.num_polytopes; row++)
	for (col=row+1; col<state.num_polytopes; col++)
	    col_activate_pair(row, col);

    return COL_OK;
} /** End of col_activate_all() **/


/*****************************************************************************\
 @ col_deactivate_pair()
 -----------------------------------------------------------------------------
 description : Deactivate collision detection for a pair of polytopes.
 input       : 
 output      : 
 notes       :
\*****************************************************************************/
int col_deactivate_pair(int id1, int id2)
{
    int first_index, second_index;
    __col_PairNode *pair, *prev_pair;
    char overlapping;
    
    __COL_GET_PAIR(state, id1, id2, pair);
    if (__COL_PAIR_ACTIVE(pair) == COL_FALSE)
	return COL_OK;
    pair->active = COL_FALSE;
    if (state.active_list == pair)
	state.active_list = pair->next_active;
    else
    {
	prev_pair = state.active_list;
	while ((prev_pair->next_active != COL_NULL) &&
	       (prev_pair->next_active != pair))
	    prev_pair = prev_pair->next_active;
	if (prev_pair->next_active == COL_NULL)
	    return COL_ERROR;
	prev_pair->next_active = pair->next_active;
    }

    __COL_OVERLAPPING(state, id1, id2, overlapping);
    if ((state.using_nbody == COL_TRUE) && overlapping)
	__col_RemovePair(&state, pair);
    
    return COL_OK;
} /** End of col_deactivate_pair() **/


/*****************************************************************************\
 @ col_deactivate_full()
 -----------------------------------------------------------------------------
 description : Deactivate collision detection for one polytope with all the
               other polytopes
 input       : ID of polytope to deactivate with all others
 output      : 
 notes       :
\*****************************************************************************/
int col_deactivate_full(int id)
{
    int count;

    for (count=0; count < state.num_polytopes; count++)
	if (count != id)
	    col_deactivate_pair(id, count);
    return COL_OK;
} /** End of col_deactivate_full() **/

/*****************************************************************************\
 @ col_deactivate_all()
 -----------------------------------------------------------------------------
 description : Dectivate all pairs of polytopes for collision detection
 input       : 
 output      : 
 notes       :
\*****************************************************************************/
int col_deactivate_all()
{
    int row, col;

    for (row=0; row < state.num_polytopes; row++)
	for (col=row+1; col<state.num_polytopes; col++)
	    col_deactivate_pair(row, col);
    return COL_OK;
} /** End of col_deactivate_all() **/


/*****************************************************************************\
 @ col_test()
 -----------------------------------------------------------------------------
 description : Do the main collision test for the current set of object
               positions and active pairs
 input       : 
 output      : 
 notes       :
\*****************************************************************************/
int col_test()
{
    __col_PairNode *pair;
    int i, j;

    if (state.using_nbody == COL_FALSE)
	pair = state.active_list;
    else
    {
#ifdef COL_GLOBAL_SORT
	__col_nbody(&state);
#endif
	pair = state.nbody_active_list;
    }
    
    while (pair != COL_NULL)
    {
        __COL_PAIR_ROW_COL(state, pair, i, j);
	if (col_test_pair(pair) == COL_ERROR)
	    return COL_ERROR;
	if (pair->distance <= COL_COLLIDING_DISTANCE)
	{
	    if (pair->colliding == COL_FALSE)
	    {
		pair->next_colliding = state.colliding_list;
		if (pair->next_colliding != COL_NULL)
		    pair->next_colliding->prev_colliding = pair;
		state.colliding_list = pair;
		pair->colliding = COL_TRUE;
	    }
	}
	else if (pair->colliding == COL_TRUE)
	{
	    if (pair->prev_colliding != COL_NULL)
		pair->prev_colliding->next_colliding =
		    pair->next_colliding;
	    else
		state.colliding_list = pair->next_colliding;
	    if (pair->next_colliding != COL_NULL)
		pair->next_colliding->prev_colliding =
		    pair->prev_colliding;
	    pair->prev_colliding = pair->next_colliding = COL_NULL;
	    pair->colliding = COL_FALSE;
	}

	if (state.using_nbody == COL_FALSE)
	    pair = pair->next_active;
	else
	    pair = pair->next_nbody_active;
    }
    return COL_OK;
} /** End of col_test() **/

/*****************************************************************************\
 @ col_test_pair()
 -----------------------------------------------------------------------------
 description : Test a pair of objects for collision
 input       : 
 output      : 
 notes       : This routine isn't callable from the top-level.  It will
               normally only be called for active pairs with overlapping
	       bounding boxes.
\*****************************************************************************/
static int col_test_pair(__col_PairNode  *pair)
{
    int row, col;
    void     **feat;
    __COL_PAIR_ROW_COL(state, pair, row, col);
 /*   dumpPolyhedron(state.polytopes[row].polytope);
    dumpPolyhedron(state.polytopes[col].polytope);   */
    pair->distance =
	__col_closestFeatures(state.polytopes[row].polytope,
			&(pair->closest_features[0]),
			state.polytopes[col].polytope,
			&(pair->closest_features[1]));
    return COL_OK;
    
} /** End of col_test_pair() **/


/*****************************************************************************\
 @ col_report_collision_pair()
 -----------------------------------------------------------------------------
 description : Checks to see if a pair was colliding in the last collision test.
 input       : Two ids and the address of a col_Report to be filled in
 output      : return valud will be COL_COLLIDING if colliding,
               COL_NOT_COLLIDING if not colliding, or COL_UNKNOWN if the pair
	       isn't active.  If the return value is COL_COLLIDING, the report
	       will be filled in for that pair
 notes       :
\*****************************************************************************/
int col_report_collision_pair(int id1, int id2, col_Report *report)
{
    __col_PairNode *pair;
    int row, col;
    
    __COL_GET_PAIR(state, id1, id2, pair);
    if (__COL_PAIR_ACTIVE(pair) == COL_FALSE)
    {
	report->ids[0] = report->ids[1] = -1;
	report->features[0] = report->features[1] = COL_NULL;
	return COL_UNKNOWN;
    }
    else if (pair->distance > COL_COLLIDING_DISTANCE)
    {
	report->ids[0] = report->ids[1] = -1;
	report->features[0] = report->features[1] = COL_NULL;
	return COL_NOT_COLLIDING;
    }
    else
    {
	__COL_PAIR_ROW_COL(state, pair, row, col);
	report->ids[0] = row;
	report->ids[1] = col;
	report->features[0] = pair->closest_features[0];
	report->features[1] = pair->closest_features[1];
	return COL_COLLIDING;
    }
} /** End of col_report_pair() **/

/*****************************************************************************\
 @ col_report_collision_full()
 -----------------------------------------------------------------------------
 description : Report all collisions from the previous test which involve one
               particular polytope 
 input       : ID of the polytope to be tested, an array of col_Reports, and
               the number of elements in the array
 output      : The return value indicates the number of collisions found and
               the first report_size collisions are report in the report
	       array.
 notes       : 
\*****************************************************************************/
int col_report_collision_full(int id, col_Report *report, int report_size)
{
    __col_PairNode *pair;
    int num_colliding;
    int row, col;

    pair = state.colliding_list;
    num_colliding = 0;

    while (pair != COL_NULL)
    {
	__COL_PAIR_ROW_COL(state, pair, row, col);
	if ((row == id) || (col == id))
	{
	    if (num_colliding < report_size)
	    {
		report[num_colliding].ids[0] = row;
		report[num_colliding].ids[1] = col;
		report[num_colliding].features[0] =
		    pair->closest_features[0];
		report[num_colliding].features[1] =
		    pair->closest_features[1];
	    }
	    num_colliding++;
	}
	pair = pair->next_colliding;
    }
    return num_colliding;
} /** End of col_report_collision_full() **/


/*****************************************************************************\
 @ col_report_collision_all()
 -----------------------------------------------------------------------------
 description : Report all collisions from the most recent test
 input       : Array of col_Reports and the number of elements in the array
 output      : The return value indicates the number of collisions, and the
               first report_size collisions are reported in the report array 
 notes       : 
\*****************************************************************************/
int col_report_collision_all(col_Report *report, int report_size)
{
    __col_PairNode *pair;
    int num_colliding;
    int row, col;
    
    num_colliding = 0;
    pair = state.colliding_list;
    while(pair != COL_NULL)
    {
	if (num_colliding < report_size)
	{
	    __COL_PAIR_ROW_COL(state, pair, row, col);
	    report[num_colliding].ids[0] = row;
	    report[num_colliding].ids[1] = col;
	    report[num_colliding].features[0] =
		pair->closest_features[0];
	    report[num_colliding].features[1] =
		pair->closest_features[1];
	}
	num_colliding++;
	pair = pair->next_colliding;
    }
    return num_colliding;
} /** End of col_report_collision_all() **/


/*****************************************************************************\
 @ col_report_distance_pair()
 -----------------------------------------------------------------------------
 description : Return the distance between a pair of polytopes and their
               closest features, if known
 input       : The ids of the polytopes in question and a pointer to a
               col_DistReport to be filled in.
 output      : The return value is COL_UNKNOWN if the pair is inactive or
               non-overlapping.  Otherwise, the return value is COL_OK and the
	       report structure is filled in.
 notes       :
\*****************************************************************************/
int col_report_distance_pair(int id1, int id2, col_DistReport *report)
{
    __col_PairNode *pair;
    int row, col;
    char overlapping;
    
    __COL_GET_PAIR(state, id1, id2, pair);
    __COL_OVERLAPPING(state, id1, id2, overlapping);
    if ((__COL_PAIR_ACTIVE(pair) == COL_FALSE) ||
	((state.using_nbody == COL_TRUE) &&
	 (!(overlapping))))
    {
	return COL_UNKNOWN;
    }
    else
    {
	__COL_PAIR_ROW_COL(state, pair, row, col);
	report->ids[0] = row;
	report->ids[1] = col;
	report->features[0] = pair->closest_features[0];
	report->features[1] = pair->closest_features[1];
	report->distance = pair->distance;
    }
    
    return COL_OK;
} /** End of col_report_distance_pair() **/

/*****************************************************************************\
 @ col_report_distance_full()
 -----------------------------------------------------------------------------
 description : Report known distances from one particular polytope with all
               other polytopes 
 input       : Id of polytope in question, array of col_DistReports, and
               number of elements in the array
 output      : The return value is the number of distances known, and the
               first report_size of these are reported in the report array. 
 notes       :
\*****************************************************************************/
int col_report_distance_full(int id, col_DistReport *report,
			     int report_size)
{
    __col_PairNode *pair;
    int distance_count;
    int row, col;

    if (state.using_nbody == COL_TRUE)
	pair = state.nbody_active_list;
    else
	pair = state.active_list;
    
    distance_count = 0;

    while (pair != COL_NULL)
    {
	__COL_PAIR_ROW_COL(state, pair, row, col);
	if ((row == id) || (col == id))
	{
	    if (distance_count < report_size)
	    {
		report[distance_count].ids[0] = row;
		report[distance_count].ids[1] = col;
		report[distance_count].features[0] =
		    pair->closest_features[0];
		report[distance_count].features[1] =
		    pair->closest_features[1];
		report[distance_count].distance = pair->distance;
	    }
	    distance_count++;
	}

	if (state.using_nbody == COL_TRUE)
	    pair = pair->next_nbody_active;
	else
	    pair = pair->next_active;
    }
    return distance_count;
} /** End of col_report_distance_full() **/

/*****************************************************************************\
 @ col_report_distance_all()
 -----------------------------------------------------------------------------
 description : Report all known distances and closest features between pairs
               of polytopes.
 input       : Array of col_DistReports and number of elements in the array
 output      : The return value is the number of distances known, and the
               first report_size of these are returned in the report array. 
 notes       :
\*****************************************************************************/
int col_report_distance_all(col_DistReport *report, int report_size)
{
    __col_PairNode *pair;
    int distance_count;
    int row, col;
    
    if (state.using_nbody == COL_TRUE)
	pair = state.nbody_active_list;
    else
	pair = state.active_list;

    distance_count = 0;

    while(pair != COL_NULL)
    {
	if (distance_count < report_size)
	{
	    __COL_PAIR_ROW_COL(state, pair, row, col);
	    report[distance_count].ids[0] = row;
	    report[distance_count].ids[1] = col;
	    report[distance_count].features[0] =
		pair->closest_features[0];
	    report[distance_count].features[1] =
		pair->closest_features[1];
	    report[distance_count].distance = pair->distance;
	}
	distance_count++;
	
	pair = pair->next_colliding;
    }
    return distance_count;
} /** End of col_report_distance_all() **/

/*****************************************************************************\
\*****************************************************************************/


