ROOT logo
// @(#)root/gl:$Id: TGLCamera.cxx 27369 2009-02-06 17:35:54Z matevz $
// Author:  Richard Maunder  25/05/2005

/*************************************************************************
 * Copyright (C) 1995-2000, 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 "TGLCamera.h"
#include "TGLIncludes.h"
#include "TGLBoundingBox.h"
#include "TError.h"
#include "TMath.h"

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGLCamera                                                            //
//                                                                      //
// Abstract base camera class - concrete classes for orthographic and   //
// persepctive cameras derive from it. This class maintains values for  //
// the current:                                                         //
// i)   Viewport                                                        //
// ii)  Projection, modelview and clip matricies - extracted from GL    //
// iii) The 6 frustum planes                                            //
// iv)  Expanded frustum interest box                                   //
//                                                                      //
// It provides methods for various projection, overlap and intersection //
// tests for viewport and world locations, against the true frustum and //
// expanded interest box, and for extracting eye position and direction.//
//                                                                      //
// It also defines the pure virtual manipulation interface methods the  //
// concrete ortho and prespective classes must implement.               //
//////////////////////////////////////////////////////////////////////////

ClassImp(TGLCamera)

const Double_t TGLCamera::fgInterestBoxExpansion = 1.3;
UInt_t         TGLCamera::fgDollyDeltaSens       = 500;

//______________________________________________________________________________
TGLCamera::TGLCamera() :
   fExternalCenter(kFALSE),
   fCenter(&fDefCenter),
   fNearClip(0), fFarClip(0),
   fDollyDefault(1.0), fDollyDistance(1.0),
   fVAxisMinAngle(0.01f),
   fCacheDirty(kTRUE),
   fTimeStamp (1),
   fProjM(), fModVM(), fClipM(),
   fViewport(0,0,100,100),
   fLargestSeen(0.0)
{
   // Default base camera constructor
   for (UInt_t i = 0; i < kPlanesPerFrustum; i++ ) {
      fFrustumPlanes[i].Set(1.0, 0.0, 0.0, 0.0);
   }
   TGLVertex3 origin;
   fCamBase.Set(origin, TGLVector3(1, 0, 0), TGLVector3(0, 0, 1));
}

//______________________________________________________________________________
TGLCamera::TGLCamera(const TGLVector3 & hAxis, const TGLVector3 & vAxis) :
   fExternalCenter(kFALSE),
   fCenter(&fDefCenter),
   fNearClip(0), fFarClip(0),
   fDollyDefault(1.0), fDollyDistance(1.0),
   fVAxisMinAngle(0.01f),
   fCacheDirty(kTRUE),
   fTimeStamp (1),
   fProjM(), fModVM(), fClipM(),
   fViewport(0,0,100,100),
   fLargestSeen(0.0)
{
   // Default base camera constructor
   for (UInt_t i = 0; i < kPlanesPerFrustum; i++ ) {
      fFrustumPlanes[i].Set(1.0, 0.0, 0.0, 0.0);
   }
   TGLVertex3 origin;
   fCamBase.Set(origin, vAxis, hAxis);
}

//______________________________________________________________________________
TGLCamera::~TGLCamera()
{
   // Base camera destructor.
}

//______________________________________________________________________________
void TGLCamera::SetViewport(const TGLRect & viewport)
{
   // Set viewport extents from passed 'viewport' rect.

   fViewport = viewport;
   IncTimeStamp();
}

//______________________________________________________________________________
void TGLCamera::UpdateCache() const
{
   // Update internally cached frustum values
   assert(fCacheDirty);

   glGetDoublev(GL_PROJECTION_MATRIX, fProjM.Arr());
   glGetDoublev(GL_MODELVIEW_MATRIX, fModVM.Arr());

   // Multiply projection by modelview to get the clip matrix
   // TODO: Move this into TGLMatrix or shift all over to ROOT ones
   fClipM  = fProjM;
   fClipM *= fModVM;

   // RIGHT clipping plane
   fFrustumPlanes[kRight].Set(fClipM[ 3] - fClipM[ 0],
                              fClipM[ 7] - fClipM[ 4],
                              fClipM[11] - fClipM[ 8],
                              fClipM[15] - fClipM[12]);

   // LEFT clipping plane
   fFrustumPlanes[kLeft].Set(fClipM[ 3] + fClipM[ 0],
                             fClipM[ 7] + fClipM[ 4],
                             fClipM[11] + fClipM[ 8],
                             fClipM[15] + fClipM[12]);

   // BOTTOM clipping plane
   fFrustumPlanes[kBottom].Set(fClipM[ 3] + fClipM[ 1],
                               fClipM[ 7] + fClipM[ 5],
                               fClipM[11] + fClipM[ 9],
                               fClipM[15] + fClipM[13]);


   // TOP clipping plane
   fFrustumPlanes[kTop].Set(fClipM[ 3] - fClipM[ 1],
                            fClipM[ 7] - fClipM[ 5],
                            fClipM[11] - fClipM[ 9],
                            fClipM[15] - fClipM[13]);

   // FAR clipping plane
   fFrustumPlanes[kFar].Set(fClipM[ 3] - fClipM[ 2],
                            fClipM[ 7] - fClipM[ 6],
                            fClipM[11] - fClipM[10],
                            fClipM[15] - fClipM[14]);

   // NEAR clipping plane
   fFrustumPlanes[kNear].Set(fClipM[ 3] + fClipM[ 2],
                             fClipM[ 7] + fClipM[ 6],
                             fClipM[11] + fClipM[10],
                             fClipM[15] + fClipM[14]);

   fCacheDirty = kFALSE;
}

//______________________________________________________________________________
TGLBoundingBox TGLCamera::Frustum(Bool_t asBox) const
{
   // Return the the current camera frustum. If asBox == kFALSE return
   // a true frustum (truncated square based pyramid). If asBox == kTRUE
   // return a true box, using the far clipping plane intersection projected
   // back to the near plane.
   //
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   //
   // Note: TGLBoundingBox is not really valid when filled with truncated pyramid
   // - this is used as a visual debug aid only so ok.

   // TODO: BoundingBox object is not always valid
   // Need a generic bounding volume object
   if (fCacheDirty) {
      Error("TGLCamera::FrustumBox()", "cache dirty - must call Apply()");
   }


   TGLVertex3 vertex[8];

   //    7-------6
   //   /|      /|
   //  3-------2 |
   //  | 4-----|-5
   //  |/      |/
   //  0-------1

   // Get four vertices of frustum on the far clipping plane
   // We assume they always intersect
   vertex[4] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kBottom], fFrustumPlanes[kLeft]).second;
   vertex[5] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kBottom], fFrustumPlanes[kRight]).second;
   vertex[6] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kTop],    fFrustumPlanes[kRight]).second;
   vertex[7] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kTop],    fFrustumPlanes[kLeft]).second;

   if (asBox) {
      // Now find the matching four verticies for above, projected onto near clip plane
      // As near and far clip planes are parallel this forms a orientated box encompassing the frustum
      vertex[0] = fFrustumPlanes[kNear].NearestOn(vertex[4]);
      vertex[1] = fFrustumPlanes[kNear].NearestOn(vertex[5]);
      vertex[2] = fFrustumPlanes[kNear].NearestOn(vertex[6]);
      vertex[3] = fFrustumPlanes[kNear].NearestOn(vertex[7]);
   } else {
      // Returing true frustum - find verticies at near clipping plane
      // We assume they always intersect
      vertex[0] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kBottom], fFrustumPlanes[kLeft]).second;
      vertex[1] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kBottom], fFrustumPlanes[kRight]).second;
      vertex[2] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kTop],    fFrustumPlanes[kRight]).second;
      vertex[3] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kTop],    fFrustumPlanes[kLeft]).second;
   }

   return TGLBoundingBox(vertex);
}

//______________________________________________________________________________
TGLVertex3 TGLCamera::EyePoint() const
{
   // Return the camera eye point (vertex) in world space
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   if (fCacheDirty) {
      Error("TGLPerspectiveCamera::FrustumBox()", "cache dirty - must call Apply()");
   }

   // Use intersection of right/left/top frustum planes - can be done in
   // other ways from camera values but this is easiest.
   // Note for an ortho camera this will result in an infinite z distance
   // which is theorectically correct although of limited use
   return Intersection(fFrustumPlanes[kRight], fFrustumPlanes[kLeft], fFrustumPlanes[kTop]).second;
}

//______________________________________________________________________________
TGLVector3 TGLCamera::EyeDirection() const
{
   // Extract the camera eye direction (vector), running from EyePoint()
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   if (fCacheDirty) {
      Error("TGLCamera::FrustumBox()", "cache dirty - must call Apply()");
   }
   // Direction is just normal of near clipping plane
   return fFrustumPlanes[kNear].Norm();
}

//______________________________________________________________________________
TGLVertex3 TGLCamera::FrustumCenter() const
{
   // Find the center of the camera frustum from intersection of planes
   // This method will work even with parallel left/right & top/bottom and
   // infinite eye point of ortho cameras
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   if (fCacheDirty) {
      Error("TGLCamera::FrustumCenter()", "cache dirty - must call Apply()");
   }
   std::pair<Bool_t, TGLVertex3> nearBottomLeft = Intersection(fFrustumPlanes[kNear],
                                                               fFrustumPlanes[kBottom],
                                                               fFrustumPlanes[kLeft]);
   std::pair<Bool_t, TGLVertex3> farTopRight    = Intersection(fFrustumPlanes[kFar],
                                                               fFrustumPlanes[kTop],
                                                               fFrustumPlanes[kRight]);
   // Planes should intersect
   if (!nearBottomLeft.first || !farTopRight.first) {
      Error("TGLCamera::FrustumCenter()", "frustum planes invalid");
      return TGLVertex3(0.0, 0.0, 0.0);
   }
   return nearBottomLeft.second + (farTopRight.second - nearBottomLeft.second)/2.0;
}

//______________________________________________________________________________
EOverlap TGLCamera::FrustumOverlap(const TGLBoundingBox & box) const
{
   // Calcaulte overlap (kInside, kOutside, kPartial) of box with camera
   // frustum
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   if (fCacheDirty) {
      Error("TGLCamera::FrustumOverlap()", "cache dirty - must call Apply()");
   }

   // Test shape against each plane in frustum - returning overlap result
   // This method can result in kFALSE positives, where shape lies outside
   // frustum, but not outside a single plane of it. In this case the shape
   // will be regarded incorrectly as intersecting (kPartial)
   // TODO: Improve this - have a reliable test (seperating axes).

   Int_t planesInside = 0; // Assume outside to start
   for (Int_t planeIndex = 0; planeIndex < kPlanesPerFrustum; ++planeIndex) {
      EOverlap planeOverlap = box.Overlap(fFrustumPlanes[planeIndex]);

      // Special case - any object which comes through the near clipping
      // plane is completely removed - disabled at present
      // TODO: In future may want to fade object (opacity) as they approach
      // near clip - how will this be returned? template pair?
      /*if (planeIndex == kNear && planeOverlap == kPartial) {
         return kOutside;
      }*/
      // Once we find a single plane which shape is outside, we are outside the frustum
      if ( planeOverlap == kOutside ) {
         return kOutside;
      } else if ( planeOverlap == kInside ) {
         planesInside++;
      }
   }
   // Completely inside frustum
   if ( planesInside == kPlanesPerFrustum ) {
      return kInside;
   } else {
      return kPartial;
   }
}

