Radium Engine  1.5.0
LinearAlgebra.hpp
1 #pragma once
2 
3 #include <Core/Math/Math.hpp>
4 #include <Core/RaCore.hpp>
5 #include <Core/Types.hpp>
6 
7 #include <Eigen/Core>
8 #include <Eigen/Geometry>
9 #include <Eigen/Sparse>
10 #include <unsupported/Eigen/AlignedVector3>
11 
12 #include <cmath>
13 #include <functional>
14 
15 namespace Ra {
16 namespace Core {
17 namespace Math {
18 //
19 // Common vector types
20 //
21 
22 inline void print( const MatrixN& matrix );
23 
24 //
25 // Geometry types
26 //
27 
28 // Todo : storage transform using quaternions ?
29 
30 //
31 // Vector Functions
32 //
33 
35 template <typename Vector>
36 inline Vector floor( const Vector& v );
37 
39 template <typename Vector>
40 inline Vector ceil( const Vector& v );
41 
43 template <typename Vector>
44 inline Vector trunc( const Vector& v );
45 
47 
48 template <typename Derived, typename DerivedA, typename DerivedB>
49 inline typename Derived::PlainMatrix clamp( const Eigen::MatrixBase<Derived>& v,
50  const Eigen::MatrixBase<DerivedA>& min,
51  const Eigen::MatrixBase<DerivedB>& max );
53 template <typename Derived>
54 inline typename Derived::PlainMatrix
55 clamp( const Eigen::MatrixBase<Derived>& v, const Scalar& min, const Scalar& max );
56 
58 template <typename S>
59 inline bool checkInvalidNumbers( Eigen::Ref<Eigen::Quaternion<S>> q,
60  const bool FAIL_ON_ASSERT = false ) {
61  return checkInvalidNumbers( q.coeffs(), FAIL_ON_ASSERT );
62 }
63 
66 template <typename Matrix_>
67 inline bool checkInvalidNumbers( Eigen::Ref<const Matrix_> matrix,
68  const bool FAIL_ON_ASSERT = false );
69 
72 inline void
73 getOrthogonalVectors( const Vector3& fx, Eigen::Ref<Vector3> fy, Eigen::Ref<Vector3> fz );
74 
77 template <typename Vector_>
78 inline Scalar angle( const Vector_& v1, const Vector_& v2 );
79 
82 template <typename Vector_>
83 inline Vector_ slerp( const Vector_& v1, const Vector_& v2, Scalar t );
84 
86 inline Vector3
87 projectOnPlane( const Vector3& planePos, const Vector3& planeNormal, const Vector3& point );
88 
91 template <typename Vector_>
92 inline Scalar cotan( const Vector_& v1, const Vector_& v2 );
93 
96 template <typename Vector_>
97 inline Scalar cos( const Vector_& v1, const Vector_& v2 );
98 
101 template <typename Vector_>
102 inline Scalar getNormAndNormalize( Vector_& v );
103 
106 template <typename Vector_>
107 inline Scalar getNormAndNormalizeSafe( Vector_& v );
108 
116 template <typename Scalar>
117 inline Eigen::ParametrizedLine<Scalar, 3>
118 transformRay( const Eigen::Transform<Scalar, 3, Eigen::Affine>& t,
119  const Eigen::ParametrizedLine<Scalar, 3>& r );
120 
121 inline Matrix4 lookAt( const Vector3& position, const Vector3& target, const Vector3& up );
122 inline Matrix4 perspective( Scalar fovy, Scalar aspect, Scalar near, Scalar zfar );
123 inline Matrix4
124 orthographic( Scalar left, Scalar right, Scalar bottom, Scalar top, Scalar near, Scalar zfar );
125 
126 //
127 // Quaternion functions
128 //
129 
130 // Define functions for multiplying a quaternion by a scalar
131 // and adding two quaternions. While Quaternion is supposed to
132 // represent a unit quaternion (thus a valid rotation), these functions
133 // are useful for linear interpolation of quaternions.
134 
136 inline Quaternion scale( const Quaternion& q, const Scalar k );
137 
139 inline Quaternion add( const Quaternion& q1, const Quaternion& q2 );
140 
145 inline Quaternion addQlerp( const Quaternion& q1, const Quaternion& q2 );
146 
147 // Note : the .inl file also define operator+ for quaternions
148 // and operator * and / between quaternions and scalar.
149 
155 inline void getSwingTwist( const Quaternion& in, Quaternion& swingOut, Quaternion& twistOut );
156 
157 inline void print( const MatrixN& matrix ) {
158  // Taken straight from :
159  // http://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html
160 
161  std::cout << "Matrix rows : " << matrix.rows() << std::endl;
162  std::cout << "Matrix cols : " << matrix.cols() << std::endl;
163  Eigen::IOFormat cleanFormat( 4, 0, ", ", "\n", "[", "]" );
164  std::cout << matrix.format( cleanFormat ) << std::endl;
165 }
166 
167 //
168 // Vector functions.
169 //
170 
171 template <typename Vector_>
172 inline Vector_ floor( const Vector_& v ) {
173  using Scalar_ = typename Vector_::Scalar;
174  return v.unaryExpr(
175  std::function<Scalar_( Scalar_ )>( static_cast<Scalar_ ( & )( Scalar_ )>( std::floor ) ) );
176 }
177 
178 template <typename Vector_>
179 inline Vector_ ceil( const Vector_& v ) {
180  using Scalar_ = typename Vector_::Scalar;
181  return v.unaryExpr(
182  std::function<Scalar_( Scalar_ )>( static_cast<Scalar_ ( & )( Scalar_ )>( std::ceil ) ) );
183 }
184 
185 template <typename Vector_>
186 inline Vector_ trunc( const Vector_& v ) {
187  using Scalar_ = typename Vector_::Scalar;
188  return v.unaryExpr(
189  std::function<Scalar_( Scalar_ )>( static_cast<Scalar_ ( & )( Scalar_ )>( std::trunc ) ) );
190 }
191 
192 template <typename Derived, typename DerivedA, typename DerivedB>
193 inline typename Derived::PlainMatrix clamp( const Eigen::MatrixBase<Derived>& v,
194  const Eigen::MatrixBase<DerivedA>& min,
195  const Eigen::MatrixBase<DerivedB>& max ) {
196  return v.cwiseMin( max ).cwiseMax( min );
197 }
198 
199 template <typename Derived>
200 inline typename Derived::PlainMatrix
201 clamp( const Eigen::MatrixBase<Derived>& v, const Scalar& min, const Scalar& max ) {
202  return v.unaryExpr( [min, max]( Scalar x ) { return std::clamp( x, min, max ); } );
203 }
204 
205 template <typename Vector_>
206 inline Scalar angle( const Vector_& v1, const Vector_& v2 ) {
207  return std::atan2( v1.cross( v2 ).norm(), v1.dot( v2 ) );
208 }
209 
210 template <typename Vector_>
211 Scalar cotan( const Vector_& v1, const Vector_& v2 ) {
212  return v1.dot( v2 ) / v1.cross( v2 ).norm();
213 }
214 
215 template <typename Vector_>
216 Scalar cos( const Vector_& v1, const Vector_& v2 ) {
217  return ( v1.dot( v2 ) ) / std::sqrt( v1.normSquared() * v2.normSquared() );
218 }
219 
220 template <typename Vector_>
221 Scalar getNormAndNormalize( Vector_& v ) {
222  const Scalar norm = v.norm();
223  v /= norm;
224  return norm;
225 }
226 
227 template <typename Vector_>
228 Scalar getNormAndNormalizeSafe( Vector_& v ) {
229  const Scalar norm = v.norm();
230  if ( norm > 0 ) { v /= norm; }
231  return norm;
232 }
233 
234 template <typename Scalar>
235 Eigen::ParametrizedLine<Scalar, 3>
236 transformRay( const Eigen::Transform<Scalar, 3, Eigen::Affine>& t,
237  const Eigen::ParametrizedLine<Scalar, 3>& r ) {
238  return { t * r.origin(), t.linear() * r.direction() };
239 }
240 
241 //
242 // Quaternion functions.
243 //
244 
245 inline Quaternion add( const Quaternion& q1, const Quaternion& q2 ) {
246  return Quaternion( q1.coeffs() + q2.coeffs() );
247 }
248 
249 inline Quaternion addQlerp( const Quaternion& q1, const Quaternion& q2 ) {
250  const Scalar sign = Ra::Core::Math::signNZ( q1.dot( q2 ) );
251  return Quaternion( q1.coeffs() + ( sign * q2.coeffs() ) );
252 }
253 
254 inline Quaternion scale( const Quaternion& q, Scalar k ) {
255  return Quaternion( k * q.coeffs() );
256 }
257 
258 inline void
259 getOrthogonalVectors( const Vector3& fx, Eigen::Ref<Vector3> fy, Eigen::Ref<Vector3> fz ) {
260  // taken from [Duff et al. 17] Building An Orthonormal Basis, Revisited. JCGT. 2017
261  Scalar sign = std::copysign( Scalar( 1.0 ), fx( 2 ) );
262  const Scalar a = Scalar( -1.0 ) / ( sign + fx( 2 ) );
263  const Scalar b = fx( 0 ) * fx( 1 ) * a;
264  fy = Ra::Core::Vector3(
265  Scalar( 1.0 ) + sign * fx( 0 ) * fx( 0 ) * a, sign * b, -sign * fx( 0 ) );
266  fz = Ra::Core::Vector3( b, sign + fx( 1 ) * fx( 1 ) * a, -fx( 1 ) );
267 }
268 
269 inline Vector3
270 projectOnPlane( const Vector3& planePos, const Vector3& planeNormal, const Vector3& point ) {
271  return point + planeNormal * ( planePos - point ).dot( planeNormal );
272 }
273 
274 template <typename Vector_>
275 Vector_ slerp( const Vector_& v1, const Vector_& v2, Scalar t ) {
276  const Scalar dot = v1.dot( v2 );
277  const Scalar theta = t * angle( v1, v2 );
278  const Vector_ relativeVec = ( v2 - v1 * dot ).normalized();
279  return ( ( v1 * std::cos( theta ) ) + ( relativeVec * std::sin( theta ) ) );
280 }
281 
282 // Reference : Paolo Baerlocher, Inverse Kinematics techniques for interactive posture control
283 // of articulated figures (PhD Thesis), p.138. EPFL, 2001.
284 // http://www0.cs.ucl.ac.uk/research/equator/papers/Documents2002/Paolo%20Baerlocher_Thesis_2001.pdf
285 inline void getSwingTwist( const Quaternion& in, Quaternion& swingOut, Quaternion& twistOut ) {
286  // singular case.
287  if ( UNLIKELY( in.z() == 0 && in.w() == 0 ) ) {
288  twistOut = in;
289  swingOut.setIdentity();
290  }
291  else {
292  const Scalar gamma = std::atan2( in.z(), in.w() );
293  // beta = atan2 ( sqrt ( in.x^2 + in.y^2), sqrt ( in.z^2 + in.w^2))
294  const Scalar beta =
295  std::atan2( in.coeffs().head<2>().norm(), in.coeffs().tail<2>().norm() );
296 
297  // k = 2 / sinc(beta) = 2 / ( sin(beta)/ beta))
298  const Scalar k = 2.f * beta / std::sin( beta );
299 
300  // The parentheses here are important. because otherwise k * rotation2D would
301  // be evaluated first (which would yield the rotation of angle k * gamma).
302  const Vector2 sxy = k * ( Eigen::Rotation2D<Scalar>( gamma ) * in.coeffs().head<2>() );
303 
304  AngleAxis twist( 2 * gamma, Vector3::UnitZ() );
305  twistOut = twist;
306 
307  Vector3 swingAxis( Vector3::Zero() );
308  swingAxis.head<2>() = sxy.normalized();
309 
310  AngleAxis swing( sxy.norm(), swingAxis );
311  swingOut = swing;
312  }
313 }
314 
315 // http://stackoverflow.com/a/13786235/4717805
316 inline Matrix4 lookAt( const Vector3& position, const Vector3& target, const Vector3& up ) {
317  Matrix3 R;
318  R.col( 2 ) = ( position - target ).normalized();
319  R.col( 0 ) = up.cross( R.col( 2 ) ).normalized();
320  R.col( 1 ) = R.col( 2 ).cross( R.col( 0 ) );
321 
322  Matrix4 result = Matrix4::Zero();
323  result.topLeftCorner<3, 3>() = R.transpose();
324  result.topRightCorner<3, 1>() = -R.transpose() * position;
325  result( 3, 3 ) = 1.0;
326 
327  return result;
328 }
329 
330 inline Matrix4 perspective( Scalar fovy, Scalar aspect, Scalar znear, Scalar zfar ) {
331  Scalar theta = fovy * 0.5f;
332  Scalar range = zfar - znear;
333  Scalar invtan = 1.f / std::tan( theta );
334 
335  Matrix4 result = Matrix4::Zero();
336  result( 0, 0 ) = invtan / aspect;
337  result( 1, 1 ) = invtan;
338  result( 2, 2 ) = -( znear + zfar ) / range;
339  result( 3, 2 ) = -1.f;
340  result( 2, 3 ) = -2 * znear * zfar / range;
341  result( 3, 3 ) = 0.0;
342 
343  return result;
344 }
345 
346 inline Matrix4 orthographic( Scalar l, Scalar r, Scalar b, Scalar t, Scalar n, Scalar f ) {
347  Matrix4 result = Matrix4::Zero();
348 
349  result( 0, 0 ) = 2.f / ( r - l );
350  result( 1, 1 ) = 2.f / ( t - b );
351  result( 2, 2 ) = -2.f / ( f - n );
352  result( 3, 3 ) = 1.f;
353 
354  result( 0, 3 ) = -( r + l ) / ( r - l );
355  result( 1, 3 ) = -( t + b ) / ( t - b );
356  result( 2, 3 ) = -( f + b ) / ( f - n );
357 
358  return result;
359 }
360 
361 template <typename Matrix_>
362 bool checkInvalidNumbers( Eigen::Ref<const Matrix_> matrix, const bool FAIL_ON_ASSERT ) {
363  bool invalid = false;
364  matrix
365  .unaryExpr( [&invalid, FAIL_ON_ASSERT]( Scalar x ) {
366  invalid |= !std::isfinite( x );
367  if ( invalid ) {
368  if ( FAIL_ON_ASSERT ) { CORE_ASSERT( false, "At least an element is nan" ); }
369  }
370  return 1;
371  } )
372  .eval();
373 
374  return !invalid;
375 }
376 
377 } // namespace Math
378 } // namespace Core
379 } // namespace Ra
380 
381 // Insert quaternions operator in Eigen namespace to allow functions outside of
382 // Ra::Core to use them without explicit calls. This is syntactic sugar,
383 // but allows to write expressions such as q = ( a*q1 + b*q2 ).normalized();
384 // anywhere in the code.
385 // See
386 
387 namespace Eigen {
388 inline Quaternion<Scalar> operator*( Scalar k, const Quaternion<Scalar>& q ) {
389  return Ra::Core::Math::scale( q, k );
390 }
391 
392 inline Quaternion<Scalar> operator*( const Quaternion<Scalar>& q, Scalar k ) {
393  return Ra::Core::Math::scale( q, k );
394 }
395 
396 inline Quaternion<Scalar> operator+( const Quaternion<Scalar>& q1, const Quaternion<Scalar>& q2 ) {
397  return Ra::Core::Math::add( q1, q2 );
398 }
399 
400 inline Quaternion<Scalar> operator/( const Quaternion<Scalar>& q, Scalar k ) {
401  return Ra::Core::Math::scale( q, Scalar( 1 ) / k );
402 }
403 } // namespace Eigen
Quaternion add(const Quaternion &q1, const Quaternion &q2)
Returns the sum of two quaternions.
Eigen::ParametrizedLine< Scalar, 3 > transformRay(const Eigen::Transform< Scalar, 3, Eigen::Affine > &t, const Eigen::ParametrizedLine< Scalar, 3 > &r)
Scalar getNormAndNormalize(Vector_ &v)
Vector floor(const Vector &v)
Component-wise floor() function on a floating-point vector.
Scalar cos(const Vector_ &v1, const Vector_ &v2)
Vector_ slerp(const Vector_ &v1, const Vector_ &v2, Scalar t)
Scalar angle(const Vector_ &v1, const Vector_ &v2)
void getOrthogonalVectors(const Vector3 &fx, Eigen::Ref< Vector3 > fy, Eigen::Ref< Vector3 > fz)
constexpr T signNZ(const T &val)
Definition: Math.hpp:114
Scalar cotan(const Vector_ &v1, const Vector_ &v2)
constexpr int sign(const T &val)
Returns the sign of any numeric type as { -1, 0, 1}.
Definition: Math.hpp:106
Quaternion scale(const Quaternion &q, const Scalar k)
Returns the quaternion q multipled by a scalar factor of k.
Vector trunc(const Vector &v)
Component-wise trunc() function on a floating-point vector.
void getSwingTwist(const Quaternion &in, Quaternion &swingOut, Quaternion &twistOut)
Quaternion addQlerp(const Quaternion &q1, const Quaternion &q2)
Vector3 projectOnPlane(const Vector3 &planePos, const Vector3 &planeNormal, const Vector3 &point)
Derived::PlainMatrix clamp(const Eigen::MatrixBase< Derived > &v, const Eigen::MatrixBase< DerivedA > &min, const Eigen::MatrixBase< DerivedB > &max)
Component-wise clamp() function on a floating-point vector.
Vector ceil(const Vector &v)
Component-wise ceil() function on a floating-point vector.
Scalar getNormAndNormalizeSafe(Vector_ &v)
bool checkInvalidNumbers(Eigen::Ref< Eigen::Quaternion< S >> q, const bool FAIL_ON_ASSERT=false)
Call std::isfinite on quaternion entries.
Definition: Cage.cpp:3