Adds angle conversion utilities

Implements type-safe angle (degrees and radians) representation classes.

Provides utilities for converting between Cartesian and cylindrical
coordinate systems.

Includes a unit test suite for validation.
This commit is contained in:
Magne Sjaastad committed 2025-09-10 08:23:09 +02:00
1 parent 0ed7821e05
commit 48ae7b2f04
6 files changed
+739

No files matched your search

@@ -56,6 +56,8 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaProjectBackupTools.h
${CMAKE_CURRENT_LIST_DIR}/RiaQuantityInfoTools.h
${CMAKE_CURRENT_LIST_DIR}/RiaHashTools.h
${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils.h
${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils.inl
)
set(SOURCE_GROUP_SOURCE_FILES
@@ -108,6 +110,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RiaFileLogger.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaProjectBackupTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaQuantityInfoTools.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils.cpp
)
list(APPEND CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
@@ -0,0 +1,69 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RiaAngleUtils.h"
#include <cmath>
namespace Ria
{
// Explicit instantiation of commonly used types
template class Degrees<float>;
template class Degrees<double>;
template class Radians<float>;
template class Radians<double>;
// Explicit instantiation of coordinate conversion functions
template cvf::Vector3<float> CoordinateConverter::cylindricalToCartesian<float>( float, const Radians<float>&, float );
template cvf::Vector3<double> CoordinateConverter::cylindricalToCartesian<double>( double, const Radians<double>&, double );
template cvf::Vector3<float> CoordinateConverter::cylindricalToCartesian<float>( float, const Degrees<float>&, float );
template cvf::Vector3<double> CoordinateConverter::cylindricalToCartesian<double>( double, const Degrees<double>&, double );
template cvf::Vector3<float> CoordinateConverter::cylindricalToCartesian<float>( const CylindricalCoordinate& );
template cvf::Vector3<double> CoordinateConverter::cylindricalToCartesian<double>( const CylindricalCoordinate& );
template CylindricalCoordinate CoordinateConverter::cartesianToCylindrical<float>( const cvf::Vector3<float>& );
template CylindricalCoordinate CoordinateConverter::cartesianToCylindrical<double>( const cvf::Vector3<double>& );
template CylindricalCoordinate CoordinateConverter::cartesianToCylindrical<float>( float, float, float );
template CylindricalCoordinate CoordinateConverter::cartesianToCylindrical<double>( double, double, double );
// Explicit instantiation of trigonometric functions
template float sin<float>( const Degrees<float>& );
template double sin<double>( const Degrees<double>& );
template float sin<float>( const Radians<float>& );
template double sin<double>( const Radians<double>& );
template float cos<float>( const Degrees<float>& );
template double cos<double>( const Degrees<double>& );
template float cos<float>( const Radians<float>& );
template double cos<double>( const Radians<double>& );
template float tan<float>( const Degrees<float>& );
template double tan<double>( const Degrees<double>& );
template float tan<float>( const Radians<float>& );
template double tan<double>( const Radians<double>& );
template Radians<float> asin<float>( float );
template Radians<double> asin<double>( double );
template Radians<float> acos<float>( float );
template Radians<double> acos<double>( double );
template Radians<float> atan<float>( float );
template Radians<double> atan<double>( double );
template Radians<float> atan2<float>( float, float );
template Radians<double> atan2<double>( double, double );
} // namespace Ria
@@ -0,0 +1,360 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cvfBase.h"
#include "cvfMath.h"
#include "cvfVector3.h"
#include <cmath>
namespace Ria
{
//==================================================================================================
//
// Type-safe angle representation classes
//
//==================================================================================================
template <typename T>
class Radians;
template <typename T>
class Degrees
{
public:
constexpr Degrees()
: m_value( T{} )
{
}
constexpr explicit Degrees( T value )
: m_value( value )
{
}
constexpr Degrees( const Degrees& other )
: m_value( other.m_value )
{
}
constexpr Degrees( const Radians<T>& radians );
constexpr T value() const { return m_value; }
constexpr operator T() const { return m_value; }
constexpr Degrees& operator=( const Degrees& other )
{
m_value = other.m_value;
return *this;
}
constexpr Degrees& operator=( const Radians<T>& radians );
constexpr Degrees operator+( const Degrees& other ) const { return Degrees( m_value + other.m_value ); }
constexpr Degrees operator-( const Degrees& other ) const { return Degrees( m_value - other.m_value ); }
constexpr Degrees operator*( T scalar ) const { return Degrees( m_value * scalar ); }
constexpr Degrees operator/( T scalar ) const { return Degrees( m_value / scalar ); }
constexpr Degrees operator-() const { return Degrees( -m_value ); }
constexpr Degrees& operator+=( const Degrees& other )
{
m_value += other.m_value;
return *this;
}
constexpr Degrees& operator-=( const Degrees& other )
{
m_value -= other.m_value;
return *this;
}
constexpr Degrees& operator*=( T scalar )
{
m_value *= scalar;
return *this;
}
constexpr Degrees& operator/=( T scalar )
{
m_value /= scalar;
return *this;
}
constexpr bool operator==( const Degrees& other ) const { return m_value == other.m_value; }
constexpr bool operator!=( const Degrees& other ) const { return m_value != other.m_value; }
constexpr bool operator<( const Degrees& other ) const { return m_value < other.m_value; }
constexpr bool operator<=( const Degrees& other ) const { return m_value <= other.m_value; }
constexpr bool operator>( const Degrees& other ) const { return m_value > other.m_value; }
constexpr bool operator>=( const Degrees& other ) const { return m_value >= other.m_value; }
Degrees normalized() const;
private:
T m_value;
};
template <typename T>
class Radians
{
public:
constexpr Radians()
: m_value( T{} )
{
}
constexpr explicit Radians( T value )
: m_value( value )
{
}
constexpr Radians( const Radians& other )
: m_value( other.m_value )
{
}
constexpr Radians( const Degrees<T>& degrees );
constexpr T value() const { return m_value; }
constexpr operator T() const { return m_value; }
constexpr Radians& operator=( const Radians& other )
{
m_value = other.m_value;
return *this;
}
constexpr Radians& operator=( const Degrees<T>& degrees );
constexpr Radians operator+( const Radians& other ) const { return Radians( m_value + other.m_value ); }
constexpr Radians operator-( const Radians& other ) const { return Radians( m_value - other.m_value ); }
constexpr Radians operator*( T scalar ) const { return Radians( m_value * scalar ); }
constexpr Radians operator/( T scalar ) const { return Radians( m_value / scalar ); }
constexpr Radians operator-() const { return Radians( -m_value ); }
constexpr Radians& operator+=( const Radians& other )
{
m_value += other.m_value;
return *this;
}
constexpr Radians& operator-=( const Radians& other )
{
m_value -= other.m_value;
return *this;
}
constexpr Radians& operator*=( T scalar )
{
m_value *= scalar;
return *this;
}
constexpr Radians& operator/=( T scalar )
{
m_value /= scalar;
return *this;
}
constexpr bool operator==( const Radians& other ) const { return m_value == other.m_value; }
constexpr bool operator!=( const Radians& other ) const { return m_value != other.m_value; }
constexpr bool operator<( const Radians& other ) const { return m_value < other.m_value; }
constexpr bool operator<=( const Radians& other ) const { return m_value <= other.m_value; }
constexpr bool operator>( const Radians& other ) const { return m_value > other.m_value; }
constexpr bool operator>=( const Radians& other ) const { return m_value >= other.m_value; }
Radians normalized() const;
private:
T m_value;
};
// Cross-conversions between Degrees and Radians
template <typename T>
constexpr Degrees<T>::Degrees( const Radians<T>& radians )
: m_value( cvf::Math::toDegrees( radians.value() ) )
{
}
template <typename T>
constexpr Degrees<T>& Degrees<T>::operator=( const Radians<T>& radians )
{
m_value = cvf::Math::toDegrees( radians.value() );
return *this;
}
template <typename T>
constexpr Radians<T>::Radians( const Degrees<T>& degrees )
: m_value( cvf::Math::toRadians( degrees.value() ) )
{
}
template <typename T>
constexpr Radians<T>& Radians<T>::operator=( const Degrees<T>& degrees )
{
m_value = cvf::Math::toRadians( degrees.value() );
return *this;
}
// Scalar multiplication (scalar * angle)
template <typename T>
constexpr Degrees<T> operator*( T scalar, const Degrees<T>& degrees )
{
return degrees * scalar;
}
template <typename T>
constexpr Radians<T> operator*( T scalar, const Radians<T>& radians )
{
return radians * scalar;
}
// Type aliases
using Degreesf = Degrees<float>;
using Degreesd = Degrees<double>;
using Radiansf = Radians<float>;
using Radiansd = Radians<double>;
//==================================================================================================
//
// Trigonometric functions for typed angles
//
//==================================================================================================
template <typename T>
T sin( const Degrees<T>& angle )
{
return cvf::Math::sin( cvf::Math::toRadians( angle.value() ) );
}
template <typename T>
T sin( const Radians<T>& angle )
{
return cvf::Math::sin( angle.value() );
}
template <typename T>
T cos( const Degrees<T>& angle )
{
return cvf::Math::cos( cvf::Math::toRadians( angle.value() ) );
}
template <typename T>
T cos( const Radians<T>& angle )
{
return cvf::Math::cos( angle.value() );
}
template <typename T>
T tan( const Degrees<T>& angle )
{
return cvf::Math::tan( cvf::Math::toRadians( angle.value() ) );
}
template <typename T>
T tan( const Radians<T>& angle )
{
return cvf::Math::tan( angle.value() );
}
template <typename T>
Radians<T> asin( T value )
{
return Radians<T>( cvf::Math::asin( value ) );
}
template <typename T>
Radians<T> acos( T value )
{
return Radians<T>( cvf::Math::acos( value ) );
}
template <typename T>
Radians<T> atan( T value )
{
return Radians<T>( cvf::Math::atan( value ) );
}
template <typename T>
Radians<T> atan2( T y, T x )
{
return Radians<T>( std::atan2( y, x ) );
}
//==================================================================================================
//
// Coordinate system conversion utilities
//
//==================================================================================================
struct CylindricalCoordinate
{
template <typename T>
CylindricalCoordinate( T radius, const Radians<T>& angle, T height )
: radius( radius )
, angle( angle.value() )
, height( height )
{
}
template <typename T>
CylindricalCoordinate( T radius, const Degrees<T>& angle, T height )
: radius( radius )
, angle( cvf::Math::toRadians( angle.value() ) )
, height( height )
{
}
double radius;
double angle; // In radians
double height;
};
class CoordinateConverter
{
public:
template <typename T>
static cvf::Vector3<T> cylindricalToCartesian( T radius, const Radians<T>& angle, T height );
template <typename T>
static cvf::Vector3<T> cylindricalToCartesian( T radius, const Degrees<T>& angle, T height );
template <typename T>
static cvf::Vector3<T> cylindricalToCartesian( const CylindricalCoordinate& coord );
template <typename T>
static CylindricalCoordinate cartesianToCylindrical( const cvf::Vector3<T>& cartesian );
template <typename T>
static CylindricalCoordinate cartesianToCylindrical( T x, T y, T z );
};
//==================================================================================================
//
// Convenience functions for creating angle literals
//
//==================================================================================================
constexpr Degreesd operator"" _deg( long double value )
{
return Degreesd( static_cast<double>( value ) );
}
constexpr Degreesf operator"" _degf( long double value )
{
return Degreesf( static_cast<float>( value ) );
}
constexpr Radiansd operator"" _rad( long double value )
{
return Radiansd( static_cast<double>( value ) );
}
constexpr Radiansf operator"" _radf( long double value )
{
return Radiansf( static_cast<float>( value ) );
}
} // namespace Ria
#include "RiaAngleUtils.inl"
@@ -0,0 +1,100 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include <cmath>
#include <type_traits>
namespace Ria
{
//==================================================================================================
// Degrees normalization
//==================================================================================================
template <typename T>
inline Degrees<T> Degrees<T>::normalized() const
{
T normalized = cvf::Math::fmod( m_value, T( 360 ) );
if ( normalized < T( 0 ) )
{
normalized += T( 360 );
}
return Degrees<T>( normalized );
}
//==================================================================================================
// Radians normalization
//==================================================================================================
template <typename T>
inline Radians<T> Radians<T>::normalized() const
{
T twoPi = T( 2 ) * ( std::is_same_v<T, float> ? cvf::PI_F : cvf::PI_D );
T normalized = cvf::Math::fmod( m_value, twoPi );
if ( normalized < T( 0 ) )
{
normalized += twoPi;
}
return Radians<T>( normalized );
}
//==================================================================================================
// Coordinate conversion implementations
//==================================================================================================
template <typename T>
inline cvf::Vector3<T> CoordinateConverter::cylindricalToCartesian( T radius, const Radians<T>& angle, T height )
{
T x = radius * cvf::Math::cos( angle.value() );
T y = radius * cvf::Math::sin( angle.value() );
return cvf::Vector3<T>( x, y, height );
}
template <typename T>
inline cvf::Vector3<T> CoordinateConverter::cylindricalToCartesian( T radius, const Degrees<T>& angle, T height )
{
T angleRad = cvf::Math::toRadians( angle.value() );
T x = radius * cvf::Math::cos( angleRad );
T y = radius * cvf::Math::sin( angleRad );
return cvf::Vector3<T>( x, y, height );
}
template <typename T>
inline cvf::Vector3<T> CoordinateConverter::cylindricalToCartesian( const CylindricalCoordinate& coord )
{
T x = static_cast<T>( coord.radius * cvf::Math::cos( coord.angle ) );
T y = static_cast<T>( coord.radius * cvf::Math::sin( coord.angle ) );
T z = static_cast<T>( coord.height );
return cvf::Vector3<T>( x, y, z );
}
template <typename T>
inline CylindricalCoordinate CoordinateConverter::cartesianToCylindrical( const cvf::Vector3<T>& cartesian )
{
return cartesianToCylindrical( cartesian.x(), cartesian.y(), cartesian.z() );
}
template <typename T>
inline CylindricalCoordinate CoordinateConverter::cartesianToCylindrical( T x, T y, T z )
{
double radius = cvf::Math::sqrt( static_cast<double>( x * x + y * y ) );
double angle = std::atan2( static_cast<double>( y ), static_cast<double>( x ) );
double height = static_cast<double>( z );
return CylindricalCoordinate( radius, Radiansd( angle ), height );
}
} // namespace Ria
@@ -113,6 +113,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RimTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifSurfio-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRmsWellPathReader-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiaAngleUtils-Test.cpp
)
if(RESINSIGHT_ENABLE_GRPC)
@@ -0,0 +1,206 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include "RiaAngleUtils.h"
using namespace Ria;
TEST( RiaAngleUtilsTest, DegreesBasicOperations )
{
Degreesd d1( 90.0 );
Degreesd d2( 45.0 );
EXPECT_DOUBLE_EQ( 90.0, d1.value() );
EXPECT_DOUBLE_EQ( 45.0, d2.value() );
auto sum = d1 + d2;
EXPECT_DOUBLE_EQ( 135.0, sum.value() );
auto diff = d1 - d2;
EXPECT_DOUBLE_EQ( 45.0, diff.value() );
auto scaled = d1 * 2.0;
EXPECT_DOUBLE_EQ( 180.0, scaled.value() );
auto divided = d1 / 2.0;
EXPECT_DOUBLE_EQ( 45.0, divided.value() );
}
TEST( RiaAngleUtilsTest, RadiansBasicOperations )
{
Radiansd r1( cvf::PI_D / 2.0 ); // 90 degrees
Radiansd r2( cvf::PI_D / 4.0 ); // 45 degrees
EXPECT_DOUBLE_EQ( cvf::PI_D / 2.0, r1.value() );
EXPECT_DOUBLE_EQ( cvf::PI_D / 4.0, r2.value() );
auto sum = r1 + r2;
EXPECT_DOUBLE_EQ( 3.0 * cvf::PI_D / 4.0, sum.value() );
auto diff = r1 - r2;
EXPECT_DOUBLE_EQ( cvf::PI_D / 4.0, diff.value() );
auto scaled = r1 * 2.0;
EXPECT_DOUBLE_EQ( cvf::PI_D, scaled.value() );
auto divided = r1 / 2.0;
EXPECT_DOUBLE_EQ( cvf::PI_D / 4.0, divided.value() );
}
TEST( RiaAngleUtilsTest, DegreesRadiansConversion )
{
Degreesd degrees( 180.0 );
Radiansd radians( degrees );
double diff1 = cvf::Math::abs( cvf::PI_D - radians.value() );
EXPECT_LT( diff1, 1e-10 );
Radiansd piRadians( cvf::PI_D );
Degreesd fromRadians( piRadians );
double diff2 = cvf::Math::abs( 180.0 - fromRadians.value() );
EXPECT_LT( diff2, 1e-10 );
Degreesd deg90( 90.0 );
Radiansd rad90( deg90 );
double diff3 = cvf::Math::abs( cvf::PI_D / 2.0 - rad90.value() );
EXPECT_LT( diff3, 1e-10 );
Degreesd original( 45.0 );
Radiansd converted( original );
Degreesd backToOriginal( converted );
double diff4 = cvf::Math::abs( 45.0 - backToOriginal.value() );
EXPECT_LT( diff4, 1e-10 );
}
TEST( RiaAngleUtilsTest, TrigonometricFunctions )
{
Degreesd deg90( 90.0 );
double sin90 = sin( deg90 );
double cos90 = cos( deg90 );
EXPECT_LT( cvf::Math::abs( 1.0 - sin90 ), 1e-10 );
EXPECT_LT( cvf::Math::abs( 0.0 - cos90 ), 1e-10 );
Degreesd deg45( 45.0 );
double sin45 = sin( deg45 );
double cos45 = cos( deg45 );
double tan45 = tan( deg45 );
EXPECT_LT( cvf::Math::abs( cvf::SQRT1_2_D - sin45 ), 1e-10 );
EXPECT_LT( cvf::Math::abs( cvf::SQRT1_2_D - cos45 ), 1e-10 );
EXPECT_LT( cvf::Math::abs( 1.0 - tan45 ), 1e-10 );
Radiansd radPiOver2( cvf::PI_D / 2.0 );
double sinPi2 = sin( radPiOver2 );
double cosPi2 = cos( radPiOver2 );
EXPECT_LT( cvf::Math::abs( 1.0 - sinPi2 ), 1e-10 );
EXPECT_LT( cvf::Math::abs( 0.0 - cosPi2 ), 1e-10 );
}
TEST( RiaAngleUtilsTest, CylindricalToCartesianConversion )
{
double radius = 5.0;
Radiansd angle( cvf::PI_D / 4.0 ); // 45 degrees
double height = 10.0;
auto cartesian = CoordinateConverter::cylindricalToCartesian( radius, angle, height );
double expectedX = radius * cvf::SQRT1_2_D;
double expectedY = radius * cvf::SQRT1_2_D;
EXPECT_LT( cvf::Math::abs( expectedX - cartesian.x() ), 1e-10 );
EXPECT_LT( cvf::Math::abs( expectedY - cartesian.y() ), 1e-10 );
EXPECT_LT( cvf::Math::abs( height - cartesian.z() ), 1e-10 );
Degreesd angleDeg( 45.0 );
auto cartesianFromDeg = CoordinateConverter::cylindricalToCartesian( radius, angleDeg, height );
EXPECT_LT( cvf::Math::abs( cartesian.x() - cartesianFromDeg.x() ), 1e-10 );
EXPECT_LT( cvf::Math::abs( cartesian.y() - cartesianFromDeg.y() ), 1e-10 );
EXPECT_LT( cvf::Math::abs( cartesian.z() - cartesianFromDeg.z() ), 1e-10 );
}
TEST( RiaAngleUtilsTest, CartesianToCylindricalConversion )
{
cvf::Vec3d cartesian( 3.0, 4.0, 5.0 );
auto cylindrical = CoordinateConverter::cartesianToCylindrical( cartesian );
EXPECT_LT( cvf::Math::abs( 5.0 - cylindrical.radius ), 1e-10 ); // sqrt(3^2 + 4^2) = 5
EXPECT_LT( cvf::Math::abs( std::atan2( 4.0, 3.0 ) - cylindrical.angle ), 1e-10 );
EXPECT_LT( cvf::Math::abs( 5.0 - cylindrical.height ), 1e-10 );
cvf::Vec3d backToCartesian = CoordinateConverter::cylindricalToCartesian<double>( cylindrical );
EXPECT_LT( cvf::Math::abs( cartesian.x() - backToCartesian.x() ), 1e-10 );
EXPECT_LT( cvf::Math::abs( cartesian.y() - backToCartesian.y() ), 1e-10 );
EXPECT_LT( cvf::Math::abs( cartesian.z() - backToCartesian.z() ), 1e-10 );
}
TEST( RiaAngleUtilsTest, ComparisonOperators )
{
Degreesd deg45( 45.0 );
Degreesd deg90( 90.0 );
Degreesd deg45_2( 45.0 );
EXPECT_TRUE( deg45 == deg45_2 );
EXPECT_FALSE( deg45 == deg90 );
EXPECT_TRUE( deg45 != deg90 );
EXPECT_FALSE( deg45 != deg45_2 );
EXPECT_TRUE( deg45 < deg90 );
EXPECT_FALSE( deg90 < deg45 );
EXPECT_TRUE( deg45 <= deg90 );
EXPECT_TRUE( deg45 <= deg45_2 );
EXPECT_TRUE( deg90 > deg45 );
EXPECT_FALSE( deg45 > deg90 );
EXPECT_TRUE( deg90 >= deg45 );
EXPECT_TRUE( deg45 >= deg45_2 );
}
TEST( RiaAngleUtilsTest, FloatPrecision )
{
Degreesf degf( 90.0f );
Radiansf radf( degf );
float expectedRad = cvf::PI_F / 2.0f;
EXPECT_LT( cvf::Math::abs( expectedRad - radf.value() ), 1e-6f );
float sinResult = sin( degf );
EXPECT_LT( cvf::Math::abs( 1.0f - sinResult ), 1e-6f );
cvf::Vec3f cartesianf = CoordinateConverter::cylindricalToCartesian( 5.0f, degf, 10.0f );
EXPECT_LT( cvf::Math::abs( 0.0f - cartesianf.x() ), 1e-6f );
EXPECT_LT( cvf::Math::abs( 5.0f - cartesianf.y() ), 1e-6f );
EXPECT_LT( cvf::Math::abs( 10.0f - cartesianf.z() ), 1e-6f );
}
TEST( RiaAngleUtilsTest, AngleLiterals )
{
auto deg90 = 90.0_deg;
EXPECT_LT( cvf::Math::abs( 90.0 - deg90.value() ), 1e-10 );
auto degFloat = 45.0_degf;
EXPECT_LT( cvf::Math::abs( 45.0f - degFloat.value() ), 1e-6f );
auto radPi = 3.14159265_rad;
EXPECT_LT( cvf::Math::abs( 3.14159265 - radPi.value() ), 1e-8 );
auto radFloat = 1.57079633_radf;
EXPECT_LT( cvf::Math::abs( 1.57079633f - radFloat.value() ), 1e-6f );
}