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 <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);
							 | 
						|
								  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>
							 | 
						|
								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);
							 | 
						|
								  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>
							 | 
						|
								EIGEN_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>
							 | 
						|
								EIGEN_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);
							 | 
						|
								}
							 | 
						|
								
							 |