Kstars

dms.h
1/*
2 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#pragma once
8
9#include "../nan.h"
10
11#include <QString>
12#include <QDataStream>
13
14#include <cmath>
15
16//#define COUNT_DMS_SINCOS_CALLS true
17//#define PROFILE_SINCOS true
18
19#ifdef PROFILE_SINCOS
20#include <ctime>
21#endif
22
23/** @class dms
24 * @short An angle, stored as degrees, but expressible in many ways.
25 * @author Jason Harris
26 * @version 1.0
27 *
28 * dms encapsulates an angle. The angle is stored as a double,
29 * equal to the value of the angle in degrees. Methods are available
30 * for setting/getting the angle as a floating-point measured in
31 * Degrees or Hours, or as integer triplets (degrees, arcminutes,
32 * arcseconds or hours, minutes, seconds). There is also a method
33 * to set the angle according to a radian value, and to return the
34 * angle expressed in radians. Finally, a SinCos() method computes
35 * the sin and cosine of the angle.
36 */
37class dms
38{
39 public:
40 /** Default constructor. */
42 : D(NaN::d)
43#ifdef COUNT_DMS_SINCOS_CALLS
44 ,
45 m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
46#endif
47 {
48#ifdef COUNT_DMS_SINCOS_CALLS
49 ++dms_constructor_calls;
50#endif
51 }
52
53 /** Empty virtual destructor */
54 virtual ~dms() = default;
55
56 /** @short Set the floating-point value of the angle according to the four integer arguments.
57 * @param d degree portion of angle (int). Defaults to zero.
58 * @param m arcminute portion of angle (int). Defaults to zero.
59 * @param s arcsecond portion of angle (int). Defaults to zero.
60 * @param ms arcsecond portion of angle (int). Defaults to zero.
61 */
62 explicit dms(const int &d, const int &m = 0, const int &s = 0, const int &ms = 0)
63#ifdef COUNT_DMS_SINCOS_CALLS
64 : m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
65#endif
66 {
67 dms::setD(d, m, s, ms);
68#ifdef COUNT_DMS_SINCOS_CALLS
69 ++dms_constructor_calls;
70#endif
71 }
72
73 /** @short Construct an angle from a double value.
74 *
75 * Creates an angle whose value in Degrees is equal to the argument.
76 * @param x angle expressed as a floating-point number (in degrees)
77 */
78 explicit dms(const double &x)
79 : D(x)
80#ifdef COUNT_DMS_SINCOS_CALLS
81 ,
82 m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
83#endif
84 {
85#ifdef COUNT_DMS_SINCOS_CALLS
86 ++dms_constructor_calls;
87#endif
88 }
89
90 /** @short Construct an angle from a string representation.
91 *
92 * Attempt to create the angle according to the string argument. If the string
93 * cannot be parsed as an angle value, the angle is set to zero.
94 *
95 * @warning There is not an unambiguous notification that it failed to parse the string,
96 * since the string could have been a valid representation of zero degrees.
97 * If this is a concern, use the setFromString() function directly instead.
98 *
99 * @param s the string to parse as a dms value.
100 * @param isDeg if true, value is in degrees; if false, value is in hours.
101 * @sa setFromString()
102 */
103 explicit dms(const QString &s, bool isDeg = true)
104#ifdef COUNT_DMS_SINCOS_CALLS
105 : m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
106#endif
107 {
108 setFromString(s, isDeg);
109#ifdef COUNT_DMS_SINCOS_CALLS
110 ++dms_constructor_calls;
111#endif
112 }
113
114 /** @return integer degrees portion of the angle
115 */
116 inline int degree() const
117 {
118 if (std::isnan(D))
119 return 0;
120
121 return int(D);
122 }
123
124 /** @return integer arcminutes portion of the angle.
125 * @note an arcminute is 1/60 degree.
126 */
127 int arcmin() const;
128
129 /** @return integer arcseconds portion of the angle
130 * @note an arcsecond is 1/60 arcmin, or 1/3600 degree.
131 */
132 int arcsec() const;
133
134 /** @return integer milliarcseconds portion of the angle
135 * @note a milliarcsecond is 1/1000 arcsecond.
136 */
137 int marcsec() const;
138
139 /** @return angle in degrees expressed as a double.
140 */
141 inline const double &Degrees() const { return D; }
142
143 /** @return integer hours portion of the angle
144 * @note an angle can be measured in degrees/arcminutes/arcseconds
145 * or hours/minutes/seconds. An hour is equal to 15 degrees.
146 */
147 inline int hour() const { return int(reduce().Degrees() / 15.0); }
148
149 /** @return integer minutes portion of the angle
150 * @note a minute is 1/60 hour (not the same as an arcminute)
151 */
152 int minute() const;
153
154 /** @return integer seconds portion of the angle
155 * @note a second is 1/3600 hour (not the same as an arcsecond)
156 */
157 int second() const;
158
159 /** @return integer milliseconds portion of the angle
160 * @note a millisecond is 1/1000 second (not the same as a milliarcsecond)
161 */
162 int msecond() const;
163
164 /** @return angle in hours expressed as a double in the range 0 to 23.999...
165 * @note an angle can be measured in degrees/arcminutes/arcseconds
166 * or hours/minutes/seconds. An hour is equal to 15 degrees.
167 */
168 inline double Hours() const { return reduce().Degrees() / 15.0; }
169
170 /** @return angle in hours expressed as a double in the range -11.999 to 0 to 12.0
171 * @note an angle can be measured in degrees/arcminutes/arcseconds
172 * or hours/minutes/seconds. An hour is equal to 15 degrees.
173 */
174 inline double HoursHa() const { return Hours() <= 12.0 ? Hours() : Hours() - 24.0; }
175
176 /** Sets floating-point value of angle, in degrees.
177 * @param x new angle (double)
178 */
179 inline virtual void setD(const double &x)
180 {
181#ifdef COUNT_DMS_SINCOS_CALLS
182 m_sinDirty = m_cosDirty = true;
183#endif
184 D = x;
185 }
186
187 /** @short Sets floating-point value of angle, in degrees.
188 *
189 * This is an overloaded member function; it behaves essentially
190 * like the above function. The floating-point value of the angle
191 * (D) is determined from the following formulae:
192 *
193 * \f$ fabs(D) = fabs(d) + \frac{(m + (s/60))}{60} \f$
194 * \f$ sgn(D) = sgn(d) \f$
195 *
196 * @param d integer degrees portion of angle
197 * @param m integer arcminutes portion of angle
198 * @param s integer arcseconds portion of angle
199 * @param ms integer arcseconds portion of angle
200 */
201 virtual void setD(const int &d, const int &m, const int &s, const int &ms = 0);
202
203 /** @short Sets floating-point value of angle, in hours.
204 *
205 * Converts argument from hours to degrees, then
206 * sets floating-point value of angle, in degrees.
207 * @param x new angle, in hours (double)
208 * @sa setD()
209 */
210 inline virtual void setH(const double &x)
211 {
212 dms::setD(x * 15.0);
213#ifdef COUNT_DMS_SINCOS_CALLS
214 m_cosDirty = m_sinDirty = true;
215#endif
216 }
217
218 /** @short Sets floating-point value of angle, in hours.
219 *
220 * Converts argument values from hours to degrees, then
221 * sets floating-point value of angle, in degrees.
222 * This is an overloaded member function, provided for convenience. It
223 * behaves essentially like the above function.
224 * @param h integer hours portion of angle
225 * @param m integer minutes portion of angle
226 * @param s integer seconds portion of angle
227 * @param ms integer milliseconds portion of angle
228 * @sa setD()
229 */
230 virtual void setH(const int &h, const int &m, const int &s, const int &ms = 0);
231
232 /** @short Attempt to parse the string argument as a dms value, and set the dms object
233 * accordingly.
234 * @param s the string to be parsed as a dms value. The string can be an int or
235 * floating-point value, or a triplet of values (d/h, m, s) separated by spaces or colons.
236 * @param isDeg if true, the value is in degrees. Otherwise, it is in hours.
237 * @return true if sting was parsed successfully. Otherwise, set the dms value
238 * to 0.0 and return false.
239 */
240 virtual bool setFromString(const QString &s, bool isDeg = true);
241
242 /** @short Compute Sine and Cosine of the angle simultaneously.
243 * On machines using glibc >= 2.1, calling SinCos() is somewhat faster
244 * than calling sin() and cos() separately.
245 * The values are returned through the arguments (passed by reference).
246 *
247 * @param s Sine of the angle
248 * @param c Cosine of the angle
249 * @sa sin() cos()
250 */
251 inline void SinCos(double &s, double &c) const;
252
253 /** @short Compute the Angle's Sine.
254 *
255 * @return the Sine of the angle.
256 * @sa cos()
257 */
258 double sin() const
259 {
260#ifdef COUNT_DMS_SINCOS_CALLS
261 if (!m_sinCosCalled)
262 {
263 m_sinCosCalled = true;
264 ++dms_with_sincos_called;
265 }
266 if (m_sinDirty)
267 m_sinDirty = false;
268 else
269 ++redundant_trig_function_calls;
270 ++trig_function_calls;
271#endif
272#ifdef PROFILE_SINCOS
273 std::clock_t start, stop;
274 double s;
275 start = std::clock();
276 s = ::sin(D * DegToRad);
277 stop = std::clock();
278 seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC);
279 return s;
280#else
281 return ::sin(D * DegToRad);
282#endif
283 }
284
285 /** @short Compute the Angle's Cosine.
286 *
287 * @return the Cosine of the angle.
288 * @sa sin()
289 */
290 double cos() const
291 {
292#ifdef COUNT_DMS_SINCOS_CALLS
293 if (!m_sinCosCalled)
294 {
295 m_sinCosCalled = true;
296 ++dms_with_sincos_called;
297 }
298 if (m_cosDirty)
299 m_cosDirty = false;
300 else
301 ++redundant_trig_function_calls;
302 ++trig_function_calls;
303#endif
304#ifdef PROFILE_SINCOS
305 std::clock_t start, stop;
306 double c;
307 start = std::clock();
308 c = ::cos(D * DegToRad);
309 stop = std::clock();
310 seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC);
311 return c;
312#else
313 return ::cos(D * DegToRad);
314#endif
315 }
316
317 /**
318 * @short Convenience method to return tangent of the angle
319 */
320 inline double tan() const { return sin()/cos(); }
321
322 /** @short Express the angle in radians.
323 * @return the angle in radians (double)
324 */
325 inline double radians() const { return D * DegToRad; }
326
327 /** @short Set angle according to the argument, in radians.
328 *
329 * This function converts the argument to degrees, then sets the angle
330 * with setD().
331 * @param Rad an angle in radians
332 */
333 inline virtual void setRadians(const double &Rad)
334 {
335 dms::setD(Rad / DegToRad);
336#ifdef COUNT_DMS_SINCOS_CALLS
337 m_cosDirty = m_sinDirty = true;
338#endif
339 }
340
341 /** return the equivalent angle between 0 and 360 degrees.
342 * @warning does not change the value of the parent angle itself.
343 */
344 const dms reduce() const;
345
346 /**
347 * @brief deltaAngle Return the shortest difference (path) between this angle and the supplied angle. The range is normalized to [-180,+180]
348 * @param angle Angle to subtract from current angle.
349 * @return Normalized angle in the range [-180,+180]
350 */
351 const dms deltaAngle(dms angle) const;
352
353 /**
354 * @short an enum defining standard angle ranges
355 */
357 {
358 ZERO_TO_2PI,
359 MINUSPI_TO_PI
360 };
361
362 /**
363 * @short Reduce _this_ angle to the given range
364 */
365 void reduceToRange(enum dms::AngleRanges range);
366
367 /** @return a nicely-formatted string representation of the angle
368 * in degrees, arcminutes, and arcseconds.
369 * @param forceSign if @c true then adds '+' or '-' to the string
370 * @param machineReadable uses a colon separator and produces +/-dd:mm:ss format instead
371 * @param highPrecision adds milliseconds, if @c false the seconds will be shown as an integer
372 */
373 const QString toDMSString(const bool forceSign = false, const bool machineReadable = false, const bool highPrecision=false) const;
374
375 /** @return a nicely-formatted string representation of the angle
376 * in hours, minutes, and seconds.
377 * @param machineReadable uses a colon separator and produces hh:mm:ss format instead
378 * @param highPrecision adds milliseconds, if @c false the seconds will be shown as an integer
379 */
380 const QString toHMSString(const bool machineReadable = false, const bool highPrecision=false) const;
381
382 /** PI is a const static member; it's public so that it can be used anywhere,
383 * as long as dms.h is included.
384 */
385 static constexpr double PI = { M_PI };
386
387 /** DegToRad is a const static member equal to the number of radians in
388 * one degree (dms::PI/180.0).
389 */
390 static constexpr double DegToRad = { M_PI / 180.0 };
391
392 /** @short Static function to create a DMS object from a QString.
393 *
394 * There are several ways to specify the angle:
395 * @li Integer numbers ( 5 or -33 )
396 * @li Floating-point numbers ( 5.0 or -33.0 )
397 * @li colon-delimited integers ( 5:0:0 or -33:0:0 )
398 * @li colon-delimited with float seconds ( 5:0:0.0 or -33:0:0.0 )
399 * @li colon-delimited with float minutes ( 5:0.0 or -33:0.0 )
400 * @li space-delimited ( 5 0 0; -33 0 0 ) or ( 5 0.0 or -33 0.0 )
401 * @li space-delimited, with unit labels ( 5h 0m 0s or -33d 0m 0s )
402 * @param s the string to be parsed as an angle value
403 * @param deg if true, s is expressed in degrees; if false, s is expressed in hours
404 * @return a dms object whose value is parsed from the string argument
405 */
406 static dms fromString(const QString &s, bool deg);
407
408 /** Reduce an angle in degrees expressed as a double */
409 static double reduce(const double D);
410
411 inline dms operator-() { return dms(-D); }
412#ifdef COUNT_DMS_SINCOS_CALLS
413 static long unsigned dms_constructor_calls; // counts number of DMS constructor calls
414 static long unsigned dms_with_sincos_called;
415 static long unsigned trig_function_calls; // total number of trig function calls
416 static long unsigned redundant_trig_function_calls; // counts number of redundant trig function calls
417 static double seconds_in_trig; // accumulates number of seconds spent in trig function calls
418#endif
419
420 protected:
421 double D;
422
423 private:
424#ifdef COUNT_DMS_SINCOS_CALLS
425 mutable bool m_sinDirty, m_cosDirty, m_sinCosCalled;
426#endif
427
428 friend dms operator+(dms, dms);
429 friend dms operator-(dms, dms);
430 friend QDataStream &operator<<(QDataStream &out, const dms &d);
431 friend QDataStream &operator>>(QDataStream &in, dms &d);
432};
433
434/// Add two angles
435inline dms operator+(dms a, dms b)
436{
437 return dms(a.D + b.D);
438}
439
440/// Subtract angles
441inline dms operator-(dms a, dms b)
442{
443 return dms(a.D - b.D);
444}
445
446// Inline sincos
447inline void dms::SinCos(double &s, double &c) const
448{
449#ifdef PROFILE_SINCOS
450 std::clock_t start, stop;
451 start = std::clock();
452#endif
453
454#ifdef HAVE_SINCOS
455 sincos(radians(), &s, &c);
456#else
457 s = ::sin(radians());
458 c = ::cos(radians());
459#endif
460
461#ifdef PROFILE_SINCOS
462 stop = std::clock();
463 seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC);
464#endif
465
466#ifdef COUNT_DMS_SINCOS_CALLS
467 if (!m_sinCosCalled)
468 {
469 m_sinCosCalled = true;
470 ++dms_with_sincos_called;
471 }
472 if (m_sinDirty)
473 m_sinDirty = false;
474 else
475 ++redundant_trig_function_calls;
476
477 if (m_cosDirty)
478 m_cosDirty = false;
479 else
480 ++redundant_trig_function_calls;
481
482 trig_function_calls += 2;
483#endif
484}
485
486/** Overloaded equality operator */
487inline bool operator==(const dms &a1, const dms &a2)
488{
489 return a1.Degrees() == a2.Degrees();
490}
491
492/**
493 * User-defined dms literals for convenience
494 */
495
496/**
497 * Create a constant angle in degrees
498 */
499inline dms operator "" _deg(long double x) { return dms(double(x)); }
500
501/**
502 * Create a constant angle in hours
503 */
504inline dms operator "" _h(long double x) { return dms(double(x * 15.0)); }
505
506/**
507 * Create a constant angle in radians
508 */
509inline dms operator "" _rad(long double x) { return dms(double(x / dms::DegToRad)); }
510
511/**
512 * Create a constant angle from a DMS string
513 */
514inline dms operator "" _dms(const char *dmsString) { return dms::fromString(QString(dmsString), true); }
515
516/**
517 * Create a constant angle from a HMS string
518 */
519inline dms operator "" _hms(const char *hmsString) { return dms::fromString(QString(hmsString), false); }
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
double cos() const
Compute the Angle's Cosine.
Definition dms.h:290
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition dms.cpp:429
double Hours() const
Definition dms.h:168
void reduceToRange(enum dms::AngleRanges range)
Reduce this angle to the given range.
Definition dms.cpp:446
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
Definition dms.h:210
dms(const QString &s, bool isDeg=true)
Construct an angle from a string representation.
Definition dms.h:103
int second() const
Definition dms.cpp:231
const dms reduce() const
return the equivalent angle between 0 and 360 degrees.
Definition dms.cpp:251
void SinCos(double &s, double &c) const
Compute Sine and Cosine of the angle simultaneously.
Definition dms.h:447
double HoursHa() const
Definition dms.h:174
double tan() const
Convenience method to return tangent of the angle.
Definition dms.h:320
virtual bool setFromString(const QString &s, bool isDeg=true)
Attempt to parse the string argument as a dms value, and set the dms object accordingly.
Definition dms.cpp:48
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
Definition dms.h:385
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
const dms deltaAngle(dms angle) const
deltaAngle Return the shortest difference (path) between this angle and the supplied angle.
Definition dms.cpp:267
int minute() const
Definition dms.cpp:221
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:378
friend dms operator+(dms, dms)
Add two angles.
Definition dms.h:435
virtual ~dms()=default
Empty virtual destructor.
int msecond() const
Definition dms.cpp:241
int marcsec() const
Definition dms.cpp:207
double radians() const
Express the angle in radians.
Definition dms.h:325
AngleRanges
an enum defining standard angle ranges
Definition dms.h:357
dms(const double &x)
Construct an angle from a double value.
Definition dms.h:78
dms()
Default constructor.
Definition dms.h:41
dms(const int &d, const int &m=0, const int &s=0, const int &ms=0)
Set the floating-point value of the angle according to the four integer arguments.
Definition dms.h:62
int degree() const
Definition dms.h:116
int arcmin() const
Definition dms.cpp:180
int arcsec() const
Definition dms.cpp:193
virtual void setRadians(const double &Rad)
Set angle according to the argument, in radians.
Definition dms.h:333
int hour() const
Definition dms.h:147
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition dms.h:179
const double & Degrees() const
Definition dms.h:141
double sin() const
Compute the Angle's Sine.
Definition dms.h:258
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
Definition dms.h:390
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:39 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.