// @(#)root/eve:$Id$
// Author: Matevz Tadel 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 "TGLAutoRotator.h"

#include "TGLPhysicalShape.h"
#include "TGLLogicalShape.h"
#include "TGLViewer.h"
#include "TGLCamera.h"
#include "TGLScene.h"

#include "TMath.h"
#include "TTimer.h"
#include "TStopwatch.h"

//______________________________________________________________________________
//
// Automatically rotates GL camera.
// 
// W's are angular velocities.
// ATheta -- Theta amplitude in units of Pi/2.
// ADolly -- In/out amplitude in units of initial distance.
//
// Can also save images automatically.

// fGUIOutMode is used internally between TGLAutoRotator and TGLViewerEditor,
// allowed values are:
//   1 - animated gif
//   2 - a series of png images

ClassImp(TGLAutoRotator);

//______________________________________________________________________________
TGLAutoRotator::TGLAutoRotator(TGLViewer* v) :
   fViewer(v), fCamera(0),
   fTimer(new TTimer), fWatch(new TStopwatch),
   fRotateScene(kFALSE),
   fDeltaPhi(0.005),
   fDt    (0.01),
   fWPhi  (0.40),
   fWTheta(0.15), fATheta(0.5),
   fWDolly(0.30), fADolly(0.4),
   fTimerRunning(kFALSE),
   fImageCount(0), fImageAutoSave(kFALSE),
   fImageGUIBaseName("animation"), fImageGUIOutMode(1)
{
   // Constructor.

   fTimer->Connect("Timeout()", "TGLAutoRotator", this, "Timeout()");
}

//______________________________________________________________________________
TGLAutoRotator::~TGLAutoRotator()
{
   // Destructor.

   delete fWatch;
   delete fTimer;
}

//==============================================================================

//______________________________________________________________________________
void TGLAutoRotator::SetDt(Double_t dt)
{
   // Set time between two redraws in seconds.
   // Range: 0.001 -> 1.

   fDt = TMath::Range(0.01, 1.0, dt);
   if (fTimerRunning)
   {
      fTimer->SetTime(TMath::Nint(1000*fDt));
      fTimer->Reset();
   }
}

//______________________________________________________________________________
void TGLAutoRotator::SetATheta(Double_t a)
{
   // Set relative amplitude of theta oscilation.
   // Value range: 0.01 -> 1.

   a = TMath::Range(0.01, 1.0, a);
   if (fTimerRunning)
   {
      fThetaA0 = fThetaA0 * a / fATheta;
   }
   fATheta = a;
}

//______________________________________________________________________________
void TGLAutoRotator::SetADolly(Double_t a)
{
   // Set relative amplitude of forward/backward oscilation.
   // Value range: 0.01 -> 1.

  a = TMath::Range(0.01, 1.0, a);
  if (fTimerRunning)
  {
     fDollyA0 = fDollyA0 * a / fADolly;
  }
  fADolly = a;
}

//==============================================================================

//______________________________________________________________________________
void TGLAutoRotator::Start()
{
   // Start the auto-rotator.

   if (fTimerRunning)
   {
      Stop();
   }

   fCamera = & fViewer->CurrentCamera();

   fThetaA0 = fATheta * TMath::PiOver2();
   fDollyA0 = fADolly * fCamera->GetCamTrans().GetBaseVec(4).Mag();

   fTimerRunning = kTRUE;
   fTimer->SetTime(TMath::Nint(1000*fDt));
   fTimer->Reset();
   fTimer->TurnOn();
   fWatch->Start();
}

//______________________________________________________________________________
void TGLAutoRotator::Stop()
{
   // Stop the auto-rotator.

   if (fTimerRunning)
   {
      fWatch->Stop();
      fTimer->TurnOff();
      fTimerRunning = kFALSE;
   }
}

