1 /* -------------------------------------------------------------------------- *
2  *                       Simbody(tm): SimTKcommon                             *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2005-14 Stanford University and the Authors.        *
10  * Authors: Paul Mitiguy, Michael Sherman                                     *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 /**@file
25  * Implementations of non-inline methods associated with Rotation class.
26  */
27 
28 #include "SimTKcommon/basics.h"
29 #include "SimTKcommon/internal/Rotation.h"
30 
31 #include <cmath>
32 #include <utility>
33 
34 //------------------------------------------------------------------------------
35 namespace SimTK {
36 
37 
38 //------------------------------------------------------------------------------
39 // Set Rotation for ANY two-angle ij rotation sequence (i,j = X,Y,Z)
40 //------------------------------------------------------------------------------
41 template <class P> Rotation_<P>&
setRotationFromTwoAnglesTwoAxes(BodyOrSpaceType bodyOrSpace,RealP angle1,const CoordinateAxis & axis1In,RealP angle2,const CoordinateAxis & axis2In)42 Rotation_<P>::setRotationFromTwoAnglesTwoAxes
43   ( BodyOrSpaceType bodyOrSpace,
44     RealP angle1, const CoordinateAxis& axis1In,
45     RealP angle2, const CoordinateAxis& axis2In )
46 {
47     // Non-const CoordinateAxes in case we have to switch axes
48     CoordinateAxis axis1 = axis1In;
49     CoordinateAxis axis2 = axis2In;
50 
51     // If axis2 is same as axis1, efficiently calculate with a one-angle,
52     // one-axis rotation.
53     if( axis1.isSameAxis(axis2) )
54     { return setRotationFromAngleAboutAxis( angle1+angle2, axis1 ); }
55 
56     // If using a SpaceRotationSequence, switch order of axes and angles.
57     if( bodyOrSpace == SpaceRotationSequence )
58     { std::swap(angle1,angle2);  std::swap(axis1,axis2); }
59 
60     // If using a reverse cyclical, negate the signs of the angles
61     if( axis1.isReverseCyclical(axis2) )
62     { angle1 = -angle1; angle2 = -angle2; }
63 
64     // Calculate the sines and cosines (some hardware can do this more
65     // efficiently as one Taylor series).
66     const RealP c1 = std::cos( angle1 ),  s1 = std::sin( angle1 );
67     const RealP c2 = std::cos( angle2 ),  s2 = std::sin( angle2 );
68 
69     // All calculations are based on a body-fixed forward-cyclical rotation
70     // sequence.
71     return setTwoAngleTwoAxesBodyFixedForwardCyclicalRotation( c1,s1,axis1,
72                                                                c2,s2,axis2 );
73 }
74 
75 
76 //------------------------------------------------------------------------------
77 // Set Rotation for ANY three-angle ijk rotation sequence where (i,j,k = X,Y,Z)
78 //------------------------------------------------------------------------------
79 template <class P> Rotation_<P>&
setRotationFromThreeAnglesThreeAxes(BodyOrSpaceType bodyOrSpace,RealP angle1,const CoordinateAxis & axis1In,RealP angle2,const CoordinateAxis & axis2,RealP angle3,const CoordinateAxis & axis3In)80 Rotation_<P>::setRotationFromThreeAnglesThreeAxes
81   ( BodyOrSpaceType bodyOrSpace,
82     RealP angle1, const CoordinateAxis& axis1In,
83     RealP angle2, const CoordinateAxis& axis2,
84     RealP angle3, const CoordinateAxis& axis3In ) {
85 
86     // Non-const CoordinateAxes in case we have to switch axes.
87     CoordinateAxis axis1 = axis1In;
88     CoordinateAxis axis3 = axis3In;
89 
90     // If axis2 is same as axis1 or axis3, efficiently calculate with a
91     // two-angle, two-axis rotation.
92     if( axis2.isSameAxis(axis1) )
93         return setRotationFromTwoAnglesTwoAxes(bodyOrSpace, angle1+angle2,
94                                                axis1,  angle3,axis3);
95     if( axis2.isSameAxis(axis3) )
96         return setRotationFromTwoAnglesTwoAxes(bodyOrSpace, angle1,axis1,
97                                                angle2+angle3, axis3);
98 
99     // If using a SpaceRotationSequence, switch order of the axes and angles.
100     if( bodyOrSpace == SpaceRotationSequence )
101     { std::swap(angle1,angle3);  std::swap(axis1,axis3); }
102 
103     // If using a reverse cyclical, negate the signs of the angles.
104     if( axis1.isReverseCyclical(axis2) )
105     { angle1 = -angle1;   angle2 = -angle2;   angle3 = -angle3; }
106 
107     // Calculate the sines and cosines (some hardware can do this more
108     // efficiently as one Taylor series).
109     const RealP c1 = std::cos( angle1 ),  s1 = std::sin( angle1 );
110     const RealP c2 = std::cos( angle2 ),  s2 = std::sin( angle2 );
111     const RealP c3 = std::cos( angle3 ),  s3 = std::sin( angle3 );
112 
113     // All calculations are based on a body-fixed rotation sequence.
114     // Determine whether this is a BodyXYX or BodyXYZ type of rotation sequence.
115     if( axis1.isSameAxis(axis3) )
116         setThreeAngleTwoAxesBodyFixedForwardCyclicalRotation
117                                         (c1,s1,axis1, c2,s2,axis2, c3,s3);
118     else
119         setThreeAngleThreeAxesBodyFixedForwardCyclicalRotation
120                                         (c1,s1,axis1, c2,s2,axis2, c3,s3,axis3);
121 
122     return *this;
123 }
124 
125 
126 //------------------------------------------------------------------------------
127 // Calculate angle for ANY X or Y or Z rotation sequence
128 //------------------------------------------------------------------------------
129 template <class P> P
convertOneAxisRotationToOneAngle(const CoordinateAxis & axis1) const130 Rotation_<P>::convertOneAxisRotationToOneAngle(const CoordinateAxis& axis1) const
131 {
132     // Get proper indices into Rotation matrix
133     const CoordinateAxis axis2 = axis1.getNextAxis();
134     const CoordinateAxis axis3 = axis2.getNextAxis();
135     const int j = int( axis2 );
136     const int k = int( axis3 );
137 
138     const Mat33P& R = asMat33();
139     const RealP sinTheta = ( R[k][j] - R[j][k] ) / 2;
140     const RealP cosTheta = ( R[j][j] + R[k][k] ) / 2;
141 
142     return std::atan2( sinTheta, cosTheta );
143 }
144 
145 
146 //------------------------------------------------------------------------------
147 // Calculate angles for ANY two-angle ij rotation sequence (i,j = X,Y,Z)
148 //------------------------------------------------------------------------------
149 template <class P> Vec<2,P>
convertTwoAxesRotationToTwoAngles(BodyOrSpaceType bodyOrSpace,const CoordinateAxis & axis1In,const CoordinateAxis & axis2In) const150 Rotation_<P>::convertTwoAxesRotationToTwoAngles
151   ( BodyOrSpaceType bodyOrSpace,
152     const CoordinateAxis& axis1In,
153     const CoordinateAxis& axis2In ) const
154 {
155     // Non-const CoordinateAxes in case we have to switch axes
156     CoordinateAxis axis1 = axis1In;
157     CoordinateAxis axis2 = axis2In;
158 
159     // If axis2 is same as axis1, efficiently calculate with a one-axis,
160     // one-angle method.
161     if( axis1.isSameAxis(axis2) )
162     {   const RealP theta = convertOneAxisRotationToOneAngle(axis1) / 2;
163         return Vec<2,P>(theta,theta); }
164 
165     // If using a SpaceRotationSequence, switch the order of the axes (later
166     // switch the angles).
167     if( bodyOrSpace == SpaceRotationSequence )  std::swap(axis1,axis2);
168 
169     // All calculations are based on a body-fixed rotation sequence
170     Vec<2,P> ans = convertTwoAxesBodyFixedRotationToTwoAngles( axis1, axis2 );
171 
172     // If using a SpaceRotationSequence, switch the angles now.
173     if( bodyOrSpace == SpaceRotationSequence )  std::swap( ans[0], ans[1] );
174 
175     return ans;
176 }
177 
178 
179 //------------------------------------------------------------------------------
180 // Calculate angles for ANY 3-angle ijk rotation sequence where (i,j,k = X,Y,Z)
181 //------------------------------------------------------------------------------
182 template <class P> Vec<3,P>
convertThreeAxesRotationToThreeAngles(BodyOrSpaceType bodyOrSpace,const CoordinateAxis & axis1In,const CoordinateAxis & axis2,const CoordinateAxis & axis3In) const183 Rotation_<P>::convertThreeAxesRotationToThreeAngles
184   ( BodyOrSpaceType         bodyOrSpace,
185     const CoordinateAxis&   axis1In,
186     const CoordinateAxis&   axis2,
187     const CoordinateAxis&   axis3In ) const
188 {
189     // Non-const CoordinateAxes in case we have to switch axes.
190     CoordinateAxis axis1 = axis1In;
191     CoordinateAxis axis3 = axis3In;
192 
193     // If all axes are same, efficiently calculate with a one-axis, one-angle
194     // method.
195     if( axis1.areAllSameAxes(axis2,axis3) ) {
196         RealP theta = convertOneAxisRotationToOneAngle(axis1) / 3;
197         return Vec3P(theta,theta,theta);
198     }
199 
200     // If axis2 is same as axis1, efficiently calculate with a two-angle,
201     // two-axis rotation.
202     if( axis2.isSameAxis(axis1) ) {
203         const Vec2P xz = convertTwoAxesRotationToTwoAngles
204                                                     (bodyOrSpace,axis1,axis3);
205         const RealP theta = xz[0] / 2;
206         return Vec3P( theta, theta, xz[1] );
207     }
208 
209     // If axis2 is same as axis3, efficiently calculate with a two-angle,
210     // two-axis rotation.
211     if( axis2.isSameAxis(axis3) ) {
212         const Vec2P xz = convertTwoAxesRotationToTwoAngles
213                                                     (bodyOrSpace,axis1,axis3);
214         const RealP theta = xz[1] / 2;
215         return Vec3P( xz[0], theta, theta);
216     }
217 
218     // If using a SpaceRotationSequence, switch the order of the axes (later
219     // switch the angles).
220     if( bodyOrSpace == SpaceRotationSequence )  std::swap(axis1,axis3);
221 
222     // All calculations are based on a body-fixed rotation sequence.
223     // Determine whether this is a BodyXYX or BodyXYZ type of rotation sequence.
224     Vec3P ans = axis1.isSameAxis(axis3)
225         ? convertTwoAxesBodyFixedRotationToThreeAngles(axis1, axis2)
226         : convertThreeAxesBodyFixedRotationToThreeAngles(axis1, axis2, axis3);
227 
228     // If using a SpaceRotationSequence, switch the angles now.
229     if( bodyOrSpace == SpaceRotationSequence ) std::swap(ans[0], ans[2]);
230 
231     return ans;
232 }
233 
234 
235 
236 //------------------------------------------------------------------------------
237 // Set Rotation ONLY for two-angle, two-axes, body-fixed, ij rotation sequence
238 // where i != j.
239 //------------------------------------------------------------------------------
240 template <class P> Rotation_<P>&
setTwoAngleTwoAxesBodyFixedForwardCyclicalRotation(RealP cosAngle1,RealP sinAngle1,const CoordinateAxis & axis1,RealP cosAngle2,RealP sinAngle2,const CoordinateAxis & axis2)241 Rotation_<P>::setTwoAngleTwoAxesBodyFixedForwardCyclicalRotation
242   ( RealP cosAngle1, RealP sinAngle1, const CoordinateAxis& axis1,
243     RealP cosAngle2, RealP sinAngle2, const CoordinateAxis& axis2 )
244 {
245     // Ensure this method has proper arguments
246     assert( axis1.isDifferentAxis( axis2 ) );
247 
248     CoordinateAxis axis3 = axis1.getThirdAxis( axis2 );
249     const int i = int( axis1 );
250     const int j = int( axis2 );
251     const int k = int( axis3 );
252 
253     Mat33P& R = *this;
254     R[i][i] =  cosAngle2;
255     R[i][j] =  0;
256     R[i][k] =  sinAngle2;
257     R[j][i] =  sinAngle2 * sinAngle1;
258     R[j][j] =  cosAngle1;
259     R[j][k] = -sinAngle1 * cosAngle2;
260     R[k][i] = -sinAngle2 * cosAngle1;
261     R[k][j] =  sinAngle1;
262     R[k][k] =  cosAngle1 * cosAngle2;
263     return *this;
264 }
265 
266 
267 //------------------------------------------------------------------------------
268 // Set Rotation ONLY for three-angle, two-axes, body-fixed, iji rotation
269 // sequence where i != j.
270 //------------------------------------------------------------------------------
271 template <class P> Rotation_<P>&
setThreeAngleTwoAxesBodyFixedForwardCyclicalRotation(RealP cosAngle1,RealP sinAngle1,const CoordinateAxis & axis1,RealP cosAngle2,RealP sinAngle2,const CoordinateAxis & axis2,RealP cosAngle3,RealP sinAngle3)272 Rotation_<P>::setThreeAngleTwoAxesBodyFixedForwardCyclicalRotation
273   ( RealP cosAngle1, RealP sinAngle1, const CoordinateAxis& axis1,
274     RealP cosAngle2, RealP sinAngle2, const CoordinateAxis& axis2,
275     RealP cosAngle3, RealP sinAngle3 )
276 {
277     // Ensure this method has proper arguments
278     assert( axis1.isDifferentAxis( axis2 ) );
279 
280     // Repeated calculations (for efficiency)
281     RealP s1c3 = sinAngle1 * cosAngle3;
282     RealP s3c1 = sinAngle3 * cosAngle1;
283     RealP s1s3 = sinAngle1 * sinAngle3;
284     RealP c1c3 = cosAngle1 * cosAngle3;
285 
286     CoordinateAxis axis3 = axis1.getThirdAxis( axis2 );
287     const int i = int( axis1 );
288     const int j = int( axis2 );
289     const int k = int( axis3 );
290 
291     Mat33P& R =  *this;
292     R[i][i] =  cosAngle2;
293     R[i][j] =  sinAngle2 * sinAngle3;
294     R[i][k] =  sinAngle2 * cosAngle3;
295     R[j][i] =  sinAngle1 * sinAngle2;
296     R[j][j] =  c1c3 - cosAngle2 * s1s3;
297     R[j][k] = -s3c1 - cosAngle2 * s1c3;
298     R[k][i] = -sinAngle2 * cosAngle1;
299     R[k][j] =  s1c3 + cosAngle2 * s3c1;
300     R[k][k] = -s1s3 + cosAngle2 * c1c3;
301     return *this;
302 }
303 
304 
305 //------------------------------------------------------------------------------
306 // Set Rotation ONLY for three-angle, three-axes, body-fixed, ijk rotation
307 // sequence where i != j != k.
308 //------------------------------------------------------------------------------
309 template <class P> Rotation_<P>&
setThreeAngleThreeAxesBodyFixedForwardCyclicalRotation(RealP cosAngle1,RealP sinAngle1,const CoordinateAxis & axis1,RealP cosAngle2,RealP sinAngle2,const CoordinateAxis & axis2,RealP cosAngle3,RealP sinAngle3,const CoordinateAxis & axis3)310 Rotation_<P>::setThreeAngleThreeAxesBodyFixedForwardCyclicalRotation
311   ( RealP cosAngle1, RealP sinAngle1, const CoordinateAxis& axis1,
312     RealP cosAngle2, RealP sinAngle2, const CoordinateAxis& axis2,
313     RealP cosAngle3, RealP sinAngle3, const CoordinateAxis& axis3 )
314 {
315     // Ensure this method has proper arguments
316     assert( axis1.areAllDifferentAxes(axis2,axis3) );
317 
318     // Repeated calculations (for efficiency)
319     RealP s1c3 = sinAngle1 * cosAngle3;
320     RealP s3c1 = sinAngle3 * cosAngle1;
321     RealP s1s3 = sinAngle1 * sinAngle3;
322     RealP c1c3 = cosAngle1 * cosAngle3;
323 
324     const int i = int(axis1);
325     const int j = int(axis2);
326     const int k = int(axis3);
327 
328     Mat33P& R = *this;
329     R[i][i] =  cosAngle2 * cosAngle3;
330     R[i][j] = -sinAngle3 * cosAngle2;
331     R[i][k] =  sinAngle2;
332     R[j][i] =  s3c1 + sinAngle2 * s1c3;
333     R[j][j] =  c1c3 - sinAngle2 * s1s3;
334     R[j][k] = -sinAngle1 * cosAngle2;
335     R[k][i] =  s1s3 - sinAngle2 * c1c3;
336     R[k][j] =  s1c3 + sinAngle2 * s3c1;
337     R[k][k] =  cosAngle1 * cosAngle2;
338     return *this;
339 }
340 
341 
342 //------------------------------------------------------------------------------
343 // Calculate angles ONLY for a two-angle, two-axes, body-fixed, ij rotation
344 // sequence where i != j.
345 //------------------------------------------------------------------------------
346 template <class P> Vec<2,P>
convertTwoAxesBodyFixedRotationToTwoAngles(const CoordinateAxis & axis1,const CoordinateAxis & axis2) const347 Rotation_<P>::convertTwoAxesBodyFixedRotationToTwoAngles
348   ( const CoordinateAxis& axis1, const CoordinateAxis& axis2 ) const
349 {
350     // Ensure this method has proper arguments
351     assert( axis1.isDifferentAxis( axis2 ) );
352 
353     CoordinateAxis axis3 = axis1.getThirdAxis(axis2);
354     const int i = int(axis1);
355     const int j = int(axis2);
356     const int k = int(axis3);
357     const Mat33P& R =  asMat33();
358 
359     // Can use either direct method (fast) or all matrix elements with the
360     // overhead of two additional square roots (possibly more accurate).
361     const RealP sinTheta1Direct = R[k][j];
362     const RealP signSinTheta1 = sinTheta1Direct > 0 ? RealP(1) : RealP(-1);
363     const RealP sinTheta1Alternate =
364         signSinTheta1 * std::sqrt( square(R[j][i]) + square(R[j][k]) );
365     const RealP sinTheta1 = ( sinTheta1Direct + sinTheta1Alternate ) / 2;
366 
367     const RealP cosTheta1Direct = R[j][j];
368     const RealP signCosTheta1 = cosTheta1Direct > 0 ? RealP(1) : RealP(-1);
369     const RealP cosTheta1Alternate =
370         signCosTheta1 * std::sqrt( square(R[k][i]) + square(R[k][k]) );
371     const RealP cosTheta1 = ( cosTheta1Direct + cosTheta1Alternate ) / 2;
372 
373     RealP theta1 = std::atan2( sinTheta1, cosTheta1 );
374 
375     // Repeat for theta2
376     const RealP sinTheta2Direct = R[i][k];
377     const RealP signSinTheta2 = sinTheta2Direct > 0 ? RealP(1) : RealP(-1);
378     const RealP sinTheta2Alternate =
379         signSinTheta2 * std::sqrt( square(R[j][i]) + square(R[k][i]) );
380     const RealP sinTheta2 = ( sinTheta2Direct + sinTheta2Alternate ) / 2;
381 
382     const RealP cosTheta2Direct = R[i][i];
383     const RealP signCosTheta2 = cosTheta2Direct > 0 ? RealP(1) : RealP(-1);
384     const RealP cosTheta2Alternate =
385         signCosTheta2 * std::sqrt( square(R[j][k]) + square(R[k][k]) );
386     const RealP cosTheta2 = ( cosTheta2Direct + cosTheta2Alternate ) / 2;
387 
388     RealP theta2 = std::atan2( sinTheta2, cosTheta2 );
389 
390     // If using a reverse cyclical, negate the signs of the angles
391     if( axis1.isReverseCyclical(axis2) )  { theta1 = -theta1;  theta2 = -theta2; }
392 
393     // Return values have the following ranges:
394     // -pi   <=  theta1  <=  +pi
395     // -pi   <=  theta2  <=  +pi
396     return Vec2P( theta1, theta2 );
397 }
398 
399 
400 //------------------------------------------------------------------------------
401 // Calculate angles ONLY for a three-angle, two-axes, body-fixed, iji rotation
402 // sequence where i != j.
403 //------------------------------------------------------------------------------
404 template <class P> Vec<3,P>
convertTwoAxesBodyFixedRotationToThreeAngles(const CoordinateAxis & axis1,const CoordinateAxis & axis2) const405 Rotation_<P>::convertTwoAxesBodyFixedRotationToThreeAngles
406   ( const CoordinateAxis& axis1, const CoordinateAxis& axis2 ) const
407 {
408     // Ensure this method has proper arguments.
409     assert( axis1.isDifferentAxis( axis2 ) );
410 
411     CoordinateAxis axis3 = axis1.getThirdAxis( axis2 );
412     const int i = int( axis1 );
413     const int j = int( axis2 );
414     const int k = int( axis3 );
415 
416     // Need to know if using a forward or reverse cyclical.
417     RealP plusMinus = 1.0,  minusPlus = -1.0;
418     if( axis1.isReverseCyclical(axis2) ) { plusMinus = -1.0,  minusPlus = 1.0; }
419 
420     // Shortcut to the elements of the rotation matrix.
421     const Mat33P& R = asMat33();
422 
423     // Calculate theta2 using lots of information in the rotation matrix.
424     const RealP Rsum   = std::sqrt( (  square(R[i][j]) + square(R[i][k])
425                                      + square(R[j][i]) + square(R[k][i])) / 2 );
426     // Rsum = abs(sin(theta2)) is inherently positive.
427     const RealP theta2 = std::atan2( Rsum, R[i][i] );
428     RealP theta1, theta3;
429 
430     // There is a "singularity" when sin(theta2) == 0
431     if( Rsum > 4*Eps ) {
432         theta1  =  std::atan2( R[j][i], minusPlus*R[k][i] );
433         theta3  =  std::atan2( R[i][j], plusMinus*R[i][k] );
434     }
435     else if( R[i][i] > 0 ) {
436         const RealP spos = plusMinus*R[k][j] + minusPlus*R[j][k];  // 2*sin(theta1 + theta3)
437         const RealP cpos = R[j][j] + R[k][k];                      // 2*cos(theta1 + theta3)
438         const RealP theta1PlusTheta3 = std::atan2( spos, cpos );
439         theta1 = theta1PlusTheta3;  // Arbitrary split
440         theta3 = 0;                 // Arbitrary split
441     }
442     else {
443         const RealP sneg = plusMinus*R[k][j] + plusMinus*R[j][k];  // 2*sin(theta1 - theta3)
444         const RealP cneg = R[j][j] - R[k][k];                      // 2*cos(theta1 - theta3)
445         const RealP theta1MinusTheta3 = std::atan2( sneg, cneg );
446         theta1 = theta1MinusTheta3;  // Arbitrary split
447         theta3 = 0;                  // Arbitrary split
448     }
449 
450     // Return values have the following ranges:
451     // -pi   <=  theta1  <=  +pi
452     //   0   <=  theta2  <=  +pi    (Rsum is inherently positive)
453     // -pi   <=  theta3  <=  +pi
454     return Vec3P( theta1, theta2, theta3 );
455 }
456 
457 
458 //------------------------------------------------------------------------------
459 // Calculate angles ONLY for a three-angle, three-axes, body-fixed, ijk rotation
460 // sequence where i != j and j != k.
461 //------------------------------------------------------------------------------
462 template <class P> Vec<3,P>
convertThreeAxesBodyFixedRotationToThreeAngles(const CoordinateAxis & axis1,const CoordinateAxis & axis2,const CoordinateAxis & axis3) const463 Rotation_<P>::convertThreeAxesBodyFixedRotationToThreeAngles
464   ( const CoordinateAxis& axis1, const CoordinateAxis& axis2,
465     const CoordinateAxis& axis3 ) const
466 {
467     // Ensure this method has proper arguments.
468     assert( axis1.areAllDifferentAxes(axis2,axis3) );
469 
470     const int i = int(axis1);
471     const int j = int(axis2);
472     const int k = int(axis3);
473 
474     // Need to know if using a forward or reverse cyclical.
475     RealP plusMinus = 1.0,  minusPlus = -1.0;
476     if( axis1.isReverseCyclical(axis2) ) { plusMinus = -1.0,  minusPlus = 1.0; }
477 
478     // Shortcut to the elements of the rotation matrix.
479     const Mat33P& R = asMat33();
480 
481     // Calculate theta2 using lots of information in the rotation matrix.
482     RealP Rsum   =  std::sqrt((  square(R[i][i]) + square(R[i][j])
483                                + square(R[j][k]) + square(R[k][k])) / 2);
484     // Rsum = abs(cos(theta2)) is inherently positive.
485     RealP theta2 =  std::atan2( plusMinus*R[i][k], Rsum );
486     RealP theta1, theta3;
487 
488     // There is a "singularity" when cos(theta2) == 0
489     if( Rsum > 4*Eps ) {
490         theta1 =  std::atan2( minusPlus*R[j][k], R[k][k] );
491         theta3 =  std::atan2( minusPlus*R[i][j], R[i][i] );
492     }
493     else if( plusMinus*R[i][k] > 0 ) {
494         const RealP spos = R[j][i] + plusMinus*R[k][j];  // 2*sin(theta1 + plusMinus*theta3)
495         const RealP cpos = R[j][j] + minusPlus*R[k][i];  // 2*cos(theta1 + plusMinus*theta3)
496         const RealP theta1PlusMinusTheta3 = std::atan2( spos, cpos );
497         theta1 = theta1PlusMinusTheta3;  // Arbitrary split
498         theta3 = 0;                      // Arbitrary split
499     }
500     else {
501         const RealP sneg = plusMinus*(R[k][j] + minusPlus*R[j][i]);  // 2*sin(theta1 + minusPlus*theta3)
502         const RealP cneg = R[j][j] + plusMinus*R[k][i];              // 2*cos(theta1 + minusPlus*theta3)
503         const RealP theta1MinusPlusTheta3 = std::atan2( sneg, cneg );
504         theta1 = theta1MinusPlusTheta3;  // Arbitrary split
505         theta3 = 0;                      // Arbitrary split
506     }
507 
508     // Return values have the following ranges:
509     // -pi   <=  theta1  <=  +pi
510     // -pi/2 <=  theta2  <=  +pi/2   (Rsum is inherently positive)
511     // -pi   <=  theta3  <=  +pi
512     return Vec3P( theta1, theta2, theta3 );
513 }
514 
515 
516 //------------------------------------------------------------------------------
517 // Calculate R_AB by knowing one of B's unit vector expressed in A.
518 // Note: The other vectors are perpendicular (but somewhat arbitrarily so).
519 //------------------------------------------------------------------------------
520 template <class P> Rotation_<P>&
setRotationFromOneAxis(const UnitVec3P & uveci,CoordinateAxis axisi)521 Rotation_<P>::setRotationFromOneAxis(const UnitVec3P& uveci, CoordinateAxis axisi)
522 {
523     // Find a unit vector that is perpendicular to uveci.
524     const UnitVec3P uvecj = uveci.perp();
525 
526     // Find another unit vector that is perpendicular to both uveci and uvecj.
527     const UnitVec3P uveck = UnitVec3P( cross( uveci, uvecj ) );
528 
529     // Determine which of the axes this corresponds to.
530     CoordinateAxis axisj = axisi.getNextAxis();
531     CoordinateAxis axisk = axisj.getNextAxis();
532 
533     // Fill in the correct elements of the Rotation matrix.
534     setRotationColFromUnitVecTrustMe( int(axisi), uveci );
535     setRotationColFromUnitVecTrustMe( int(axisj), uvecj );
536     setRotationColFromUnitVecTrustMe( int(axisk), uveck );
537 
538     return *this;
539 }
540 
541 
542 //------------------------------------------------------------------------------
543 // Calculate A.RotationMatrix.B by knowing one of B's unit vectors expressed in
544 // A and another vector that may be perpendicular. If the 2nd vector is not
545 // perpendicular, no worries - we'll make it so it is perpendicular.
546 //------------------------------------------------------------------------------
547 template <class P> Rotation_<P>&
setRotationFromTwoAxes(const UnitVec3P & uveci,const CoordinateAxis & axisi,const Vec3P & vecjApprox,const CoordinateAxis & axisjApprox)548 Rotation_<P>::setRotationFromTwoAxes
549   ( const UnitVec3P& uveci, const CoordinateAxis& axisi,
550     const Vec3P& vecjApprox, const CoordinateAxis& axisjApprox )
551 {
552     // Ensure vecjApprox is not a zero vector and the axes are not the same.
553     RealP magnitudeOfVecjApprox = vecjApprox.normSqr();
554     if( magnitudeOfVecjApprox==0  ||  axisi.isSameAxis(axisjApprox) )
555       return setRotationFromOneAxis( uveci, axisi );
556 
557     // Create a unit vector that is perpendicular to both uveci and vecjApprox.
558     Vec3P veck = cross( uveci, vecjApprox );
559 
560     // Make sure (a x b) is not too close to the zero vector (vectors are nearly
561     // parallel). Since |a x b| = |a|*|b|*sin(theta), it is easy to determine
562     // when sin(theta) = |a x b| / (|a| * |b|) is small. In other words,
563     // sin(theta) = |a x b| / (|a| * |b|)  where |a| = 1 (since uveci is a unit
564     // vector). I use SqrtEps (which is approx 1e-8 in double) which means
565     // that the angle between the vectors is less than 1e-8 rads = 6e-7 deg.
566     RealP magnitudeOfVeck = veck.normSqr();   // Magnitude of the cross product.
567     if( magnitudeOfVeck < SimTK::SqrtEps * magnitudeOfVecjApprox )
568         return setRotationFromOneAxis( uveci, axisi );
569 
570     // Find a unit vector that is perpendicular to both uveci and vecjApprox.
571     UnitVec3P uveck = UnitVec3P( veck );
572 
573     // Compute the unit vector perpendicular to both uveci and uveck.
574     UnitVec3P uvecj = UnitVec3P( cross( uveck, uveci ) );
575 
576     // Determine which of the axes this corresponds to
577     CoordinateAxis axisj = axisi.getNextAxis();
578     CoordinateAxis axisk = axisj.getNextAxis();
579 
580     // If axisj is axisjApprox, all is good, otherwise switch axisj and axisk
581     // and negate the k'th axis.
582     if( axisj.isDifferentAxis(axisjApprox) ) {
583         std::swap( axisj, axisk );
584         uveck = -uveck;
585     }
586 
587     // Fill in the correct elements of the Rotation matrix
588     setRotationColFromUnitVecTrustMe( int(axisi), uveci );
589     setRotationColFromUnitVecTrustMe( int(axisj), uvecj );
590     setRotationColFromUnitVecTrustMe( int(axisk), uveck );
591 
592     return *this;
593 }
594 
595 
596 //------------------------------------------------------------------------------
597 // Set this rotation matrix from the associated quaternion. (29 flops)
598 //------------------------------------------------------------------------------
599 template <class P> Rotation_<P>&
setRotationFromQuaternion(const Quaternion_<P> & q)600 Rotation_<P>::setRotationFromQuaternion( const Quaternion_<P>& q )  {
601     const RealP q00=q[0]*q[0], q11=q[1]*q[1], q22=q[2]*q[2], q33=q[3]*q[3];
602     const RealP q01=q[0]*q[1], q02=q[0]*q[2], q03=q[0]*q[3];
603     const RealP q12=q[1]*q[2], q13=q[1]*q[3], q23=q[2]*q[3];
604     const RealP q00mq11 = q00-q11, q22mq33 = q22-q33;
605 
606     Mat33P::operator=(Mat33P(q00+q11-q22-q33,  2*(q12-q03)  ,  2*(q13+q02),
607                                2*(q12+q03)  ,q00mq11+q22mq33,  2*(q23-q01),
608                                2*(q13-q02)  ,  2*(q23+q01)  ,q00mq11-q22mq33));
609     return *this;
610 }
611 
612 
613 //------------------------------------------------------------------------------
614 // Convert this Rotation matrix to the equivalent quaternion.
615 //
616 // We use a modification of Richard Spurrier's method: Spurrier, R.A., "Comment
617 // on 'Singularity-Free Extraction of a Quaternion from a Direction-Cosine
618 // Matrix'", J. Spacecraft and Rockets, 15(4):255, 1977. Our modification
619 // avoids all but one square root and divide. In each of the four cases we
620 // compute 4q[m]*q where m is the "max" element, with
621 //   m=0 if the trace is larger than any diagonal or
622 //   m=i if the i,i element is the largest diagonal and larger than the trace.
623 // Then when we normalize at the end the scalar 4q[m] evaporates leaving us
624 // with a perfectly normalized quaternion.
625 //
626 // The returned quaternion can be interpreted as a rotation angle a about a unit
627 // vector v=[vx vy vz] like this:    q = [ cos(a/2) sin(a/2)*v ]
628 // We canonicalize the returned quaternion by insisting that
629 // cos(a/2) >= 0, meaning that -180 < a <= 180.
630 //
631 // This takes about 40 flops.
632 //------------------------------------------------------------------------------
633 template <class P> Quaternion_<P>
convertRotationToQuaternion() const634 Rotation_<P>::convertRotationToQuaternion() const {
635     const Mat33P& R = asMat33();
636 
637     // Stores the return values [cos(theta/2), lambda1*sin(theta/2),
638     //                           lambda2*sin(theta/2), lambda3*sin(theta/2)]
639     Vec4P q;
640 
641     // Check if the trace is larger than any diagonal
642     const RealP tr = R.trace();
643     if( tr >= R(0,0)  &&  tr >= R(1,1)  &&  tr >= R(2,2) ) {
644         q[0] = 1 + tr;
645         q[1] = R(2,1) - R(1,2);
646         q[2] = R(0,2) - R(2,0);
647         q[3] = R(1,0) - R(0,1);
648 
649     // Check if R(0,0) is largest along the diagonal
650     } else if( R(0,0) >= R(1,1)  &&  R(0,0) >= R(2,2)  ) {
651         q[0] = R(2,1) - R(1,2);
652         q[1] = 1 - (tr - 2*R(0,0));
653         q[2] = R(0,1)+R(1,0);
654         q[3] = R(0,2)+R(2,0);
655 
656     // Check if R(1,1) is largest along the diagonal
657     } else if( R(1,1) >= R(2,2) ) {
658         q[0] = R(0,2) - R(2,0);
659         q[1] = R(0,1) + R(1,0);
660         q[2] = 1 - (tr - 2*R(1,1));
661         q[3] = R(1,2) + R(2,1);
662 
663     // R(2,2) is largest along the diagonal
664     } else {
665         q[0] = R(1,0) - R(0,1);
666         q[1] = R(0,2) + R(2,0);
667         q[2] = R(1,2) + R(2,1);
668         q[3] = 1 - (tr - 2*R(2,2));
669     }
670     RealP scale = q.norm();
671     if( q[0] < 0 )  scale = -scale;   // canonicalize
672     return Quaternion_<P>(q/scale, true); // prevent re-normalization
673 }
674 
675 
676 
677 //------------------------------------------------------------------------------
678 // Determine whether "this" Rotation matrix is nearly identical to the one
679 // passed in (R) within a specified "pointing angle error".  If "this" and "R"
680 // are nearly identical, transpose(this) * R = closeToIdentity matrix. After
681 // finding the equivalent angle-axis for closeToIdentityMatrix, check the angle
682 // to see if it is less than okPointingAngleErrorRads.
683 //------------------------------------------------------------------------------
684 template <class P> bool
isSameRotationToWithinAngle(const Rotation_<P> & R,RealP okPointingAngleErrorRads) const685 Rotation_<P>::isSameRotationToWithinAngle
686    (const Rotation_<P>& R, RealP okPointingAngleErrorRads ) const {
687     // The absolute minimum pointing error is 0 if "this" and R are identical.
688     // The absolute maximum pointing error should be Pi (to machine precision).
689     assert( 0 <= okPointingAngleErrorRads && okPointingAngleErrorRads <= Pi);
690     const Rotation_<P> closeToIdentityMatrix = ~(*this) * R;
691     const Vec4P angleAxisEquivalent =
692         closeToIdentityMatrix.convertRotationToAngleAxis();
693     const RealP pointingError = std::abs( angleAxisEquivalent[0] );
694     return pointingError <= okPointingAngleErrorRads;
695 }
696 
697 
698 //------------------------------------------------------------------------------
699 // This method is a poor man's orthogonalization from the supplied matrix to
700 // make a legitimate rotation. This is done by conversion to quaternions and
701 // back to Rotation and may not produce the closest rotation.
702 //------------------------------------------------------------------------------
703 template <class P> Rotation_<P>&
setRotationFromApproximateMat33(const Mat33P & m)704 Rotation_<P>::setRotationFromApproximateMat33( const Mat33P& m ) {
705     // First, create a Rotation from the given Mat33P
706     const Rotation_<P> approximateRotation(m, true);
707     const Quaternion_<P> q = approximateRotation.convertRotationToQuaternion();
708     this->setRotationFromQuaternion( q );
709     return *this;
710 }
711 
712 
713 //------------------------------------------------------------------------------
714 // Note: It may be slightly more efficient to set this matrix directly rather
715 // than via the quaternion.
716 //------------------------------------------------------------------------------
717 template <class P> Rotation_<P>&
setRotationFromAngleAboutUnitVector(RealP angleInRad,const UnitVec3P & unitVector)718 Rotation_<P>::setRotationFromAngleAboutUnitVector
719   ( RealP angleInRad, const UnitVec3P& unitVector )
720 {
721     QuaternionP q;
722     q.setQuaternionFromAngleAxis( angleInRad, unitVector );
723     return setRotationFromQuaternion( q );
724 }
725 
726 //------------------------------------------------------------------------------
727 // Use sneaky tricks from Featherstone to rotate a symmetric dyadic
728 // matrix. Consider the current Rotation matrix to be R_AB. We want
729 // to return S_AA=R_AB*S_BB*R_BA. Would be 90 flops for 3x3s, 75
730 // since we only need six elements in final result. Here we'll get
731 // it done in 57 flops.
732 // Consider S=[ a d e ]
733 //            [ d b f ]
734 //            [ e f c ]
735 //
736 // First, factor S into S=L+D+vx with v=~[-f e 0] (x means cross
737 // product matrix):
738 //        [a-c   d   0]     [c 0 0]       [ 0  0  e]
739 //    L = [ d   b-c  0] D = [0 c 0]  vx = [ 0  0  f]
740 //        [2e   2f   0]     [0 0 c]       [-e -f  0]
741 // (4 flops to calculate L)
742 //
743 // A cross product matrix identity says R*vx*~R=(R*v)x, so:
744 //    S'=R*S*~R = R*L*~R + D + (R*v)x.
745 // Let Y'=R*L, Z=Y'*~R. We only need the lower triangle of Z and a
746 // 2x2 square of Y'.
747 //
748 // Don't-care's below are marked "-". Reminder: square bracket [i]
749 // index of a matrix means "row i", round bracket (j) means "col j".
750 //
751 //        [  -   -  0 ]
752 //   Y' = [ Y00 Y01 0 ]   Y = [ R[1]*L(0)  R[1]*L(1) ]  20 flops
753 //        [ Y10 Y11 0 ]       [ R[2]*L(0)  R[2]*L(1) ]
754 //
755 //   Z = [   Z00           -           -      ]
756 //       [ Y[0]*~R[0]  Y[0]*~R[1]      -      ]   15 flops (use only 2
757 //       [ Y[1]*~R[0]  Y[1]*~R[1]  Y[1]*~R[2] ]   elements of R's rows)
758 //
759 //   Z00 = (L00+L11)-(Z11+Z22)  3 flops ( because rotation preserves trace)
760 //
761 //        [R01*e-R00*f]            [  0       -    -  ]
762 //   R*v =[R11*e-R10*f]   (R*v)x = [ Rv[2]    0    -  ]
763 //        [R21*e-R20*f]            [-Rv[1]  Rv[0]  0  ]
764 // (R*v is 9 flops)
765 //
766 //        [  Z00 + c          -           -    ]
767 //   S' = [ Z10 + Rv[2]   Z11 + c         -    ]
768 //        [ Z20 - Rv[1]  Z21 + Rv[0]   Z22 + c ]
769 //
770 // which takes 6 more flops. Total 6+9Rv+18Z+20Y+4L=57.
771 //
772 // (I actually looked at the generated code in VC++ 2005 and Intel C++
773 //  version 11.1 and counted exactly 57 inline flops.)
774 //
775 // NOTE: there are two implementations of this routine that have
776 // to be kept in sync -- this one and the identical one for
777 // InverseRotation right below.
778 //------------------------------------------------------------------------------
779 template <class P> SymMat<3,P>
reexpressSymMat33(const SymMat33P & S_BB) const780 Rotation_<P>::reexpressSymMat33(const SymMat33P& S_BB) const {
781     const RealP a=S_BB(0,0), b=S_BB(1,1), c=S_BB(2,2);
782     const RealP d=S_BB(1,0), e=S_BB(2,0), f=S_BB(2,1);
783     const Mat33P& R   = this->asMat33();
784     const Mat32P& RR  = R.template getSubMat<3,2>(0,0); //first two columns of R
785 
786     const Mat32P L( a-c ,  d,
787                      d  , b-c,
788                     2*e , 2*f );
789 
790     const Mat22P Y( R[1]*L(0), R[1]*L(1),
791                     R[2]*L(0), R[2]*L(1) );
792 
793     const RealP Z10 = Y[0]*~RR[0], Z11 = Y[0]*~RR[1],
794                 Z20 = Y[1]*~RR[0], Z21 = Y[1]*~RR[1], Z22= Y[1]*~RR[2];
795     const RealP Z00 = (L(0,0)+L(1,1)) - (Z11+Z22);
796 
797     const Vec3P Rv( R(0,1)*e-R(0,0)*f,
798                     R(1,1)*e-R(1,0)*f,
799                     R(2,1)*e-R(2,0)*f );
800 
801     return SymMat33P( Z00 + c,
802                       Z10 + Rv[2], Z11 + c,
803                       Z20 - Rv[1], Z21 + Rv[0], Z22 + c );
804 }
805 
806 // See above method for details. This method is identical except that
807 // the layout of the matrix used to store the rotation matrix has
808 // changed. Note that all the indexing here is identical to the normal
809 // case above; but the rotation matrix elements are drawn from different
810 // memory locations so that the net effect is to use the transpose of
811 // the original rotation from which this was created.
812 template <class P> SymMat<3,P>
reexpressSymMat33(const SymMat<3,P> & S_BB) const813 InverseRotation_<P>::reexpressSymMat33(const SymMat<3,P>& S_BB) const {
814     const P a=S_BB(0,0), b=S_BB(1,1), c=S_BB(2,2);
815     const P d=S_BB(1,0), e=S_BB(2,0), f=S_BB(2,1);
816     // Note reversal of row and column spacing here (normal is 3,1).
817     const Mat<3,3,P,1,3>& R   = this->asMat33();
818     // RR is just the first two columns of R.
819     const Mat<3,2,P,1,3>& RR  = R.template getSubMat<3,2>(0,0);
820 
821     const Mat32P L( a-c ,  d,
822                      d  , b-c,
823                     2*e , 2*f );
824 
825     const Mat22P Y( R[1]*L(0), R[1]*L(1),
826                     R[2]*L(0), R[2]*L(1) );
827 
828     const P Z10 = Y[0]*~RR[0], Z11 = Y[0]*~RR[1],
829             Z20 = Y[1]*~RR[0], Z21 = Y[1]*~RR[1], Z22= Y[1]*~RR[2];
830     const P Z00 = (L(0,0)+L(1,1)) - (Z11+Z22);
831 
832     const Vec3P Rv( R(0,1)*e-R(0,0)*f,
833                     R(1,1)*e-R(1,0)*f,
834                     R(2,1)*e-R(2,0)*f );
835 
836     return SymMat<3,P>( Z00 + c,
837                       Z10 + Rv[2], Z11 + c,
838                       Z20 - Rv[1], Z21 + Rv[0], Z22 + c );
839 }
840 
841 // Make sure there are instantiations for all the non-inline methods.
842 template class Rotation_<float>;
843 template class Rotation_<double>;
844 template class InverseRotation_<float>;
845 template class InverseRotation_<double>;
846 
847 //------------------------------------------------------------------------------
848 template <class P> std::ostream&
operator <<(std::ostream & o,const Rotation_<P> & m)849 operator<<( std::ostream& o, const Rotation_<P>& m )
850 { return o << m.asMat33(); }
851 template <class P> std::ostream&
operator <<(std::ostream & o,const InverseRotation_<P> & m)852 operator<<( std::ostream& o, const InverseRotation_<P>& m )
853 { return o << Rotation_<P>(m); }
854 
855 template SimTK_SimTKCOMMON_EXPORT std::ostream&
856 operator<<(std::ostream&, const Rotation_<float>&);
857 template SimTK_SimTKCOMMON_EXPORT std::ostream&
858 operator<<(std::ostream&, const Rotation_<double>&);
859 
860 template SimTK_SimTKCOMMON_EXPORT std::ostream&
861 operator<<(std::ostream&, const InverseRotation_<float>&);
862 template SimTK_SimTKCOMMON_EXPORT std::ostream&
863 operator<<(std::ostream&, const InverseRotation_<double>&);
864 
865 
866 
867 //------------------------------------------------------------------------------
868 }  // End of namespace SimTK
869 
870