//______________________________________________________________________________
EOverlap TGLCamera::ViewportOverlap(const TGLBoundingBox & box) const
{
   // Calculate overlap (kInside, kOutside, kPartial) of box projection onto viewport
   // (as rect) against the viewport rect.
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using.

   return ViewportRect(box).Overlap(fViewport);
}

//______________________________________________________________________________
TGLRect TGLCamera::ViewportRect(const TGLBoundingBox & box,
                                const TGLBoundingBox::EFace face) const
{
   // Calculate viewport rectangle which just contains projection of single 'face'
   // of world frame bounding box 'box' onto the viewport. Note use other version
   // of ViewportRect() if you want whole 'box' contained
   return ViewportRect(box, &face);
}

//______________________________________________________________________________
TGLRect TGLCamera::ViewportRect(const TGLBoundingBox & box,
                                const TGLBoundingBox::EFace * face) const
{
   // Calculate viewport rectangle which just contains projection of
   // world frame bounding box 'box' onto the viewport. If face is
   // null the rect contains the whole bounding box (8 vertices/6
   // faces). If face is non-null it indicates a box face, and the
   // rect contains the single face (4 vertices). Note use other
   // version of ViewportRect() if you wish to just pass a static
   // EFace enum member (e.g. kFaceLowX)
   //
   // Note:
   //    i)   Rectangle is NOT clipped by viewport limits - so can result
   //         in rect with corners outside viewport - negative etc
   //    ii)  TGLRect provides int (pixel based) values - not subpxiel accurate
   //    iii) Camera must have valid frustum cache - call Apply() after last
   //         modifcation, before calling

   if (fCacheDirty) {
      Error("TGLCamera::ViewportSize()", "cache dirty - must call Apply()");
   }

   // TODO: Maybe TGLRect should be converted to Double_t so subpixel accurate
   // Would give better LOD calculations at small sizes

   // May often result in a rect bigger then the viewport
   // as gluProject does not clip.
   Double_t winX, winY, winZ;
   TGLRect  screenRect;

   // TGLBoundingBox::Vertices() & TGLBoundingBox::FaceVertices() return
   // const & vectors so this *should* all be effficient...
   UInt_t vertexCount;
   if (face) {
      vertexCount = box.FaceVertices(*face).size();
   } else {
      vertexCount = box.Vertices().size();
   }

   for (UInt_t i = 0; i < vertexCount; i++)
   {
      const TGLVertex3 & vertex = face ? box.Vertices().at(box.FaceVertices(*face).at(i)) :
                                      box.Vertices().at(i);

      gluProject(vertex.X(), vertex.Y(), vertex.Z(),
                 fModVM.CArr(), fProjM.CArr(), fViewport.CArr(),
                 &winX, &winY, &winZ);

      if (i == 0) {
         screenRect.SetCorner(static_cast<Int_t>(winX),static_cast<Int_t>(winY));
      } else {
         screenRect.Expand(static_cast<Int_t>(winX), static_cast<Int_t>(winY));
      }
   }

   return screenRect;
}

//______________________________________________________________________________
TGLVertex3 TGLCamera::WorldToViewport(const TGLVertex3 & worldVertex,
                                      TGLMatrix* modviewMat) const
{
   // Convert a 3D world vertex to '3D' viewport (screen) one. The X()/Y()
   // components of the viewport vertex are the horizontal/vertical pixel
   // positions. The Z() component is the viewport depth value - for a
   // default depth range this is 0.0 (at near clip plane) to 1.0 (at far
   // clip plane). See OpenGL gluProject & glDepth documentation
   //
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using

   if (fCacheDirty) {
      Error("TGLCamera::WorldToViewport()", "cache dirty - must call Apply()");
   }
   TGLVertex3 viewportVertex;
   gluProject(worldVertex[0], worldVertex[1], worldVertex[2],
              modviewMat ? modviewMat->CArr() : fModVM.CArr(),
              fProjM.CArr(), fViewport.CArr(),
              &viewportVertex[0], &viewportVertex[1], &viewportVertex[2]);
   return viewportVertex;
}

//______________________________________________________________________________
TGLVector3 TGLCamera::WorldDeltaToViewport(const TGLVertex3 & worldRef,
                                           const TGLVector3 & worldDelta) const
{
   // Convert a 3D vector worldDelta (shift) about vertex worldRef to a viewport
   // (screen) '3D' vector. The X()/Y() components of the vector are the horizontal /
   // vertical pixel deltas. The Z() component is the viewport depth delta - for a
   // default depth range between 0.0 (at near clip plane) to 1.0 (at far clip plane)
   // See OpenGL gluProject & glDepth documentation
   //
   // Camera must have valid frustum cache - call Apply()
   if (fCacheDirty) {
      Error("TGLCamera::WorldToViewport()", "cache dirty - must call Apply()");
   }
   TGLVertex3 other = worldRef + worldDelta;
   TGLVertex3 v1 = WorldToViewport(worldRef);
   TGLVertex3 v2 = WorldToViewport(other);
   return v2 - v1;
}

