1 /*
2     SPDX-FileCopyrightText: 2016 Akarsh Simha <akarsh.simha@kdemail.net>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "dms.h"
10 
11 /**
12  * @class CachingDms
13  * @short a dms subclass that caches its sine and cosine values every time the angle is changed.
14  * @note This is to be used for those angles where sin/cos is repeatedly computed.
15  * @author Akarsh Simha <akarsh@kde.org>
16  */
17 
18 class CachingDms : public dms
19 {
20   public:
21     /**
22      * @short Default Constructor
23      */
CachingDms()24     CachingDms() : dms(), m_sin(NaN::d), m_cos(NaN::d)
25     {
26 #ifdef COUNT_DMS_SINCOS_CALLS
27         m_cacheUsed = true;
28         ++cachingdms_constructor_calls;
29 #endif
30     };
31 
32     /**
33      * @short Degree angle constructor
34      * @param x is the angle in degrees
35      */
36     explicit CachingDms(const double &x);
37 
38     /**
39      * @short QString constructor
40      */
41     explicit CachingDms(const QString &s, bool isDeg = true);
42 
43     /**
44      * @short DMS representation constructor
45      */
46     explicit CachingDms(const int &d, const int &m = 0, const int &s = 0, const int &ms = 0);
47 
48 #ifdef COUNT_DMS_SINCOS_CALLS
49     /**
50      * @short Destructor must count bad cache uses
51      */
52     ~CachingDms();
53 #endif
54 
55     /**
56      * @short Sets the angle in degrees supplied as a double
57      * @note Re-implements dms::setD() with sine/cosine caching
58      */
setD(const double & x)59     inline void setD(const double &x) override
60     {
61         dms::setD(x);
62         dms::SinCos(m_sin, m_cos);
63 #ifdef COUNT_DMS_SINCOS_CALLS
64         cachingdms_delta -= 2;
65         if (!m_cacheUsed)
66             ++cachingdms_bad_uses;
67         m_cacheUsed = false;
68 #endif
69     }
70 
71     /**
72      * @short Overrides dms::setD()
73      */
74     inline void setD(const int &d, const int &m, const int &s, const int &ms = 0) override
75     {
76         dms::setD(d, m, s, ms);
77         dms::SinCos(m_sin, m_cos);
78 #ifdef COUNT_DMS_SINCOS_CALLS
79         cachingdms_delta -= 2;
80         if (!m_cacheUsed)
81             ++cachingdms_bad_uses;
82         m_cacheUsed = false;
83 #endif
84     }
85 
86     /**
87      * @short Sets the angle in hours, supplied as a double
88      * @note Re-implements dms::setH() with sine/cosine caching
89      * @note While this and other methods internally call setD, we want to avoid unnecessary vtable lookups. We'd rather have inline than virtual when speed matters in general.
90      */
setH(const double & x)91     inline void setH(const double &x) override
92     {
93         dms::setH(x);
94         dms::SinCos(m_sin, m_cos);
95 #ifdef COUNT_DMS_SINCOS_CALLS
96         cachingdms_delta -= 2;
97         if (!m_cacheUsed)
98             ++cachingdms_bad_uses;
99         m_cacheUsed = false;
100 #endif
101     }
102 
103     /**
104      * @short Sets the angle in HMS form
105      * @note Re-implements dms::setH() with sine/cosine caching
106      */
107     inline void setH(const int &h, const int &m, const int &s, const int &ms = 0) override
108     {
109         dms::setH(h, m, s, ms);
110         dms::SinCos(m_sin, m_cos);
111 #ifdef COUNT_DMS_SINCOS_CALLS
112         cachingdms_delta -= 2;
113 #endif
114     }
115 
116     /**
117      * @short Sets the angle from string
118      * @note Re-implements dms::setFromString()
119      */
120     inline bool setFromString(const QString &s, bool isDeg = true) override
121     {
122         bool retval = dms::setFromString(s, isDeg);
123         dms::SinCos(m_sin, m_cos);
124 #ifdef COUNT_DMS_SINCOS_CALLS
125         cachingdms_delta -= 2;
126         if (!m_cacheUsed)
127             ++cachingdms_bad_uses;
128         m_cacheUsed = false;
129 #endif
130         return retval;
131     }
132 
133     /**
134      * @short Sets the angle in radians
135      */
setRadians(const double & a)136     inline void setRadians(const double &a) override
137     {
138         dms::setRadians(a);
139         dms::SinCos(m_sin, m_cos);
140 #ifdef COUNT_DMS_SINCOS_CALLS
141         cachingdms_delta -= 2;
142         if (!m_cacheUsed)
143             ++cachingdms_bad_uses;
144         m_cacheUsed = false;
145 #endif
146     }
147 
148     /**
149      * @short Sets the angle using atan2()
150      * @note The advantage is that we can calculate sin/cos faster because we know the tangent
151      */
152     void setUsing_atan2(const double &y, const double &x);
153 
154     /**
155      * @short Sets the angle using asin()
156      * @param sine Sine of the angle
157      * @note The advantage is that we can cache the sine value supplied
158      * @note The limited range of asin must be borne in mind
159      */
160     void setUsing_asin(const double &sine);
161 
162     /**
163      * @short Sets the angle using acos()
164      * @param cosine Cosine of the angle
165      * @note The advantage is that we can cache the cosine value supplied
166      * @note The limited range of acos must be borne in mind
167      */
168     void setUsing_acos(const double &cosine);
169 
170     /**
171      * @short Get the sine and cosine together
172      * @note Re-implements dms::SinCos()
173      * @note This just uses the cached values assuming that they are good
174      */
SinCos(double & s,double & c)175     inline void SinCos(double &s, double &c) const
176     {
177         s = m_sin;
178         c = m_cos;
179 #ifdef COUNT_DMS_SINCOS_CALLS
180         cachingdms_delta += 2;
181         m_cacheUsed = true;
182 #endif
183     }
184 
185     /**
186      * @short Get the sine of this angle
187      * @note Re-implements dms::sin()
188      * @note This just uses the cached value assuming that it is good
189      */
sin()190     inline double sin() const
191     {
192 #ifdef COUNT_DMS_SINCOS_CALLS
193         ++cachingdms_delta;
194         m_cacheUsed = true;
195 #endif
196         return m_sin;
197     }
198 
199     /**
200      * @short Get the cosine of this angle
201      * @note Re-implements dms::cos()
202      * @note This just uses the cached value assuming that it is good
203      */
cos()204     inline double cos() const
205     {
206 #ifdef COUNT_DMS_SINCOS_CALLS
207         ++cachingdms_delta;
208         m_cacheUsed = true;
209 #endif
210         return m_cos;
211     }
212 
213     /**
214      * @short Construct an angle from the given string
215      * @note Re-implements dms::fromString()
216      */
217     static CachingDms fromString(const QString &s, bool deg);
218 
219     /**
220      * @short operator -
221      * @note In addition to negating the angle, we negate the sine value
222      */
223     CachingDms operator-();
224 
225     /**
226      * @short Casting constructor
227      */
228     CachingDms(const dms &angle);
229 
230 #ifdef COUNT_DMS_SINCOS_CALLS
231     /**
232      * Copy constructor that sets m_cacheUsed to true
233      */
234     CachingDms(const CachingDms &o);
235     CachingDms &operator=(const CachingDms &o);
236 #endif
237 
238   private:
239     double m_sin, m_cos; // Cached values
240 
241     /**
242      * @short Private constructor used to create a CachingDms with known sine and cosine
243      */
CachingDms(const double & degrees,const double & sine,const double & cosine)244     explicit CachingDms(const double &degrees, const double &sine, const double &cosine)
245         : dms(degrees), m_sin(sine), m_cos(cosine)
246     {
247 #ifdef COUNT_DMS_SINCOS_CALLS
248         ++cachingdms_constructor_calls;
249         m_cacheUsed = false;
250 #endif
251     }
252 
253     /**
254      * @short Addition and subtraction operators
255      * @note Uses trigonometric identities to find the new trigonometric values
256      * @note Avoid repeated use, as the round-off errors will accumulate!
257      */
258     friend CachingDms operator+(const CachingDms &, const CachingDms &);
259     friend CachingDms operator-(const CachingDms &, const CachingDms &);
260     friend CachingDms operator+(const dms &a, const CachingDms &b);
261     friend CachingDms operator-(const dms &a, const CachingDms &b);
262     friend CachingDms operator+(const CachingDms &a, const dms &b);
263     friend CachingDms operator-(const CachingDms &a, const dms &b);
264 
265 #ifdef COUNT_DMS_SINCOS_CALLS
266   private:
267     mutable bool m_cacheUsed;
268 
269   public:
270     static unsigned long cachingdms_constructor_calls;
271     static long cachingdms_delta; // difference of ( trig function calls ) - ( trig computations )
272     static unsigned long cachingdms_bad_uses;
273 #endif
274 };
275