GCC Code Coverage Report


Directory: ./
File: include/pinocchio/math/quaternion.hpp
Date: 2025-04-30 16:14:33
Exec Total Coverage
Lines: 69 69 100.0%
Branches: 152 287 53.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2016-2020 CNRS INRIA
3 //
4
5 #ifndef __pinocchio_math_quaternion_hpp__
6 #define __pinocchio_math_quaternion_hpp__
7
8 #ifndef PINOCCHIO_DEFAULT_QUATERNION_NORM_TOLERANCE_VALUE
9 #define PINOCCHIO_DEFAULT_QUATERNION_NORM_TOLERANCE_VALUE 1e-8
10 #endif
11
12 #include "pinocchio/math/fwd.hpp"
13 #include "pinocchio/math/comparison-operators.hpp"
14 #include "pinocchio/math/matrix.hpp"
15 #include "pinocchio/math/sincos.hpp"
16 #include "pinocchio/utils/static-if.hpp"
17
18 #include <boost/type_traits.hpp>
19 #include <Eigen/Geometry>
20
21 namespace pinocchio
22 {
23 namespace quaternion
24 {
25
26 ///
27 /// \brief Compute the minimal angle between q1 and q2.
28 ///
29 /// \param[in] q1 input quaternion.
30 /// \param[in] q2 input quaternion.
31 ///
32 /// \return angle between the two quaternions
33 ///
34 template<typename D1, typename D2>
35 typename D1::Scalar angleBetweenQuaternions(
36 const Eigen::QuaternionBase<D1> & q1, const Eigen::QuaternionBase<D2> & q2)
37 {
38 typedef typename D1::Scalar Scalar;
39 const Scalar innerprod = q1.dot(q2);
40 Scalar theta = math::acos(innerprod);
41 static const Scalar PI_value = PI<Scalar>();
42
43 theta = internal::if_then_else(
44 internal::LT, innerprod, Scalar(0), static_cast<Scalar>(PI_value - theta), theta);
45 return theta;
46 }
47
48 ///
49 /// \brief Check if two quaternions define the same rotations.
50 /// \note Two quaternions define the same rotation iff q1 == q2 OR q1 == -q2.
51 ///
52 /// \param[in] q1 input quaternion.
53 /// \param[in] q2 input quaternion.
54 ///
55 /// \return Return true if the two input quaternions define the same rotation.
56 ///
57 template<typename D1, typename D2>
58 10011 bool defineSameRotation(
59 const Eigen::QuaternionBase<D1> & q1,
60 const Eigen::QuaternionBase<D2> & q2,
61 const typename D1::RealScalar & prec =
62 Eigen::NumTraits<typename D1::Scalar>::dummy_precision())
63 {
64
10/18
✓ Branch 1 taken 10011 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10011 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10011 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 515 times.
✓ Branch 10 taken 9496 times.
✓ Branch 12 taken 515 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 515 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 515 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 515 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 515 times.
✗ Branch 24 not taken.
10011 return (q1.coeffs().isApprox(q2.coeffs(), prec) || q1.coeffs().isApprox(-q2.coeffs(), prec));
65 }
66
67 /// Approximately normalize by applying the first order limited development
68 /// of the normalization function.
69 ///
70 /// Only additions and multiplications are required. Neither square root nor
71 /// division are used (except a division by 2). Let \f$ \delta = ||q||^2 - 1 \f$.
72 /// Using the following limited development:
73 /// \f[ \frac{1}{||q||} = (1 + \delta)^{-\frac{1}{2}} = 1 - \frac{\delta}{2} +
74 /// \mathcal{O}(\delta^2) \f]
75 ///
76 /// The output is
77 /// \f[ q_{out} = q \times \frac{3 - ||q_{in}||^2}{2} \f]
78 ///
79 /// The output quaternion is guaranted to statisfy the following:
80 /// \f[ | ||q_{out}|| - 1 | \le \frac{M}{2} ||q_{in}|| ( ||q_{in}||^2 - 1 )^2 \f]
81 /// where \f$ M = \frac{3}{4} (1 - \epsilon)^{-\frac{5}{2}} \f$
82 /// and \f$ \epsilon \f$ is the maximum tolerance of \f$ ||q_{in}||^2 - 1 \f$.
83 ///
84 /// \warning \f$ ||q||^2 - 1 \f$ should already be close to zero.
85 ///
86 /// \note See
87 /// http://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html#title3
88 /// to know the reason why the argument is const.
89 template<typename D>
90 20346 void firstOrderNormalize(const Eigen::QuaternionBase<D> & q)
91 {
92 typedef typename D::Scalar Scalar;
93
1/2
✓ Branch 1 taken 20344 times.
✗ Branch 2 not taken.
20346 const Scalar N2 = q.squaredNorm();
94 #ifndef NDEBUG
95
3/6
✓ Branch 1 taken 334 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 334 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
20681 const Scalar epsilon = sqrt(sqrt(Eigen::NumTraits<Scalar>::epsilon()));
96 typedef apply_op_if<less_than_or_equal_to_op, is_floating_point<Scalar>::value, true>
97 static_leq;
98
6/9
✓ Branch 1 taken 334 times.
✓ Branch 2 taken 20010 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 334 times.
✓ Branch 5 taken 20010 times.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 334 times.
20346 assert(static_leq::op(math::fabs(static_cast<Scalar>(N2 - Scalar(1))), epsilon));
99 #endif
100
4/8
✓ Branch 1 taken 334 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 334 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 334 times.
✗ Branch 11 not taken.
20681 const Scalar alpha = ((Scalar)3 - N2) / Scalar(2);
101
1/2
✓ Branch 3 taken 20344 times.
✗ Branch 4 not taken.
20346 PINOCCHIO_EIGEN_CONST_CAST(D, q).coeffs() *= alpha;
102 #ifndef NDEBUG
103
5/10
✓ Branch 1 taken 334 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 334 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 334 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 334 times.
✗ Branch 14 not taken.
22021 const Scalar M =
104
6/12
✓ Branch 1 taken 20344 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 334 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 334 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 334 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 334 times.
✗ Branch 17 not taken.
22021 Scalar(3) * math::pow(Scalar(1) - epsilon, ((Scalar)-Scalar(5)) / Scalar(2)) / Scalar(4);
105
21/38
✓ Branch 1 taken 334 times.
✓ Branch 2 taken 20010 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 334 times.
✓ Branch 5 taken 20010 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 20010 times.
✓ Branch 10 taken 334 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 20010 times.
✓ Branch 13 taken 334 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 334 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 334 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 334 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 334 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 334 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 334 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 334 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 334 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 334 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 334 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 334 times.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✓ Branch 50 taken 334 times.
20346 assert(static_leq::op(
106 math::fabs(static_cast<Scalar>(q.norm() - Scalar(1))),
107 math::max(
108 M * sqrt(N2) * (N2 - Scalar(1)) * (N2 - Scalar(1)) / Scalar(2),
109 Eigen::NumTraits<Scalar>::dummy_precision())));
110 #endif
111 20346 }
112
113 /// Uniformly random quaternion sphere.
114 template<typename Derived>
115 342933 void uniformRandom(Eigen::QuaternionBase<Derived> & q)
116 {
117 typedef typename Derived::Scalar Scalar;
118
119 // Rotational part
120
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
342935 const Scalar u1 = (Scalar)rand() / RAND_MAX;
121
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
342935 const Scalar u2 = (Scalar)rand() / RAND_MAX;
122
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
342935 const Scalar u3 = (Scalar)rand() / RAND_MAX;
123
124
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
342935 const Scalar mult1 = sqrt(Scalar(1) - u1);
125
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
342933 const Scalar mult2 = sqrt(u1);
126
127
4/8
✓ Branch 0 taken 195 times.
✓ Branch 1 taken 332218 times.
✓ Branch 3 taken 195 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
342933 static const Scalar PI_value = PI<Scalar>();
128
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 Scalar s2, c2;
129
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
342933 SINCOS(Scalar(2) * PI_value * u2, &s2, &c2);
130
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 Scalar s3, c3;
131
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
342933 SINCOS(Scalar(2) * PI_value * u3, &s3, &c3);
132
133
3/6
✓ Branch 1 taken 332413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
342933 q.w() = mult1 * s2;
134
3/6
✓ Branch 1 taken 332413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
342933 q.x() = mult1 * c2;
135
3/6
✓ Branch 1 taken 332413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
342933 q.y() = mult2 * s3;
136
3/6
✓ Branch 1 taken 332413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
342933 q.z() = mult2 * c3;
137 342933 }
138
139 namespace internal
140 {
141
142 template<typename Scalar, bool value = is_floating_point<Scalar>::value>
143 struct quaternionbase_assign_impl;
144
145 template<Eigen::DenseIndex i>
146 struct quaternionbase_assign_impl_if_t_negative
147 {
148 template<typename Scalar, typename Matrix3, typename QuaternionDerived>
149 static inline void
150 121854 run(Scalar t, Eigen::QuaternionBase<QuaternionDerived> & q, const Matrix3 & mat)
151 {
152 using pinocchio::math::sqrt;
153
154 121854 Eigen::DenseIndex j = (i + 1) % 3;
155 121854 Eigen::DenseIndex k = (j + 1) % 3;
156
157
8/16
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 3 times.
✗ Branch 24 not taken.
121854 t = sqrt(mat.coeff(i, i) - mat.coeff(j, j) - mat.coeff(k, k) + Scalar(1.0));
158
3/7
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
121854 q.coeffs().coeffRef(i) = Scalar(0.5) * t;
159
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
121854 t = Scalar(0.5) / t;
160
3/6
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
121854 q.w() = (mat.coeff(k, j) - mat.coeff(j, k)) * t;
161
3/7
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
121854 q.coeffs().coeffRef(j) = (mat.coeff(j, i) + mat.coeff(i, j)) * t;
162
3/7
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
121854 q.coeffs().coeffRef(k) = (mat.coeff(k, i) + mat.coeff(i, k)) * t;
163 121854 }
164 };
165
166 struct quaternionbase_assign_impl_if_t_positive
167 {
168 template<typename Scalar, typename Matrix3, typename QuaternionDerived>
169 static inline void
170 39077 run(Scalar t, Eigen::QuaternionBase<QuaternionDerived> & q, const Matrix3 & mat)
171 {
172 using pinocchio::math::sqrt;
173
174
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
39077 t = sqrt(t + Scalar(1.0));
175
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
39077 q.w() = Scalar(0.5) * t;
176
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
39077 t = Scalar(0.5) / t;
177
3/6
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
39077 q.x() = (mat.coeff(2, 1) - mat.coeff(1, 2)) * t;
178
3/6
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
39077 q.y() = (mat.coeff(0, 2) - mat.coeff(2, 0)) * t;
179
3/6
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
39077 q.z() = (mat.coeff(1, 0) - mat.coeff(0, 1)) * t;
180 39077 }
181 };
182
183 template<typename Scalar>
184 struct quaternionbase_assign_impl<Scalar, true>
185 {
186 template<typename Matrix3, typename QuaternionDerived>
187 100000 static inline void run(Eigen::QuaternionBase<QuaternionDerived> & q, const Matrix3 & mat)
188 {
189 using pinocchio::math::sqrt;
190
191 100000 Scalar t = mat.trace();
192
2/2
✓ Branch 0 taken 39076 times.
✓ Branch 1 taken 60924 times.
100000 if (t > Scalar(0.))
193 39076 quaternionbase_assign_impl_if_t_positive::run(t, q, mat);
194 else
195 {
196 60924 Eigen::DenseIndex i = 0;
197
2/2
✓ Branch 2 taken 30300 times.
✓ Branch 3 taken 30624 times.
60924 if (mat.coeff(1, 1) > mat.coeff(0, 0))
198 30300 i = 1;
199
2/2
✓ Branch 2 taken 20270 times.
✓ Branch 3 taken 40654 times.
60924 if (mat.coeff(2, 2) > mat.coeff(i, i))
200 20270 i = 2;
201
202
2/2
✓ Branch 0 taken 20376 times.
✓ Branch 1 taken 40548 times.
60924 if (i == 0)
203 20376 quaternionbase_assign_impl_if_t_negative<0>::run(t, q, mat);
204
2/2
✓ Branch 0 taken 20278 times.
✓ Branch 1 taken 20270 times.
40548 else if (i == 1)
205 20278 quaternionbase_assign_impl_if_t_negative<1>::run(t, q, mat);
206 else
207 20270 quaternionbase_assign_impl_if_t_negative<2>::run(t, q, mat);
208 }
209 100000 }
210 };
211
212 } // namespace internal
213
214 template<typename D, typename Matrix3>
215 100001 void assignQuaternion(Eigen::QuaternionBase<D> & quat, const Eigen::MatrixBase<Matrix3> & R)
216 {
217 200002 internal::quaternionbase_assign_impl<typename Matrix3::Scalar>::run(
218 100001 quat.derived(), R.derived());
219 100001 }
220
221 ///
222 /// \brief Check whether the input quaternion is Normalized within the given precision.
223 ///
224 /// \param[in] quat Input quaternion
225 /// \param[in] prec Required precision
226 ///
227 /// \returns true if quat is normalized within the precision prec.
228 ///
229 template<typename Quaternion>
230 inline bool isNormalized(
231 const Eigen::QuaternionBase<Quaternion> & quat,
232 const typename Quaternion::Coefficients::RealScalar & prec)
233 {
234 return pinocchio::isNormalized(quat.coeffs(), prec);
235 }
236
237 ///
238 /// \brief Check whether the input quaternion is Normalized within the default precision.
239 ///
240 /// \param[in] quat Input quaternion
241 ///
242 /// \returns true if quat is normalized within the default precision.
243 ///
244 template<typename Quaternion>
245 93213 inline bool isNormalized(const Eigen::QuaternionBase<Quaternion> & quat)
246 {
247 typedef typename Quaternion::Coefficients::RealScalar RealScalar;
248
2/4
✓ Branch 1 taken 1007 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1007 times.
✗ Branch 5 not taken.
93213 const RealScalar prec = math::sqrt(Eigen::NumTraits<RealScalar>::epsilon());
249
3/5
✓ Branch 1 taken 1286 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1286 times.
✗ Branch 5 not taken.
186426 return pinocchio::isNormalized(quat.coeffs(), prec);
250 1690 }
251
252 ///
253 /// \brief Normalize the input quaternion.
254 ///
255 /// \param[in] quat Input quaternion
256 ///
257 template<typename Quaternion>
258 inline void normalize(const Eigen::QuaternionBase<Quaternion> & quat)
259 {
260 return pinocchio::normalize(quat.const_cast_derived().coeffs());
261 }
262
263 ///
264 /// \returns the spherical linear interpolation between the two quaternions
265 ///
266 /// \param[in] u Interpolation factor
267 /// \param[in] quat Input quaternion
268 ///
269 template<
270 typename Scalar,
271 typename QuaternionIn1,
272 typename QuaternionIn2,
273 typename QuaternionOut>
274 inline void slerp(
275 const Scalar & u,
276 const Eigen::QuaternionBase<QuaternionIn1> & quat0,
277 const Eigen::QuaternionBase<QuaternionIn2> & quat1,
278 const Eigen::QuaternionBase<QuaternionOut> & res)
279 {
280 const Scalar one = Scalar(1) - Eigen::NumTraits<Scalar>::epsilon();
281 const Scalar d = quat0.dot(quat1);
282 const Scalar absD = fabs(d);
283
284 const Scalar theta = acos(absD);
285 const Scalar sinTheta = sin(theta);
286
287 using namespace pinocchio::internal;
288
289 const Scalar scale0 = if_then_else(
290 pinocchio::internal::GE, absD, one,
291 static_cast<Scalar>(Scalar(1) - u), // then
292 static_cast<Scalar>(sin((Scalar(1) - u) * theta) / sinTheta) // else
293 );
294
295 const Scalar scale1_factor =
296 if_then_else(pinocchio::internal::LT, d, Scalar(0), Scalar(-1), Scalar(1));
297 const Scalar scale1 = if_then_else(
298 pinocchio::internal::GE, absD, one,
299 u, // then
300 static_cast<Scalar>(sin((u * theta)) / sinTheta) // else
301 )
302 * scale1_factor;
303
304 PINOCCHIO_EIGEN_CONST_CAST(QuaternionOut, res.derived()).coeffs() =
305 scale0 * quat0.coeffs() + scale1 * quat1.coeffs();
306 }
307
308 } // namespace quaternion
309
310 } // namespace pinocchio
311 #endif // #ifndef __pinocchio_math_quaternion_hpp__
312