LCOV - code coverage report
Current view: top level - math - geo.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 93.8 % 16 15
Test Date: 2026-02-19 16:58:26 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /** \file geo.hpp
       2              :  * \author Jared R. Males
       3              :  * \brief Utilities for working with angles
       4              :  * \ingroup gen_math_files
       5              :  *
       6              :  */
       7              : 
       8              : //***********************************************************************//
       9              : // Copyright 2015, 2016, 2017, 2018 Jared R. Males (jaredmales@gmail.com)
      10              : //
      11              : // This file is part of mxlib.
      12              : //
      13              : // mxlib is free software: you can redistribute it and/or modify
      14              : // it under the terms of the GNU General Public License as published by
      15              : // the Free Software Foundation, either version 3 of the License, or
      16              : // (at your option) any later version.
      17              : //
      18              : // mxlib is distributed in the hope that it will be useful,
      19              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      20              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21              : // GNU General Public License for more details.
      22              : //
      23              : // You should have received a copy of the GNU General Public License
      24              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      25              : //***********************************************************************//
      26              : 
      27              : #ifndef math_geo_hpp
      28              : #define math_geo_hpp
      29              : 
      30              : #include <vector>
      31              : 
      32              : #include <cmath>
      33              : 
      34              : #include "constants.hpp"
      35              : 
      36              : namespace mx
      37              : {
      38              : namespace math
      39              : {
      40              : 
      41              : /// Calculate the semi-latus rectum of a <a href="http://en.wikipedia.org/wiki/Conic_section">conic section</a>
      42              : /**
      43              :  * \ingroup geo
      44              :  */
      45              : #define semilatrect( a, e )                                                                                            \
      46              :     ( e == 0.0 ? a : ( e == 1.0 ? 2. * a : ( e < 1. ? a * ( 1 - e * e ) : a * ( e * e - 1 ) ) ) )
      47              : 
      48              : /// Calculate the focal parameter of a <a href="http://en.wikipedia.org/wiki/Conic_section">conic section</a>
      49              : /**
      50              :  * \ingroup geo
      51              :  */
      52              : #define focus( a, e )                                                                                                  \
      53              :     ( e == 0.0 ? 1e34 : ( e == 1.0 ? 2. * a : ( e < 1. ? a * ( 1 - e * e ) / e : a * ( e * e - 1 ) / e ) ) )
      54              : 
      55              : /// Calculate the semi-major axis of a <a href="http://en.wikipedia.org/wiki/Conic_section">conic section</a>, given the
      56              : /// focal parameter and the eccentricity
      57              : /**
      58              :  * \ingroup geo
      59              :  */
      60              : #define semimaj( p, e ) ( e == 1.0 ? 1e34 : ( e < 1 ? p * e / ( 1 - e * e ) : p * e / ( e * e - 1 ) ) )
      61              : 
      62              : /// Calculate the eccentricity of a <a href="http://en.wikipedia.org/wiki/Conic_section">conic section</a> given the
      63              : /// semi-major axis and the focal parameter
      64              : /**
      65              :  * \ingroup geo
      66              :  */
      67              : #define eccent( a, p )                                                                                                 \
      68              :     ( a == 0.0 ? 1e34                                                                                                  \
      69              :                : ( p >= 1e9 ? 0.0                                                                                      \
      70              :                             : ( p > 0 ? ( -p / ( 2 * a ) + 0.5 * std::sqrt( p * p / ( a * a ) + 4 ) )                  \
      71              :                                       : ( p / ( 2 * a ) + 0.5 * std::sqrt( p * p / ( a * a ) + 4 ) ) ) ) )
      72              : 
      73              : /// Type specifying angles in radians
      74              : /**
      75              :  * \test Verify compilation and calculations of math::angleDiff \ref tests_math_geo_angleDiff "[test doc]"
      76              :  */
      77              : struct radians;
      78              : 
      79              : /// Type specifying angles in degrees
      80              : /**
      81              :  * \test Verify compilation and calculations of math::angleDiff \ref tests_math_geo_angleDiff "[test doc]"
      82              :  */
      83              : struct degrees;
      84              : 
      85              : /// Type holding constants related to angle calculations in degrees
      86              : template <typename degrad, typename realT>
      87              : struct degradT;
      88              : 
      89              : /// Type holding constants related to angle calculations in degrees
      90              : /**
      91              :  * \test Verify compilation and calculations of math::angleDiff \ref tests_math_geo_angleDiff "[test doc]"
      92              :  */
      93              : template <typename _realT>
      94              : struct degradT<degrees, _realT>
      95              : {
      96              :     typedef _realT realT;
      97              :     static constexpr realT scale =
      98              :         static_cast<realT>( 180 ) / pi<realT>(); // Scale factor to convert from radians to this unit.
      99              :     static constexpr realT degrees = 1;
     100              :     static constexpr realT radians = pi<realT>() / static_cast<realT>( 180 );
     101              :     static constexpr realT full = static_cast<realT>( 360.0 );
     102              :     static constexpr realT half = static_cast<realT>( 180.0 );
     103              : };
     104              : 
     105              : template <typename realT>
     106              : using degreesT = degradT<degrees, realT>;
     107              : 
     108              : /// Type holding constants related to angle calculations in radians
     109              : /**
     110              :  * \test Verify compilation and calculations of math::angleDiff \ref tests_math_geo_angleDiff "[test doc]"
     111              :  */
     112              : template <typename _realT>
     113              : struct degradT<radians, _realT>
     114              : {
     115              :     typedef _realT realT;
     116              :     static constexpr realT scale = 1; // Scale factor to convert from radians to this unit.
     117              :     static constexpr realT degrees = static_cast<realT>( 180 ) / pi<realT>();
     118              :     static constexpr realT radians = 1;
     119              :     static constexpr realT full = two_pi<realT>();
     120              :     static constexpr realT half = pi<realT>();
     121              : };
     122              : 
     123              : template <typename realT>
     124              : using radiansT = degradT<radians, realT>;
     125              : 
     126              : /// Convert from degrees to radians
     127              : /**
     128              :  *
     129              :  * \param q is the angle to convert
     130              :  *
     131              :  * \return the angle q converted to radians
     132              :  *
     133              :  * \test Verify compilation and calculations of math::angleDiff \ref tests_math_geo_angleDiff "[test doc]"
     134              :  *
     135              :  * \ingroup geo
     136              :  */
     137              : template <typename realT>
     138           38 : realT dtor( realT q )
     139              : {
     140           38 :     return q * degradT<degrees, realT>::radians;
     141              : }
     142              : 
     143              : /// Convert from radians to degrees
     144              : /**
     145              :  *
     146              :  * \param q is the angle to convert
     147              :  *
     148              :  * \return the angle q converted to degrees
     149              :  *
     150              :  * \ingroup geo
     151              :  */
     152              : template <typename realT>
     153            2 : realT rtod( realT q )
     154              : {
     155            2 :     return q * degradT<radians, realT>::degrees;
     156              : }
     157              : 
     158              : /// Calculate the angle modulo full-circle, normalizing to a positive value.
     159              : /** The output will be betweeen 0 and 360 (or 0 and 2pi).
     160              :  *
     161              :  * \returns the value of q normalized to 0 <= q < 360[2pi]
     162              :  *
     163              :  * \tparam degrad controls whether this is in degrees (0, default) or radians (1)
     164              :  * \tparam realT is the type in which to do arithmetic
     165              :  *
     166              :  * \ingroup geo
     167              :  */
     168              : template <class angleT>
     169            8 : typename angleT::realT angleMod( typename angleT::realT q /**< [in] the angle */ )
     170              : {
     171              :     static_assert( std::is_floating_point<typename angleT::realT>::value,
     172              :                    "angleMod: angleT::realT must be floating point" );
     173              : 
     174            8 :     q = fmod( q, angleT::full );
     175              : 
     176            8 :     if( q < 0 )
     177            0 :         q += angleT::full;
     178              : 
     179            8 :     return q;
     180              : }
     181              : 
     182              : /// Calculate the difference between two angles, correctly across 0/360.
     183              : /** Calculates \f$ dq = q2- q1 \f$, but accounts for crossing 0/360.  This implies
     184              :  * that \f$ dq \le 180 \f$.
     185              :  *
     186              :  *
     187              :  * \returns the difference of q2 and q1
     188              :  *
     189              :  * \tparam angleT controls whether this is in degrees (0, default) or radians (1)
     190              :  * \tparam realT is the type in which to do arithmetic
     191              :  *
     192              :  * \test Verify compilation and calculations of math::angleDiff \ref tests_math_geo_angleDiff "[test doc]"
     193              :  *
     194              :  * \ingroup geo
     195              :  */
     196              : template <class angleT>
     197           16 : typename angleT::realT angleDiff( typename angleT::realT q1, ///< [in] angle to subtract from q2.
     198              :                                   typename angleT::realT q2  ///< [in] angle to subtract q1 from.
     199              : )
     200              : {
     201              :     static_assert( std::is_floating_point<typename angleT::realT>::value, "angleDiff: realT must be floating point" );
     202              : 
     203           16 :     typename angleT::realT dq = q2 - q1;
     204              : 
     205           16 :     if( std::abs( dq ) > angleT::half )
     206              :     {
     207            8 :         if( dq < 0 )
     208              :         {
     209            4 :             dq = dq + static_cast<typename angleT::realT>( 2 ) * angleT::half;
     210              :         }
     211              :         else
     212              :         {
     213            4 :             dq = dq - static_cast<typename angleT::realT>( 2 ) * angleT::half;
     214              :         }
     215              :     }
     216              : 
     217           16 :     return dq;
     218              : }
     219              : 
     220              : /// Calculate the mean of a set of angles, correctly across 0/360.
     221              : /** Calculates the mean by decomposing into vectors and averaging the components. This accounts for crossing 0/360.
     222              :  *
     223              :  * \returns the mean angle
     224              :  *
     225              :  * \tparam angleT is the angle type, either radians<realT> or degrees<realT>.  angleT::realT is the type in which to do
     226              :  * arithmetic.
     227              :  *
     228              :  * \ingroup geo
     229              :  */
     230              : template <class angleT>
     231              : typename angleT::realT
     232              : angleMean( const std::vector<typename angleT::realT> &q /**< [in] vector of angles to average.*/ )
     233              : {
     234              :     static_assert( std::is_floating_point<typename angleT::realT>::value, "angleMean: realT must be floating point" );
     235              : 
     236              :     typename angleT::realT s = 0;
     237              :     typename angleT::realT c = 0;
     238              : 
     239              :     for( int i = 0; i < q.size(); ++i )
     240              :     {
     241              :         s += sin( q[i] / angleT::scale );
     242              :         c += cos( q[i] / angleT::scale );
     243              :     }
     244              : 
     245              :     s /= q.size();
     246              :     c /= q.size();
     247              : 
     248              :     return atan2( s, c ) * angleT::scale;
     249              : }
     250              : 
     251              : /// Make a vector of angles continuous, fixing the 0/360 crossing.
     252              : /** The vector is modified so it is continuous.
     253              :  *
     254              :  *
     255              :  * \tparam degrad controls whether angles are degrees (false) or radians (true)
     256              :  * \tparam realT is the type in which to do arithmetic
     257              :  *
     258              :  *  \ingroup geo
     259              :  */
     260              : template <int degrad = 0, typename realT>
     261              : int continueAngles( std::vector<realT> &angles, ///< [in] the vector of angles
     262              :                     realT threshold = 0.75 ///< [in] [optional] the fraction of a full circle at which to consider a
     263              :                                            ///< difference in angle discontinuous.
     264              : )
     265              : {
     266              :     realT full;
     267              : 
     268              :     if( degrad )
     269              :         full = two_pi<realT>();
     270              :     else
     271              :         full = static_cast<realT>( 360 );
     272              : 
     273              :     threshold *= full;
     274              : 
     275              :     realT adj = 0;
     276              : 
     277              :     if( fabs( angles[1] - angles[0] ) > threshold )
     278              :     {
     279              :         if( angles[1] > angles[0] )
     280              :             adj = -full;
     281              :         else
     282              :             adj = full;
     283              : 
     284              :         angles[1] += adj;
     285              :     }
     286              : 
     287              :     if( angles.size() == 2 )
     288              :         return 0;
     289              : 
     290              :     for( int i = 2; i < angles.size(); ++i )
     291              :     {
     292              :         angles[i] += adj;
     293              : 
     294              :         if( fabs( angles[i] - angles[i - 1] ) > threshold )
     295              :         {
     296              :             if( angles[i] > angles[i - 1] )
     297              :                 adj += -full;
     298              :             else
     299              :                 adj += full;
     300              : 
     301              :             angles[i] += adj;
     302              :         }
     303              :     }
     304              : 
     305              :     return 0;
     306              : }
     307              : 
     308              : /// Rotate a point about the origin.
     309              : /** The rotation is counter-clockwise for positive angles.
     310              :  *
     311              :  * \tparam realT a real floating point type
     312              :  *
     313              :  *  \ingroup geo
     314              :  */
     315              : template <typename realT>
     316              : void rotatePoint( realT &x0, ///< [in.out] the x-coordinate of the point to rotate.  On exit contains the rotated value.
     317              :                   realT &y0, ///< [in.out] the y-coordinate of the point to rotate.  On exit contains the rotated value.
     318              :                   realT angle ///< [in] the angle by which to rotate [radians]
     319              : )
     320              : {
     321              :     realT x1;
     322              :     realT y1;
     323              : 
     324              :     realT cq = cos( angle );
     325              :     realT sq = sin( angle );
     326              : 
     327              :     x1 = x0 * cq - y0 * sq;
     328              :     y1 = x0 * sq + y0 * cq;
     329              : 
     330              :     x0 = x1;
     331              :     y0 = y1;
     332              : }
     333              : 
     334              : } // namespace math
     335              : } // namespace mx
     336              : 
     337              : #endif // math_geo_hpp
        

Generated by: LCOV version 2.0-1