source: trunk/CLHEP/src/RotationE.cc@ 20

Last change on this file since 20 was 4, checked in by Pavel Demin, 16 years ago

first commit

File size: 7.3 KB
Line 
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
26using std::abs;
27
28namespace CLHEP {
29
30static 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
39HepRotation & 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
61HepRotation::HepRotation( double phi, double theta, double psi )
62{
63 set (phi, theta, psi);
64}
65HepRotation & HepRotation::set( const HepEulerAngles & e ) {
66 return set(e.phi(), e.theta(), e.psi());
67}
68HepRotation::HepRotation ( const HepEulerAngles & e )
69{
70 set(e.phi(), e.theta(), e.psi());
71}
72
73
74
75
76double 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
110double HepRotation::theta() const {
111
112 return safe_acos( rzz );
113
114} // theta()
115
116double 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
155static
156void 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
169static
170void 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
211HepEulerAngles 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
274void HepRotation::setPhi (double phi) {
275 set ( phi, theta(), psi() );
276}
277
278void HepRotation::setTheta (double theta) {
279 set ( phi(), theta, psi() );
280}
281
282void HepRotation::setPsi (double psi) {
283 set ( phi(), theta(), psi );
284}
285
286} // namespace CLHEP
287
Note: See TracBrowser for help on using the repository browser.