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