// @(#)root/gl:$Name:  $:$Id: TGLRotateManip.cxx
// Author:  Richard Maunder  04/10/2005

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGLRotateManip                                                       //
//                                                                      //
// Rotate manipulator - attaches to physical shape and draws local axes //
// widgets - rings drawn from attached physical center, in plane defined// 
// by axis. User can mouse over (turns yellow) and L click/drag to      //
// rotate attached physical round the ring center.                      // 
// Widgets use standard 3D package axes colours: X red, Y green, Z blue.//
//////////////////////////////////////////////////////////////////////////

ClassImp(TGLRotateManip)

//______________________________________________________________________________
 TGLRotateManip::TGLRotateManip(TGLViewer & viewer) : 
   TGLManip(viewer),
   fShallowRing(kFALSE), fShallowFront(kTRUE),
   fActiveRingPlane(TGLVector3(1.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
   fActiveRingCenter(TGLVertex3(0.0, 0.0, 0.0)),
   fRingLine(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
   fRingLineOld(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0))
{
   // Construct rotation manipulator, attached to supplied TGLViewer 
   // 'viewer', not bound to any physical shape.
}

//______________________________________________________________________________
 TGLRotateManip::TGLRotateManip(TGLViewer & viewer, TGLPhysicalShape * shape) : 
   TGLManip(viewer, shape),
   fShallowRing(kFALSE), fShallowFront(kTRUE),
   fActiveRingPlane(TGLVector3(1.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
   fActiveRingCenter(TGLVertex3(0.0, 0.0, 0.0)),
   fRingLine(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
   fRingLineOld(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0))
{
   // Construct rotation manipulator, attached to supplied TGLViewer 
   // 'viewer', bound to TGLPhysicalShape 'shape'.
}

//______________________________________________________________________________
 TGLRotateManip::~TGLRotateManip() 
{
   // Destory the rotation manipulator
}
   
//______________________________________________________________________________
 void TGLRotateManip::Draw(const TGLCamera & camera) const
{
   // Draw rotate manipulator - axis rings drawn from attached physical center,
   // in plane defined by axis as normal, in red(X), green(Y) and blue(Z), with 
   // white center sphere. If selected widget (mouse over) this is drawn in active 
   // colour (yellow).
   if (!fShape) {
      return;
   }

   const TGLBoundingBox & box = fShape->BoundingBox();
   Double_t widgetScale = CalcDrawScale(box, camera);
   Double_t ringRadius = widgetScale*10.0;

   // Get permitted manipulations on shape
   TGLPhysicalShape::EManip manip = fShape->GetManip();

   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glDisable(GL_CULL_FACE);

   // Draw three axis rings where permitted
   // Not drawing will prevent interaction
   // GL name loading for hit testing - 0 reserved for no selection
   if (manip & TGLPhysicalShape::kRotateX) {
      glPushName(1);
      TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, 
                        fSelectedWidget == 1 ? fgYellow : fgRed);
      glPopName();
   } else {
      TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, fgGrey);
   }
   if (manip & TGLPhysicalShape::kRotateY) {
      glPushName(2);
      TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, 
                        fSelectedWidget == 2 ? fgYellow : fgGreen);
      glPopName();
   } else {
      TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, fgGrey);
   }
   if (manip & TGLPhysicalShape::kRotateZ) {
      glPushName(3);
      TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, 
                        fSelectedWidget == 3 ? fgYellow : fgBlue);
      glPopName();
   } else {
      TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, fgGrey);
   }
   // Draw white center sphere
   TGLUtil::DrawSphere(box.Center(), ringRadius/20.0, fgWhite);

   // Indicate we are in ring follow (non-shallow) mode 
   // by drawing line from center to dragged ring point 
   if (fActive) {
      if (fShallowRing) {
         TGLVertex3 eyeOnRing;
         if (fShallowFront) {
            eyeOnRing = fActiveRingCenter - (camera.EyeDirection()*ringRadius);
         } else {
            eyeOnRing = fActiveRingCenter + (camera.EyeDirection()*ringRadius);
         }

         eyeOnRing = fActiveRingPlane.NearestOn(eyeOnRing);
         TGLVector3 arrowDir = Cross(fActiveRingPlane.Norm(), eyeOnRing - fActiveRingCenter);
         arrowDir.Normalise();
         TGLUtil::DrawLine(eyeOnRing, arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, widgetScale, fgYellow);
         TGLUtil::DrawLine(eyeOnRing, -arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, widgetScale, fgYellow);
      } else {
         TGLVector3 activeVector = fRingLine.Vector();
         activeVector.Normalise();
         activeVector *= ringRadius;
         TGLUtil::DrawLine(fRingLine.Start(), activeVector, 
                           TGLUtil::kLineHeadNone, widgetScale, fgYellow);
      }
   }

   glEnable(GL_CULL_FACE);
   glDisable(GL_BLEND);
}