//______________________________________________________________________________
TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex,
                                      TGLMatrix* modviewMat) const
{
   // Convert a '3D' viewport vertex to 3D world one. The X()/Y() components
   // of viewportVertex are the horizontal/vertical pixel position.

   // The Z() component is the viewport depth value - for a default
   // depth range this is 0.0 (at near clip plane) to 1.0 (at far clip
   // plane). Without Z() the viewport position corresponds to a line
   // in 3D world space - see:
   // TGLLine3 TGLCamera::ViewportToWorld(Double_t viewportX, Double_t viewportY) const
   //
   // See also OpenGL gluUnProject & glDepth documentation.
   //
   // Camera must have valid frustum cache - call Apply() after last
   // modifcation, before using.

   if (fCacheDirty) {
      Error("TGLCamera::ViewportToWorld()", "cache dirty - must call Apply()");
   }
   TGLVertex3 worldVertex;
   gluUnProject(viewportVertex[0], viewportVertex[1], viewportVertex[2],
                modviewMat ? modviewMat->CArr() : fModVM.CArr(),
                fProjM.CArr(), fViewport.CArr(),
                &worldVertex[0], &worldVertex[1], &worldVertex[2]);
   return worldVertex;
}

//______________________________________________________________________________
TGLLine3 TGLCamera::ViewportToWorld(Double_t viewportX, Double_t viewportY) const
{
   // Convert a 2D viewport position to 3D world line - the projection of the
   // viewport point into 3D space. Line runs from near to far camera clip planes
   // (the minimum and maximum visible depth). See also
   //    TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex) const
   // for 3D viewport -> 3D world vertex conversions.
   // See also OpenGL gluUnProject & glDepth documentation
   //
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   if (fCacheDirty) {
      Error("TGLCamera::Viewport2DToWorldLine()", "cache dirty - must call Apply()");
   }
   // Find world verticies at near and far clip planes, and return line through them
   TGLVertex3 nearClipWorld = ViewportToWorld(TGLVertex3(viewportX, viewportY, 0.0));
   TGLVertex3 farClipWorld = ViewportToWorld(TGLVertex3(viewportX, viewportY, 1.0));
   return TGLLine3(nearClipWorld, farClipWorld - nearClipWorld);
}

//______________________________________________________________________________
TGLLine3 TGLCamera::ViewportToWorld(const TPoint & viewport) const
{
   // Convert a 2D viewport position to 3D world line - the projection of the
   // viewport point into 3D space. Line runs from near to far camera clip planes
   // (the minimum and maximum visible depth). See also
   //    TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex) const
   // for 3D viewport -> 3D world vertex conversions.
   // See also OpenGL gluUnProject & glDepth documentation
   //
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   return ViewportToWorld(viewport.GetX(), viewport.GetY());
}

//______________________________________________________________________________
std::pair<Bool_t, TGLVertex3> TGLCamera::ViewportPlaneIntersection(Double_t viewportX, Double_t viewportY,
                                                                   const TGLPlane & worldPlane) const
{
   // Find the intersection of projection of supplied viewport point (a 3D world
   // line - see ViewportToWorld) with supplied world plane. Returns std::pair
   // of Bool_t and TGLVertex3. If line intersects std::pair.first (Bool_t) is
   // kTRUE, and std::pair.second (TGLVertex) contains the intersection vertex.
   // If line does not intersect (line and plane parallel) std::pair.first
   // (Bool_t) if kFALSE, and std::pair.second (TGLVertex) is invalid.
   //
   // NOTE: The projection lines is extended for the plane intersection test
   // hence the intersection vertex can lie outside the near/far clip regions
   // (not visible)
   //
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   TGLLine3 worldLine = ViewportToWorld(viewportX, viewportY);

   // Find intersection of line with plane
   return Intersection(worldPlane, worldLine, kTRUE /* extended */ );
}

//______________________________________________________________________________
std::pair<Bool_t, TGLVertex3> TGLCamera::ViewportPlaneIntersection(const TPoint & viewport,
                                                                   const TGLPlane & worldPlane) const
{
   // Find the intersection of projection of supplied viewport TPoint (a 3D world
   // line - see ViewportToWorld) with supplied world plane. Returns std::pair
   // of bool and vertex. If line intersects
   //
   // Camera must have valid frustum cache - call Apply() after last modifcation, before using
   return ViewportPlaneIntersection(viewport.GetX(), viewport.GetY(), worldPlane);
}

//______________________________________________________________________________
TGLVector3 TGLCamera::ViewportDeltaToWorld(const TGLVertex3 & worldRef, Double_t viewportXDelta,
                                           Double_t viewportYDelta, TGLMatrix* modviewMat) const
{
   // Apply a 2D viewport delta (shift) to the projection of worldRef onto viewport,
   // returning the resultant world vector which equates to it. Useful for making
   // 3D world objects track mouse moves.
   //
   // Camera must have valid frustum cache - call Apply()
   if (fCacheDirty) {
      Error("TGLCamera::ViewportDeltaToWorld()", "cache dirty - must call Apply()");
   }
   TGLVertex3 winVertex = WorldToViewport(worldRef, modviewMat);
   winVertex.Shift(viewportXDelta, viewportYDelta, 0.0);
   return (ViewportToWorld(winVertex, modviewMat) - worldRef);
}

//______________________________________________________________________________
Bool_t TGLCamera::OfInterest(const TGLBoundingBox & box, Bool_t ignoreSize) const
{
   // Calculate if the an object defined by world frame bounding box
   // is 'of interest' to the camera. This is defined as box:
   //
   // i) intersecting completely or partially (kInside/kPartial) with
   // cameras interest box (fInterestBox)
   // ii) having significant length OR volume ratio compared to this
   // interest box
   //
   // If a box is 'of interest' returns kTRUE, kFALSE otherwise.  See
   // TGLCamera::UpdateInterest() for more details of camera interest
   // box.
   //
   // Note: Length/volume ratios NOT dependent on the projected size
   // of box at current camera configuration as we do not want
   // continual changes.  This is used when (re) populating the scene
   // with objects from external client.
   //
   // TODO: Might be more logical to move this test out to client -
   // and have accessor for fInterestBox instead?

   Bool_t interest = kFALSE;

   // *********** IMPORTANT - Bootstrapping the camera with empty scene
   //
   // Initially the camera can't be Setup() (limits etc) until the
   // scene is populated and it has a valid bounding box to pass to
   // the camera.  However the scene can't be populated without
   // knowing if objects sent are 'of interest' - which needs a camera
   // interest box, made from a properly setup camera frustum - catch
   // 22.
   //
   // To overcome this we track the largest box volume seen so far and
   // regard anything over 0.001 of this as 'of interest'. This enables
   // us to get a roughly populated scene with largest objects, setup
   // the camera, and do first draw.  We then do a
   // TGLCamera::UpdateInterest() - which always return kTRUE, and
   // thus fires an internal rebuild to fill scene properly and
   // finally setup camera properly.

   if (fInterestBox.IsEmpty()) {
      if (box.Volume() >= fLargestSeen * 0.001) {
         if (box.Volume() > fLargestSeen) {
            fLargestSeen = box.Volume();
         }
         interest = kTRUE;
      }
   } else {
      // Objects are of interest if the have length ratio c.f. the
      // current interest box, and they at least partially overlap it.
      // Some objects have zero volume BBs - e.g. single points - skip
      // the test for these as there is no way to threshold on 0.
      if (box.IsEmpty()) {
         interest = kTRUE;
      } else {
         if (ignoreSize || box.Diagonal() / fInterestBox.Diagonal() > 0.0001)
            interest = fInterestBox.Overlap(box) != kOutside;
      }
   }

   return interest;
}

//______________________________________________________________________________
Bool_t TGLCamera::UpdateInterest(Bool_t force)
{
   // Update the internal interest box (fInterestBox) of the camera.
   // The interest box is an orientated bounding box, calculated as
   // an expanded container round the frustum. It is used to test if
   // if object bounding boxes are of interest (should be accepted
   // into viewer scene) for a camera - see TGLCamera::OfInterest()
   //
   // The interest box is updated if the frustum is no longer contained
   // in the existing one, or a new one calculated on the current frustum
   // differs significantly in volume (camera has been zoomed/dollyed
   // sizable amount).
   //
   // If the interest box is updated we return kTRUE - kFALSE otherwise.
   //
   Bool_t exposedUpdate = kFALSE;

   // Construct a new interest box using the current frustum box as a basis
   TGLBoundingBox frustumBox = Frustum(kTRUE);
   TGLBoundingBox newInterestBox(frustumBox);

   // The Z(2) axis of frustum (near->far plane) can be quite shallow c.f. X(0)/Y(1)
   // For interest box we want to expand to ensure it is at least size
   // of smaller X/Y to avoid excessive interest box recalculations
   TGLVector3 frustumExtents = frustumBox.Extents();
   Double_t minBoxLength = frustumExtents.Mag() * fgInterestBoxExpansion;
   newInterestBox.Scale(minBoxLength/frustumExtents[0], minBoxLength/frustumExtents[1], minBoxLength/frustumExtents[2]);

   // Calculate volume ratio of new to old
   Double_t volRatio = 0.0;

   // If the interest box is empty the interest is ALWAYS updated
   // See TGLCamera::OfInterest() comment on bootstrapping
   if (!fInterestBox.IsEmpty()) {
      volRatio = newInterestBox.Volume() / fInterestBox.Volume();
   }

   // Update the existing interest box with new one if:
   // i) Volume ratio old/new interest has changed significantly
   // ii) The current frustum is not inside existing interest
   // iii) Force case (debugging)
   if (volRatio > 8.0 || volRatio < 0.125 || fInterestBox.IsEmpty() ||
       fInterestBox.Overlap(frustumBox) != kInside || force) {
      fPreviousInterestBox = fInterestBox;
      fInterestBox = newInterestBox;

      // Frustum should be fully contained now
      if (fInterestBox.Overlap(frustumBox) != kInside) {
         Error("TGLCamera::UpdateInterest", "update interest box does not contain frustum");
      }

      exposedUpdate = kTRUE;

      // Keep the real frustum (true and box versions) as debuging aid
      fInterestFrustum = Frustum(kFALSE);
      fInterestFrustumAsBox = frustumBox;

      if (gDebug>2 || force) {
         Info("TGLCamera::UpdateInterest", "changed - volume ratio %f", volRatio );
      }
   }

   return exposedUpdate;
}

