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