//______________________________________________________________________________
 Bool_t TGLRotateManip::HandleButton(const Event_t * event, const TGLCamera & camera)
{
   // Handle mouse button event over manipulator - returns kTRUE if redraw required 
   // kFALSE otherwise.
   Bool_t captured = TGLManip::HandleButton(event, camera);

   if (captured) {
      // Find active selected axis
      UInt_t axisIndex = fSelectedWidget - 1; // Ugg sort out axis / widget id mapping
      TGLVector3 widgetAxis = fShape->BoundingBox().Axis(axisIndex, kTRUE);

      // Construct plane for the axis ring, using normal and center point
      fActiveRingPlane.Set(widgetAxis, fShape->BoundingBox().Center());
      fActiveRingCenter.Set(fShape->BoundingBox().Center());

      fRingLineOld = fRingLine = CalculateRingLine(fLastMouse, camera);
      
      // Is plane at shallow angle to eye line if angle between normal of plane and
      // eye line is ~90 deg (PI/4)
      Double_t planeEyeAngle = Angle(fActiveRingPlane.Norm(), camera.EyeDirection()) - TMath::ASin(1.0);
      Double_t shallowDelta = 0.15;
      if ((planeEyeAngle > -shallowDelta) && (planeEyeAngle < shallowDelta)) {  
         fShallowRing = kTRUE;

         // Work out ring follow direction - if clicked on back or front of ring.
         // If plane/eye angle very shallow force to front
			
			/* DISABLED - Force onto front always */
			fShallowFront = kTRUE;
			/*
         if ((planeEyeAngle > -shallowDelta/3.0) && (planeEyeAngle < shallowDelta/3.0) ||
             Dot(fRingLine.Vector(), camera.FrustumPlane(TGLCamera::kNear).Norm()) < 0.0) {
            fShallowFront = kTRUE;
         } else {
            fShallowFront = kFALSE;
         }*/
      } else {
         fShallowRing = kFALSE;
      }
   }

   return captured;
}

//______________________________________________________________________________
 Bool_t TGLRotateManip::HandleMotion(const Event_t * event, const TGLCamera & camera)
{
   // Handle mouse motion over manipulator - if active (selected widget) rotate 
   // physical around selected ring widget plane normal. Returns kTRUE if redraw 
   // required kFALSE otherwise.
   if (fActive) {
      TPoint newMouse(event->fX, event->fY);

      // Calculate singed angle delta between old and new ring position using
      Double_t angle = CalculateAngleDelta(newMouse, camera);
      fShape->Rotate(fActiveRingCenter, fActiveRingPlane.Norm(), angle);
      fLastMouse = newMouse;
      return kTRUE;
   } else {
      return TGLManip::HandleMotion(event, camera);
   }
}

//______________________________________________________________________________
 Double_t TGLRotateManip::CalculateAngleDelta(const TPoint & mouse, const TGLCamera & camera)
{
   // Calculate angle delta for rotation based on new mouse position
   if (fShallowRing) {
      std::pair<Bool_t, TGLLine3> nearLineIntersection = Intersection(fActiveRingPlane, 
                                                                      camera.FrustumPlane(TGLCamera::kNear));
      if (!nearLineIntersection.first) {
         Error("TGLRotateManip::CalculateAngleDelta", "active ring plane parallel to near clip?");
         return 1.0;
      }
      TGLLine3 nearLine = nearLineIntersection.second;
      TGLVector3 activePlaneNear = camera.WorldDeltaToViewport(nearLine.Start(), nearLine.Vector());
		activePlaneNear.Normalise();
      TGLVector3 mouseDelta(mouse.GetX() - fLastMouse.GetX(),
                            -(mouse.GetY() - fLastMouse.GetY()),
                            0.0);

      Double_t angle = Dot(activePlaneNear, mouseDelta) / 150.0;
      if (fShallowFront) {
         return -angle;
      } else {
         return angle;
      }
   } else {
      fRingLineOld = fRingLine;
      fRingLine = CalculateRingLine(fLastMouse, camera);
      return Angle(fRingLineOld.Vector(), fRingLine.Vector(), fActiveRingPlane.Norm());
   }
}

//______________________________________________________________________________
 TGLLine3 TGLRotateManip::CalculateRingLine(const TPoint & mouse, const TGLCamera & camera) const
{
   // Calculated interaction line between 'mouse' viewport point, and current selected
   // widget (ring), under supplied 'camera' projection.
   // Find mouse position in viewport coords
   TPoint mouseViewport(mouse);
   camera.WindowToViewport(mouseViewport);

   // Find projection of mouse into world
   TGLLine3 viewportProjection = camera.ViewportToWorld(mouseViewport);

   // Find rotation line from ring center to this intersection on plane
   std::pair<Bool_t, TGLVertex3> ringPlaneInter =  Intersection(fActiveRingPlane, viewportProjection, kTRUE);
   
   // If intersection fails then ring is parallel to eye line - in this case
   // force line to run from center back towards viewer (opposite eye line)
   if (!ringPlaneInter.first) {
      return TGLLine3(fActiveRingCenter, -camera.EyeDirection());
   }
   return TGLLine3(fActiveRingCenter, ringPlaneInter.second);
}



ROOT page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.