//______________________________________________________________________________
void TGLCamera::ResetInterest()
{
   // Clear out the existing interest box
   fInterestBox.SetEmpty();

   // We also reset the bootstrapping variable - see TGLCamera::OfInterest comments
   fLargestSeen = 0.0;
}

//______________________________________________________________________________
Bool_t TGLCamera::AdjustAndClampVal(Double_t & val, Double_t min, Double_t max,
                                    Int_t screenShift, Int_t screenShiftRange,
                                    Bool_t mod1, Bool_t mod2) const
{
   // Adjust a passed REFERENCE value 'val', based on screenShift delta.
   // Two modifier flags ('mod1' / 'mod2' ) for sensitivity:
   //
   // mod1 = kFALSE, mod2 = kFALSE : normal sensitivity (screenShift/screenShiftRange)
   // mod1 = kTRUE,  mod2 = kFALSE : 0.1x sensitivity
   // mod1 = kTRUE,  mod2 = kTRUE  : 0.01x sensitivity
   // mod1 = kFALSE, mod2 = kTRUE  : 10.0x sensitivity
   //
   // 'val' is modified and clamped to 'min' / 'max' range.
   // Return bool kTRUE if val actually changed.
   //
   // Used as common interaction function for adjusting zoom/dolly etc
   if (screenShift == 0) {
      return kFALSE;
   }

   // Calculate a sensitivity based on passed modifiers
   Double_t sens = val * static_cast<Double_t>(screenShift);

   if (mod1) {
      sens *= 0.1;
      if (mod2) {
         sens *= 0.1;
      }
   } else {
      if (mod2) {
         sens *= 10.0;
      }
   }

   Double_t oldVal = val;
   Double_t shift  = sens / static_cast<Double_t>(screenShiftRange);
   val -= shift;

   if (val < min) {
      val = min;
   }
   else if (val > max) {
      val = max;
   }

   return val != oldVal;
}

//______________________________________________________________________________
Double_t TGLCamera::AdjustDelta(Double_t screenShift, Double_t deltaFactor,
                                Bool_t mod1, Bool_t mod2) const
{
   // Adjust a passed screen value and apply modifiers.
   // See AdjustAndClampVal() for details.

   if (screenShift == 0)
      return 0;

   // Calculate a sensitivity based on passed modifiers
   Double_t sens = 1.0;

   if (mod1) {
      sens *= 0.1;
      if (mod2) {
         sens *= 0.1;
      }
   } else {
      if (mod2) {
         sens *= 10.0;
      }
   }

   return sens * deltaFactor * screenShift;
}

//______________________________________________________________________________
void TGLCamera::DrawDebugAids() const
{
   // Draw out some debugging aids for the camera:
   //
   // i) The frustum used to create the current interest box (RED)
   // ii) The same frustum as a squared off box (ORANGE)
   // iii) The axis aligned version of the frustum used as interest box basis (YELLOW)
   // iv) The current interest box (BLUE)

   // Interest box frustum base (RED)
   glColor3d(1.0,0.0,0.0);
   fInterestFrustum.Draw();

   // Interest box frustum as box (ORANGE)
   glColor3d(1.0,0.65,0.15);
   fInterestFrustumAsBox.Draw();

   // Current Interest box (BLUE)
   glColor3d(0.0,0.0,1.0);
   fInterestBox.Draw();

   // Previous interest (GREY)
   glColor3d(.8,.7,.6);
   fPreviousInterestBox.Draw();

   // Also draw line from current eye point out in eye direction - should not
   // appear if calculated correctly
   TGLVertex3 start = EyePoint();
   TGLVertex3 end = start + EyeDirection();
   glColor3d(1.0,1.0,1.0);
   glBegin(GL_LINES);
   glVertex3dv(start.CArr());
   glVertex3dv(end.CArr());
   glEnd();
}

//______________________________________________________________________________
void TGLCamera::SetExternalCenter(Bool_t enable)
{
   // Set camera center diffrent than scene center, if enable is kTRUE.

   if (fExternalCenter == enable)
      return;

   fExternalCenter = enable;
   if (fExternalCenter)
      fCenter = &fExtCenter;
   else
      fCenter = &fDefCenter;

   TGLMatrix bt = fCamBase * fCamTrans;
   fCamBase.SetBaseVec(4, *fCenter);
   TGLMatrix binv = fCamBase; binv.Invert();
   fCamTrans = binv * bt;

   IncTimeStamp();
}

//______________________________________________________________________________
void TGLCamera::SetCenterVec(Double_t x, Double_t y, Double_t z)
{
   // Set camera center vector.

   if (fExternalCenter)
      fExtCenter.Set(x, y, z);
   else
      fDefCenter.Set(x, y, z);

   TGLMatrix bt = fCamBase * fCamTrans;
   fCamBase.SetBaseVec(4, *fCenter);
   TGLMatrix binv = fCamBase; binv.Invert();
   fCamTrans = binv * bt;

   IncTimeStamp();
}

//______________________________________________________________________________
Double_t TGLCamera::GetTheta() const
{
   // Get angle between camera up axis.

   TGLVector3 fwd  = fCamTrans.GetBaseVec(1);
   TGLVector3 zdir = fCamBase.GetBaseVec(3);
   fCamBase.RotateIP(fwd);
   return TMath::ACos(fwd*zdir);
}

//______________________________________________________________________________
Bool_t TGLCamera::Truck(Double_t xDelta, Double_t yDelta)
{
   // Truck the camera - 'move camera parallel to film plane'.
   // Returns kTRUE is redraw required (camera change), kFALSE otherwise.

   if (xDelta != 0 || yDelta != 0)
   {
      fCamTrans.MoveLF(2, xDelta);
      fCamTrans.MoveLF(3, yDelta);

      IncTimeStamp();
      return kTRUE;
   }
   else
   {
      return kFALSE;
   }
}

