diff --git a/math/genvector/inc/Math/GenVector/VectorUtil.h b/math/genvector/inc/Math/GenVector/VectorUtil.h index dbd7661c009a4..e4fc0333058d9 100644 --- a/math/genvector/inc/Math/GenVector/VectorUtil.h +++ b/math/genvector/inc/Math/GenVector/VectorUtil.h @@ -275,15 +275,16 @@ namespace ROOT { // rotation and transformations - /** rotation along X axis for a generic vector by an Angle alpha returning a new vector. - The only pre requisite on the Vector is that it has to implement the X() , Y() and Z() + The only pre requisite on the Vector is that it has to implement the X(), Y() and Z() and SetXYZ methods. */ template Vector RotateX(const Vector & v, double alpha) { + if (std::fmod(alpha, 2 * M_PI) == 0.) + return v; using std::sin; double sina = sin(alpha); using std::cos; @@ -298,11 +299,13 @@ namespace ROOT { /** rotation along Y axis for a generic vector by an Angle alpha returning a new vector. - The only pre requisite on the Vector is that it has to implement the X() , Y() and Z() + The only pre requisite on the Vector is that it has to implement the X(), Y() and Z() and SetXYZ methods. */ template Vector RotateY(const Vector & v, double alpha) { + if (std::fmod(alpha, 2 * M_PI) == 0.) + return v; using std::sin; double sina = sin(alpha); using std::cos; @@ -317,11 +320,13 @@ namespace ROOT { /** rotation along Z axis for a generic vector by an Angle alpha returning a new vector. - The only pre requisite on the Vector is that it has to implement the X() , Y() and Z() + The only pre requisite on the Vector is that it has to implement the X(), Y() and Z() and SetXYZ methods. */ template Vector RotateZ(const Vector & v, double alpha) { + if (std::fmod(alpha, 2 * M_PI) == 0.) + return v; using std::sin; double sina = sin(alpha); using std::cos; @@ -333,6 +338,40 @@ namespace ROOT { return vrot; } + /** + rotation along a custom axis for a generic vector by an Angle alpha (in rad) + returning a new vector. + The only pre requisite on the Vector is that it has to implement the X(), Y() and Z() + and SetXYZ methods. + */ + template + Vector Rotate(const Vector &v, double alpha, const Vector &axis) + { + if (std::fmod(alpha, 2 * M_PI) == 0.) + return v; + const double ll = std::sqrt(axis.X() * axis.X() + axis.Y() * axis.Y() + axis.Z() * axis.Z()); + if (ll == 0.) + GenVector::Throw("Axis Vector has zero magnitude"); + const double sa = std::sin(alpha); + const double ca = std::cos(alpha); + const double dx = axis.X() / ll; + const double dy = axis.Y() / ll; + const double dz = axis.Z() / ll; + // clang-format off + const double rot00 = (1 - ca) * dx * dx + ca , rot01 = (1 - ca) * dx * dy - sa * dz, rot02 = (1 - ca) * dx * dz + sa * dy, + rot10 = (1 - ca) * dy * dx + sa * dz, rot11 = (1 - ca) * dy * dy + ca , rot12 = (1 - ca) * dy * dz - sa * dx, + rot20 = (1 - ca) * dz * dx - sa * dy, rot21 = (1 - ca) * dz * dy + sa * dx, rot22 = (1 - ca) * dz * dz + ca ; + // clang-format on + const double xX = v.X(); + const double yY = v.Y(); + const double zZ = v.Z(); + const double x2 = rot00 * xX + rot01 * yY + rot02 * zZ; + const double y2 = rot10 * xX + rot11 * yY + rot12 * zZ; + const double z2 = rot20 * xX + rot21 * yY + rot22 * zZ; + Vector vrot; + vrot.SetXYZ(x2, y2, z2); + return vrot; + } /** rotation on a generic vector using a generic rotation class. diff --git a/math/genvector/test/testGenVector.cxx b/math/genvector/test/testGenVector.cxx index 9a6cb9f210be6..2a73505fd90d1 100644 --- a/math/genvector/test/testGenVector.cxx +++ b/math/genvector/test/testGenVector.cxx @@ -466,6 +466,21 @@ int testRotations3D() { iret |= compare(qr1.Y(), qr2.Y(),"y diff",10 ); iret |= compare(qr1.Z(), qr2.Z(),"z diff",10 ); + // test TVector3-like Rotate function around some axis by an angle. Test case taken from cwessel: + // https://root-forum.cern.ch/t/tvector3-rotate-to-arbitrary-rotation-using-xyzvector/63244/7 + XYZVector ag(17, -4, -23); + double angle = 100; + XYZVector axisg(-23.4, 1.7, -0.3); + XYZVector rotated = Rotate(ag, angle, axisg); + // should be equivalent to: + // TVector3 at(17, -4, -23); + // TVector3 axist(-23.4, 1.7, -0.3); + // at.Rotate(angle, axist); + // at.Print(); + // (17.856456,8.106555,-21.199782) + iret |= compare(rotated.X(), 17.856456, "x diff", 1e10); + iret |= compare(rotated.Y(), 8.106555, "y diff", 1e10); + iret |= compare(rotated.Z(), -21.199782, "z diff", 1e10); if (iret == 0) std::cout << "\tOK\n"; else std::cout << "\t FAILED\n"; diff --git a/math/physics/src/TRotation.cxx b/math/physics/src/TRotation.cxx index cbe0a49e26bf4..61c7187948a38 100644 --- a/math/physics/src/TRotation.cxx +++ b/math/physics/src/TRotation.cxx @@ -185,6 +185,7 @@ TVector3 class: #include "TRotation.h" #include "TMath.h" #include "TQuaternion.h" +#include ClassImp(TRotation); @@ -323,7 +324,7 @@ TRotation::TRotation(const TQuaternion & Q) { /// Rotate along an axis. TRotation & TRotation::Rotate(Double_t a, const TVector3& axis) { - if (a != 0.0) { + if (std::fmod(a, 2 * TMath::Pi()) != 0.) { Double_t ll = axis.Mag(); if (ll == 0.0) { Warning("Rotate(angle,axis)"," zero axis");