// @(#)root/eve:$Id: TEveQuadSet.cxx 33864 2010-06-14 09:47:19Z matevz $
// Authors: Matevz Tadel & Alja Mrak-Tadel: 2006, 2007

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

#include "TEveQuadSet.h"

#include "TEveManager.h"

#include "TColor.h"

#include "TBuffer3D.h"
#include "TBuffer3DTypes.h"
#include "TVirtualPad.h"
#include "TVirtualViewer3D.h"

#include "TROOT.h"
#include "TRandom.h"

//______________________________________________________________________________
// TEveQuadSet
//
// Supports various internal formats that result in rendering of a
// set of planar (lines, rectangles, hegagons with shared normal) objects.
//
// Names of internal structures and their variables use A, B and C as
// names for coordinate value-holders. Typical assignment is A->X,
// B->Y, C->Z but each render mode can override this convention and
// impose y or x as a fixed (third or C) coordinate. Alphabetic order
// is obeyed in this correspondence.
//
// For quad modes the deltas are expected to be positive.
// For line modes negative deltas are ok.

ClassImp(TEveQuadSet);

//______________________________________________________________________________
TEveQuadSet::TEveQuadSet(const char* n, const char* t) :
   TEveDigitSet   (n, t),

   fQuadType  (kQT_Undef),
   fDefWidth  (1),
   fDefHeight (1),
   fDefCoord  (0)
{
   // Constructor.
}

//______________________________________________________________________________
TEveQuadSet::TEveQuadSet(EQuadType_e quadType, Bool_t valIsCol, Int_t chunkSize,
                         const char* n, const char* t) :
   TEveDigitSet   (n, t),

   fQuadType  (kQT_Undef),
   fDefWidth  (1),
   fDefHeight (1),
   fDefCoord  (0)
{
   // Constructor.

   Reset(quadType, valIsCol, chunkSize);
}

/******************************************************************************/

//______________________________________________________________________________
Int_t TEveQuadSet::SizeofAtom(TEveQuadSet::EQuadType_e qt)
{
   // Return size of given atom type.

   static const TEveException eH("TEveQuadSet::SizeofAtom ");

   switch (qt) {
      case kQT_Undef:                return 0;
      case kQT_FreeQuad:             return sizeof(QFreeQuad_t);
      case kQT_RectangleXY:
      case kQT_RectangleXZ:
      case kQT_RectangleYZ:          return sizeof(QRect_t);
      case kQT_RectangleXYFixedDim:  return sizeof(QRectFixDim_t);
      case kQT_RectangleXYFixedZ:
      case kQT_RectangleXZFixedY:
      case kQT_RectangleYZFixedX:    return sizeof(QRectFixC_t);
      case kQT_RectangleXYFixedDimZ:
      case kQT_RectangleXZFixedDimY:
      case kQT_RectangleYZFixedDimX: return sizeof(QRectFixDimC_t);
      case kQT_LineXZFixedY:
      case kQT_LineXYFixedZ:         return sizeof(QLineFixC_t);
      case kQT_HexagonXY:
      case kQT_HexagonYX:            return sizeof(QHex_t);
      default:                      throw(eH + "unexpected atom type.");
   }
   return 0;
}

/******************************************************************************/

//______________________________________________________________________________
void TEveQuadSet::Reset(TEveQuadSet::EQuadType_e quadType, Bool_t valIsCol,
                        Int_t chunkSize)
{
   // Clear the quad-set and reset the basic parameters.

   fQuadType     = quadType;
   fValueIsColor = valIsCol;
   fDefaultValue = valIsCol ? 0 : kMinInt;
   if (fOwnIds)
      ReleaseIds();
   fPlex.Reset(SizeofAtom(fQuadType), chunkSize);
}

/******************************************************************************/

//______________________________________________________________________________
void TEveQuadSet::AddQuad(Float_t verts[12])
{
   // Add a quad specified with 4 vertices.

   static const TEveException eH("TEveQuadSet::AddQuad ");

   if (fQuadType != kQT_FreeQuad)
      throw(eH + "expect free quad-type.");

   QFreeQuad_t* fq = (QFreeQuad_t*) NewDigit();
   if (verts != 0)
     memcpy(fq->fVertices, verts, sizeof(fq->fVertices));
}

