Revision 4bee12876976bec72820e6464bf8665e58e624d0 authored by Gerardo Ganis on 24 July 2009, 07:33:50 UTC, committed by Gerardo Ganis on 24 July 2009, 07:33:50 UTC
git-svn-id: http://root.cern.ch/svn/root/branches/v5-24-00-patches@29570 27541ba8-7e3a-0410-8455-c3a389f83636
1 parent 3517aba
3DConversions.cxx
// @(#)root/mathcore:$Id$
// Authors: W. Brown, M. Fischler, L. Moneta 2005
/**********************************************************************
* *
* Copyright (c) 2005, LCG ROOT FNAL MathLib Team *
* *
* *
**********************************************************************/
// Source file for something else
//
// Created by: Mark Fischler and Walter Brown Thurs July 7, 2005
//
// Last update: $Id$
//
// TODO - For now, all conversions are grouped in this one compilation unit.
// The intention is to seraparte them into a few .cpp files instead,
// so that users needing one form need not incorporate code for them all.
#include <cmath>
#include <limits>
#include "Math/Math.h"
#include "Math/GenVector/3DConversions.h"
#include "Math/GenVector/Rotation3D.h"
#include "Math/GenVector/AxisAngle.h"
#include "Math/GenVector/EulerAngles.h"
#include "Math/GenVector/Quaternion.h"
#include "Math/GenVector/RotationZYX.h"
#include "Math/GenVector/RotationX.h"
#include "Math/GenVector/RotationY.h"
#include "Math/GenVector/RotationZ.h"
namespace ROOT {
namespace Math {
namespace gv_detail {
enum ERotation3DMatrixIndex
{ kXX = Rotation3D::kXX, kXY = Rotation3D::kXY, kXZ = Rotation3D::kXZ
, kYX = Rotation3D::kYX, kYY = Rotation3D::kYY, kYZ = Rotation3D::kYZ
, kZX = Rotation3D::kZX, kZY = Rotation3D::kZY, kZZ = Rotation3D::kZZ
};
// ----------------------------------------------------------------------
void convert( Rotation3D const & from, AxisAngle & to)
{
// conversions from Rotation3D
double m[9];
from.GetComponents(m, m+9);
const double uZ = m[kYX] - m[kXY];
const double uY = m[kXZ] - m[kZX];
const double uX = m[kZY] - m[kYZ];
// in case of rotaiton of an angle PI, the rotation matrix is symmetric and
// uX = uY = uZ = 0. Use then conversion through the quaternion
if ( std::fabs( uX ) < 8.*std::numeric_limits<double>::epsilon() &&
std::fabs( uY ) < 8.*std::numeric_limits<double>::epsilon() &&
std::fabs( uZ ) < 8.*std::numeric_limits<double>::epsilon() ) {
Quaternion tmp;
convert (from,tmp);
convert (tmp,to);
return;
}
AxisAngle::AxisVector u;
u.SetCoordinates( uX, uY, uZ );
static const double pi = M_PI;
double angle;
const double cosdelta = (m[kXX] + m[kYY] + m[kZZ] - 1.0) / 2.0;
if (cosdelta > 1.0) {
angle = 0;
} else if (cosdelta < -1.0) {
angle = pi;
} else {
angle = std::acos( cosdelta );
}
//to.SetAngle(angle);
to.SetComponents(u, angle);
to.Rectify();
} // convert to AxisAngle
static void correctByPi ( double& psi, double& phi ) {
static const double pi = M_PI;
if (psi > 0) {
psi -= pi;
} else {
psi += pi;
}
if (phi > 0) {
phi -= pi;
} else {
phi += pi;
}
}
void convert( Rotation3D const & from, EulerAngles & to)
{
// conversion from Rotation3D to Euler Angles
// Mathematical justification appears in
// http://www.cern.ch/mathlibs/documents/eulerAngleComputation.pdf
double r[9];
from.GetComponents(r,r+9);
double phi, theta, psi;
double psiPlusPhi, psiMinusPhi;
static const double pi = M_PI;
static const double pi_2 = M_PI_2;
theta = (std::fabs(r[kZZ]) <= 1.0) ? std::acos(r[kZZ]) :
(r[kZZ] > 0.0) ? 0 : pi;
double cosTheta = r[kZZ];
if (cosTheta > 1) cosTheta = 1;
if (cosTheta < -1) cosTheta = -1;
// Compute psi +/- phi:
// Depending on whether cosTheta is positive or negative and whether it
// is less than 1 in absolute value, different mathematically equivalent
// expressions are numerically stable.
if (cosTheta == 1) {
psiPlusPhi = atan2 ( r[kXY] - r[kYX], r[kXX] + r[kYY] );
psiMinusPhi = 0;
} else if (cosTheta >= 0) {
psiPlusPhi = atan2 ( r[kXY] - r[kYX], r[kXX] + r[kYY] );
double s = -r[kXY] - r[kYX]; // sin (psi-phi) * (1 - cos theta)
double c = r[kXX] - r[kYY]; // cos (psi-phi) * (1 - cos theta)
psiMinusPhi = atan2 ( s, c );
} else if (cosTheta > -1) {
psiMinusPhi = atan2 ( -r[kXY] - r[kYX], r[kXX] - r[kYY] );
double s = r[kXY] - r[kYX]; // sin (psi+phi) * (1 + cos theta)
double c = r[kXX] + r[kYY]; // cos (psi+phi) * (1 + cos theta)
psiPlusPhi = atan2 ( s, c );
} else { // cosTheta == -1
psiMinusPhi = atan2 ( -r[kXY] - r[kYX], r[kXX] - r[kYY] );
psiPlusPhi = 0;
}
psi = .5 * (psiPlusPhi + psiMinusPhi);
phi = .5 * (psiPlusPhi - psiMinusPhi);
// Now correct by pi if we have managed to get a value of psiPlusPhi
// or psiMinusPhi that was off by 2 pi:
// set up w[i], all of which would be positive if sin and cosine of
// psi and phi were positive:
double w[4];
w[0] = r[kXZ]; w[1] = r[kZX]; w[2] = r[kYZ]; w[3] = -r[kZY];
// find biggest relevant term, which is the best one to use in correcting.
double maxw = std::fabs(w[0]);
int imax = 0;
for (int i = 1; i < 4; ++i) {
if (std::fabs(w[i]) > maxw) {
maxw = std::fabs(w[i]);
imax = i;
}
}
// Determine if the correction needs to be applied: The criteria are
// different depending on whether a sine or cosine was the determinor:
switch (imax) {
case 0:
if (w[0] > 0 && psi < 0) correctByPi ( psi, phi );
if (w[0] < 0 && psi > 0) correctByPi ( psi, phi );
break;
case 1:
if (w[1] > 0 && phi < 0) correctByPi ( psi, phi );
if (w[1] < 0 && phi > 0) correctByPi ( psi, phi );
break;
case 2:
if (w[2] > 0 && std::fabs(psi) > pi_2) correctByPi ( psi, phi );
if (w[2] < 0 && std::fabs(psi) < pi_2) correctByPi ( psi, phi );
break;
case 3:
if (w[3] > 0 && std::fabs(phi) > pi_2) correctByPi ( psi, phi );
if (w[3] < 0 && std::fabs(phi) < pi_2) correctByPi ( psi, phi );
break;
}
to.SetComponents( phi, theta, psi );
} // convert to EulerAngles
//------------------------------------------------------------------------
void convert( Rotation3D const & from, Quaternion & to)
{
// conversion from Rotation3D to Quaternion
double m[9];
from.GetComponents(m, m+9);
const double d0 = m[kXX] + m[kYY] + m[kZZ];
const double d1 = + m[kXX] - m[kYY] - m[kZZ];
const double d2 = - m[kXX] + m[kYY] - m[kZZ];
const double d3 = - m[kXX] - m[kYY] + m[kZZ];
// these are related to the various q^2 values;
// choose the largest to avoid dividing two small numbers and losing accuracy.
if ( d0 >= d1 && d0 >= d2 && d0 >= d3 ) {
const double q0 = .5*std::sqrt(1+d0);
const double f = .25/q0;
const double q1 = f*(m[kZY]-m[kYZ]);
const double q2 = f*(m[kXZ]-m[kZX]);
const double q3 = f*(m[kYX]-m[kXY]);
to.SetComponents(q0,q1,q2,q3);
to.Rectify();
return;
} else if ( d1 >= d2 && d1 >= d3 ) {
const double q1 = .5*std::sqrt(1+d1);
const double f = .25/q1;
const double q0 = f*(m[kZY]-m[kYZ]);
const double q2 = f*(m[kXY]+m[kYX]);
const double q3 = f*(m[kXZ]+m[kZX]);
to.SetComponents(q0,q1,q2,q3);
to.Rectify();
return;
} else if ( d2 >= d3 ) {
const double q2 = .5*std::sqrt(1+d2);
const double f = .25/q2;
const double q0 = f*(m[kXZ]-m[kZX]);
const double q1 = f*(m[kXY]+m[kYX]);
const double q3 = f*(m[kYZ]+m[kZY]);
to.SetComponents(q0,q1,q2,q3);
to.Rectify();
return;
} else {
const double q3 = .5*std::sqrt(1+d3);
const double f = .25/q3;
const double q0 = f*(m[kYX]-m[kXY]);
const double q1 = f*(m[kXZ]+m[kZX]);
const double q2 = f*(m[kYZ]+m[kZY]);
to.SetComponents(q0,q1,q2,q3);
to.Rectify();
return;
}
} // convert to Quaternion
//------------------------------------------------------------------------
void convert( Rotation3D const & from, RotationZYX & to)
{
// conversion from Rotation3D to RotationZYX
// same Math used as for EulerAngles apart from some different meaning of angles and
// matrix elements. But the basic algoprithms principles are the same described in
// http://www.cern.ch/mathlibs/documents/eulerAngleComputation.pdf
// theta is assumed to be in range [-PI/2,PI/2].
// this is guranteed by the Rectify function
static const double pi_2 = M_PI_2;
double r[9];
from.GetComponents(r,r+9);
double phi,theta,psi = 0;
// careful for numeical error make sin(theta) ourtside [-1,1]
double sinTheta = r[kXZ];
if ( sinTheta < -1.0) sinTheta = -1.0;
if ( sinTheta > 1.0) sinTheta = 1.0;
theta = std::asin( sinTheta );
// compute psi +/- phi
// Depending on whether cosTheta is positive or negative and whether it
// is less than 1 in absolute value, different mathematically equivalent
// expressions are numerically stable.
// algorithm from
// adapted for the case 3-2-1
double psiPlusPhi = 0;
double psiMinusPhi = 0;
// valid if sinTheta not eq to -1 otherwise is zero
if (sinTheta > - 1.0)
psiPlusPhi = atan2 ( r[kYX] + r[kZY], r[kYY] - r[kZX] );
// valid if sinTheta not eq. to 1
if (sinTheta < 1.0)
psiMinusPhi = atan2 ( r[kZY] - r[kYX] , r[kYY] + r[kZX] );
psi = .5 * (psiPlusPhi + psiMinusPhi);
phi = .5 * (psiPlusPhi - psiMinusPhi);
// correction is not necessary if sinTheta = +/- 1
//if (sinTheta == 1.0 || sinTheta == -1.0) return;
// apply the corrections according to max of the other terms
// I think is assumed convention that theta is between -PI/2,PI/2.
// OTHERWISE RESULT MIGHT BE DIFFERENT ???
//since we determine phi+psi or phi-psi phi and psi can be both have a shift of +/- PI.
// The shift must be applied on both (the sum (or difference) is knows to +/- 2PI )
//This can be fixed looking at the other 4 matrix terms, which have terms in sin and cos of psi
// and phi. sin(psi+/-PI) = -sin(psi) and cos(psi+/-PI) = -cos(psi).
//Use then the biggest term for making the correction to minimize possible numerical errors
// set up w[i], all of which would be positive if sin and cosine of
// psi and phi were positive:
double w[4];
w[0] = -r[kYZ]; w[1] = -r[kXY]; w[2] = r[kZZ]; w[3] = r[kXX];
// find biggest relevant term, which is the best one to use in correcting.
double maxw = std::fabs(w[0]);
int imax = 0;
for (int i = 1; i < 4; ++i) {
if (std::fabs(w[i]) > maxw) {
maxw = std::fabs(w[i]);
imax = i;
}
}
// Determine if the correction needs to be applied: The criteria are
// different depending on whether a sine or cosine was the determinor:
switch (imax) {
case 0:
if (w[0] > 0 && psi < 0) correctByPi ( psi, phi );
if (w[0] < 0 && psi > 0) correctByPi ( psi, phi );
break;
case 1:
if (w[1] > 0 && phi < 0) correctByPi ( psi, phi );
if (w[1] < 0 && phi > 0) correctByPi ( psi, phi );
break;
case 2:
if (w[2] > 0 && std::fabs(psi) > pi_2) correctByPi ( psi, phi );
if (w[2] < 0 && std::fabs(psi) < pi_2) correctByPi ( psi, phi );
break;
case 3:
if (w[3] > 0 && std::fabs(phi) > pi_2) correctByPi ( psi, phi );
if (w[3] < 0 && std::fabs(phi) < pi_2) correctByPi ( psi, phi );
break;
}
to.SetComponents(phi, theta, psi);
} // convert to RotationZYX
// ----------------------------------------------------------------------
// conversions from AxisAngle
void convert( AxisAngle const & from, Rotation3D & to)
{
// conversion from AxixAngle to Rotation3D
const double sinDelta = std::sin( from.Angle() );
const double cosDelta = std::cos( from.Angle() );
const double oneMinusCosDelta = 1.0 - cosDelta;
const AxisAngle::AxisVector & u = from.Axis();
const double uX = u.X();
const double uY = u.Y();
const double uZ = u.Z();
double m[9];
m[kXX] = oneMinusCosDelta * uX * uX + cosDelta;
m[kXY] = oneMinusCosDelta * uX * uY - sinDelta * uZ;
m[kXZ] = oneMinusCosDelta * uX * uZ + sinDelta * uY;
m[kYX] = oneMinusCosDelta * uY * uX + sinDelta * uZ;
m[kYY] = oneMinusCosDelta * uY * uY + cosDelta;
m[kYZ] = oneMinusCosDelta * uY * uZ - sinDelta * uX;
m[kZX] = oneMinusCosDelta * uZ * uX - sinDelta * uY;
m[kZY] = oneMinusCosDelta * uZ * uY + sinDelta * uX;
m[kZZ] = oneMinusCosDelta * uZ * uZ + cosDelta;
to.SetComponents(m,m+9);
} // convert to Rotation3D
void convert( AxisAngle const & from , EulerAngles & to )
{
// conversion from AxixAngle to EulerAngles
// TODO better : temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( AxisAngle const & from, Quaternion & to)
{
// conversion from AxixAngle to Quaternion
double s = std::sin (from.Angle()/2);
DisplacementVector3D< Cartesian3D<double> > axis = from.Axis();
to.SetComponents( std::cos(from.Angle()/2),
s*axis.X(),
s*axis.Y(),
s*axis.Z()
);
} // convert to Quaternion
void convert( AxisAngle const & from , RotationZYX & to )
{
// conversion from AxisAngle to RotationZYX
// TODO better : temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
// ----------------------------------------------------------------------
// conversions from EulerAngles
void convert( EulerAngles const & from, Rotation3D & to)
{
// conversion from EulerAngles to Rotation3D
typedef double Scalar;
const Scalar sPhi = std::sin( from.Phi() );
const Scalar cPhi = std::cos( from.Phi() );
const Scalar sTheta = std::sin( from.Theta() );
const Scalar cTheta = std::cos( from.Theta() );
const Scalar sPsi = std::sin( from.Psi() );
const Scalar cPsi = std::cos( from.Psi() );
to.SetComponents
( cPsi*cPhi-sPsi*cTheta*sPhi, cPsi*sPhi+sPsi*cTheta*cPhi, sPsi*sTheta
, -sPsi*cPhi-cPsi*cTheta*sPhi, -sPsi*sPhi+cPsi*cTheta*cPhi, cPsi*sTheta
, sTheta*sPhi, -sTheta*cPhi, cTheta
);
}
void convert( EulerAngles const & from, AxisAngle & to)
{
// conversion from EulerAngles to AxisAngle
// make converting first to quaternion
Quaternion q;
convert (from, q);
convert (q, to);
}
void convert( EulerAngles const & from, Quaternion & to)
{
// conversion from EulerAngles to Quaternion
typedef double Scalar;
const Scalar plus = (from.Phi()+from.Psi())/2;
const Scalar minus = (from.Phi()-from.Psi())/2;
const Scalar sPlus = std::sin( plus );
const Scalar cPlus = std::cos( plus );
const Scalar sMinus = std::sin( minus );
const Scalar cMinus = std::cos( minus );
const Scalar sTheta = std::sin( from.Theta()/2 );
const Scalar cTheta = std::cos( from.Theta()/2 );
to.SetComponents ( cTheta*cPlus, -sTheta*cMinus, -sTheta*sMinus, -cTheta*sPlus );
// TODO -- carefully check that this is correct
}
void convert( EulerAngles const & from , RotationZYX & to )
{
// conversion from EulerAngles to RotationZYX
// TODO better : temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
// ----------------------------------------------------------------------
// conversions from Quaternion
void convert( Quaternion const & from, Rotation3D & to)
{
// conversion from Quaternion to Rotation3D
const double q0 = from.U();
const double q1 = from.I();
const double q2 = from.J();
const double q3 = from.K();
const double q00 = q0*q0;
const double q01 = q0*q1;
const double q02 = q0*q2;
const double q03 = q0*q3;
const double q11 = q1*q1;
const double q12 = q1*q2;
const double q13 = q1*q3;
const double q22 = q2*q2;
const double q23 = q2*q3;
const double q33 = q3*q3;
to.SetComponents (
q00+q11-q22-q33 , 2*(q12-q03) , 2*(q02+q13),
2*(q12+q03) , q00-q11+q22-q33 , 2*(q23-q01),
2*(q13-q02) , 2*(q23+q01) , q00-q11-q22+q33 );
} // conversion to Rotation3D
void convert( Quaternion const & from, AxisAngle & to)
{
// conversion from Quaternion to AxisAngle
double u = from.U();
if ( u >= 0 ) {
if ( u > 1 ) u = 1;
const double angle = 2.0 * std::acos ( from.U() );
DisplacementVector3D< Cartesian3D<double> >
axis (from.I(), from.J(), from.K());
to.SetComponents ( axis, angle );
} else {
if ( u < -1 ) u = -1;
const double angle = 2.0 * std::acos ( -from.U() );
DisplacementVector3D< Cartesian3D<double> >
axis (-from.I(), -from.J(), -from.K());
to.SetComponents ( axis, angle );
}
} // conversion to AxisAngle
void convert( Quaternion const & from, EulerAngles & to )
{
// conversion from Quaternion to EulerAngles
// TODO better
// temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( Quaternion const & from , RotationZYX & to )
{
// conversion from Quaternion to RotationZYX
// TODO better : temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
// ----------------------------------------------------------------------
// conversions from RotationZYX
void convert( RotationZYX const & from, Rotation3D & to) {
// conversion to Rotation3D (matrix)
double phi,theta,psi = 0;
from.GetComponents(phi,theta,psi);
to.SetComponents( std::cos(theta)*std::cos(phi),
- std::cos(theta)*std::sin(phi),
std::sin(theta),
std::cos(psi)*std::sin(phi) + std::sin(psi)*std::sin(theta)*std::cos(phi),
std::cos(psi)*std::cos(phi) - std::sin(psi)*std::sin(theta)*std::sin(phi),
-std::sin(psi)*std::cos(theta),
std::sin(psi)*std::sin(phi) - std::cos(psi)*std::sin(theta)*std::cos(phi),
std::sin(psi)*std::cos(phi) + std::cos(psi)*std::sin(theta)*std::sin(phi),
std::cos(psi)*std::cos(theta)
);
}
void convert( RotationZYX const & from, AxisAngle & to) {
// conversion to axis angle
// TODO better : temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( RotationZYX const & from, EulerAngles & to) {
// conversion to Euler angle
// TODO better : temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( RotationZYX const & from, Quaternion & to) {
double phi,theta,psi = 0;
from.GetComponents(phi,theta,psi);
double sphi2 = std::sin(phi/2);
double cphi2 = std::cos(phi/2);
double stheta2 = std::sin(theta/2);
double ctheta2 = std::cos(theta/2);
double spsi2 = std::sin(psi/2);
double cpsi2 = std::cos(psi/2);
to.SetComponents( cphi2 * cpsi2 * ctheta2 - sphi2 * spsi2 * stheta2,
sphi2 * cpsi2 * stheta2 + cphi2 * spsi2 * ctheta2,
cphi2 * cpsi2 * stheta2 - sphi2 * spsi2 * ctheta2,
sphi2 * cpsi2 * ctheta2 + cphi2 * spsi2 * stheta2
);
}
// ----------------------------------------------------------------------
// conversions from RotationX
void convert( RotationX const & from, Rotation3D & to)
{
// conversion from RotationX to Rotation3D
const double c = from.CosAngle();
const double s = from.SinAngle();
to.SetComponents ( 1, 0, 0,
0, c, -s,
0, s, c );
}
void convert( RotationX const & from, AxisAngle & to)
{
// conversion from RotationX to AxisAngle
DisplacementVector3D< Cartesian3D<double> > axis (1, 0, 0);
to.SetComponents ( axis, from.Angle() );
}
void convert( RotationX const & from , EulerAngles & to )
{
// conversion from RotationX to EulerAngles
//TODO better: temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( RotationX const & from, Quaternion & to)
{
// conversion from RotationX to Quaternion
to.SetComponents (std::cos(from.Angle()/2), std::sin(from.Angle()/2), 0, 0);
}
void convert( RotationX const & from , RotationZYX & to )
{
// conversion from RotationX to RotationZYX
to.SetComponents(0,0,from.Angle());
}
// ----------------------------------------------------------------------
// conversions from RotationY
void convert( RotationY const & from, Rotation3D & to)
{
// conversion from RotationY to Rotation3D
const double c = from.CosAngle();
const double s = from.SinAngle();
to.SetComponents ( c, 0, s,
0, 1, 0,
-s, 0, c );
}
void convert( RotationY const & from, AxisAngle & to)
{
// conversion from RotationY to AxisAngle
DisplacementVector3D< Cartesian3D<double> > axis (0, 1, 0);
to.SetComponents ( axis, from.Angle() );
}
void convert( RotationY const & from, EulerAngles & to )
{
// conversion from RotationY to EulerAngles
// TODO better: temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( RotationY const & from , RotationZYX & to )
{
// conversion from RotationY to RotationZYX
to.SetComponents(0,from.Angle(),0);
}
void convert( RotationY const & from, Quaternion & to)
{
// conversion from RotationY to Quaternion
to.SetComponents (std::cos(from.Angle()/2), 0, std::sin(from.Angle()/2), 0);
}
// ----------------------------------------------------------------------
// conversions from RotationZ
void convert( RotationZ const & from, Rotation3D & to)
{
// conversion from RotationZ to Rotation3D
const double c = from.CosAngle();
const double s = from.SinAngle();
to.SetComponents ( c, -s, 0,
s, c, 0,
0, 0, 1 );
}
void convert( RotationZ const & from, AxisAngle & to)
{
// conversion from RotationZ to AxisAngle
DisplacementVector3D< Cartesian3D<double> > axis (0, 0, 1);
to.SetComponents ( axis, from.Angle() );
}
void convert( RotationZ const & from , EulerAngles & to )
{
// conversion from RotationZ to EulerAngles
// TODO better: temporary make conversion using Rotation3D
Rotation3D tmp;
convert(from,tmp);
convert(tmp,to);
}
void convert( RotationZ const & from , RotationZYX & to )
{
// conversion from RotationY to RotationZYX
to.SetComponents(from.Angle(),0,0);
}
void convert( RotationZ const & from, Quaternion & to)
{
// conversion from RotationZ to Quaternion
to.SetComponents (std::cos(from.Angle()/2), 0, 0, std::sin(from.Angle()/2));
}
} //namespace gv_detail
} //namespace Math
} //namespace ROOT
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...