ROOT logo
// @(#)root/gl:$Id$
// Author:  Timur Pocheptsov  03/08/2004

/*************************************************************************
 * Copyright (C) 1995-2004, 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 "TArcBall.h"
#include "TPoint.h"
#include "TMath.h"

const Double_t Epsilon = 1.0e-5;

//______________________________________________________________________________
//
// Implements the arc-ball rotation manipulator.
// Used by plot-painters.

ClassImp(TArcBall)

//Arcball sphere constants:
//Diameter is       2.0f
//Radius is         1.0f

//______________________________________________________________________________
inline void Vector3dCross(Double_t *NewObj, const Double_t * v1, const Double_t *v2)
{
   NewObj[0] = v1[1] * v2[2] - v1[2] * v2[1];
   NewObj[1] = v1[2] * v2[0] - v1[0] * v2[2];
   NewObj[2] = v1[0] * v2[1] - v1[1] * v2[0];
}

//______________________________________________________________________________
inline Double_t Vector3dDot(const Double_t *NewObj, const Double_t *v1)
{
   return  NewObj[0] * v1[0] + NewObj[1] * v1[1] + NewObj[2] * v1[2];
}

//______________________________________________________________________________
inline Double_t Vector3dLengthSquared(const Double_t *NewObj)
{
   return  NewObj[0] * NewObj[0] + NewObj[1] * NewObj[1] + NewObj[2] * NewObj[2];
}

//______________________________________________________________________________
inline Double_t Vector3dLength(const Double_t *NewObj)
{
   return TMath::Sqrt(Vector3dLengthSquared(NewObj));
}

//______________________________________________________________________________
inline void Matrix3dSetZero(Double_t * NewObj)
{
   for (Int_t i = 0; i < 9; ++i)
      NewObj[i] = 0.;
}

//______________________________________________________________________________
inline void Matrix3dSetIdentity(Double_t *NewObj)
{
   Matrix3dSetZero(NewObj);
   //then set diagonal as 1
   NewObj[0] = NewObj[4] = NewObj[8] = 1.;
}

//______________________________________________________________________________
void Matrix3dSetRotationFromQuat4d(Double_t *NewObj, const Double_t *q1)
{
   Double_t n = (q1[0] * q1[0]) + (q1[1] * q1[1]) + (q1[2] * q1[2]) + (q1[3] * q1[3]);
   Double_t s = (n > 0.0f) ? (2.0f / n) : 0.0f;
   Double_t xs = q1[0] * s,  ys = q1[1] * s,  zs = q1[2] * s;
   Double_t wx = q1[3] * xs, wy = q1[3] * ys, wz = q1[3] * zs;
   Double_t xx = q1[0] * xs, xy = q1[0] * ys, xz = q1[0] * zs;
   Double_t yy = q1[1] * ys, yz = q1[1] * zs, zz = q1[2] * zs;

   NewObj[0] = 1.0f - (yy + zz); NewObj[3] = xy - wz;          NewObj[6] = xz + wy;
   NewObj[1] = xy + wz;          NewObj[4] = 1.0f - (xx + zz); NewObj[7] = yz - wx;
   NewObj[2] = xz - wy;          NewObj[5] = yz + wx;          NewObj[8] = 1.0f - (xx + yy);
}

//______________________________________________________________________________
void Matrix3dMulMatrix3d(Double_t *NewObj, const Double_t *m1)
{
   Double_t result[9];

   result[0] = (NewObj[0] * m1[0]) + (NewObj[3] * m1[1]) + (NewObj[6] * m1[2]);
   result[3] = (NewObj[0] * m1[3]) + (NewObj[3] * m1[4]) + (NewObj[6] * m1[5]);
   result[6] = (NewObj[0] * m1[6]) + (NewObj[3] * m1[7]) + (NewObj[6] * m1[8]);

   result[1] = (NewObj[1] * m1[0]) + (NewObj[4] * m1[1]) + (NewObj[7] * m1[2]);
   result[4] = (NewObj[1] * m1[3]) + (NewObj[4] * m1[4]) + (NewObj[7] * m1[5]);
   result[7] = (NewObj[1] * m1[6]) + (NewObj[4] * m1[7]) + (NewObj[7] * m1[8]);

   result[2] = (NewObj[2] * m1[0]) + (NewObj[5] * m1[1]) + (NewObj[8] * m1[2]);
   result[5] = (NewObj[2] * m1[3]) + (NewObj[5] * m1[4]) + (NewObj[8] * m1[5]);
   result[8] = (NewObj[2] * m1[6]) + (NewObj[5] * m1[7]) + (NewObj[8] * m1[8]);

   for (Int_t i = 0; i < 9; ++i)
      NewObj[i] = result[i];
}

//______________________________________________________________________________
inline void Matrix4dSetRotationScaleFromMatrix4d(Double_t *NewObj, const Double_t *m1)
{
   NewObj[0] = m1[0]; NewObj[4] = m1[4]; NewObj[8] = m1[8];
   NewObj[1] = m1[1]; NewObj[5] = m1[5]; NewObj[9] = m1[9];
   NewObj[2] = m1[2]; NewObj[6] = m1[6]; NewObj[10] = m1[10];
}

//______________________________________________________________________________
inline Double_t Matrix4fSVD(const Double_t *NewObj, Double_t *rot3, Double_t *rot4)
{
   Double_t s = TMath::Sqrt(
                ( (NewObj[0] * NewObj[0]) + (NewObj[1] * NewObj[1]) + (NewObj[2] * NewObj[2]) +
                  (NewObj[4] * NewObj[4]) + (NewObj[5] * NewObj[5]) + (NewObj[6] * NewObj[6]) +
                  (NewObj[8] * NewObj[8]) + (NewObj[9] * NewObj[9]) + (NewObj[10] * NewObj[10]) ) / 3.0f );

   if (rot3) {
      rot3[0] = NewObj[0]; rot3[1] = NewObj[1]; rot3[2] = NewObj[2];
      rot3[3] = NewObj[4]; rot3[4] = NewObj[5]; rot3[5] = NewObj[6];
      rot3[6] = NewObj[8]; rot3[7] = NewObj[9]; rot3[8] = NewObj[10];

      Double_t n = 1. / TMath::Sqrt(NewObj[0] * NewObj[0] + NewObj[1] * NewObj[1] + NewObj[2] * NewObj[2] + 0.0001);

      rot3[0] *= n;
      rot3[1] *= n;
      rot3[2] *= n;

      n = 1. / TMath::Sqrt(NewObj[4] * NewObj[4] + NewObj[5] * NewObj[5] + NewObj[6] * NewObj[6] + 0.0001);
      rot3[3] *= n;
      rot3[4] *= n;
      rot3[5] *= n;

      n = 1.0f / TMath::Sqrt(NewObj[8] * NewObj[8] + NewObj[9] * NewObj[9] + NewObj[10] * NewObj[10] + 0.0001);
      rot3[6] *= n;
      rot3[7] *= n;
      rot3[8] *= n;
   }

   if (rot4) {
      if (rot4 != NewObj)
         Matrix4dSetRotationScaleFromMatrix4d(rot4, NewObj);

      Double_t n = 1. / TMath::Sqrt(NewObj[0] * NewObj[0] + NewObj[1] * NewObj[1] + NewObj[2] * NewObj[2] + 0.0001);

      rot4[0] *= n;
      rot4[1] *= n;
      rot4[2] *= n;

      n = 1. / TMath::Sqrt(NewObj[4] * NewObj[4] + NewObj[5] * NewObj[5] + NewObj[6] * NewObj[6] + 0.0001);
      rot4[4] *= n;
      rot4[5] *= n;
      rot4[6] *= n;

      n = 1. / TMath::Sqrt(NewObj[8] * NewObj[8] + NewObj[9] * NewObj[9] + NewObj[10] * NewObj[10] + 0.0001);
      rot4[8] *= n;
      rot4[9] *= n;
      rot4[10] *= n;
   }

   return s;
}

//______________________________________________________________________________
inline void Matrix4dSetRotationScaleFromMatrix3d(Double_t *NewObj, const Double_t *m1)
{
   NewObj[0] = m1[0]; NewObj[4] = m1[3]; NewObj[8] = m1[6];
   NewObj[1] = m1[1]; NewObj[5] = m1[4]; NewObj[9] = m1[7];
   NewObj[2] = m1[2]; NewObj[6] = m1[5]; NewObj[10] = m1[8];
}

//______________________________________________________________________________
inline void Matrix4dMulRotationScale(Double_t *NewObj, Double_t scale)
{
   NewObj[0] *= scale; NewObj[4] *= scale; NewObj[8] *= scale;
   NewObj[1] *= scale; NewObj[5] *= scale; NewObj[9] *= scale;
   NewObj[2] *= scale; NewObj[6] *= scale; NewObj[10] *= scale;
}

//______________________________________________________________________________
void Matrix4dSetRotationFromMatrix3d(Double_t *NewObj, const Double_t *m1)
{
   Double_t scale = Matrix4fSVD(NewObj, 0, 0);
   Matrix4dSetRotationScaleFromMatrix3d(NewObj, m1);
   Matrix4dMulRotationScale(NewObj, scale);
}

//______________________________________________________________________________
inline void TArcBall::MapToSphere(const TPoint &NewPt, Double_t *NewVec) const
{
   //map to sphere
   Double_t tempPt[] = { static_cast<Double_t>(NewPt.fX), static_cast<Double_t>(NewPt.fY)};
   //Adjust point coords and scale down to range of [-1 ... 1]
   tempPt[0]  = tempPt[0] * fAdjustWidth  - 1.;
   tempPt[1]  = 1. - tempPt[1] * fAdjustHeight;
   //Compute the square of the length of the vector to the point from the center
   Double_t length = tempPt[0] * tempPt[0] + tempPt[1] * tempPt[1];
   //If the point is mapped outside of the sphere... (length > radius squared)
   if (length > 1.) {
      Double_t norm = 1.0f / TMath::Sqrt(length);
      //Return the "normalized" vector, a point on the sphere
      NewVec[0] = tempPt[0] * norm;
      NewVec[1] = tempPt[1] * norm;
      NewVec[2] = 0.;
   } else {   //Else it's on the inside
    //Return a vector to a point mapped inside the sphere sqrt(radius squared - length)
      NewVec[0] = tempPt[0];
      NewVec[1] = tempPt[1];
      NewVec[2] = TMath::Sqrt(1. - length);
   }
}

//______________________________________________________________________________
TArcBall::TArcBall(UInt_t Width, UInt_t Height)
         :fThisRot(), fLastRot(),
         fTransform(), fStVec(),
         fEnVec(), fAdjustWidth(0.),
         fAdjustHeight(0.)
{
   // constructor
   SetBounds(Width, Height);
   ResetMatrices();
}

//______________________________________________________________________________
void TArcBall::Click(const TPoint &NewPt)
{
   //Mouse down
   MapToSphere(NewPt, fStVec);

   for (Int_t i = 0; i < 9; ++i)
      fLastRot[i] = fThisRot[i];
}

//______________________________________________________________________________
void TArcBall::Drag(const TPoint &NewPt)
{
   //Mouse drag, calculate rotation
   MapToSphere(NewPt, fEnVec);
   //Return the quaternion equivalent to the rotation
   Double_t newRot[4] = {0.};
   Double_t perp[3] = {0.};

   Vector3dCross(perp, fStVec, fEnVec);
   //Compute the length of the perpendicular vector
   if (Vector3dLength(perp) > Epsilon) {
   //We're ok, so return the perpendicular vector as the transform after all
      newRot[0] = perp[0];
      newRot[1] = perp[1];
      newRot[2] = perp[2];
      //In the quaternion values, w is cosine (theta / 2), where theta is rotation angle
      newRot[3]= Vector3dDot(fStVec, fEnVec);
   } else  //if it's zero
      newRot[0] = newRot[1] = newRot[2] = newRot[3] = 0.;

   Matrix3dSetRotationFromQuat4d(fThisRot, newRot);
   Matrix3dMulMatrix3d(fThisRot, fLastRot);
   Matrix4dSetRotationFromMatrix3d(fTransform, fThisRot);
}

//______________________________________________________________________________
void TArcBall::ResetMatrices()
{
   //Set rotation matrix as union
   fTransform[0] = 1.f, fTransform[1] = fTransform[2] = fTransform[3] =
   fTransform[4] = 0.f, fTransform[5] = 1.f, fTransform[6] = fTransform[7] =
   fTransform[8] = fTransform[9] = 0.f, fTransform[10] = 1.f, fTransform[11] =
   fTransform[12] = fTransform[13] = fTransform[14] = 0.f, fTransform[15] = 1.f;
   Matrix3dSetIdentity(fLastRot);
   Matrix3dSetIdentity(fThisRot);
}

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