/*
 * Hull.C - methods for hull manipulations.
 *
 * Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
 *                     University of Berne, Switzerland
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#include <values.h>
#include "Hull.h"

//___________________________________________________________ Hull

implementTable(HullSymtab, rcString, HullPtr);

Hull::Hull()
: primitives(new GeoObjectList(10))
{}

Hull::~Hull()
{
#ifndef OLD_STYLE_CPP
  // AT&T 2.x has problems: too compilcated pointer expression
  for (register long i=0; i<primitives->count(); i++)
    delete primitives->item(i);
#endif

  delete primitives;
}

void Hull::addPrimitive(GeoObject* obj)
{
  primitives->append(obj);
}

long Hull::numPrimitives() const
{
  return primitives->count();
}

/*
 * Intersect ray with hull primitives and calculate closest object
 * and distance to this object.
 * intersect return TRUE if hit occured, FALSE otherwise.
 */

int Hull::intersect(const Ray& ray, real& dist, GeoObject*& obj) const
{
  int hit = FALSE;
  TransMatrix* itrans;

  dist = MAXFLOAT;
  obj = NULL;

  for (register long i=0; i<primitives->count(); i++) {

    /*
     * primitive has transformation matrix attached 
     *  -> transform the ray
     */
    if ((itrans = primitives->item(i)->getInvTrans()) != NULL) {
      Ray newRay(ray);
      real distfact = newRay.transform(*itrans);

      /*
       * Transformimg the ray can change the distance between the
       * ray origin and the Point of intersection. We save the amount
       * the is "stretched" and later devide the computed distance
       * by this amount.
       */
      dist *= distfact;
      if (primitives->item(i)->intersect(newRay, EPSILON*distfact, dist)) {
	obj = primitives->item(i);
	hit = TRUE;
      }
      dist /= distfact;
    }
    else {
      if (primitives->item(i)->intersect(ray, EPSILON, dist)) {
	obj = primitives->item(i);
	hit = TRUE;
      }
    }
  }
  return hit;
}

/*
 * convertToPolygonList tesselates all the primitives in the hull 
 * and returns the result as a list of polygons.
 * If there is no polygon in the hull, a empty polygon list is
 * returned.
 */

PolygonList* Hull::convertToPolygonList(const BoundingBox& bbox) const
{
  PolygonList* polys = new PolygonList;
  PolygonList* polytmp;
  
  for (register long i=0; i<primitives->count(); i++) {
    if ((polytmp = primitives->item(i)->tesselate(bbox)) != NULL) {
      for (register long j=0; j<polytmp->count(); j++)
	polys->append(polytmp->item(j));
      delete polytmp;
    }
  }

  return polys;
}