//______________________________________________________________________________
void TEveQuadSet::AddQuad(Float_t a, Float_t b)
{
   // Add a quad with a and b coordinates. Defaults are applied for
   // c coordinate and sizes.

   AddQuad(a, b, fDefCoord, fDefWidth, fDefHeight);
}

//______________________________________________________________________________
void TEveQuadSet::AddQuad(Float_t a, Float_t b, Float_t c)
{
   // Add a quad with a, b and c coordinates. Defaults are applied
   // for sizes.

   AddQuad(a, b, c, fDefWidth, fDefHeight);
}

//______________________________________________________________________________
void TEveQuadSet::AddQuad(Float_t a, Float_t b, Float_t w, Float_t h)
{
   // Add a quad with a and b coordinates and sizes. Default is applied
   // for c coordinate.

   AddQuad(a, b, fDefCoord, w, h);
}

//______________________________________________________________________________
void TEveQuadSet::AddQuad(Float_t a, Float_t b, Float_t c, Float_t w, Float_t h)
{
   // Add a quad with a, b and c coordinates and sizes.

   static const TEveException eH("TEveQuadSet::AddAAQuad ");

   QOrigin_t& fq = * (QOrigin_t*) NewDigit();
   fq.fA = a; fq.fB = b;
   switch (fQuadType)
   {
      case kQT_RectangleXY:
      case kQT_RectangleXZ:
      case kQT_RectangleYZ:
      {
         QRect_t& q = (QRect_t&) fq;
         q.fC = c; q.fW = w; q.fH = h;
         break;
      }

      case kQT_RectangleXYFixedDim:
      {
         QRectFixDim_t& q =  (QRectFixDim_t&) fq;
         q.fC = c;
         break;
      }

      case kQT_RectangleXYFixedZ:
      case kQT_RectangleXZFixedY:
      case kQT_RectangleYZFixedX:
      {
         QRectFixC_t& q = (QRectFixC_t&) fq;
         q.fW = w; q.fH = h;
         break;
      }

      case kQT_RectangleXYFixedDimZ:
      case kQT_RectangleXZFixedDimY:
      case kQT_RectangleYZFixedDimX:
      {
         break;
      }

      default:
         throw(eH + "expect axis-aligned quad-type.");
   }
}

//______________________________________________________________________________
void TEveQuadSet::AddLine(Float_t a, Float_t b, Float_t w, Float_t h)
{
   // Add a line with starting coordinates and displacements.

   static const TEveException eH("TEveQuadSet::AddLine ");

   QOrigin_t& fq = * (QOrigin_t*) NewDigit();
   fq.fA = a; fq.fB = b;
   switch (fQuadType)
   {
      case kQT_LineXZFixedY:
      case kQT_LineXYFixedZ: {
         QLineFixC_t& q = (QLineFixC_t&) fq;
         q.fDx = w; q.fDy = h;
         break;
      }
      default:
         throw(eH + "expect line quad-type.");
   }
}

//______________________________________________________________________________
void TEveQuadSet::AddHexagon(Float_t a, Float_t b, Float_t c, Float_t r)
{
   // Add a hexagon with given center (a,b,c) and radius.

   static const TEveException eH("TEveQuadSet::AddHexagon ");

   QOrigin_t& fq = * (QOrigin_t*) NewDigit();
   fq.fA = a; fq.fB = b;
   switch (fQuadType)
   {
      case kQT_HexagonXY:
      case kQT_HexagonYX: {
         QHex_t& q = (QHex_t&) fq;
         q.fC = c; q.fR = r;
         break;
      }
      default:
         throw(eH + "expect line quad-type.");
   }
}

/******************************************************************************/

