Radium Engine  1.5.20
Loading...
Searching...
No Matches
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
15namespace Ra {
16namespace Core {
17namespace Math {
18//
19// Common vector types
20//
21
22inline 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
35template <typename Vector>
36inline Vector floor( const Vector& v );
37
39template <typename Vector>
40inline Vector ceil( const Vector& v );
41
43template <typename Vector>
44inline Vector trunc( const Vector& v );
45
47
48template <typename Derived, typename DerivedA, typename DerivedB>
49inline typename Derived::PlainMatrix clamp( const Eigen::MatrixBase<Derived>& v,
50 const Eigen::MatrixBase<DerivedA>& min,
51 const Eigen::MatrixBase<DerivedB>& max );
53template <typename Derived>
54inline typename Derived::PlainMatrix
55clamp( const Eigen::MatrixBase<Derived>& v, const Scalar& min, const Scalar& max );
56
58template <typename S>
59inline 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
66template <typename Matrix_>
67inline bool checkInvalidNumbers( Eigen::Ref<const Matrix_> matrix,
68 const bool FAIL_ON_ASSERT = false );
69
72inline void
73getOrthogonalVectors( const Vector3& fx, Eigen::Ref<Vector3> fy, Eigen::Ref<Vector3> fz );
74
77template <typename Vector_>
78inline Scalar angle( const Vector_& v1, const Vector_& v2 );
79
82template <typename Vector_>
83inline Vector_ slerp( const Vector_& v1, const Vector_& v2, Scalar t );
84
86inline Vector3
87projectOnPlane( const Vector3& planePos, const Vector3& planeNormal, const Vector3& point );
88
91template <typename Vector_>
92inline Scalar cotan( const Vector_& v1, const Vector_& v2 );
93
96template <typename Vector_>
97inline Scalar cos( const Vector_& v1, const Vector_& v2 );
98
101template <typename Vector_>
102inline Scalar getNormAndNormalize( Vector_& v );
103
106template <typename Vector_>
107inline Scalar getNormAndNormalizeSafe( Vector_& v );
108
116template <typename Scalar>
117inline Eigen::ParametrizedLine<Scalar, 3>
118transformRay( const Eigen::Transform<Scalar, 3, Eigen::Affine>& t,
119 const Eigen::ParametrizedLine<Scalar, 3>& r );
120
121inline Matrix4 lookAt( const Vector3& position, const Vector3& target, const Vector3& up );
122inline Matrix4 perspective( Scalar fovy, Scalar aspect, Scalar near, Scalar zfar );
123inline Matrix4
124orthographic( 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
136inline Quaternion scale( const Quaternion& q, const Scalar k );
137
139inline Quaternion add( const Quaternion& q1, const Quaternion& q2 );
140
145inline 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
155inline void getSwingTwist( const Quaternion& in, Quaternion& swingOut, Quaternion& twistOut );
156
157inline 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
171template <typename Vector_>
172inline 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
178template <typename Vector_>
179inline 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
185template <typename Vector_>
186inline 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
192template <typename Derived, typename DerivedA, typename DerivedB>
193inline 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
199template <typename Derived>
200inline typename Derived::PlainMatrix
201clamp( 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
205template <typename Vector_>
206inline Scalar angle( const Vector_& v1, const Vector_& v2 ) {
207 return std::atan2( v1.cross( v2 ).norm(), v1.dot( v2 ) );
208}
209
210template <typename Vector_>
211Scalar cotan( const Vector_& v1, const Vector_& v2 ) {
212 return v1.dot( v2 ) / v1.cross( v2 ).norm();
213}
214
215template <typename Vector_>
216Scalar cos( const Vector_& v1, const Vector_& v2 ) {
217 return ( v1.dot( v2 ) ) / std::sqrt( v1.normSquared() * v2.normSquared() );
218}
219
220template <typename Vector_>
221Scalar getNormAndNormalize( Vector_& v ) {
222 const Scalar norm = v.norm();
223 v /= norm;
224 return norm;
225}
226
227template <typename Vector_>
228Scalar getNormAndNormalizeSafe( Vector_& v ) {
229 const Scalar norm = v.norm();
230 if ( norm > 0 ) { v /= norm; }
231 return norm;
232}
233
234template <typename Scalar>
235Eigen::ParametrizedLine<Scalar, 3>
236transformRay( 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
245inline Quaternion add( const Quaternion& q1, const Quaternion& q2 ) {
246 return Quaternion( q1.coeffs() + q2.coeffs() );
247}
248
249inline 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
254inline Quaternion scale( const Quaternion& q, Scalar k ) {
255 return Quaternion( k * q.coeffs() );
256}
257
258inline void
259getOrthogonalVectors( 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
269inline Vector3
270projectOnPlane( const Vector3& planePos, const Vector3& planeNormal, const Vector3& point ) {
271 return point + planeNormal * ( planePos - point ).dot( planeNormal );
272}
273
274template <typename Vector_>
275Vector_ 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
285inline 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
316inline 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
330inline 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
346inline 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
361template <typename Matrix_>
362bool 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
378
391template <typename DerivedA, typename DerivedB>
392bool allClose( const Eigen::DenseBase<DerivedA>& a,
393 const Eigen::DenseBase<DerivedB>& b,
394 const typename DerivedA::RealScalar& rtol =
395 Eigen::NumTraits<typename DerivedA::RealScalar>::dummy_precision(),
396 const typename DerivedA::RealScalar& atol =
397 Eigen::NumTraits<typename DerivedA::RealScalar>::epsilon() ) {
398
399 return ( ( a.derived() - b.derived() ).array().abs() <=
400 ( atol + rtol * a.derived().array().abs().max( b.derived().array().abs() ) ) )
401 .all();
402}
403
404} // namespace Math
405} // namespace Core
406} // namespace Ra
407
408// Insert quaternions operator in Eigen namespace to allow functions outside of
409// Ra::Core to use them without explicit calls. This is syntactic sugar,
410// but allows to write expressions such as q = ( a*q1 + b*q2 ).normalized();
411// anywhere in the code.
412// See
413
414namespace Eigen {
415inline Quaternion<Scalar> operator*( Scalar k, const Quaternion<Scalar>& q ) {
416 return Ra::Core::Math::scale( q, k );
417}
418
419inline Quaternion<Scalar> operator*( const Quaternion<Scalar>& q, Scalar k ) {
420 return Ra::Core::Math::scale( q, k );
421}
422
423inline Quaternion<Scalar> operator+( const Quaternion<Scalar>& q1, const Quaternion<Scalar>& q2 ) {
424 return Ra::Core::Math::add( q1, q2 );
425}
426
427inline Quaternion<Scalar> operator/( const Quaternion<Scalar>& q, Scalar k ) {
428 return Ra::Core::Math::scale( q, Scalar( 1 ) / k );
429}
430} // namespace Eigen
T atan2(T... args)
T ceil(T... args)
T copysign(T... args)
T cos(T... args)
T endl(T... args)
T floor(T... args)
T isfinite(T... args)
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.
bool checkInvalidNumbers(Eigen::Ref< Eigen::Quaternion< S > > q, const bool FAIL_ON_ASSERT=false)
Call std::isfinite on quaternion entries.
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)
bool allClose(const Eigen::DenseBase< DerivedA > &a, const Eigen::DenseBase< DerivedB > &b, const typename DerivedA::RealScalar &rtol=Eigen::NumTraits< typename DerivedA::RealScalar >::dummy_precision(), const typename DerivedA::RealScalar &atol=Eigen::NumTraits< typename DerivedA::RealScalar >::epsilon())
Returns True if two arrays are element-wise equal within a tolerance.
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)
DualQuaternion operator*(Scalar scalar, const DualQuaternion &dq)
Pre-multiplication of dual quaternion.
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T sin(T... args)
T sqrt(T... args)
T tan(T... args)
T trunc(T... args)