You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							247 lines
						
					
					
						
							5.9 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							247 lines
						
					
					
						
							5.9 KiB
						
					
					
				| 
 | |
| #include <iostream> | |
| #include <StormEigen/Geometry> | |
| #include <bench/BenchTimer.h> | |
| using namespace StormEigen; | |
| using namespace std; | |
| 
 | |
| 
 | |
| 
 | |
| template<typename Q> | |
| STORMEIGEN_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> | |
| STORMEIGEN_DONT_INLINE Q slerp_eigen(const Q& a, const Q& b, typename Q::Scalar t) | |
| { | |
|   return a.slerp(t,b); | |
| } | |
| 
 | |
| template<typename Q> | |
| STORMEIGEN_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); | |
|   Scalar absD = internal::abs(d); | |
|   if (absD>=one) | |
|     return a; | |
| 
 | |
|   // theta is the angle between the 2 quaternions | |
|   Scalar theta = std::acos(absD); | |
|   Scalar sinTheta = internal::sin(theta); | |
| 
 | |
|   Scalar scale0 = internal::sin( ( Scalar(1) - t ) * theta) / sinTheta; | |
|   Scalar scale1 = internal::sin( ( t * theta) ) / sinTheta; | |
|   if (d<0) | |
|     scale1 = -scale1; | |
| 
 | |
|   return Q(scale0 * a.coeffs() + scale1 * b.coeffs()); | |
| } | |
| 
 | |
| template<typename Q> | |
| STORMEIGEN_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); | |
|   Scalar absD = internal::abs(d); | |
|    | |
|   Scalar scale0; | |
|   Scalar scale1; | |
|    | |
|   if (absD>=one) | |
|   { | |
|     scale0 = Scalar(1) - t; | |
|     scale1 = t; | |
|   } | |
|   else | |
|   { | |
|     // theta is the angle between the 2 quaternions | |
|     Scalar theta = std::acos(absD); | |
|     Scalar sinTheta = internal::sin(theta); | |
| 
 | |
|     scale0 = internal::sin( ( Scalar(1) - t ) * theta) / sinTheta; | |
|     scale1 = internal::sin( ( t * theta) ) / sinTheta; | |
|     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> | |
| STORMEIGEN_DONT_INLINE Q slerp_rw(const Q& a, const Q& b, typename Q::Scalar t) | |
| { | |
|   typedef typename Q::Scalar Scalar; | |
|    | |
|   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 ); | |
|    | |
|   // 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> | |
| STORMEIGEN_DONT_INLINE Q slerp_gael(const Q& a, const Q& b, typename Q::Scalar t) | |
| { | |
|   typedef typename Q::Scalar Scalar; | |
|    | |
|   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; | |
|    | |
|   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 ); | |
|    | |
|    | |
|   Scalar scale0; | |
|   Scalar scale1; | |
|   if(theta*theta-Scalar(6)==-Scalar(6)) | |
|   { | |
|     scale0 = Scalar(1) - t; | |
|     scale1 = t; | |
|   } | |
|   else | |
|   { | |
|     Scalar sinTheta = std::sin(theta); | |
|     scale0 = internal::sin( ( Scalar(1) - t ) * theta) / sinTheta; | |
|     scale1 = internal::sin( ( t * theta) ) / sinTheta; | |
|     if (d<0) | |
|       scale1 = -scale1; | |
|   } | |
| 
 | |
|   return Quaternion<Scalar>(scale0 * a.coeffs() + scale1 * b.coeffs()); | |
| } | |
| 
 | |
| int main() | |
| { | |
|   typedef double RefScalar; | |
|   typedef float TestScalar; | |
|    | |
|   typedef Quaternion<RefScalar>  Qd; | |
|   typedef Quaternion<TestScalar> Qf; | |
|    | |
|   unsigned int g_seed = (unsigned int) time(NULL); | |
|   std::cout << g_seed << "\n"; | |
| //   g_seed = 1259932496; | |
|   srand(g_seed); | |
|    | |
|   Matrix<RefScalar,Dynamic,1> maxerr(7); | |
|   maxerr.setZero(); | |
|    | |
|   Matrix<RefScalar,Dynamic,1> avgerr(7); | |
|   avgerr.setZero(); | |
|    | |
|   cout << "double=>float=>double       nlerp        eigen        legacy(snap)         legacy(nlerp)        rightway         gael's criteria\n"; | |
|    | |
|   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(); | |
|      | |
|     Qf c[6]; | |
|      | |
|     Qd ar(a.cast<RefScalar>()); | |
|     Qd br(b.cast<RefScalar>()); | |
|     Qd cr; | |
|      | |
|      | |
|      | |
|     cout.precision(8); | |
|     cout << std::scientific; | |
|     for (int i=0; i<iters; ++i) | |
|     { | |
|       RefScalar t = 0.65; | |
|       cr = slerp_rw(ar,br,t); | |
|        | |
|       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); | |
|        | |
|       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"; | |
|    | |
|   // perf bench | |
|   Quaternionf a,b; | |
|   a.coeffs().setRandom(); | |
|   a.normalize(); | |
|   b.coeffs().setRandom(); | |
|   b.normalize(); | |
|   //b = a; | |
|   float s = 0.65; | |
|      | |
|   #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"; \ | |
|   } | |
|    | |
|   cout << "\nSpeed:\n" << std::fixed; | |
|   BENCH(nlerp); | |
|   BENCH(slerp_eigen); | |
|   BENCH(slerp_legacy); | |
|   BENCH(slerp_legacy_nlerp); | |
|   BENCH(slerp_rw); | |
|   BENCH(slerp_gael); | |
| } | |
| 
 |