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