//______________________________________________________________________________
Bool_t TGLCamera::Rotate(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
{
   // Rotate the camera round view volume center established in Setup().
   // Arguments are:
   // xDelta - horizontal delta (pixels)
   // YDelta - vertical delta (pixels)

   Double_t vRotate = AdjustDelta(xDelta, TMath::TwoPi() / fViewport.Width(), mod1, mod2);
   Double_t hRotate = AdjustDelta(yDelta, TMath::Pi()   / fViewport.Height(), mod1, mod2);

   return RotateRad(hRotate, vRotate);
}

//______________________________________________________________________________
Bool_t TGLCamera::RotateRad(Double_t hRotate, Double_t vRotate)
{
   // Rotate camera around center.

   using namespace TMath;
   if (hRotate != 0.0)
   {
      TGLVector3 fwd  = fCamTrans.GetBaseVec(1);
      TGLVector3 lft  = fCamTrans.GetBaseVec(2);
      TGLVector3 up   = fCamTrans.GetBaseVec(3);
      TGLVector3 pos  = fCamTrans.GetTranslation();

      TGLVector3 deltaT = pos - (pos*lft)*lft;
      Double_t   deltaF = deltaT * fwd;
      Double_t   deltaU = deltaT * up;

      // up vector lock
      TGLVector3 zdir = fCamBase.GetBaseVec(3);
      fCamBase.RotateIP(fwd);
      Double_t theta = ACos(fwd*zdir);
      if(theta+hRotate < fVAxisMinAngle)
         hRotate = fVAxisMinAngle - theta;
      else if(theta+hRotate > Pi() - fVAxisMinAngle)
         hRotate = Pi() - fVAxisMinAngle - theta;

      fCamTrans.MoveLF(1, -deltaF);
      fCamTrans.MoveLF(3, -deltaU);
      fCamTrans.RotateLF(3, 1, hRotate);
      fCamTrans.MoveLF(3,  deltaU);
      fCamTrans.MoveLF(1,  deltaF);
   }
   if (vRotate != 0.0)
   {
      fCamTrans.RotatePF(1, 2, -vRotate);
   }

   IncTimeStamp();
   return kTRUE;
}

//______________________________________________________________________________
Bool_t TGLCamera::Dolly(Int_t delta, Bool_t mod1, Bool_t mod2)
{
   // Dolly the camera - 'move camera along eye line, retaining lens focal length'.
   // Arguments are:
   //
   // 'delta' - mouse viewport delta (pixels) - +ive dolly in, -ive dolly out
   // 'mod1' / 'mod2' - sensitivity modifiers - see TGLCamera::AdjustAndClampVal()
   //
   // Returns kTRUE is redraw required (camera change), kFALSE otherwise.

   Double_t step = AdjustDelta(delta, fDollyDistance, mod1, mod2);
   if (step == 0)
      return kFALSE;

   fCamTrans.MoveLF(1, -step);

   IncTimeStamp();
   return kTRUE;
}
 TGLCamera.cxx:1
 TGLCamera.cxx:2
 TGLCamera.cxx:3
 TGLCamera.cxx:4
 TGLCamera.cxx:5
 TGLCamera.cxx:6
 TGLCamera.cxx:7
 TGLCamera.cxx:8
 TGLCamera.cxx:9
 TGLCamera.cxx:10
 TGLCamera.cxx:11
 TGLCamera.cxx:12
 TGLCamera.cxx:13
 TGLCamera.cxx:14
 TGLCamera.cxx:15
 TGLCamera.cxx:16
 TGLCamera.cxx:17
 TGLCamera.cxx:18
 TGLCamera.cxx:19
 TGLCamera.cxx:20
 TGLCamera.cxx:21
 TGLCamera.cxx:22
 TGLCamera.cxx:23
 TGLCamera.cxx:24
 TGLCamera.cxx:25
 TGLCamera.cxx:26
 TGLCamera.cxx:27
 TGLCamera.cxx:28
 TGLCamera.cxx:29
 TGLCamera.cxx:30
 TGLCamera.cxx:31
 TGLCamera.cxx:32
 TGLCamera.cxx:33
 TGLCamera.cxx:34
 TGLCamera.cxx:35
 TGLCamera.cxx:36
 TGLCamera.cxx:37
 TGLCamera.cxx:38
 TGLCamera.cxx:39
 TGLCamera.cxx:40
 TGLCamera.cxx:41
 TGLCamera.cxx:42
 TGLCamera.cxx:43
 TGLCamera.cxx:44
 TGLCamera.cxx:45
 TGLCamera.cxx:46
 TGLCamera.cxx:47
 TGLCamera.cxx:48
 TGLCamera.cxx:49
 TGLCamera.cxx:50
 TGLCamera.cxx:51
 TGLCamera.cxx:52
 TGLCamera.cxx:53
 TGLCamera.cxx:54
 TGLCamera.cxx:55
 TGLCamera.cxx:56
 TGLCamera.cxx:57
 TGLCamera.cxx:58
 TGLCamera.cxx:59
 TGLCamera.cxx:60
 TGLCamera.cxx:61
 TGLCamera.cxx:62
 TGLCamera.cxx:63
 TGLCamera.cxx:64
 TGLCamera.cxx:65
 TGLCamera.cxx:66
 TGLCamera.cxx:67
 TGLCamera.cxx:68
 TGLCamera.cxx:69
 TGLCamera.cxx:70
 TGLCamera.cxx:71
 TGLCamera.cxx:72
 TGLCamera.cxx:73
 TGLCamera.cxx:74
 TGLCamera.cxx:75
 TGLCamera.cxx:76
 TGLCamera.cxx:77
 TGLCamera.cxx:78
 TGLCamera.cxx:79
 TGLCamera.cxx:80
 TGLCamera.cxx:81
 TGLCamera.cxx:82
 TGLCamera.cxx:83
 TGLCamera.cxx:84
 TGLCamera.cxx:85
 TGLCamera.cxx:86
 TGLCamera.cxx:87
 TGLCamera.cxx:88
 TGLCamera.cxx:89
 TGLCamera.cxx:90
 TGLCamera.cxx:91
 TGLCamera.cxx:92
 TGLCamera.cxx:93
 TGLCamera.cxx:94
 TGLCamera.cxx:95
 TGLCamera.cxx:96
 TGLCamera.cxx:97
 TGLCamera.cxx:98
 TGLCamera.cxx:99
 TGLCamera.cxx:100
 TGLCamera.cxx:101
 TGLCamera.cxx:102
 TGLCamera.cxx:103
 TGLCamera.cxx:104
 TGLCamera.cxx:105
 TGLCamera.cxx:106
 TGLCamera.cxx:107
 TGLCamera.cxx:108
 TGLCamera.cxx:109
 TGLCamera.cxx:110
 TGLCamera.cxx:111
 TGLCamera.cxx:112
 TGLCamera.cxx:113
 TGLCamera.cxx:114
 TGLCamera.cxx:115
 TGLCamera.cxx:116
 TGLCamera.cxx:117
 TGLCamera.cxx:118
 TGLCamera.cxx:119
 TGLCamera.cxx:120
 TGLCamera.cxx:121
 TGLCamera.cxx:122
 TGLCamera.cxx:123
 TGLCamera.cxx:124
 TGLCamera.cxx:125
 TGLCamera.cxx:126
 TGLCamera.cxx:127
 TGLCamera.cxx:128
 TGLCamera.cxx:129
 TGLCamera.cxx:130
 TGLCamera.cxx:131
 TGLCamera.cxx:132
 TGLCamera.cxx:133
 TGLCamera.cxx:134
 TGLCamera.cxx:135
 TGLCamera.cxx:136
 TGLCamera.cxx:137
 TGLCamera.cxx:138
 TGLCamera.cxx:139
 TGLCamera.cxx:140
 TGLCamera.cxx:141
 TGLCamera.cxx:142
 TGLCamera.cxx:143
 TGLCamera.cxx:144
 TGLCamera.cxx:145
 TGLCamera.cxx:146
 TGLCamera.cxx:147
 TGLCamera.cxx:148
 TGLCamera.cxx:149
 TGLCamera.cxx:150
 TGLCamera.cxx:151
 TGLCamera.cxx:152
 TGLCamera.cxx:153
 TGLCamera.cxx:154
 TGLCamera.cxx:155
 TGLCamera.cxx:156
 TGLCamera.cxx:157
 TGLCamera.cxx:158
 TGLCamera.cxx:159
 TGLCamera.cxx:160
 TGLCamera.cxx:161
 TGLCamera.cxx:162
 TGLCamera.cxx:163
 TGLCamera.cxx:164
 TGLCamera.cxx:165
 TGLCamera.cxx:166
 TGLCamera.cxx:167
 TGLCamera.cxx:168
 TGLCamera.cxx:169
 TGLCamera.cxx:170
 TGLCamera.cxx:171
 TGLCamera.cxx:172
 TGLCamera.cxx:173
 TGLCamera.cxx:174
 TGLCamera.cxx:175
 TGLCamera.cxx:176
 TGLCamera.cxx:177
 TGLCamera.cxx:178
 TGLCamera.cxx:179
 TGLCamera.cxx:180
 TGLCamera.cxx:181
 TGLCamera.cxx:182
 TGLCamera.cxx:183
 TGLCamera.cxx:184
 TGLCamera.cxx:185
 TGLCamera.cxx:186
 TGLCamera.cxx:187
 TGLCamera.cxx:188
 TGLCamera.cxx:189
 TGLCamera.cxx:190
 TGLCamera.cxx:191
 TGLCamera.cxx:192
 TGLCamera.cxx:193
 TGLCamera.cxx:194
 TGLCamera.cxx:195
 TGLCamera.cxx:196
 TGLCamera.cxx:197
 TGLCamera.cxx:198
 TGLCamera.cxx:199
 TGLCamera.cxx:200
 TGLCamera.cxx:201
 TGLCamera.cxx:202
 TGLCamera.cxx:203
 TGLCamera.cxx:204
 TGLCamera.cxx:205
 TGLCamera.cxx:206
 TGLCamera.cxx:207
 TGLCamera.cxx:208
 TGLCamera.cxx:209
 TGLCamera.cxx:210
 TGLCamera.cxx:211
 TGLCamera.cxx:212
 TGLCamera.cxx:213
 TGLCamera.cxx:214
 TGLCamera.cxx:215
 TGLCamera.cxx:216
 TGLCamera.cxx:217
 TGLCamera.cxx:218
 TGLCamera.cxx:219
 TGLCamera.cxx:220
 TGLCamera.cxx:221
 TGLCamera.cxx:222
 TGLCamera.cxx:223
 TGLCamera.cxx:224
 TGLCamera.cxx:225
 TGLCamera.cxx:226
 TGLCamera.cxx:227
 TGLCamera.cxx:228
 TGLCamera.cxx:229
 TGLCamera.cxx:230
 TGLCamera.cxx:231
 TGLCamera.cxx:232
 TGLCamera.cxx:233
 TGLCamera.cxx:234
 TGLCamera.cxx:235
 TGLCamera.cxx:236
 TGLCamera.cxx:237
 TGLCamera.cxx:238
 TGLCamera.cxx:239
 TGLCamera.cxx:240
 TGLCamera.cxx:241
 TGLCamera.cxx:242
 TGLCamera.cxx:243
 TGLCamera.cxx:244
 TGLCamera.cxx:245
 TGLCamera.cxx:246
 TGLCamera.cxx:247
 TGLCamera.cxx:248
 TGLCamera.cxx:249
 TGLCamera.cxx:250
 TGLCamera.cxx:251
 TGLCamera.cxx:252
 TGLCamera.cxx:253
 TGLCamera.cxx:254
 TGLCamera.cxx:255
 TGLCamera.cxx:256
 TGLCamera.cxx:257
 TGLCamera.cxx:258
 TGLCamera.cxx:259
 TGLCamera.cxx:260
 TGLCamera.cxx:261
 TGLCamera.cxx:262
 TGLCamera.cxx:263
 TGLCamera.cxx:264
 TGLCamera.cxx:265
 TGLCamera.cxx:266
 TGLCamera.cxx:267
 TGLCamera.cxx:268
 TGLCamera.cxx:269
 TGLCamera.cxx:270
 TGLCamera.cxx:271
 TGLCamera.cxx:272
 TGLCamera.cxx:273
 TGLCamera.cxx:274
 TGLCamera.cxx:275
 TGLCamera.cxx:276
 TGLCamera.cxx:277
 TGLCamera.cxx:278
 TGLCamera.cxx:279
 TGLCamera.cxx:280
 TGLCamera.cxx:281
 TGLCamera.cxx:282
 TGLCamera.cxx:283
 TGLCamera.cxx:284
 TGLCamera.cxx:285
 TGLCamera.cxx:286
 TGLCamera.cxx:287
 TGLCamera.cxx:288
 TGLCamera.cxx:289
 TGLCamera.cxx:290
 TGLCamera.cxx:291
 TGLCamera.cxx:292
 TGLCamera.cxx:293
 TGLCamera.cxx:294
 TGLCamera.cxx:295
 TGLCamera.cxx:296
 TGLCamera.cxx:297
 TGLCamera.cxx:298
 TGLCamera.cxx:299
 TGLCamera.cxx:300
 TGLCamera.cxx:301
 TGLCamera.cxx:302
 TGLCamera.cxx:303
 TGLCamera.cxx:304
 TGLCamera.cxx:305
 TGLCamera.cxx:306
 TGLCamera.cxx:307
 TGLCamera.cxx:308
 TGLCamera.cxx:309
 TGLCamera.cxx:310
 TGLCamera.cxx:311
 TGLCamera.cxx:312
 TGLCamera.cxx:313
 TGLCamera.cxx:314
 TGLCamera.cxx:315
 TGLCamera.cxx:316
 TGLCamera.cxx:317
 TGLCamera.cxx:318
 TGLCamera.cxx:319
 TGLCamera.cxx:320
 TGLCamera.cxx:321
 TGLCamera.cxx:322
 TGLCamera.cxx:323
 TGLCamera.cxx:324
 TGLCamera.cxx:325
 TGLCamera.cxx:326
 TGLCamera.cxx:327
 TGLCamera.cxx:328
 TGLCamera.cxx:329
 TGLCamera.cxx:330
 TGLCamera.cxx:331
 TGLCamera.cxx:332
 TGLCamera.cxx:333
 TGLCamera.cxx:334
 TGLCamera.cxx:335
 TGLCamera.cxx:336
 TGLCamera.cxx:337
 TGLCamera.cxx:338
 TGLCamera.cxx:339
 TGLCamera.cxx:340
 TGLCamera.cxx:341
 TGLCamera.cxx:342
 TGLCamera.cxx:343
 TGLCamera.cxx:344
 TGLCamera.cxx:345
 TGLCamera.cxx:346
 TGLCamera.cxx:347
 TGLCamera.cxx:348
 TGLCamera.cxx:349
 TGLCamera.cxx:350
 TGLCamera.cxx:351
 TGLCamera.cxx:352
 TGLCamera.cxx:353
 TGLCamera.cxx:354
 TGLCamera.cxx:355
 TGLCamera.cxx:356
 TGLCamera.cxx:357
 TGLCamera.cxx:358
 TGLCamera.cxx:359
 TGLCamera.cxx:360
 TGLCamera.cxx:361
 TGLCamera.cxx:362
 TGLCamera.cxx:363
 TGLCamera.cxx:364
 TGLCamera.cxx:365
 TGLCamera.cxx:366
 TGLCamera.cxx:367
 TGLCamera.cxx:368
 TGLCamera.cxx:369
 TGLCamera.cxx:370
 TGLCamera.cxx:371
 TGLCamera.cxx:372
 TGLCamera.cxx:373
 TGLCamera.cxx:374
 TGLCamera.cxx:375
 TGLCamera.cxx:376
 TGLCamera.cxx:377
 TGLCamera.cxx:378
 TGLCamera.cxx:379
 TGLCamera.cxx:380
 TGLCamera.cxx:381
 TGLCamera.cxx:382
 TGLCamera.cxx:383
 TGLCamera.cxx:384
 TGLCamera.cxx:385
 TGLCamera.cxx:386
 TGLCamera.cxx:387
 TGLCamera.cxx:388
 TGLCamera.cxx:389
 TGLCamera.cxx:390
 TGLCamera.cxx:391
 TGLCamera.cxx:392
 TGLCamera.cxx:393
 TGLCamera.cxx:394
 TGLCamera.cxx:395
 TGLCamera.cxx:396
 TGLCamera.cxx:397
 TGLCamera.cxx:398
 TGLCamera.cxx:399
 TGLCamera.cxx:400
 TGLCamera.cxx:401
 TGLCamera.cxx:402
 TGLCamera.cxx:403
 TGLCamera.cxx:404
 TGLCamera.cxx:405
 TGLCamera.cxx:406
 TGLCamera.cxx:407
 TGLCamera.cxx:408
 TGLCamera.cxx:409
 TGLCamera.cxx:410
 TGLCamera.cxx:411
 TGLCamera.cxx:412
 TGLCamera.cxx:413
 TGLCamera.cxx:414
 TGLCamera.cxx:415
 TGLCamera.cxx:416
 TGLCamera.cxx:417
 TGLCamera.cxx:418
 TGLCamera.cxx:419
 TGLCamera.cxx:420
 TGLCamera.cxx:421
 TGLCamera.cxx:422
 TGLCamera.cxx:423
 TGLCamera.cxx:424
 TGLCamera.cxx:425
 TGLCamera.cxx:426
 TGLCamera.cxx:427
 TGLCamera.cxx:428
 TGLCamera.cxx:429
 TGLCamera.cxx:430
 TGLCamera.cxx:431
 TGLCamera.cxx:432
 TGLCamera.cxx:433
 TGLCamera.cxx:434
 TGLCamera.cxx:435
 TGLCamera.cxx:436
 TGLCamera.cxx:437
 TGLCamera.cxx:438
 TGLCamera.cxx:439
 TGLCamera.cxx:440
 TGLCamera.cxx:441
 TGLCamera.cxx:442
 TGLCamera.cxx:443
 TGLCamera.cxx:444
 TGLCamera.cxx:445
 TGLCamera.cxx:446
 TGLCamera.cxx:447
 TGLCamera.cxx:448
 TGLCamera.cxx:449
 TGLCamera.cxx:450
 TGLCamera.cxx:451
 TGLCamera.cxx:452
 TGLCamera.cxx:453
 TGLCamera.cxx:454
 TGLCamera.cxx:455
 TGLCamera.cxx:456
 TGLCamera.cxx:457
 TGLCamera.cxx:458
 TGLCamera.cxx:459
 TGLCamera.cxx:460
 TGLCamera.cxx:461
 TGLCamera.cxx:462
 TGLCamera.cxx:463
 TGLCamera.cxx:464
 TGLCamera.cxx:465
 TGLCamera.cxx:466
 TGLCamera.cxx:467
 TGLCamera.cxx:468
 TGLCamera.cxx:469
 TGLCamera.cxx:470
 TGLCamera.cxx:471
 TGLCamera.cxx:472
 TGLCamera.cxx:473
 TGLCamera.cxx:474
 TGLCamera.cxx:475
 TGLCamera.cxx:476
 TGLCamera.cxx:477
 TGLCamera.cxx:478
 TGLCamera.cxx:479
 TGLCamera.cxx:480
 TGLCamera.cxx:481
 TGLCamera.cxx:482
 TGLCamera.cxx:483
 TGLCamera.cxx:484
 TGLCamera.cxx:485
 TGLCamera.cxx:486
 TGLCamera.cxx:487
 TGLCamera.cxx:488
 TGLCamera.cxx:489
 TGLCamera.cxx:490
 TGLCamera.cxx:491
 TGLCamera.cxx:492
 TGLCamera.cxx:493
 TGLCamera.cxx:494
 TGLCamera.cxx:495
 TGLCamera.cxx:496
 TGLCamera.cxx:497
 TGLCamera.cxx:498
 TGLCamera.cxx:499
 TGLCamera.cxx:500
 TGLCamera.cxx:501
 TGLCamera.cxx:502
 TGLCamera.cxx:503
 TGLCamera.cxx:504
 TGLCamera.cxx:505
 TGLCamera.cxx:506
 TGLCamera.cxx:507
 TGLCamera.cxx:508
 TGLCamera.cxx:509
 TGLCamera.cxx:510
 TGLCamera.cxx:511
 TGLCamera.cxx:512
 TGLCamera.cxx:513
 TGLCamera.cxx:514
 TGLCamera.cxx:515
 TGLCamera.cxx:516
 TGLCamera.cxx:517
 TGLCamera.cxx:518
 TGLCamera.cxx:519
 TGLCamera.cxx:520
 TGLCamera.cxx:521
 TGLCamera.cxx:522
 TGLCamera.cxx:523
 TGLCamera.cxx:524
 TGLCamera.cxx:525
 TGLCamera.cxx:526
 TGLCamera.cxx:527
 TGLCamera.cxx:528
 TGLCamera.cxx:529
 TGLCamera.cxx:530
 TGLCamera.cxx:531
 TGLCamera.cxx:532
 TGLCamera.cxx:533
 TGLCamera.cxx:534
 TGLCamera.cxx:535
 TGLCamera.cxx:536
 TGLCamera.cxx:537
 TGLCamera.cxx:538
 TGLCamera.cxx:539
 TGLCamera.cxx:540
 TGLCamera.cxx:541
 TGLCamera.cxx:542
 TGLCamera.cxx:543
 TGLCamera.cxx:544
 TGLCamera.cxx:545
 TGLCamera.cxx:546
 TGLCamera.cxx:547
 TGLCamera.cxx:548
 TGLCamera.cxx:549
 TGLCamera.cxx:550
 TGLCamera.cxx:551
 TGLCamera.cxx:552
 TGLCamera.cxx:553
 TGLCamera.cxx:554
 TGLCamera.cxx:555
 TGLCamera.cxx:556
 TGLCamera.cxx:557
 TGLCamera.cxx:558
 TGLCamera.cxx:559
 TGLCamera.cxx:560
 TGLCamera.cxx:561
 TGLCamera.cxx:562
 TGLCamera.cxx:563
 TGLCamera.cxx:564
 TGLCamera.cxx:565
 TGLCamera.cxx:566
 TGLCamera.cxx:567
 TGLCamera.cxx:568
 TGLCamera.cxx:569
 TGLCamera.cxx:570
 TGLCamera.cxx:571
 TGLCamera.cxx:572
 TGLCamera.cxx:573
 TGLCamera.cxx:574
 TGLCamera.cxx:575
 TGLCamera.cxx:576
 TGLCamera.cxx:577
 TGLCamera.cxx:578
 TGLCamera.cxx:579
 TGLCamera.cxx:580
 TGLCamera.cxx:581
 TGLCamera.cxx:582
 TGLCamera.cxx:583
 TGLCamera.cxx:584
 TGLCamera.cxx:585
 TGLCamera.cxx:586
 TGLCamera.cxx:587
 TGLCamera.cxx:588
 TGLCamera.cxx:589
 TGLCamera.cxx:590
 TGLCamera.cxx:591
 TGLCamera.cxx:592
 TGLCamera.cxx:593
 TGLCamera.cxx:594
 TGLCamera.cxx:595
 TGLCamera.cxx:596
 TGLCamera.cxx:597
 TGLCamera.cxx:598
 TGLCamera.cxx:599
 TGLCamera.cxx:600
 TGLCamera.cxx:601
 TGLCamera.cxx:602
 TGLCamera.cxx:603
 TGLCamera.cxx:604
 TGLCamera.cxx:605
 TGLCamera.cxx:606
 TGLCamera.cxx:607
 TGLCamera.cxx:608
 TGLCamera.cxx:609
 TGLCamera.cxx:610
 TGLCamera.cxx:611
 TGLCamera.cxx:612
 TGLCamera.cxx:613
 TGLCamera.cxx:614
 TGLCamera.cxx:615
 TGLCamera.cxx:616
 TGLCamera.cxx:617
 TGLCamera.cxx:618
 TGLCamera.cxx:619
 TGLCamera.cxx:620
 TGLCamera.cxx:621
 TGLCamera.cxx:622
 TGLCamera.cxx:623
 TGLCamera.cxx:624
 TGLCamera.cxx:625
 TGLCamera.cxx:626
 TGLCamera.cxx:627
 TGLCamera.cxx:628
 TGLCamera.cxx:629
 TGLCamera.cxx:630
 TGLCamera.cxx:631
 TGLCamera.cxx:632
 TGLCamera.cxx:633
 TGLCamera.cxx:634
 TGLCamera.cxx:635
 TGLCamera.cxx:636
 TGLCamera.cxx:637
 TGLCamera.cxx:638
 TGLCamera.cxx:639
 TGLCamera.cxx:640
 TGLCamera.cxx:641
 TGLCamera.cxx:642
 TGLCamera.cxx:643
 TGLCamera.cxx:644
 TGLCamera.cxx:645
 TGLCamera.cxx:646
 TGLCamera.cxx:647
 TGLCamera.cxx:648
 TGLCamera.cxx:649
 TGLCamera.cxx:650
 TGLCamera.cxx:651
 TGLCamera.cxx:652
 TGLCamera.cxx:653
 TGLCamera.cxx:654
 TGLCamera.cxx:655
 TGLCamera.cxx:656
 TGLCamera.cxx:657
 TGLCamera.cxx:658
 TGLCamera.cxx:659
 TGLCamera.cxx:660
 TGLCamera.cxx:661
 TGLCamera.cxx:662
 TGLCamera.cxx:663
 TGLCamera.cxx:664
 TGLCamera.cxx:665
 TGLCamera.cxx:666
 TGLCamera.cxx:667
 TGLCamera.cxx:668
 TGLCamera.cxx:669
 TGLCamera.cxx:670
 TGLCamera.cxx:671
 TGLCamera.cxx:672
 TGLCamera.cxx:673
 TGLCamera.cxx:674
 TGLCamera.cxx:675
 TGLCamera.cxx:676
 TGLCamera.cxx:677
 TGLCamera.cxx:678
 TGLCamera.cxx:679
 TGLCamera.cxx:680
 TGLCamera.cxx:681
 TGLCamera.cxx:682
 TGLCamera.cxx:683
 TGLCamera.cxx:684
 TGLCamera.cxx:685
 TGLCamera.cxx:686
 TGLCamera.cxx:687
 TGLCamera.cxx:688
 TGLCamera.cxx:689
 TGLCamera.cxx:690
 TGLCamera.cxx:691
 TGLCamera.cxx:692
 TGLCamera.cxx:693
 TGLCamera.cxx:694
 TGLCamera.cxx:695
 TGLCamera.cxx:696
 TGLCamera.cxx:697
 TGLCamera.cxx:698
 TGLCamera.cxx:699
 TGLCamera.cxx:700
 TGLCamera.cxx:701
 TGLCamera.cxx:702
 TGLCamera.cxx:703
 TGLCamera.cxx:704
 TGLCamera.cxx:705
 TGLCamera.cxx:706
 TGLCamera.cxx:707
 TGLCamera.cxx:708
 TGLCamera.cxx:709
 TGLCamera.cxx:710
 TGLCamera.cxx:711
 TGLCamera.cxx:712
 TGLCamera.cxx:713
 TGLCamera.cxx:714
 TGLCamera.cxx:715
 TGLCamera.cxx:716
 TGLCamera.cxx:717
 TGLCamera.cxx:718
 TGLCamera.cxx:719
 TGLCamera.cxx:720
 TGLCamera.cxx:721
 TGLCamera.cxx:722
 TGLCamera.cxx:723
 TGLCamera.cxx:724
 TGLCamera.cxx:725
 TGLCamera.cxx:726
 TGLCamera.cxx:727
 TGLCamera.cxx:728
 TGLCamera.cxx:729
 TGLCamera.cxx:730
 TGLCamera.cxx:731
 TGLCamera.cxx:732
 TGLCamera.cxx:733
 TGLCamera.cxx:734
 TGLCamera.cxx:735
 TGLCamera.cxx:736
 TGLCamera.cxx:737
 TGLCamera.cxx:738
 TGLCamera.cxx:739
 TGLCamera.cxx:740
 TGLCamera.cxx:741
 TGLCamera.cxx:742
 TGLCamera.cxx:743
 TGLCamera.cxx:744
 TGLCamera.cxx:745
 TGLCamera.cxx:746
 TGLCamera.cxx:747
 TGLCamera.cxx:748
 TGLCamera.cxx:749
 TGLCamera.cxx:750
 TGLCamera.cxx:751
 TGLCamera.cxx:752
 TGLCamera.cxx:753
 TGLCamera.cxx:754
 TGLCamera.cxx:755
 TGLCamera.cxx:756
 TGLCamera.cxx:757
 TGLCamera.cxx:758
 TGLCamera.cxx:759
 TGLCamera.cxx:760
 TGLCamera.cxx:761
 TGLCamera.cxx:762
 TGLCamera.cxx:763
 TGLCamera.cxx:764
 TGLCamera.cxx:765
 TGLCamera.cxx:766
 TGLCamera.cxx:767
 TGLCamera.cxx:768
 TGLCamera.cxx:769
 TGLCamera.cxx:770
 TGLCamera.cxx:771
 TGLCamera.cxx:772
 TGLCamera.cxx:773
 TGLCamera.cxx:774
 TGLCamera.cxx:775
 TGLCamera.cxx:776
 TGLCamera.cxx:777
 TGLCamera.cxx:778
 TGLCamera.cxx:779
 TGLCamera.cxx:780
 TGLCamera.cxx:781
 TGLCamera.cxx:782
 TGLCamera.cxx:783
 TGLCamera.cxx:784
 TGLCamera.cxx:785
 TGLCamera.cxx:786
 TGLCamera.cxx:787
 TGLCamera.cxx:788
 TGLCamera.cxx:789
 TGLCamera.cxx:790
 TGLCamera.cxx:791
 TGLCamera.cxx:792
 TGLCamera.cxx:793
 TGLCamera.cxx:794
 TGLCamera.cxx:795
 TGLCamera.cxx:796
 TGLCamera.cxx:797
 TGLCamera.cxx:798
 TGLCamera.cxx:799
 TGLCamera.cxx:800
 TGLCamera.cxx:801
 TGLCamera.cxx:802
 TGLCamera.cxx:803
 TGLCamera.cxx:804
 TGLCamera.cxx:805
 TGLCamera.cxx:806
 TGLCamera.cxx:807
 TGLCamera.cxx:808
 TGLCamera.cxx:809
 TGLCamera.cxx:810
 TGLCamera.cxx:811
 TGLCamera.cxx:812
 TGLCamera.cxx:813
 TGLCamera.cxx:814
 TGLCamera.cxx:815
 TGLCamera.cxx:816
 TGLCamera.cxx:817
 TGLCamera.cxx:818
 TGLCamera.cxx:819
 TGLCamera.cxx:820
 TGLCamera.cxx:821
 TGLCamera.cxx:822
 TGLCamera.cxx:823
 TGLCamera.cxx:824
 TGLCamera.cxx:825
 TGLCamera.cxx:826
 TGLCamera.cxx:827
 TGLCamera.cxx:828
 TGLCamera.cxx:829
 TGLCamera.cxx:830
 TGLCamera.cxx:831
 TGLCamera.cxx:832
 TGLCamera.cxx:833
 TGLCamera.cxx:834
 TGLCamera.cxx:835
 TGLCamera.cxx:836
 TGLCamera.cxx:837
 TGLCamera.cxx:838
 TGLCamera.cxx:839
 TGLCamera.cxx:840
 TGLCamera.cxx:841
 TGLCamera.cxx:842
 TGLCamera.cxx:843
 TGLCamera.cxx:844
 TGLCamera.cxx:845
 TGLCamera.cxx:846
 TGLCamera.cxx:847
 TGLCamera.cxx:848
 TGLCamera.cxx:849
 TGLCamera.cxx:850
 TGLCamera.cxx:851
 TGLCamera.cxx:852
 TGLCamera.cxx:853
 TGLCamera.cxx:854
 TGLCamera.cxx:855
 TGLCamera.cxx:856
 TGLCamera.cxx:857
 TGLCamera.cxx:858
 TGLCamera.cxx:859
 TGLCamera.cxx:860
 TGLCamera.cxx:861
 TGLCamera.cxx:862
 TGLCamera.cxx:863
 TGLCamera.cxx:864
 TGLCamera.cxx:865
 TGLCamera.cxx:866
 TGLCamera.cxx:867
 TGLCamera.cxx:868
 TGLCamera.cxx:869
 TGLCamera.cxx:870
 TGLCamera.cxx:871
 TGLCamera.cxx:872
 TGLCamera.cxx:873
 TGLCamera.cxx:874
 TGLCamera.cxx:875
 TGLCamera.cxx:876
 TGLCamera.cxx:877
 TGLCamera.cxx:878
 TGLCamera.cxx:879
 TGLCamera.cxx:880
 TGLCamera.cxx:881
 TGLCamera.cxx:882
 TGLCamera.cxx:883
 TGLCamera.cxx:884
 TGLCamera.cxx:885
 TGLCamera.cxx:886
 TGLCamera.cxx:887
 TGLCamera.cxx:888
 TGLCamera.cxx:889
 TGLCamera.cxx:890
 TGLCamera.cxx:891
 TGLCamera.cxx:892
 TGLCamera.cxx:893
 TGLCamera.cxx:894
 TGLCamera.cxx:895
 TGLCamera.cxx:896
 TGLCamera.cxx:897
 TGLCamera.cxx:898
 TGLCamera.cxx:899
 TGLCamera.cxx:900
 TGLCamera.cxx:901
 TGLCamera.cxx:902
 TGLCamera.cxx:903
 TGLCamera.cxx:904
 TGLCamera.cxx:905
 TGLCamera.cxx:906
 TGLCamera.cxx:907
 TGLCamera.cxx:908
 TGLCamera.cxx:909
 TGLCamera.cxx:910
 TGLCamera.cxx:911
 TGLCamera.cxx:912
 TGLCamera.cxx:913
 TGLCamera.cxx:914
 TGLCamera.cxx:915
 TGLCamera.cxx:916
 TGLCamera.cxx:917
 TGLCamera.cxx:918
 TGLCamera.cxx:919
 TGLCamera.cxx:920
 TGLCamera.cxx:921
 TGLCamera.cxx:922
 TGLCamera.cxx:923
 TGLCamera.cxx:924
 TGLCamera.cxx:925
 TGLCamera.cxx:926
 TGLCamera.cxx:927
 TGLCamera.cxx:928
 TGLCamera.cxx:929
 TGLCamera.cxx:930
 TGLCamera.cxx:931
 TGLCamera.cxx:932
 TGLCamera.cxx:933
 TGLCamera.cxx:934
 TGLCamera.cxx:935
 TGLCamera.cxx:936
 TGLCamera.cxx:937
 TGLCamera.cxx:938