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

  1. #include <iostream>
  2. #include <Eigen/Geometry>
  3. #include <bench/BenchTimer.h>
  4. using namespace StormEigen;
  5. using namespace std;
  6. template<typename Q>
  7. EIGEN_DONT_INLINE Q nlerp(const Q& a, const Q& b, typename Q::Scalar t)
  8. {
  9. return Q((a.coeffs() * (1.0-t) + b.coeffs() * t).normalized());
  10. }
  11. template<typename Q>
  12. EIGEN_DONT_INLINE Q slerp_eigen(const Q& a, const Q& b, typename Q::Scalar t)
  13. {
  14. return a.slerp(t,b);
  15. }
  16. template<typename Q>
  17. EIGEN_DONT_INLINE Q slerp_legacy(const Q& a, const Q& b, typename Q::Scalar t)
  18. {
  19. typedef typename Q::Scalar Scalar;
  20. static const Scalar one = Scalar(1) - dummy_precision<Scalar>();
  21. Scalar d = a.dot(b);
  22. Scalar absD = internal::abs(d);
  23. if (absD>=one)
  24. return a;
  25. // theta is the angle between the 2 quaternions
  26. Scalar theta = std::acos(absD);
  27. Scalar sinTheta = internal::sin(theta);
  28. Scalar scale0 = internal::sin( ( Scalar(1) - t ) * theta) / sinTheta;
  29. Scalar scale1 = internal::sin( ( t * theta) ) / sinTheta;
  30. if (d<0)
  31. scale1 = -scale1;
  32. return Q(scale0 * a.coeffs() + scale1 * b.coeffs());
  33. }
  34. template<typename Q>
  35. EIGEN_DONT_INLINE Q slerp_legacy_nlerp(const Q& a, const Q& b, typename Q::Scalar t)
  36. {
  37. typedef typename Q::Scalar Scalar;
  38. static const Scalar one = Scalar(1) - epsilon<Scalar>();
  39. Scalar d = a.dot(b);
  40. Scalar absD = internal::abs(d);
  41. Scalar scale0;
  42. Scalar scale1;
  43. if (absD>=one)
  44. {
  45. scale0 = Scalar(1) - t;
  46. scale1 = t;
  47. }
  48. else
  49. {
  50. // theta is the angle between the 2 quaternions
  51. Scalar theta = std::acos(absD);
  52. Scalar sinTheta = internal::sin(theta);
  53. scale0 = internal::sin( ( Scalar(1) - t ) * theta) / sinTheta;
  54. scale1 = internal::sin( ( t * theta) ) / sinTheta;
  55. if (d<0)
  56. scale1 = -scale1;
  57. }
  58. return Q(scale0 * a.coeffs() + scale1 * b.coeffs());
  59. }
  60. template<typename T>
  61. inline T sin_over_x(T x)
  62. {
  63. if (T(1) + x*x == T(1))
  64. return T(1);
  65. else
  66. return std::sin(x)/x;
  67. }
  68. template<typename Q>
  69. EIGEN_DONT_INLINE Q slerp_rw(const Q& a, const Q& b, typename Q::Scalar t)
  70. {
  71. typedef typename Q::Scalar Scalar;
  72. Scalar d = a.dot(b);
  73. Scalar theta;
  74. if (d<0.0)
  75. theta = /*M_PI -*/ Scalar(2)*std::asin( (a.coeffs()+b.coeffs()).norm()/2 );
  76. else
  77. theta = Scalar(2)*std::asin( (a.coeffs()-b.coeffs()).norm()/2 );
  78. // theta is the angle between the 2 quaternions
  79. // Scalar theta = std::acos(absD);
  80. Scalar sinOverTheta = sin_over_x(theta);
  81. Scalar scale0 = (Scalar(1)-t)*sin_over_x( ( Scalar(1) - t ) * theta) / sinOverTheta;
  82. Scalar scale1 = t * sin_over_x( ( t * theta) ) / sinOverTheta;
  83. if (d<0)
  84. scale1 = -scale1;
  85. return Quaternion<Scalar>(scale0 * a.coeffs() + scale1 * b.coeffs());
  86. }
  87. template<typename Q>
  88. EIGEN_DONT_INLINE Q slerp_gael(const Q& a, const Q& b, typename Q::Scalar t)
  89. {
  90. typedef typename Q::Scalar Scalar;
  91. Scalar d = a.dot(b);
  92. Scalar theta;
  93. // theta = Scalar(2) * atan2((a.coeffs()-b.coeffs()).norm(),(a.coeffs()+b.coeffs()).norm());
  94. // if (d<0.0)
  95. // theta = M_PI-theta;
  96. if (d<0.0)
  97. theta = /*M_PI -*/ Scalar(2)*std::asin( (-a.coeffs()-b.coeffs()).norm()/2 );
  98. else
  99. theta = Scalar(2)*std::asin( (a.coeffs()-b.coeffs()).norm()/2 );
  100. Scalar scale0;
  101. Scalar scale1;
  102. if(theta*theta-Scalar(6)==-Scalar(6))
  103. {
  104. scale0 = Scalar(1) - t;
  105. scale1 = t;
  106. }
  107. else
  108. {
  109. Scalar sinTheta = std::sin(theta);
  110. scale0 = internal::sin( ( Scalar(1) - t ) * theta) / sinTheta;
  111. scale1 = internal::sin( ( t * theta) ) / sinTheta;
  112. if (d<0)
  113. scale1 = -scale1;
  114. }
  115. return Quaternion<Scalar>(scale0 * a.coeffs() + scale1 * b.coeffs());
  116. }
  117. int main()
  118. {
  119. typedef double RefScalar;
  120. typedef float TestScalar;
  121. typedef Quaternion<RefScalar> Qd;
  122. typedef Quaternion<TestScalar> Qf;
  123. unsigned int g_seed = (unsigned int) time(NULL);
  124. std::cout << g_seed << "\n";
  125. // g_seed = 1259932496;
  126. srand(g_seed);
  127. Matrix<RefScalar,Dynamic,1> maxerr(7);
  128. maxerr.setZero();
  129. Matrix<RefScalar,Dynamic,1> avgerr(7);
  130. avgerr.setZero();
  131. cout << "double=>float=>double nlerp eigen legacy(snap) legacy(nlerp) rightway gael's criteria\n";
  132. int rep = 100;
  133. int iters = 40;
  134. for (int w=0; w<rep; ++w)
  135. {
  136. Qf a, b;
  137. a.coeffs().setRandom();
  138. a.normalize();
  139. b.coeffs().setRandom();
  140. b.normalize();
  141. Qf c[6];
  142. Qd ar(a.cast<RefScalar>());
  143. Qd br(b.cast<RefScalar>());
  144. Qd cr;
  145. cout.precision(8);
  146. cout << std::scientific;
  147. for (int i=0; i<iters; ++i)
  148. {
  149. RefScalar t = 0.65;
  150. cr = slerp_rw(ar,br,t);
  151. Qf refc = cr.cast<TestScalar>();
  152. c[0] = nlerp(a,b,t);
  153. c[1] = slerp_eigen(a,b,t);
  154. c[2] = slerp_legacy(a,b,t);
  155. c[3] = slerp_legacy_nlerp(a,b,t);
  156. c[4] = slerp_rw(a,b,t);
  157. c[5] = slerp_gael(a,b,t);
  158. VectorXd err(7);
  159. err[0] = (cr.coeffs()-refc.cast<RefScalar>().coeffs()).norm();
  160. // std::cout << err[0] << " ";
  161. for (int k=0; k<6; ++k)
  162. {
  163. err[k+1] = (c[k].coeffs()-refc.coeffs()).norm();
  164. // std::cout << err[k+1] << " ";
  165. }
  166. maxerr = maxerr.cwise().max(err);
  167. avgerr += err;
  168. // std::cout << "\n";
  169. b = cr.cast<TestScalar>();
  170. br = cr;
  171. }
  172. // std::cout << "\n";
  173. }
  174. avgerr /= RefScalar(rep*iters);
  175. cout << "\n\nAccuracy:\n"
  176. << " max: " << maxerr.transpose() << "\n";
  177. cout << " avg: " << avgerr.transpose() << "\n";
  178. // perf bench
  179. Quaternionf a,b;
  180. a.coeffs().setRandom();
  181. a.normalize();
  182. b.coeffs().setRandom();
  183. b.normalize();
  184. //b = a;
  185. float s = 0.65;
  186. #define BENCH(FUNC) {\
  187. BenchTimer t; \
  188. for(int k=0; k<2; ++k) {\
  189. t.start(); \
  190. for(int i=0; i<1000000; ++i) \
  191. FUNC(a,b,s); \
  192. t.stop(); \
  193. } \
  194. cout << " " << #FUNC << " => \t " << t.value() << "s\n"; \
  195. }
  196. cout << "\nSpeed:\n" << std::fixed;
  197. BENCH(nlerp);
  198. BENCH(slerp_eigen);
  199. BENCH(slerp_legacy);
  200. BENCH(slerp_legacy_nlerp);
  201. BENCH(slerp_rw);
  202. BENCH(slerp_gael);
  203. }