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