| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-09 09:59:36 +08:00
										 |  |  | #include <iostream>
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  | #include <Eigen/Geometry>
 | 
					
						
							|  |  |  | #include <bench/BenchTimer.h>
 | 
					
						
							|  |  |  | using namespace Eigen; | 
					
						
							|  |  |  | using namespace std; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename Q> | 
					
						
							|  |  |  | EIGEN_DONT_INLINE Q nlerp(const Q& a, const Q& b, typename Q::Scalar t) { | 
					
						
							|  |  |  |   return Q((a.coeffs() * (1.0 - t) + b.coeffs() * t).normalized()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename Q> | 
					
						
							|  |  |  | EIGEN_DONT_INLINE Q slerp_eigen(const Q& a, const Q& b, typename Q::Scalar t) { | 
					
						
							|  |  |  |   return a.slerp(t, b); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename Q> | 
					
						
							|  |  |  | EIGEN_DONT_INLINE Q slerp_legacy(const Q& a, const Q& b, typename Q::Scalar t) { | 
					
						
							|  |  |  |   typedef typename Q::Scalar Scalar; | 
					
						
							|  |  |  |   static const Scalar one = Scalar(1) - dummy_precision<Scalar>(); | 
					
						
							|  |  |  |   Scalar d = a.dot(b); | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |   Scalar absD = internal::abs(d); | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   if (absD >= one) return a; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // theta is the angle between the 2 quaternions
 | 
					
						
							|  |  |  |   Scalar theta = std::acos(absD); | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |   Scalar sinTheta = internal::sin(theta); | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |   Scalar scale0 = internal::sin((Scalar(1) - t) * theta) / sinTheta; | 
					
						
							|  |  |  |   Scalar scale1 = internal::sin((t * theta)) / sinTheta; | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   if (d < 0) scale1 = -scale1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Q(scale0 * a.coeffs() + scale1 * b.coeffs()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename Q> | 
					
						
							|  |  |  | EIGEN_DONT_INLINE Q slerp_legacy_nlerp(const Q& a, const Q& b, typename Q::Scalar t) { | 
					
						
							|  |  |  |   typedef typename Q::Scalar Scalar; | 
					
						
							|  |  |  |   static const Scalar one = Scalar(1) - epsilon<Scalar>(); | 
					
						
							|  |  |  |   Scalar d = a.dot(b); | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |   Scalar absD = internal::abs(d); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   Scalar scale0; | 
					
						
							|  |  |  |   Scalar scale1; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   if (absD >= one) { | 
					
						
							|  |  |  |     scale0 = Scalar(1) - t; | 
					
						
							|  |  |  |     scale1 = t; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // theta is the angle between the 2 quaternions
 | 
					
						
							|  |  |  |     Scalar theta = std::acos(absD); | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |     Scalar sinTheta = internal::sin(theta); | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |     scale0 = internal::sin((Scalar(1) - t) * theta) / sinTheta; | 
					
						
							|  |  |  |     scale1 = internal::sin((t * theta)) / sinTheta; | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |     if (d < 0) scale1 = -scale1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Q(scale0 * a.coeffs() + scale1 * b.coeffs()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename T> | 
					
						
							|  |  |  | inline T sin_over_x(T x) { | 
					
						
							|  |  |  |   if (T(1) + x * x == T(1)) | 
					
						
							|  |  |  |     return T(1); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return std::sin(x) / x; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename Q> | 
					
						
							|  |  |  | EIGEN_DONT_INLINE Q slerp_rw(const Q& a, const Q& b, typename Q::Scalar t) { | 
					
						
							|  |  |  |   typedef typename Q::Scalar Scalar; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   Scalar d = a.dot(b); | 
					
						
							|  |  |  |   Scalar theta; | 
					
						
							|  |  |  |   if (d < 0.0) | 
					
						
							|  |  |  |     theta = /*M_PI -*/ Scalar(2) * std::asin((a.coeffs() + b.coeffs()).norm() / 2); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     theta = Scalar(2) * std::asin((a.coeffs() - b.coeffs()).norm() / 2); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   // theta is the angle between the 2 quaternions
 | 
					
						
							|  |  |  |   //   Scalar theta = std::acos(absD);
 | 
					
						
							|  |  |  |   Scalar sinOverTheta = sin_over_x(theta); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Scalar scale0 = (Scalar(1) - t) * sin_over_x((Scalar(1) - t) * theta) / sinOverTheta; | 
					
						
							|  |  |  |   Scalar scale1 = t * sin_over_x((t * theta)) / sinOverTheta; | 
					
						
							|  |  |  |   if (d < 0) scale1 = -scale1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Quaternion<Scalar>(scale0 * a.coeffs() + scale1 * b.coeffs()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename Q> | 
					
						
							|  |  |  | EIGEN_DONT_INLINE Q slerp_gael(const Q& a, const Q& b, typename Q::Scalar t) { | 
					
						
							|  |  |  |   typedef typename Q::Scalar Scalar; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   Scalar d = a.dot(b); | 
					
						
							|  |  |  |   Scalar theta; | 
					
						
							|  |  |  |   //   theta = Scalar(2) * atan2((a.coeffs()-b.coeffs()).norm(),(a.coeffs()+b.coeffs()).norm());
 | 
					
						
							|  |  |  |   //   if (d<0.0)
 | 
					
						
							|  |  |  |   //     theta = M_PI-theta;
 | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   if (d < 0.0) | 
					
						
							|  |  |  |     theta = /*M_PI -*/ Scalar(2) * std::asin((-a.coeffs() - b.coeffs()).norm() / 2); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     theta = Scalar(2) * std::asin((a.coeffs() - b.coeffs()).norm() / 2); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   Scalar scale0; | 
					
						
							|  |  |  |   Scalar scale1; | 
					
						
							|  |  |  |   if (theta * theta - Scalar(6) == -Scalar(6)) { | 
					
						
							|  |  |  |     scale0 = Scalar(1) - t; | 
					
						
							|  |  |  |     scale1 = t; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     Scalar sinTheta = std::sin(theta); | 
					
						
							| 
									
										
										
										
											2010-10-25 22:15:22 +08:00
										 |  |  |     scale0 = internal::sin((Scalar(1) - t) * theta) / sinTheta; | 
					
						
							|  |  |  |     scale1 = internal::sin((t * theta)) / sinTheta; | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |     if (d < 0) scale1 = -scale1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Quaternion<Scalar>(scale0 * a.coeffs() + scale1 * b.coeffs()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main() { | 
					
						
							|  |  |  |   typedef double RefScalar; | 
					
						
							|  |  |  |   typedef float TestScalar; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   typedef Quaternion<RefScalar> Qd; | 
					
						
							|  |  |  |   typedef Quaternion<TestScalar> Qf; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   unsigned int g_seed = (unsigned int)time(NULL); | 
					
						
							|  |  |  |   std::cout << g_seed << "\n"; | 
					
						
							|  |  |  |   //   g_seed = 1259932496;
 | 
					
						
							|  |  |  |   srand(g_seed); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   Matrix<RefScalar, Dynamic, 1> maxerr(7); | 
					
						
							|  |  |  |   maxerr.setZero(); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   Matrix<RefScalar, Dynamic, 1> avgerr(7); | 
					
						
							|  |  |  |   avgerr.setZero(); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   cout << "double=>float=>double       nlerp        eigen        legacy(snap)         legacy(nlerp)        rightway    " | 
					
						
							|  |  |  |           "     gael's criteria\n"; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   int rep = 100; | 
					
						
							|  |  |  |   int iters = 40; | 
					
						
							|  |  |  |   for (int w = 0; w < rep; ++w) { | 
					
						
							|  |  |  |     Qf a, b; | 
					
						
							|  |  |  |     a.coeffs().setRandom(); | 
					
						
							|  |  |  |     a.normalize(); | 
					
						
							|  |  |  |     b.coeffs().setRandom(); | 
					
						
							|  |  |  |     b.normalize(); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |     Qf c[6]; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |     Qd ar(a.cast<RefScalar>()); | 
					
						
							|  |  |  |     Qd br(b.cast<RefScalar>()); | 
					
						
							|  |  |  |     Qd cr; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |     cout.precision(8); | 
					
						
							|  |  |  |     cout << std::scientific; | 
					
						
							|  |  |  |     for (int i = 0; i < iters; ++i) { | 
					
						
							|  |  |  |       RefScalar t = 0.65; | 
					
						
							|  |  |  |       cr = slerp_rw(ar, br, t); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |       Qf refc = cr.cast<TestScalar>(); | 
					
						
							|  |  |  |       c[0] = nlerp(a, b, t); | 
					
						
							|  |  |  |       c[1] = slerp_eigen(a, b, t); | 
					
						
							|  |  |  |       c[2] = slerp_legacy(a, b, t); | 
					
						
							|  |  |  |       c[3] = slerp_legacy_nlerp(a, b, t); | 
					
						
							|  |  |  |       c[4] = slerp_rw(a, b, t); | 
					
						
							|  |  |  |       c[5] = slerp_gael(a, b, t); | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |       VectorXd err(7); | 
					
						
							|  |  |  |       err[0] = (cr.coeffs() - refc.cast<RefScalar>().coeffs()).norm(); | 
					
						
							|  |  |  |       //       std::cout << err[0] << "    ";
 | 
					
						
							|  |  |  |       for (int k = 0; k < 6; ++k) { | 
					
						
							|  |  |  |         err[k + 1] = (c[k].coeffs() - refc.coeffs()).norm(); | 
					
						
							|  |  |  |         //         std::cout << err[k+1] << "    ";
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       maxerr = maxerr.cwise().max(err); | 
					
						
							|  |  |  |       avgerr += err; | 
					
						
							|  |  |  |       //       std::cout << "\n";
 | 
					
						
							|  |  |  |       b = cr.cast<TestScalar>(); | 
					
						
							|  |  |  |       br = cr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     //     std::cout << "\n";
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   avgerr /= RefScalar(rep * iters); | 
					
						
							|  |  |  |   cout << "\n\nAccuracy:\n" | 
					
						
							|  |  |  |        << "  max: " << maxerr.transpose() << "\n"; | 
					
						
							|  |  |  |   cout << "  avg: " << avgerr.transpose() << "\n"; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   // perf bench
 | 
					
						
							|  |  |  |   Quaternionf a, b; | 
					
						
							|  |  |  |   a.coeffs().setRandom(); | 
					
						
							|  |  |  |   a.normalize(); | 
					
						
							|  |  |  |   b.coeffs().setRandom(); | 
					
						
							|  |  |  |   b.normalize(); | 
					
						
							|  |  |  |   // b = a;
 | 
					
						
							|  |  |  |   float s = 0.65; | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  | #define BENCH(FUNC)                                           \
 | 
					
						
							|  |  |  |   {                                                           \ | 
					
						
							|  |  |  |     BenchTimer t;                                             \ | 
					
						
							|  |  |  |     for (int k = 0; k < 2; ++k) {                             \ | 
					
						
							|  |  |  |       t.start();                                              \ | 
					
						
							|  |  |  |       for (int i = 0; i < 1000000; ++i) FUNC(a, b, s);        \ | 
					
						
							|  |  |  |       t.stop();                                               \ | 
					
						
							|  |  |  |     }                                                         \ | 
					
						
							|  |  |  |     cout << "  " << #FUNC << " => \t " << t.value() << "s\n"; \ | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-12-06 05:22:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-04 22:02:38 +08:00
										 |  |  |   cout << "\nSpeed:\n" << std::fixed; | 
					
						
							|  |  |  |   BENCH(nlerp); | 
					
						
							|  |  |  |   BENCH(slerp_eigen); | 
					
						
							|  |  |  |   BENCH(slerp_legacy); | 
					
						
							|  |  |  |   BENCH(slerp_legacy_nlerp); | 
					
						
							|  |  |  |   BENCH(slerp_rw); | 
					
						
							|  |  |  |   BENCH(slerp_gael); | 
					
						
							| 
									
										
										
										
											2010-07-09 09:59:36 +08:00
										 |  |  | } |