File indexing completed on 2024-04-21 14:43:56

0001 /*
0002     SPDX-FileCopyrightText: 2016 Akarsh Simha <akarsh.simha@kdemail.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 /* Project Includes */
0008 #include "cachingdms.h"
0009 
0010 /* KDE Includes */
0011 
0012 /* Qt Includes */
0013 #include <QString>
0014 
0015 /* STL Includes */
0016 #include <cmath>
0017 
0018 #ifdef COUNT_DMS_SINCOS_CALLS
0019 unsigned long CachingDms::cachingdms_constructor_calls = 0;
0020 long CachingDms::cachingdms_delta             = 0; // difference of ( trig function calls ) - ( trig computations )
0021 unsigned long CachingDms::cachingdms_bad_uses = 0;
0022 #endif
0023 
0024 CachingDms::CachingDms(const double &x) : dms(x)
0025 {
0026     dms::SinCos(m_sin, m_cos);
0027 #ifdef COUNT_DMS_SINCOS_CALLS
0028     ++cachingdms_constructor_calls;
0029     cachingdms_delta -= 2;
0030     m_cacheUsed = false;
0031 #endif
0032 }
0033 
0034 #ifdef COUNT_DMS_SINCOS_CALLS
0035 CachingDms::~CachingDms()
0036 {
0037     if (!m_cacheUsed)
0038         ++cachingdms_bad_uses;
0039 }
0040 #endif
0041 
0042 CachingDms::CachingDms(const QString &s, bool isDeg) : dms(s, isDeg)
0043 {
0044     dms::SinCos(m_sin, m_cos);
0045 #ifdef COUNT_DMS_SINCOS_CALLS
0046     ++cachingdms_constructor_calls;
0047     cachingdms_delta -= 2;
0048     m_cacheUsed = false;
0049 #endif
0050 }
0051 
0052 CachingDms::CachingDms(const int &d, const int &m, const int &s, const int &ms) : dms(d, m, s, ms)
0053 {
0054     dms::SinCos(m_sin, m_cos);
0055 #ifdef COUNT_DMS_SINCOS_CALLS
0056     ++cachingdms_constructor_calls;
0057     cachingdms_delta -= 2;
0058     m_cacheUsed = false;
0059 #endif
0060 }
0061 
0062 void CachingDms::setUsing_atan2(const double &y, const double &x)
0063 {
0064     /*
0065      * NOTE: A bit of independent profiling shows that on my machine
0066      * (Intel Core i5, x86_64, glibc 2.24-2) the square-root based
0067      * computation below has some advantage, running ~ 70% faster on
0068      * average for some range of values.
0069      *
0070      */
0071     dms::setRadians(atan2(y, x));
0072     double r = sqrt(y * y + x * x);
0073     m_cos             = x / r;
0074     m_sin             = y / r;
0075 
0076 #ifdef COUNT_DMS_SINCOS_CALLS
0077     if (!m_cacheUsed)
0078         ++cachingdms_bad_uses;
0079     m_cacheUsed = false;
0080 #endif
0081     // One may be tempted to do the following:
0082     //   dms::setRadians( atan2( y, x ) );
0083     //   m_cos = dms::cos();
0084     //   m_sin = (y/x) * m_cos;
0085     // However, this has a problem when x = 0. The result for m_sin
0086     // must be 1, but instead the above code will result in NaN.
0087     // So we will need a conditional:
0088     //   m_sin = (x == 0) ? 1. : (y/x) * m_cos;
0089     // The conditional makes the performance worse than just setting
0090     // the angle and using sincos()
0091 }
0092 
0093 void CachingDms::setUsing_asin(const double &sine)
0094 {
0095     dms::setRadians(asin(sine));
0096     m_sin = sine;
0097     // Note: The below is valid because in the range of asin, which is
0098     // [-pi/2, pi/2], cosine is always non-negative
0099     m_cos = std::sqrt(1 - sine * sine);
0100 #ifdef COUNT_DMS_SINCOS_CALLS
0101     if (!m_cacheUsed)
0102         ++cachingdms_bad_uses;
0103     m_cacheUsed = false;
0104 #endif
0105 }
0106 
0107 void CachingDms::setUsing_acos(const double &cosine)
0108 {
0109     dms::setRadians(acos(cosine));
0110     m_cos = cosine;
0111     // Note: The below is valid because in the range of acos, which is
0112     // [0, pi], sine is always non-negative
0113     m_sin = std::sqrt(1 - cosine * cosine);
0114 #ifdef COUNT_DMS_SINCOS_CALLS
0115     if (!m_cacheUsed)
0116         ++cachingdms_bad_uses;
0117     m_cacheUsed = false;
0118 #endif
0119 }
0120 
0121 CachingDms CachingDms::fromString(const QString &s, bool deg)
0122 {
0123     CachingDms result;
0124     result.setFromString(s, deg);
0125     return result;
0126 }
0127 
0128 CachingDms CachingDms::operator-()
0129 {
0130     return CachingDms(-D, -m_sin, m_cos);
0131 }
0132 
0133 CachingDms::CachingDms(const dms &angle)
0134 {
0135     D = angle.Degrees();
0136     dms::SinCos(m_sin, m_cos);
0137 #ifdef COUNT_DMS_SINCOS_CALLS
0138     ++cachingdms_constructor_calls;
0139     cachingdms_delta -= 2;
0140     m_cacheUsed = false;
0141 #endif
0142 }
0143 
0144 #ifdef COUNT_DMS_SINCOS_CALLS
0145 CachingDms::CachingDms(const CachingDms &o)
0146 {
0147     m_sin       = o.sin();
0148     m_cos       = o.cos();
0149     D           = o.D;
0150     m_cacheUsed = false;
0151 }
0152 CachingDms &CachingDms::operator=(const CachingDms &o)
0153 {
0154     if (!m_cacheUsed)
0155         ++cachingdms_bad_uses;
0156     m_sin       = o.sin();
0157     m_cos       = o.cos();
0158     D           = o.D;
0159     m_cacheUsed = false;
0160     return (*this);
0161 }
0162 #endif
0163 
0164 // Makes trig identities more readable:
0165 #define sinA a.sin()
0166 #define cosA a.cos()
0167 #define sinB b.sin()
0168 #define cosB b.cos()
0169 
0170 // We use trigonometric addition / subtraction formulae to speed up
0171 // computation. This way, we have no trigonometric function calls at
0172 // all, but only floating point multiplications and
0173 // addition/subtraction instead.
0174 // The only caveat is that error can accumulate if used repeatedly!
0175 
0176 CachingDms operator+(const CachingDms &a, const CachingDms &b)
0177 {
0178     return CachingDms(a.Degrees() + b.Degrees(), sinA * cosB + cosA * sinB, cosA * cosB - sinA * sinB);
0179 }
0180 
0181 CachingDms operator-(const CachingDms &a, const CachingDms &b)
0182 {
0183     return CachingDms(a.Degrees() - b.Degrees(), sinA * cosB - cosA * sinB, cosA * cosB + sinA * sinB);
0184 }
0185 
0186 CachingDms operator+(const dms &a, const CachingDms &b)
0187 {
0188     return CachingDms(a + dms(b));
0189 }
0190 
0191 CachingDms operator-(const dms &a, const CachingDms &b)
0192 {
0193     return CachingDms(a - dms(b));
0194 }
0195 
0196 CachingDms operator+(const CachingDms &a, const dms &b)
0197 {
0198     return CachingDms(dms(a) + b);
0199 }
0200 
0201 CachingDms operator-(const CachingDms &a, const dms &b)
0202 {
0203     return CachingDms(dms(a) - b);
0204 }
0205 
0206 #undef sinA
0207 #undef cosA
0208 #undef sinB
0209 #undef cosB