//______________________________________________________________________________
void TGLAutoRotator::Timeout()
{
   // Called on every timer timeout. Moves / rotates the camera and optionally
   // produces a screenshot.

   if (!fTimerRunning || gTQSender != fTimer)
   {
      Error("Timeout", "Not running or not called via timer.");
      return;
   }

   using namespace TMath;

   fWatch->Stop();
   Double_t time = fWatch->RealTime();
   fWatch->Continue();

   if (fRotateScene) {
      RotateScene();
   } else {
      Double_t delta_p = fWPhi*fDt;
      Double_t delta_t = fThetaA0*fWTheta*Cos(fWTheta*time)*fDt;
      Double_t delta_d = fDollyA0*fWDolly*Cos(fWDolly*time)*fDt;
      Double_t th      = fCamera->GetTheta();

      if (th + delta_t > 3.0 || th + delta_t < 0.1416)
         delta_t = 0;

      fCamera->RotateRad(delta_t, delta_p);
      fCamera->RefCamTrans().MoveLF(1, -delta_d);
   }

   fViewer->RequestDraw(TGLRnrCtx::kLODHigh);

   if (fImageAutoSave)
   {
      TString filename;
      if (fImageName.Contains("%"))
      {
         filename.Form(fImageName, fImageCount);
      }
      else
      {
         filename = fImageName;
      }
      fViewer->SavePicture(filename);
      ++fImageCount;
   }
}

//==============================================================================

//______________________________________________________________________________
void TGLAutoRotator::StartImageAutoSaveAnimatedGif(const TString& filename)
{
   // Start saving into animated gif. The provided name will be used as it is,
   // so make sure to end it with '.gif+'.
   // Use convert tool from ImageMagic if you want to set a different delay or
   // enable looping.

   if ( ! filename.Contains(".gif+"))
   {
      Error("StartImageAutoSaveAnimatedGif", "Name should end with '.gif+'. Not starting.");
      return;
   }

   fImageName     = filename;
   fImageCount    = 0;
   fImageAutoSave = kTRUE;
}

//______________________________________________________________________________
void TGLAutoRotator::StartImageAutoSave(const TString& filename)
{
   // Start saving into a set of images. The provided name will be used as a
   // format to insert additional image sequence number so it should include
   // an '%' character. A good name would be something like:
   //   "image-%04d.png"
   // On GNU/Linux use mencoder and/or ffmpeg to bundle images into a movie.

   if ( ! filename.Contains("%"))
   {
      Error("StartImageAutoSave", "Name should include a '%%' character, like 'image-%%05d.png'. Not starting.");
      return;
   }

   fImageName     = filename;
   fImageCount    = 0;
   fImageAutoSave = kTRUE;
}

//______________________________________________________________________________
void TGLAutoRotator::StopImageAutoSave()
{
   // Stops automatic saving of images.

   fImageAutoSave = kFALSE;
}

//______________________________________________________________________________
void TGLAutoRotator::SetImageGUIOutMode(Int_t m)
{
   // Set output mode for GUI operation:
   //   1 - animated gif;
   //   2 - a series of pngs

   if (m < 1 || m > 2)
   {
      Warning("SetImageGUIOutMode", "Invalid value, ignoring");
      return;
   }
   fImageGUIOutMode = m;
}

//______________________________________________________________________________
void TGLAutoRotator::StartImageAutoSaveWithGUISettings()
{
   // Start auto-saving images as set-up via GUI.

   if (fImageGUIOutMode == 1)
   {
      TString name = fImageGUIBaseName + ".gif+";
      StartImageAutoSaveAnimatedGif(name);
   }
   else if (fImageGUIOutMode == 2)
   {
      TString name = fImageGUIBaseName + "-%05d.png";
      StartImageAutoSave(name);
   }
   else
   {
      Warning("StartImageAutoSaveWithGUISettings", "Unsupported mode '%d'.", fImageGUIOutMode);
   }
}

