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