mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 06:38:13 -05:00
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:
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 );
|
||||
}
|
||||
Reference in new issue
Block a user