//______________________________________________________________________________
void TGLAutoRotator::RotateScene()
{
   //"Scene rotation": either find a special object,
   //which will be an axis of rotation (it's Z actually)
   //or use a "global" Z axis.
   TGLViewer::SceneInfoList_t & scenes = fViewer->fScenes;
   TGLViewer::SceneInfoList_i sceneIter = scenes.begin();
   
   for (; sceneIter != scenes.end(); ++sceneIter) {
     TGLScene::TSceneInfo *sceneInfo = dynamic_cast<TGLScene::TSceneInfo *>(*sceneIter);
      if (sceneInfo) {
         TGLPhysicalShape *axisShape = 0;
         TGLScene::ShapeVec_i shapeIter = sceneInfo->fShapesOfInterest.begin();
         for (; shapeIter != sceneInfo->fShapesOfInterest.end(); ++shapeIter) {
            TGLPhysicalShape * const testShape = const_cast<TGLPhysicalShape *>(*shapeIter);
            if (testShape && testShape->GetLogical()->ID()->TestBit(13)) {
               axisShape = testShape;
               break;
            }
         }
         
         TGLVector3 axis;
         TGLVertex3 center;
         
         if (!axisShape) {
            const TGLBoundingBox &bbox = sceneInfo->GetTransformedBBox();
            axis = bbox.Axis(2);
            center = bbox.Center();
         } else {
            axis = axisShape->BoundingBox().Axis(2);
            center = axisShape->BoundingBox().Center();
         }
         
         shapeIter = sceneInfo->fShapesOfInterest.begin();
         for (; shapeIter != sceneInfo->fShapesOfInterest.end(); ++shapeIter) {
            if (TGLPhysicalShape * const shape = const_cast<TGLPhysicalShape *>(*shapeIter))
               shape->Rotate(center, axis, fDeltaPhi);
         }
      }
   }
}
 TGLAutoRotator.cxx:1
 TGLAutoRotator.cxx:2
 TGLAutoRotator.cxx:3
 TGLAutoRotator.cxx:4
 TGLAutoRotator.cxx:5
 TGLAutoRotator.cxx:6
 TGLAutoRotator.cxx:7
 TGLAutoRotator.cxx:8
 TGLAutoRotator.cxx:9
 TGLAutoRotator.cxx:10
 TGLAutoRotator.cxx:11
 TGLAutoRotator.cxx:12
 TGLAutoRotator.cxx:13
 TGLAutoRotator.cxx:14
 TGLAutoRotator.cxx:15
 TGLAutoRotator.cxx:16
 TGLAutoRotator.cxx:17
 TGLAutoRotator.cxx:18
 TGLAutoRotator.cxx:19
 TGLAutoRotator.cxx:20
 TGLAutoRotator.cxx:21
 TGLAutoRotator.cxx:22
 TGLAutoRotator.cxx:23
 TGLAutoRotator.cxx:24
 TGLAutoRotator.cxx:25
 TGLAutoRotator.cxx:26
 TGLAutoRotator.cxx:27
 TGLAutoRotator.cxx:28
 TGLAutoRotator.cxx:29
 TGLAutoRotator.cxx:30
 TGLAutoRotator.cxx:31
 TGLAutoRotator.cxx:32
 TGLAutoRotator.cxx:33
 TGLAutoRotator.cxx:34
 TGLAutoRotator.cxx:35
 TGLAutoRotator.cxx:36
 TGLAutoRotator.cxx:37
 TGLAutoRotator.cxx:38
 TGLAutoRotator.cxx:39
 TGLAutoRotator.cxx:40
 TGLAutoRotator.cxx:41
 TGLAutoRotator.cxx:42
 TGLAutoRotator.cxx:43
 TGLAutoRotator.cxx:44
 TGLAutoRotator.cxx:45
 TGLAutoRotator.cxx:46
 TGLAutoRotator.cxx:47
 TGLAutoRotator.cxx:48
 TGLAutoRotator.cxx:49
 TGLAutoRotator.cxx:50
 TGLAutoRotator.cxx:51
 TGLAutoRotator.cxx:52
 TGLAutoRotator.cxx:53
 TGLAutoRotator.cxx:54
 TGLAutoRotator.cxx:55
 TGLAutoRotator.cxx:56
 TGLAutoRotator.cxx:57
 TGLAutoRotator.cxx:58
 TGLAutoRotator.cxx:59
 TGLAutoRotator.cxx:60
 TGLAutoRotator.cxx:61
 TGLAutoRotator.cxx:62
 TGLAutoRotator.cxx:63
 TGLAutoRotator.cxx:64
 TGLAutoRotator.cxx:65
 TGLAutoRotator.cxx:66
 TGLAutoRotator.cxx:67
 TGLAutoRotator.cxx:68
 TGLAutoRotator.cxx:69
 TGLAutoRotator.cxx:70
 TGLAutoRotator.cxx:71
 TGLAutoRotator.cxx:72
 TGLAutoRotator.cxx:73
 TGLAutoRotator.cxx:74
 TGLAutoRotator.cxx:75
 TGLAutoRotator.cxx:76
 TGLAutoRotator.cxx:77
 TGLAutoRotator.cxx:78
 TGLAutoRotator.cxx:79
 TGLAutoRotator.cxx:80
 TGLAutoRotator.cxx:81
 TGLAutoRotator.cxx:82
 TGLAutoRotator.cxx:83
 TGLAutoRotator.cxx:84
 TGLAutoRotator.cxx:85
 TGLAutoRotator.cxx:86
 TGLAutoRotator.cxx:87
 TGLAutoRotator.cxx:88
 TGLAutoRotator.cxx:89
 TGLAutoRotator.cxx:90
 TGLAutoRotator.cxx:91
 TGLAutoRotator.cxx:92
 TGLAutoRotator.cxx:93
 TGLAutoRotator.cxx:94
 TGLAutoRotator.cxx:95
 TGLAutoRotator.cxx:96
 TGLAutoRotator.cxx:97
 TGLAutoRotator.cxx:98
 TGLAutoRotator.cxx:99
 TGLAutoRotator.cxx:100
 TGLAutoRotator.cxx:101
 TGLAutoRotator.cxx:102
 TGLAutoRotator.cxx:103
 TGLAutoRotator.cxx:104
 TGLAutoRotator.cxx:105
 TGLAutoRotator.cxx:106
 TGLAutoRotator.cxx:107
 TGLAutoRotator.cxx:108
 TGLAutoRotator.cxx:109
 TGLAutoRotator.cxx:110
 TGLAutoRotator.cxx:111
 TGLAutoRotator.cxx:112
 TGLAutoRotator.cxx:113
 TGLAutoRotator.cxx:114
 TGLAutoRotator.cxx:115
 TGLAutoRotator.cxx:116
 TGLAutoRotator.cxx:117
 TGLAutoRotator.cxx:118
 TGLAutoRotator.cxx:119
 TGLAutoRotator.cxx:120
 TGLAutoRotator.cxx:121
 TGLAutoRotator.cxx:122
 TGLAutoRotator.cxx:123
 TGLAutoRotator.cxx:124
 TGLAutoRotator.cxx:125
 TGLAutoRotator.cxx:126
 TGLAutoRotator.cxx:127
 TGLAutoRotator.cxx:128
 TGLAutoRotator.cxx:129
 TGLAutoRotator.cxx:130
 TGLAutoRotator.cxx:131
 TGLAutoRotator.cxx:132
 TGLAutoRotator.cxx:133
 TGLAutoRotator.cxx:134
 TGLAutoRotator.cxx:135
 TGLAutoRotator.cxx:136
 TGLAutoRotator.cxx:137
 TGLAutoRotator.cxx:138
 TGLAutoRotator.cxx:139
 TGLAutoRotator.cxx:140
 TGLAutoRotator.cxx:141
 TGLAutoRotator.cxx:142
 TGLAutoRotator.cxx:143
 TGLAutoRotator.cxx:144
 TGLAutoRotator.cxx:145
 TGLAutoRotator.cxx:146
 TGLAutoRotator.cxx:147
 TGLAutoRotator.cxx:148
 TGLAutoRotator.cxx:149
 TGLAutoRotator.cxx:150
 TGLAutoRotator.cxx:151
 TGLAutoRotator.cxx:152
 TGLAutoRotator.cxx:153
 TGLAutoRotator.cxx:154
 TGLAutoRotator.cxx:155
 TGLAutoRotator.cxx:156
 TGLAutoRotator.cxx:157
 TGLAutoRotator.cxx:158
 TGLAutoRotator.cxx:159
 TGLAutoRotator.cxx:160
 TGLAutoRotator.cxx:161
 TGLAutoRotator.cxx:162
 TGLAutoRotator.cxx:163
 TGLAutoRotator.cxx:164
 TGLAutoRotator.cxx:165
 TGLAutoRotator.cxx:166
 TGLAutoRotator.cxx:167
 TGLAutoRotator.cxx:168
 TGLAutoRotator.cxx:169
 TGLAutoRotator.cxx:170
 TGLAutoRotator.cxx:171
 TGLAutoRotator.cxx:172
 TGLAutoRotator.cxx:173
 TGLAutoRotator.cxx:174
 TGLAutoRotator.cxx:175
 TGLAutoRotator.cxx:176
 TGLAutoRotator.cxx:177
 TGLAutoRotator.cxx:178
 TGLAutoRotator.cxx:179
 TGLAutoRotator.cxx:180
 TGLAutoRotator.cxx:181
 TGLAutoRotator.cxx:182
 TGLAutoRotator.cxx:183
 TGLAutoRotator.cxx:184
 TGLAutoRotator.cxx:185
 TGLAutoRotator.cxx:186
 TGLAutoRotator.cxx:187
 TGLAutoRotator.cxx:188
 TGLAutoRotator.cxx:189
 TGLAutoRotator.cxx:190
 TGLAutoRotator.cxx:191
 TGLAutoRotator.cxx:192
 TGLAutoRotator.cxx:193
 TGLAutoRotator.cxx:194
 TGLAutoRotator.cxx:195
 TGLAutoRotator.cxx:196
 TGLAutoRotator.cxx:197
 TGLAutoRotator.cxx:198
 TGLAutoRotator.cxx:199
 TGLAutoRotator.cxx:200
 TGLAutoRotator.cxx:201
 TGLAutoRotator.cxx:202
 TGLAutoRotator.cxx:203
 TGLAutoRotator.cxx:204
 TGLAutoRotator.cxx:205
 TGLAutoRotator.cxx:206
 TGLAutoRotator.cxx:207
 TGLAutoRotator.cxx:208
 TGLAutoRotator.cxx:209
 TGLAutoRotator.cxx:210
 TGLAutoRotator.cxx:211
 TGLAutoRotator.cxx:212
 TGLAutoRotator.cxx:213
 TGLAutoRotator.cxx:214
 TGLAutoRotator.cxx:215
 TGLAutoRotator.cxx:216
 TGLAutoRotator.cxx:217
 TGLAutoRotator.cxx:218
 TGLAutoRotator.cxx:219
 TGLAutoRotator.cxx:220
 TGLAutoRotator.cxx:221
 TGLAutoRotator.cxx:222
 TGLAutoRotator.cxx:223
 TGLAutoRotator.cxx:224
 TGLAutoRotator.cxx:225
 TGLAutoRotator.cxx:226
 TGLAutoRotator.cxx:227
 TGLAutoRotator.cxx:228
 TGLAutoRotator.cxx:229
 TGLAutoRotator.cxx:230
 TGLAutoRotator.cxx:231
 TGLAutoRotator.cxx:232
 TGLAutoRotator.cxx:233
 TGLAutoRotator.cxx:234
 TGLAutoRotator.cxx:235
 TGLAutoRotator.cxx:236
 TGLAutoRotator.cxx:237
 TGLAutoRotator.cxx:238
 TGLAutoRotator.cxx:239
 TGLAutoRotator.cxx:240
 TGLAutoRotator.cxx:241
 TGLAutoRotator.cxx:242
 TGLAutoRotator.cxx:243
 TGLAutoRotator.cxx:244
 TGLAutoRotator.cxx:245
 TGLAutoRotator.cxx:246
 TGLAutoRotator.cxx:247
 TGLAutoRotator.cxx:248
 TGLAutoRotator.cxx:249
 TGLAutoRotator.cxx:250
 TGLAutoRotator.cxx:251
 TGLAutoRotator.cxx:252
 TGLAutoRotator.cxx:253
 TGLAutoRotator.cxx:254
 TGLAutoRotator.cxx:255
 TGLAutoRotator.cxx:256
 TGLAutoRotator.cxx:257
 TGLAutoRotator.cxx:258
 TGLAutoRotator.cxx:259
 TGLAutoRotator.cxx:260
 TGLAutoRotator.cxx:261
 TGLAutoRotator.cxx:262
 TGLAutoRotator.cxx:263
 TGLAutoRotator.cxx:264
 TGLAutoRotator.cxx:265
 TGLAutoRotator.cxx:266
 TGLAutoRotator.cxx:267
 TGLAutoRotator.cxx:268
 TGLAutoRotator.cxx:269
 TGLAutoRotator.cxx:270
 TGLAutoRotator.cxx:271
 TGLAutoRotator.cxx:272
 TGLAutoRotator.cxx:273
 TGLAutoRotator.cxx:274
 TGLAutoRotator.cxx:275
 TGLAutoRotator.cxx:276
 TGLAutoRotator.cxx:277
 TGLAutoRotator.cxx:278
 TGLAutoRotator.cxx:279
 TGLAutoRotator.cxx:280
 TGLAutoRotator.cxx:281
 TGLAutoRotator.cxx:282
 TGLAutoRotator.cxx:283
 TGLAutoRotator.cxx:284
 TGLAutoRotator.cxx:285
 TGLAutoRotator.cxx:286
 TGLAutoRotator.cxx:287
 TGLAutoRotator.cxx:288
 TGLAutoRotator.cxx:289
 TGLAutoRotator.cxx:290
 TGLAutoRotator.cxx:291
 TGLAutoRotator.cxx:292
 TGLAutoRotator.cxx:293
 TGLAutoRotator.cxx:294
 TGLAutoRotator.cxx:295
 TGLAutoRotator.cxx:296
 TGLAutoRotator.cxx:297
 TGLAutoRotator.cxx:298
 TGLAutoRotator.cxx:299
 TGLAutoRotator.cxx:300
 TGLAutoRotator.cxx:301
 TGLAutoRotator.cxx:302
 TGLAutoRotator.cxx:303
 TGLAutoRotator.cxx:304
 TGLAutoRotator.cxx:305
 TGLAutoRotator.cxx:306
 TGLAutoRotator.cxx:307
 TGLAutoRotator.cxx:308
 TGLAutoRotator.cxx:309
 TGLAutoRotator.cxx:310
 TGLAutoRotator.cxx:311
 TGLAutoRotator.cxx:312
 TGLAutoRotator.cxx:313
 TGLAutoRotator.cxx:314
 TGLAutoRotator.cxx:315
 TGLAutoRotator.cxx:316
 TGLAutoRotator.cxx:317
 TGLAutoRotator.cxx:318
 TGLAutoRotator.cxx:319
 TGLAutoRotator.cxx:320
 TGLAutoRotator.cxx:321
 TGLAutoRotator.cxx:322
 TGLAutoRotator.cxx:323
 TGLAutoRotator.cxx:324
 TGLAutoRotator.cxx:325
 TGLAutoRotator.cxx:326
 TGLAutoRotator.cxx:327