[4] | 1 | // -*- C++ -*-
|
---|
| 2 | // ---------------------------------------------------------------------------
|
---|
| 3 | //
|
---|
| 4 | // This file is a part of the CLHEP - a Class Library for High Energy Physics.
|
---|
| 5 | //
|
---|
| 6 | // This is the implementation of methods of the HepRotation class which
|
---|
| 7 | // were introduced when ZOOM PhysicsVectors was merged in, and which involve
|
---|
| 8 | // Euler Angles representation.
|
---|
| 9 | //
|
---|
| 10 | // Apr 28, 2003 mf Modified way of computing Euler angles to avoid flawed
|
---|
| 11 | // answers in the case where theta is near 0 of pi, and
|
---|
| 12 | // the matrix is not a perfect rotation (due to roundoff).
|
---|
| 13 |
|
---|
| 14 | #ifdef GNUPRAGMA
|
---|
| 15 | #pragma implementation
|
---|
| 16 | #endif
|
---|
| 17 |
|
---|
| 18 | #include "CLHEP/Vector/defs.h"
|
---|
| 19 | #include "CLHEP/Vector/Rotation.h"
|
---|
| 20 | #include "CLHEP/Vector/EulerAngles.h"
|
---|
| 21 | #include "CLHEP/Units/PhysicalConstants.h"
|
---|
| 22 |
|
---|
| 23 | #include <cmath>
|
---|
| 24 | #include <stdlib.h>
|
---|
| 25 |
|
---|
| 26 | using std::abs;
|
---|
| 27 |
|
---|
| 28 | namespace CLHEP {
|
---|
| 29 |
|
---|
| 30 | static inline double safe_acos (double x) {
|
---|
| 31 | if (abs(x) <= 1.0) return acos(x);
|
---|
| 32 | return ( (x>0) ? 0 : CLHEP::pi );
|
---|
| 33 | }
|
---|
| 34 |
|
---|
| 35 | // ---------- Constructors and Assignment:
|
---|
| 36 |
|
---|
| 37 | // Euler angles
|
---|
| 38 |
|
---|
| 39 | HepRotation & HepRotation::set(double phi, double theta, double psi) {
|
---|
| 40 |
|
---|
| 41 | register double sinPhi = sin( phi ), cosPhi = cos( phi );
|
---|
| 42 | register double sinTheta = sin( theta ), cosTheta = cos( theta );
|
---|
| 43 | register double sinPsi = sin( psi ), cosPsi = cos( psi );
|
---|
| 44 |
|
---|
| 45 | rxx = cosPsi * cosPhi - cosTheta * sinPhi * sinPsi;
|
---|
| 46 | rxy = cosPsi * sinPhi + cosTheta * cosPhi * sinPsi;
|
---|
| 47 | rxz = sinPsi * sinTheta;
|
---|
| 48 |
|
---|
| 49 | ryx = - sinPsi * cosPhi - cosTheta * sinPhi * cosPsi;
|
---|
| 50 | ryy = - sinPsi * sinPhi + cosTheta * cosPhi * cosPsi;
|
---|
| 51 | ryz = cosPsi * sinTheta;
|
---|
| 52 |
|
---|
| 53 | rzx = sinTheta * sinPhi;
|
---|
| 54 | rzy = - sinTheta * cosPhi;
|
---|
| 55 | rzz = cosTheta;
|
---|
| 56 |
|
---|
| 57 | return *this;
|
---|
| 58 |
|
---|
| 59 | } // Rotation::set(phi, theta, psi)
|
---|
| 60 |
|
---|
| 61 | HepRotation::HepRotation( double phi, double theta, double psi )
|
---|
| 62 | {
|
---|
| 63 | set (phi, theta, psi);
|
---|
| 64 | }
|
---|
| 65 | HepRotation & HepRotation::set( const HepEulerAngles & e ) {
|
---|
| 66 | return set(e.phi(), e.theta(), e.psi());
|
---|
| 67 | }
|
---|
| 68 | HepRotation::HepRotation ( const HepEulerAngles & e )
|
---|
| 69 | {
|
---|
| 70 | set(e.phi(), e.theta(), e.psi());
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | |
---|
| 74 |
|
---|
| 75 |
|
---|
| 76 | double HepRotation::phi () const {
|
---|
| 77 |
|
---|
| 78 | double s2 = 1.0 - rzz*rzz;
|
---|
| 79 | if (s2 < 0) {
|
---|
| 80 | ZMthrowC ( ZMxpvImproperRotation (
|
---|
| 81 | "HepRotation::phi() finds | rzz | > 1 "));
|
---|
| 82 | s2 = 0;
|
---|
| 83 | }
|
---|
| 84 | const double sinTheta = sqrt( s2 );
|
---|
| 85 |
|
---|
| 86 | if (sinTheta < .01) { // For theta close to 0 or PI, use the more stable
|
---|
| 87 | // algorithm to get all three Euler angles
|
---|
| 88 | HepEulerAngles ea = eulerAngles();
|
---|
| 89 | return ea.phi();
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | const double cscTheta = 1/sinTheta;
|
---|
| 93 | double cosabsphi = - rzy * cscTheta;
|
---|
| 94 | if ( fabs(cosabsphi) > 1 ) { // NaN-proofing
|
---|
| 95 | ZMthrowC ( ZMxpvImproperRotation (
|
---|
| 96 | "HepRotation::phi() finds | cos phi | > 1 "));
|
---|
| 97 | cosabsphi = 1;
|
---|
| 98 | }
|
---|
| 99 | const double absPhi = acos ( cosabsphi );
|
---|
| 100 | if (rzx > 0) {
|
---|
| 101 | return absPhi;
|
---|
| 102 | } else if (rzx < 0) {
|
---|
| 103 | return -absPhi;
|
---|
| 104 | } else {
|
---|
| 105 | return (rzy < 0) ? 0 : CLHEP::pi;
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | } // phi()
|
---|
| 109 |
|
---|
| 110 | double HepRotation::theta() const {
|
---|
| 111 |
|
---|
| 112 | return safe_acos( rzz );
|
---|
| 113 |
|
---|
| 114 | } // theta()
|
---|
| 115 |
|
---|
| 116 | double HepRotation::psi () const {
|
---|
| 117 |
|
---|
| 118 | double sinTheta;
|
---|
| 119 | if ( fabs(rzz) > 1 ) { // NaN-proofing
|
---|
| 120 | ZMthrowC ( ZMxpvImproperRotation (
|
---|
| 121 | "HepRotation::psi() finds | rzz | > 1"));
|
---|
| 122 | sinTheta = 0;
|
---|
| 123 | } else {
|
---|
| 124 | sinTheta = sqrt( 1.0 - rzz*rzz );
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | if (sinTheta < .01) { // For theta close to 0 or PI, use the more stable
|
---|
| 128 | // algorithm to get all three Euler angles
|
---|
| 129 | HepEulerAngles ea = eulerAngles();
|
---|
| 130 | return ea.psi();
|
---|
| 131 | }
|
---|
| 132 |
|
---|
| 133 | const double cscTheta = 1/sinTheta;
|
---|
| 134 | double cosabspsi = ryz * cscTheta;
|
---|
| 135 | if ( fabs(cosabspsi) > 1 ) { // NaN-proofing
|
---|
| 136 | ZMthrowC ( ZMxpvImproperRotation (
|
---|
| 137 | "HepRotation::psi() finds | cos psi | > 1"));
|
---|
| 138 | cosabspsi = 1;
|
---|
| 139 | }
|
---|
| 140 | const double absPsi = acos ( cosabspsi );
|
---|
| 141 | if (rxz > 0) {
|
---|
| 142 | return absPsi;
|
---|
| 143 | } else if (rxz < 0) {
|
---|
| 144 | return -absPsi;
|
---|
| 145 | } else {
|
---|
| 146 | return (ryz > 0) ? 0 : CLHEP::pi;
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | } // psi()
|
---|
| 150 |
|
---|
| 151 | |
---|
| 152 |
|
---|
| 153 | // Helpers for eulerAngles():
|
---|
| 154 |
|
---|
| 155 | static
|
---|
| 156 | void correctByPi ( double& psi, double& phi ) {
|
---|
| 157 | if (psi > 0) {
|
---|
| 158 | psi -= CLHEP::pi;
|
---|
| 159 | } else {
|
---|
| 160 | psi += CLHEP::pi;
|
---|
| 161 | }
|
---|
| 162 | if (phi > 0) {
|
---|
| 163 | phi -= CLHEP::pi;
|
---|
| 164 | } else {
|
---|
| 165 | phi += CLHEP::pi;
|
---|
| 166 | }
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | static
|
---|
| 170 | void correctPsiPhi ( double rxz, double rzx, double ryz, double rzy,
|
---|
| 171 | double& psi, double& phi ) {
|
---|
| 172 |
|
---|
| 173 | // set up quatities which would be positive if sin and cosine of
|
---|
| 174 | // psi and phi were positive:
|
---|
| 175 | double w[4];
|
---|
| 176 | w[0] = rxz; w[1] = rzx; w[2] = ryz; w[3] = -rzy;
|
---|
| 177 |
|
---|
| 178 | // find biggest relevant term, which is the best one to use in correcting.
|
---|
| 179 | double maxw = abs(w[0]);
|
---|
| 180 | int imax = 0;
|
---|
| 181 | for (int i = 1; i < 4; ++i) {
|
---|
| 182 | if (abs(w[i]) > maxw) {
|
---|
| 183 | maxw = abs(w[i]);
|
---|
| 184 | imax = i;
|
---|
| 185 | }
|
---|
| 186 | }
|
---|
| 187 | // Determine if the correction needs to be applied: The criteria are
|
---|
| 188 | // different depending on whether a sine or cosine was the determinor:
|
---|
| 189 | switch (imax) {
|
---|
| 190 | case 0:
|
---|
| 191 | if (w[0] > 0 && psi < 0) correctByPi ( psi, phi );
|
---|
| 192 | if (w[0] < 0 && psi > 0) correctByPi ( psi, phi );
|
---|
| 193 | break;
|
---|
| 194 | case 1:
|
---|
| 195 | if (w[1] > 0 && phi < 0) correctByPi ( psi, phi );
|
---|
| 196 | if (w[1] < 0 && phi > 0) correctByPi ( psi, phi );
|
---|
| 197 | break;
|
---|
| 198 | case 2:
|
---|
| 199 | if (w[2] > 0 && abs(psi) > CLHEP::halfpi) correctByPi ( psi, phi );
|
---|
| 200 | if (w[2] < 0 && abs(psi) < CLHEP::halfpi) correctByPi ( psi, phi );
|
---|
| 201 | break;
|
---|
| 202 | case 3:
|
---|
| 203 | if (w[3] > 0 && abs(phi) > CLHEP::halfpi) correctByPi ( psi, phi );
|
---|
| 204 | if (w[3] < 0 && abs(phi) < CLHEP::halfpi) correctByPi ( psi, phi );
|
---|
| 205 | break;
|
---|
| 206 | }
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | |
---|
| 210 |
|
---|
| 211 | HepEulerAngles HepRotation::eulerAngles() const {
|
---|
| 212 |
|
---|
| 213 | // Please see the mathematical justification in eulerAngleComputations.ps
|
---|
| 214 |
|
---|
| 215 | double phi, theta, psi;
|
---|
| 216 | double psiPlusPhi, psiMinusPhi;
|
---|
| 217 |
|
---|
| 218 | theta = safe_acos( rzz );
|
---|
| 219 |
|
---|
| 220 | if (rzz > 1 || rzz < -1) {
|
---|
| 221 | ZMthrowC ( ZMxpvImproperRotation (
|
---|
| 222 | "HepRotation::eulerAngles() finds | rzz | > 1 "));
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | double cosTheta = rzz;
|
---|
| 226 | if (cosTheta > 1) cosTheta = 1;
|
---|
| 227 | if (cosTheta < -1) cosTheta = -1;
|
---|
| 228 |
|
---|
| 229 | if (cosTheta == 1) {
|
---|
| 230 | psiPlusPhi = atan2 ( rxy - ryx, rxx + ryy );
|
---|
| 231 | psiMinusPhi = 0;
|
---|
| 232 |
|
---|
| 233 | } else if (cosTheta >= 0) {
|
---|
| 234 |
|
---|
| 235 | // In this realm, the atan2 expression for psi + phi is numerically stable
|
---|
| 236 | psiPlusPhi = atan2 ( rxy - ryx, rxx + ryy );
|
---|
| 237 |
|
---|
| 238 | // psi - phi is potentially more subtle, but when unstable it is moot
|
---|
| 239 | double s = -rxy - ryx; // sin (psi-phi) * (1 - cos theta)
|
---|
| 240 | double c = rxx - ryy; // cos (psi-phi) * (1 - cos theta)
|
---|
| 241 | psiMinusPhi = atan2 ( s, c );
|
---|
| 242 |
|
---|
| 243 | } else if (cosTheta > -1) {
|
---|
| 244 |
|
---|
| 245 | // In this realm, the atan2 expression for psi - phi is numerically stable
|
---|
| 246 | psiMinusPhi = atan2 ( -rxy - ryx, rxx - ryy );
|
---|
| 247 |
|
---|
| 248 | // psi + phi is potentially more subtle, but when unstable it is moot
|
---|
| 249 | double s = rxy - ryx; // sin (psi+phi) * (1 + cos theta)
|
---|
| 250 | double c = rxx + ryy; // cos (psi+phi) * (1 + cos theta)
|
---|
| 251 | psiPlusPhi = atan2 ( s, c );
|
---|
| 252 |
|
---|
| 253 | } else { // cosTheta == -1
|
---|
| 254 |
|
---|
| 255 | psiMinusPhi = atan2 ( -rxy - ryx, rxx - ryy );
|
---|
| 256 | psiPlusPhi = 0;
|
---|
| 257 |
|
---|
| 258 | }
|
---|
| 259 |
|
---|
| 260 | psi = .5 * (psiPlusPhi + psiMinusPhi);
|
---|
| 261 | phi = .5 * (psiPlusPhi - psiMinusPhi);
|
---|
| 262 |
|
---|
| 263 | // Now correct by pi if we have managed to get a value of psiPlusPhi
|
---|
| 264 | // or psiMinusPhi that was off by 2 pi:
|
---|
| 265 | correctPsiPhi ( rxz, rzx, ryz, rzy, psi, phi );
|
---|
| 266 |
|
---|
| 267 | return HepEulerAngles( phi, theta, psi );
|
---|
| 268 |
|
---|
| 269 | } // eulerAngles()
|
---|
| 270 |
|
---|
| 271 |
|
---|
| 272 | |
---|
| 273 |
|
---|
| 274 | void HepRotation::setPhi (double phi) {
|
---|
| 275 | set ( phi, theta(), psi() );
|
---|
| 276 | }
|
---|
| 277 |
|
---|
| 278 | void HepRotation::setTheta (double theta) {
|
---|
| 279 | set ( phi(), theta, psi() );
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | void HepRotation::setPsi (double psi) {
|
---|
| 283 | set ( phi(), theta(), psi );
|
---|
| 284 | }
|
---|
| 285 |
|
---|
| 286 | } // namespace CLHEP
|
---|
| 287 |
|
---|