ROOT logo
// @(#)root/gl:$Id$
// 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"

//______________________________________________________________________________
//                                                                      
// 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);

//______________________________________________________________________________
Double_t TGLRotateManip::Angle(const TGLVector3& v1, const TGLVector3& v2)
{
   // Calculate unsigned angle between vectors v1 and v2
   return TMath::ACos(Dot(v1, v2) / (v1.Mag() * v2.Mag()));
}

//______________________________________________________________________________
Double_t TGLRotateManip::Angle(const TGLVector3& v1, const TGLVector3& v2,
                               const TGLVector3& ref)
{
   // Calculate signed angle between vectors v1 and v2, using ref to define right handed coord system
   // If v1.v2 parallel to ref vector: +ive for clockwise, -ive for anticlockwise
   // If v1.v2 antiparallel to ref vector: -ive for clockwise, +ive for anticlockwise
   TGLVector3 cross = Cross(v1, v2);
   if (Dot(cross,ref) > 0.0) {
      return Angle(v1, v2);
   } else {
      return -Angle(v1, v2);
   }
}

//______________________________________________________________________________
TGLRotateManip::TGLRotateManip() :
   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 not bound to any physical shape.
}

//______________________________________________________________________________
TGLRotateManip::TGLRotateManip(TGLPhysicalShape* shape) :
   TGLManip(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 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;
   }

   // Get draw scales
   const TGLBoundingBox& box = fShape->BoundingBox();
   Double_t baseScale;
   TGLVector3 axisScale[3];
   CalcDrawScale(box, camera, baseScale, axisScale);
   Double_t ringRadius = baseScale*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);

   TGLUtil::TDrawQualityScaler hiRes(3);

   // 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, ColorFor(1));
      glPopName();
   } else {
      TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, TGLUtil::fgGrey);
   }
   if (manip & TGLPhysicalShape::kRotateY) {
      glPushName(2);
      TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, ColorFor(2));
      glPopName();
   } else {
      TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, TGLUtil::fgGrey);
   }
   if (manip & TGLPhysicalShape::kRotateZ) {
      glPushName(3);
      TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, ColorFor(3));
      glPopName();
   } else {
      TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, TGLUtil::fgGrey);
   }
   // Draw white center sphere
   TGLUtil::DrawSphere(box.Center(), ringRadius/20.0, TGLUtil::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, baseScale, TGLUtil::fgYellow);
         TGLUtil::DrawLine(eyeOnRing, -arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, baseScale, TGLUtil::fgYellow);
      } else {
         TGLVector3 activeVector = fRingLine.Vector();
         activeVector.Normalise();
         activeVector *= ringRadius;
         TGLUtil::DrawLine(fRingLine.Start(), activeVector,
                           TGLUtil::kLineHeadNone, baseScale, TGLUtil::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;
   }
   return kFALSE;
}

