// @(#)root/gl:$Name:  $:$Id: TGLBoundingBox.h,v 1.5 2005/06/21 16:54:17 brun Exp $
// Author:  Richard Maunder  25/05/2005

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TGLBoundingBox
#define ROOT_TGLBoundingBox

#ifndef ROOT_TGLUtil
#include "TGLUtil.h"
#endif

/*************************************************************************
 * TGLBoundingBox - TODO
 *
 *
 *
 *************************************************************************/

// TODO: Create more compact version + axis aligned version, both with lazy
// sphere testing.
class TGLBoundingBox
{
private:
   // Fields

   // Box vertices are indexed thus:
   //
   //   7-------6
   //  /|      /|
   // 3-------2 |
   // | 4-----|-5
   // |/      |/
   // 0-------1
   //
   // 0123 near face
   // 4567 far face
   //
   // This could be more compact: 3 vertices which form plane cutting
   // box diagonally (e.g. 0,5,6 or 1,3,6 etc) would fix it in space - rest
   // could be calculated on demand - but not worth effort.....
   TGLVertex3 fVertex[8]; //! the 8 bounding box vertices
   Double_t   fVolume;    //! box volume - cached for speed

   // Methods
   void     UpdateVolume();
   Bool_t   ValidIndex(UInt_t index) const { return (index < 8); }
   Double_t Min(UInt_t index) const;
   Double_t Max(UInt_t index) const;

public:
   TGLBoundingBox();
   TGLBoundingBox(const TGLVertex3 vertex[8]);
   TGLBoundingBox(const Double_t vertex[8][3]);
   TGLBoundingBox(const TGLVertex3 & lowVertex, const TGLVertex3 & highVertex);
   TGLBoundingBox(const TGLBoundingBox & other);
   virtual ~TGLBoundingBox(); // ClassDef introduces virtual fns

   // Set orientated BB
   TGLBoundingBox & operator=(const TGLBoundingBox & other) { Set(other); return *this; }
   void Set(const TGLVertex3 vertex[8]);
   void Set(const Double_t vertex[8][3]);
   void Set(const TGLBoundingBox & other);
   void SetEmpty();

   // Set orientated AA
   void SetAligned(const TGLVertex3 & lowVertex, const TGLVertex3 & highVertex); // axis aligned
   void SetAligned(UInt_t nbPnts, const Double_t * pnts); // axis aligned

   void Transform(const TGLMatrix & matrix);
   void Scale(Double_t val);
   void Translate(const TGLVector3 & offset);

   inline const TGLVertex3 & operator [] (UInt_t index) const;
   Double_t XMin() const { return Min(0); }
   Double_t XMax() const { return Max(0); }
   Double_t YMin() const { return Min(1); }
   Double_t YMax() const { return Max(1); }
   Double_t ZMin() const { return Min(2); }
   Double_t ZMax() const { return Max(2); }

   inline TGLVertex3 Center() const;
   inline TGLVector3 Extents() const;
   inline TGLVector3 Axis(UInt_t i, Bool_t normalised = true) const;
   inline Double_t   Volume() const { return fVolume; }
   inline Bool_t     IsEmpty() const;

          EOverlap Overlap(const TGLPlane & plane) const;
          Bool_t   AlignedContains(const TGLBoundingBox & other) const; // we MUST be axis aligned
   static Bool_t   Intersect(const TGLBoundingBox & a, const TGLBoundingBox & b);

   void Draw() const;
   void Dump() const;

   ClassDef(TGLBoundingBox,0); // a 3D orientated bounding box
};

//______________________________________________________________________________
inline const TGLVertex3 & TGLBoundingBox::operator [] (UInt_t index) const
{
   if (ValidIndex(index)) {
      return fVertex[index];
   } else {
      assert(kFALSE);
      return fVertex[0];
   }
}

//______________________________________________________________________________
inline TGLVector3 TGLBoundingBox::Extents() const
{
   return TGLVector3(Axis(0,kFALSE).Mag(),
                     Axis(1,kFALSE).Mag(),
                     Axis(2,kFALSE).Mag());
}

//______________________________________________________________________________
inline TGLVertex3 TGLBoundingBox::Center() const
{
   TGLVector3 v = fVertex[6] - fVertex[0];
   return fVertex[0] + v/2.0;
}

//______________________________________________________________________________
inline TGLVector3 TGLBoundingBox::Axis(UInt_t i, Bool_t normalised) const
{
   //   7-------6
   //  /|      /|
   // 3-------2 |
   // | 4-----|-5
   // |/      |/
   // 0-------1

   TGLVector3 axis;
   if (i == 0) {
      axis.Set(fVertex[1] - fVertex[0]);
   } else if (i == 1) {
      axis.Set(fVertex[3] - fVertex[0]);
   } else if (i == 2) {
      axis.Set(fVertex[4] - fVertex[0]);
   } else {
      assert(kFALSE);
   }
   if (normalised) {
      axis.Normalise();
   }
   return axis;
}

//______________________________________________________________________________
inline void TGLBoundingBox::UpdateVolume()
{
   TGLVector3 extents = Extents();
   fVolume = fabs(extents.X() * extents.Y() * extents.Z());
}

inline Bool_t TGLBoundingBox::IsEmpty() const
{
   // TODO: Round errors - should have epsilon test
   return (Volume() == 0.0);
}

#endif // ROOT_TGLBoundingBox