//______________________________________________________________________________
void TEveQuadSet::ComputeBBox()
{
   // Fill bounding-box information. Virtual from TAttBBox.
   // If member 'TEveFrameBox* fFrame' is set, frame's corners are
   // used as bbox.

   static const TEveException eH("TEveQuadSet::ComputeBBox ");

   if (fFrame != 0)
   {
      BBoxInit();
      Int_t    n    = fFrame->GetFrameSize() / 3;
      Float_t *bbps = fFrame->GetFramePoints();
      for (int i=0; i<n; ++i, bbps+=3)
         BBoxCheckPoint(bbps);
   }
   else
   {
      if(fPlex.Size() == 0) {
         BBoxZero();
         return;
      }

      BBoxInit();
      if (fQuadType == kQT_RectangleXYFixedZ    ||
          fQuadType == kQT_RectangleXYFixedDimZ)
      {
         fBBox[4] = fDefCoord;
         fBBox[5] = fDefCoord;
      }
      else if (fQuadType == kQT_RectangleXZFixedY    ||
               fQuadType == kQT_RectangleXZFixedDimY)
      {
         fBBox[2] = fDefCoord;
         fBBox[3] = fDefCoord;
      }
      else if (fQuadType == kQT_RectangleYZFixedX    ||
               fQuadType == kQT_RectangleYZFixedDimX)
      {
         fBBox[0] = fDefCoord;
         fBBox[1] = fDefCoord;
      }

      TEveChunkManager::iterator qi(fPlex);

      switch (fQuadType)
      {

         case kQT_FreeQuad:
         {
            while (qi.next()) {
               const Float_t* p =  ((QFreeQuad_t*) qi())->fVertices;
               BBoxCheckPoint(p); p += 3;
               BBoxCheckPoint(p); p += 3;
               BBoxCheckPoint(p); p += 3;
               BBoxCheckPoint(p);
            }
            break;
         }

         case kQT_RectangleXY:
         {
            while (qi.next()) {
               QRect_t& q = * (QRect_t*) qi();
               if(q.fA        < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + q.fW > fBBox[1]) fBBox[1] = q.fA + q.fW;
               if(q.fB        < fBBox[2]) fBBox[2] = q.fB;
               if(q.fB + q.fH > fBBox[3]) fBBox[3] = q.fB + q.fH;
               if(q.fC        < fBBox[4]) fBBox[4] = q.fC;
               if(q.fC        > fBBox[5]) fBBox[5] = q.fC;
            }
            break;
         }

         case kQT_RectangleXZ:
         {
            while (qi.next()) {
               QRect_t& q = * (QRect_t*) qi();
               if(q.fA        < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + q.fW > fBBox[1]) fBBox[1] = q.fA + q.fW;
               if(q.fB        < fBBox[4]) fBBox[4] = q.fB;
               if(q.fB + q.fH > fBBox[5]) fBBox[5] = q.fB + q.fH;
               if(q.fC        < fBBox[2]) fBBox[2] = q.fC;
               if(q.fC        > fBBox[3]) fBBox[3] = q.fC;
            }
            break;
         }

         case kQT_RectangleYZ:
         {
            while (qi.next()) {
               QRect_t& q = * (QRect_t*) qi();
               if(q.fA        < fBBox[2]) fBBox[2] = q.fA;
               if(q.fA + q.fW > fBBox[3]) fBBox[3] = q.fA + q.fW;
               if(q.fB        < fBBox[4]) fBBox[4] = q.fB;
               if(q.fB + q.fH > fBBox[5]) fBBox[5] = q.fB + q.fH;
               if(q.fC        < fBBox[0]) fBBox[0] = q.fC;
               if(q.fC        > fBBox[1]) fBBox[1] = q.fC;
            }
            break;
         }

         case kQT_RectangleXYFixedDim:
         {
            const Float_t& w = fDefWidth;
            const Float_t& h = fDefHeight;
            while (qi.next()) {
               QRectFixDim_t& q = * (QRectFixDim_t*) qi();
               if(q.fA     < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + w > fBBox[1]) fBBox[1] = q.fA + w;
               if(q.fB     < fBBox[2]) fBBox[2] = q.fB;
               if(q.fB + h > fBBox[3]) fBBox[3] = q.fB + h;
               if(q.fC     < fBBox[4]) fBBox[4] = q.fC;
               if(q.fC     > fBBox[5]) fBBox[5] = q.fC;
            }
            break;
         }

         case kQT_RectangleXYFixedZ:
         {
            while (qi.next()) {
               QRectFixC_t& q = * (QRectFixC_t*) qi();
               if(q.fA        < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + q.fW > fBBox[1]) fBBox[1] = q.fA + q.fW;
               if(q.fB        < fBBox[2]) fBBox[2] = q.fB;
               if(q.fB + q.fH > fBBox[3]) fBBox[3] = q.fB + q.fH;
            }
            break;
         }

         case kQT_RectangleXZFixedY:
         {
            while (qi.next()) {
               QRectFixC_t& q = * (QRectFixC_t*) qi();
               if(q.fA        < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + q.fW > fBBox[1]) fBBox[1] = q.fA + q.fW;
               if(q.fB        < fBBox[4]) fBBox[4] = q.fB;
               if(q.fB + q.fH > fBBox[5]) fBBox[5] = q.fB + q.fH;
            }
            break;
         }

         case kQT_RectangleYZFixedX:
         {
            while (qi.next()) {
               QRectFixC_t& q = * (QRectFixC_t*) qi();
               if(q.fA        < fBBox[2]) fBBox[2] = q.fA;
               if(q.fA + q.fW > fBBox[3]) fBBox[3] = q.fA + q.fW;
               if(q.fB        < fBBox[4]) fBBox[4] = q.fB;
               if(q.fB + q.fH > fBBox[5]) fBBox[5] = q.fB + q.fH;
            }
            break;
         }

         case kQT_RectangleXYFixedDimZ:
         {
            const Float_t& w = fDefWidth;
            const Float_t& h = fDefHeight;
            while (qi.next()) {
               QRectFixDimC_t& q = * (QRectFixDimC_t*) qi();
               if(q.fA     < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + w > fBBox[1]) fBBox[1] = q.fA + w;
               if(q.fB     < fBBox[2]) fBBox[2] = q.fB;
               if(q.fB + h > fBBox[3]) fBBox[3] = q.fB + h;
            }
            break;
         }

         case kQT_RectangleXZFixedDimY:
         {
            const Float_t& w = fDefWidth;
            const Float_t& h = fDefHeight;
            while (qi.next()) {
               QRectFixDimC_t& q = * (QRectFixDimC_t*) qi();
               if(q.fA     < fBBox[0]) fBBox[0] = q.fA;
               if(q.fA + w > fBBox[1]) fBBox[1] = q.fA + w;
               if(q.fB     < fBBox[4]) fBBox[4] = q.fB;
               if(q.fB + h > fBBox[5]) fBBox[5] = q.fB + h;
            }
            break;
         }

         case kQT_RectangleYZFixedDimX:
         {
            const Float_t& w = fDefWidth;
            const Float_t& h = fDefHeight;
            while (qi.next()) {
               QRectFixDimC_t& q = * (QRectFixDimC_t*) qi();
               if(q.fA     < fBBox[2]) fBBox[2] = q.fA;
               if(q.fA + w > fBBox[3]) fBBox[3] = q.fA + w;
               if(q.fB     < fBBox[4]) fBBox[4] = q.fB;
               if(q.fB + h > fBBox[5]) fBBox[5] = q.fB + h;
            }
            break;
         }

         // TEveLine modes

         case kQT_LineXYFixedZ:
         {
            while (qi.next()) {
               QLineFixC_t& q = * (QLineFixC_t*) qi();
               BBoxCheckPoint(q.fA,         q.fB,         fDefCoord);
               BBoxCheckPoint(q.fA + q.fDx, q.fB + q.fDy, fDefCoord);
            }
            break;
         }

         case kQT_LineXZFixedY:
         {
            while (qi.next()) {
               QLineFixC_t& q = * (QLineFixC_t*) qi();
               BBoxCheckPoint(q.fA,         fDefCoord, q.fB);
               BBoxCheckPoint(q.fA + q.fDx, fDefCoord, q.fB + q.fDy);
            }
            break;
         }

         // Hexagon modes

         // Ignore 'slight' difference, assume square box for both cases.
         case kQT_HexagonXY:
         case kQT_HexagonYX:
         {
            while (qi.next()) {
               QHex_t& q = * (QHex_t*) qi();
               BBoxCheckPoint(q.fA-q.fR, q.fB-q.fR, q.fC);
               BBoxCheckPoint(q.fA+q.fR, q.fB+q.fR, q.fC);
            }
            break;
         }

         default:
         {
            throw(eH + "unsupported quad-type.");
         }

      } // end switch quad-type
   } // end if frame ... else ...
}