//______________________________________________________________________________
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);
}

 TGLRotateManip.cxx:1
 TGLRotateManip.cxx:2
 TGLRotateManip.cxx:3
 TGLRotateManip.cxx:4
 TGLRotateManip.cxx:5
 TGLRotateManip.cxx:6
 TGLRotateManip.cxx:7
 TGLRotateManip.cxx:8
 TGLRotateManip.cxx:9
 TGLRotateManip.cxx:10
 TGLRotateManip.cxx:11
 TGLRotateManip.cxx:12
 TGLRotateManip.cxx:13
 TGLRotateManip.cxx:14
 TGLRotateManip.cxx:15
 TGLRotateManip.cxx:16
 TGLRotateManip.cxx:17
 TGLRotateManip.cxx:18
 TGLRotateManip.cxx:19
 TGLRotateManip.cxx:20
 TGLRotateManip.cxx:21
 TGLRotateManip.cxx:22
 TGLRotateManip.cxx:23
 TGLRotateManip.cxx:24
 TGLRotateManip.cxx:25
 TGLRotateManip.cxx:26
 TGLRotateManip.cxx:27
 TGLRotateManip.cxx:28
 TGLRotateManip.cxx:29
 TGLRotateManip.cxx:30
 TGLRotateManip.cxx:31
 TGLRotateManip.cxx:32
 TGLRotateManip.cxx:33
 TGLRotateManip.cxx:34
 TGLRotateManip.cxx:35
 TGLRotateManip.cxx:36
 TGLRotateManip.cxx:37
 TGLRotateManip.cxx:38
 TGLRotateManip.cxx:39
 TGLRotateManip.cxx:40
 TGLRotateManip.cxx:41
 TGLRotateManip.cxx:42
 TGLRotateManip.cxx:43
 TGLRotateManip.cxx:44
 TGLRotateManip.cxx:45
 TGLRotateManip.cxx:46
 TGLRotateManip.cxx:47
 TGLRotateManip.cxx:48
 TGLRotateManip.cxx:49
 TGLRotateManip.cxx:50
 TGLRotateManip.cxx:51
 TGLRotateManip.cxx:52
 TGLRotateManip.cxx:53
 TGLRotateManip.cxx:54
 TGLRotateManip.cxx:55
 TGLRotateManip.cxx:56
 TGLRotateManip.cxx:57
 TGLRotateManip.cxx:58
 TGLRotateManip.cxx:59
 TGLRotateManip.cxx:60
 TGLRotateManip.cxx:61
 TGLRotateManip.cxx:62
 TGLRotateManip.cxx:63
 TGLRotateManip.cxx:64
 TGLRotateManip.cxx:65
 TGLRotateManip.cxx:66
 TGLRotateManip.cxx:67
 TGLRotateManip.cxx:68
 TGLRotateManip.cxx:69
 TGLRotateManip.cxx:70
 TGLRotateManip.cxx:71
 TGLRotateManip.cxx:72
 TGLRotateManip.cxx:73
 TGLRotateManip.cxx:74
 TGLRotateManip.cxx:75
 TGLRotateManip.cxx:76
 TGLRotateManip.cxx:77
 TGLRotateManip.cxx:78
 TGLRotateManip.cxx:79
 TGLRotateManip.cxx:80
 TGLRotateManip.cxx:81
 TGLRotateManip.cxx:82
 TGLRotateManip.cxx:83
 TGLRotateManip.cxx:84
 TGLRotateManip.cxx:85
 TGLRotateManip.cxx:86
 TGLRotateManip.cxx:87
 TGLRotateManip.cxx:88
 TGLRotateManip.cxx:89
 TGLRotateManip.cxx:90
 TGLRotateManip.cxx:91
 TGLRotateManip.cxx:92
 TGLRotateManip.cxx:93
 TGLRotateManip.cxx:94
 TGLRotateManip.cxx:95
 TGLRotateManip.cxx:96
 TGLRotateManip.cxx:97
 TGLRotateManip.cxx:98
 TGLRotateManip.cxx:99
 TGLRotateManip.cxx:100
 TGLRotateManip.cxx:101
 TGLRotateManip.cxx:102
 TGLRotateManip.cxx:103
 TGLRotateManip.cxx:104
 TGLRotateManip.cxx:105
 TGLRotateManip.cxx:106
 TGLRotateManip.cxx:107
 TGLRotateManip.cxx:108
 TGLRotateManip.cxx:109
 TGLRotateManip.cxx:110
 TGLRotateManip.cxx:111
 TGLRotateManip.cxx:112
 TGLRotateManip.cxx:113
 TGLRotateManip.cxx:114
 TGLRotateManip.cxx:115
 TGLRotateManip.cxx:116
 TGLRotateManip.cxx:117
 TGLRotateManip.cxx:118
 TGLRotateManip.cxx:119
 TGLRotateManip.cxx:120
 TGLRotateManip.cxx:121
 TGLRotateManip.cxx:122
 TGLRotateManip.cxx:123
 TGLRotateManip.cxx:124
 TGLRotateManip.cxx:125
 TGLRotateManip.cxx:126
 TGLRotateManip.cxx:127
 TGLRotateManip.cxx:128
 TGLRotateManip.cxx:129
 TGLRotateManip.cxx:130
 TGLRotateManip.cxx:131
 TGLRotateManip.cxx:132
 TGLRotateManip.cxx:133
 TGLRotateManip.cxx:134
 TGLRotateManip.cxx:135
 TGLRotateManip.cxx:136
 TGLRotateManip.cxx:137
 TGLRotateManip.cxx:138
 TGLRotateManip.cxx:139
 TGLRotateManip.cxx:140
 TGLRotateManip.cxx:141
 TGLRotateManip.cxx:142
 TGLRotateManip.cxx:143
 TGLRotateManip.cxx:144
 TGLRotateManip.cxx:145
 TGLRotateManip.cxx:146
 TGLRotateManip.cxx:147
 TGLRotateManip.cxx:148
 TGLRotateManip.cxx:149
 TGLRotateManip.cxx:150
 TGLRotateManip.cxx:151
 TGLRotateManip.cxx:152
 TGLRotateManip.cxx:153
 TGLRotateManip.cxx:154
 TGLRotateManip.cxx:155
 TGLRotateManip.cxx:156
 TGLRotateManip.cxx:157
 TGLRotateManip.cxx:158
 TGLRotateManip.cxx:159
 TGLRotateManip.cxx:160
 TGLRotateManip.cxx:161
 TGLRotateManip.cxx:162
 TGLRotateManip.cxx:163
 TGLRotateManip.cxx:164
 TGLRotateManip.cxx:165
 TGLRotateManip.cxx:166
 TGLRotateManip.cxx:167
 TGLRotateManip.cxx:168
 TGLRotateManip.cxx:169
 TGLRotateManip.cxx:170
 TGLRotateManip.cxx:171
 TGLRotateManip.cxx:172
 TGLRotateManip.cxx:173
 TGLRotateManip.cxx:174
 TGLRotateManip.cxx:175
 TGLRotateManip.cxx:176
 TGLRotateManip.cxx:177
 TGLRotateManip.cxx:178
 TGLRotateManip.cxx:179
 TGLRotateManip.cxx:180
 TGLRotateManip.cxx:181
 TGLRotateManip.cxx:182
 TGLRotateManip.cxx:183
 TGLRotateManip.cxx:184
 TGLRotateManip.cxx:185
 TGLRotateManip.cxx:186
 TGLRotateManip.cxx:187
 TGLRotateManip.cxx:188
 TGLRotateManip.cxx:189
 TGLRotateManip.cxx:190
 TGLRotateManip.cxx:191
 TGLRotateManip.cxx:192
 TGLRotateManip.cxx:193
 TGLRotateManip.cxx:194
 TGLRotateManip.cxx:195
 TGLRotateManip.cxx:196
 TGLRotateManip.cxx:197
 TGLRotateManip.cxx:198
 TGLRotateManip.cxx:199
 TGLRotateManip.cxx:200
 TGLRotateManip.cxx:201
 TGLRotateManip.cxx:202
 TGLRotateManip.cxx:203
 TGLRotateManip.cxx:204
 TGLRotateManip.cxx:205
 TGLRotateManip.cxx:206
 TGLRotateManip.cxx:207
 TGLRotateManip.cxx:208
 TGLRotateManip.cxx:209
 TGLRotateManip.cxx:210
 TGLRotateManip.cxx:211
 TGLRotateManip.cxx:212
 TGLRotateManip.cxx:213
 TGLRotateManip.cxx:214
 TGLRotateManip.cxx:215
 TGLRotateManip.cxx:216
 TGLRotateManip.cxx:217
 TGLRotateManip.cxx:218
 TGLRotateManip.cxx:219
 TGLRotateManip.cxx:220
 TGLRotateManip.cxx:221
 TGLRotateManip.cxx:222
 TGLRotateManip.cxx:223
 TGLRotateManip.cxx:224
 TGLRotateManip.cxx:225
 TGLRotateManip.cxx:226
 TGLRotateManip.cxx:227
 TGLRotateManip.cxx:228
 TGLRotateManip.cxx:229
 TGLRotateManip.cxx:230
 TGLRotateManip.cxx:231
 TGLRotateManip.cxx:232
 TGLRotateManip.cxx:233
 TGLRotateManip.cxx:234
 TGLRotateManip.cxx:235
 TGLRotateManip.cxx:236
 TGLRotateManip.cxx:237
 TGLRotateManip.cxx:238
 TGLRotateManip.cxx:239
 TGLRotateManip.cxx:240
 TGLRotateManip.cxx:241
 TGLRotateManip.cxx:242
 TGLRotateManip.cxx:243
 TGLRotateManip.cxx:244
 TGLRotateManip.cxx:245
 TGLRotateManip.cxx:246
 TGLRotateManip.cxx:247
 TGLRotateManip.cxx:248
 TGLRotateManip.cxx:249
 TGLRotateManip.cxx:250
 TGLRotateManip.cxx:251
 TGLRotateManip.cxx:252
 TGLRotateManip.cxx:253
 TGLRotateManip.cxx:254
 TGLRotateManip.cxx:255
 TGLRotateManip.cxx:256
 TGLRotateManip.cxx:257
 TGLRotateManip.cxx:258
 TGLRotateManip.cxx:259
 TGLRotateManip.cxx:260
 TGLRotateManip.cxx:261
 TGLRotateManip.cxx:262
 TGLRotateManip.cxx:263
 TGLRotateManip.cxx:264
 TGLRotateManip.cxx:265
 TGLRotateManip.cxx:266
 TGLRotateManip.cxx:267
 TGLRotateManip.cxx:268
 TGLRotateManip.cxx:269
 TGLRotateManip.cxx:270
 TGLRotateManip.cxx:271
 TGLRotateManip.cxx:272
 TGLRotateManip.cxx:273
 TGLRotateManip.cxx:274
 TGLRotateManip.cxx:275
 TGLRotateManip.cxx:276
 TGLRotateManip.cxx:277
 TGLRotateManip.cxx:278
 TGLRotateManip.cxx:279
 TGLRotateManip.cxx:280
 TGLRotateManip.cxx:281
 TGLRotateManip.cxx:282
 TGLRotateManip.cxx:283
 TGLRotateManip.cxx:284
 TGLRotateManip.cxx:285