Commit 8c2dffaf authored by Akarsh Simha's avatar Akarsh Simha
Browse files

A CachingDms class that caches trigonometric values.

parent 37ba58d5
......@@ -582,6 +582,7 @@ set(kstars_projection_SRCS
set(kstars_extra_SRCS
auxiliary/colorscheme.cpp
auxiliary/dms.cpp
auxiliary/cachingdms.cpp
auxiliary/geolocation.cpp
auxiliary/ksfilereader.cpp
auxiliary/ksuserdb.cpp
......
/***************************************************************************
cachingdms.cpp - KStars Planetarium
-------------------
begin : Sat 24 Sep 2016 23:16:25 CDT
copyright : (c) 2016 by Akarsh Simha
email : akarsh.simha@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/* Project Includes */
#include "cachingdms.h"
/* KDE Includes */
/* Qt Includes */
#include <QString>
/* STL Includes */
#include <cmath>
#ifdef COUNT_DMS_SINCOS_CALLS
unsigned long CachingDms::cachingdms_constructor_calls = 0;
unsigned long CachingDms::cachingdms_delta = 0; // difference of ( trig function calls ) - ( trig computations )
#endif
CachingDms::CachingDms( const double &x ) : dms( x )
{
dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_constructor_calls;
cachingdms_delta -= 2;
#endif
}
CachingDms::CachingDms(const QString& s, bool isDeg) : dms( s, isDeg ) {
dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_constructor_calls;
cachingdms_delta -= 2;
#endif
}
CachingDms::CachingDms(const int &d, const int &m, const int &s, const int &ms) :
dms( d, m, s, ms ) {
dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_constructor_calls;
cachingdms_delta -= 2;
#endif
}
void CachingDms::setUsing_atan2(const double & y, const double & x) {
/*
* NOTE: A bit of independent profiling shows that on my machine
* (Intel Core i5, x86_64, glibc 2.24-2) the square-root based
* computation below has some advantage, running ~ 70% faster on
* average for some range of values.
*
*/
dms::setRadians( atan2( y, x ) );
register double r = sqrt( y*y + x*x );
m_cos = x/r;
m_sin = y/r;
// One may be tempted to do the following:
// dms::setRadians( atan2( y, x ) );
// m_cos = dms::cos();
// m_sin = (y/x) * m_cos;
// However, this has a problem when x = 0. The result for m_sin
// must be 1, but instead the above code will result in NaN.
// So we will need a conditional:
// m_sin = (x == 0) ? 1. : (y/x) * m_cos;
// The conditional makes the performance worse than just setting
// the angle and using sincos()
}
void CachingDms::setUsing_asin(const double & sine) {
dms::setRadians( asin( sine ) );
m_sin = sine;
// Note: The below is valid because in the range of asin, which is
// [-pi/2, pi/2], cosine is always non-negative
m_cos = std::sqrt( 1 - sine*sine );
}
void CachingDms::setUsing_acos(const double & cosine) {
dms::setRadians( acos( cosine ) );
m_cos = cosine;
// Note: The below is valid because in the range of acos, which is
// [0, pi], sine is always non-negative
m_sin = std::sqrt( 1 - cosine*cosine );
}
CachingDms CachingDms::fromString(const QString& s, bool deg) {
CachingDms result;
result.setFromString( s, deg );
return result;
}
CachingDms CachingDms::operator -() {
return CachingDms( -D, -m_sin, m_cos );
}
CachingDms::CachingDms(const dms& angle) {
D = angle.Degrees();
dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_constructor_calls;
cachingdms_delta -= 2;
#endif
}
// Makes trig identities more readable:
#define sinA a.m_sin
#define cosA a.m_cos
#define sinB b.m_sin
#define cosB b.m_cos
// We use trigonometric addition / subtraction formulae to speed up
// computation. This way, we have no trigonometric function calls at
// all, but only floating point multiplications and
// addition/subtraction instead.
// The only caveat is that error can accumulate if used repeatedly!
CachingDms operator +(const CachingDms &a, const CachingDms &b) {
return CachingDms( a.Degrees() + b.Degrees(),
sinA * cosB + cosA * sinB,
cosA * cosB - sinA * sinB );
}
CachingDms operator -(const CachingDms &a, const CachingDms &b) {
return CachingDms( a.Degrees() - b.Degrees(),
sinA * cosB - cosA * sinB,
cosA * cosB + sinA * sinB );
}
#undef sinA
#undef cosA
#undef sinB
#undef cosB
/***************************************************************************
cachingdms.h - KStars Planetarium
-------------------
begin : Sat 24 Sep 2016 02:18:26 CDT
copyright : (c) 2016 by Akarsh Simha
email : akarsh.simha@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef CACHINGDMS_H
#define CACHINGDMS_H
#include "dms.h"
/**
* @class CachingDms
* @short a dms subclass that caches its sine and cosine values every time the angle is changed.
* @note This is to be used for those angles where sin/cos is repeatedly computed.
* @author Akarsh Simha <akarsh@kde.org>
*/
class CachingDms : public dms {
public:
/**
* @short Default Constructor
*/
CachingDms() : dms(), m_sin( NaN::d ), m_cos( NaN::d ) {
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_constructor_calls;
#endif
};
/**
* @short Degree angle constructor
* @param x is the angle in degrees
*/
explicit CachingDms(const double &x );
/**
* @short QString constructor
*/
explicit CachingDms( const QString &s, bool isDeg=true );
/**
* @short DMS representation constructor
*/
explicit CachingDms( const int &d, const int &m=0, const int &s=0, const int &ms=0 );
/**
* @short Sets the angle in degrees supplied as a double
* @note Re-implements dms::setD() with sine/cosine caching
*/
inline void setD( const double &x ) { dms::setD( x ); dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
cachingdms_delta -= 2;
#endif
}
/**
* @short Sets the angle in hours, supplied as a double
* @note Re-implements dms::setH() with sine/cosine caching
* @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.
*/
inline void setH( const double &x ) { dms::setH( x ); dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
cachingdms_delta -= 2;
#endif
}
/**
* @short Sets the angle in HMS form
* @note Re-implements dms::setH() with sine/cosine caching
*/
inline void setH( const int &h, const int &m, const int &s, const int &ms=0 ) { dms::setH( h, m, s, ms ); dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
cachingdms_delta -= 2;
#endif
}
/**
* @short Sets the angle from string
* @note Re-implements dms::setFromString()
*/
inline void setFromString( const QString &s, bool isDeg = true ) { dms::setFromString( s, isDeg ); dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
cachingdms_delta -= 2;
#endif
}
/**
* @short Sets the angle in radians
*/
inline void setRadians( const double &a ) { dms::setRadians( a ); dms::SinCos( m_sin, m_cos );
#ifdef COUNT_DMS_SINCOS_CALLS
cachingdms_delta -= 2;
#endif
}
/**
* @short Sets the angle using atan2()
* @note The advantage is that we can calculate sin/cos faster because we know the tangent
*/
void setUsing_atan2( const double & y, const double & x );
/**
* @short Sets the angle using asin()
* @param sine Sine of the angle
* @note The advantage is that we can cache the sine value supplied
* @note The limited range of asin must be borne in mind
*/
void setUsing_asin( const double & sine );
/**
* @short Sets the angle using acos()
* @param cosine Cosine of the angle
* @note The advantage is that we can cache the cosine value supplied
* @note The limited range of acos must be borne in mind
*/
void setUsing_acos( const double & cosine );
/**
* @short Get the sine and cosine together
* @note Re-implements dms::SinCos()
* @note This just uses the cached values assuming that they are good
*/
inline void SinCos( double &s, double &c ) const { s = m_sin; c = m_cos;
#ifdef COUNT_DMS_SINCOS_CALLS
cachingdms_delta += 2;
#endif
}
/**
* @short Get the sine of this angle
* @note Re-implements dms::sin()
* @note This just uses the cached value assuming that it is good
*/
inline double sin() const {
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_delta;
#endif
return m_sin; }
/**
* @short Get the cosine of this angle
* @note Re-implements dms::cos()
* @note This just uses the cached value assuming that it is good
*/
inline double cos() const {
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_delta;
#endif
return m_cos; }
/**
* @short Construct an angle from the given string
* @note Re-implements dms::fromString()
*/
static CachingDms fromString( const QString &s, bool deg );
/**
* @short operator -
* @note In addition to negating the angle, we negate the sine value
*/
CachingDms operator - ();
/**
* @short Casting constructor
*/
CachingDms( const dms &angle );
private:
double m_sin, m_cos; // Cached values
/**
* @short Private constructor used to create a CachingDms with known sine and cosine
*/
explicit CachingDms( const double &degrees, const double &sine, const double &cosine )
: dms( degrees ), m_sin( sine ), m_cos( cosine ) {
#ifdef COUNT_DMS_SINCOS_CALLS
++cachingdms_constructor_calls;
#endif
}
/**
* @short Addition and subtraction operators
* @note Uses trigonometric identities to find the new trigonometric values
* @note Avoid repeated use, as the round-off errors will accumulate!
*/
friend CachingDms operator +(const CachingDms &, const CachingDms &);
friend CachingDms operator -(const CachingDms &, const CachingDms &);
#ifdef COUNT_DMS_SINCOS_CALLS
public:
static unsigned long cachingdms_constructor_calls;
static unsigned long cachingdms_delta; // difference of ( trig function calls ) - ( trig computations )
#endif
};
#endif
......@@ -350,8 +350,9 @@ public:
static long unsigned redundant_trig_function_calls; // counts number of redundant trig function calls
static double seconds_in_trig; // accumulates number of seconds spent in trig function calls
#endif
private:
protected:
double D;
private:
#ifdef COUNT_DMS_SINCOS_CALLS
mutable bool m_sinDirty, m_cosDirty, m_sinCosCalled;
#endif
......
......@@ -22,7 +22,7 @@
#include <KLocalizedString>
#include "dms.h"
#include "cachingdms.h"
#include "timezonerule.h"
#include "kstarsdatetime.h"
......@@ -75,10 +75,10 @@ public:
double TZ=0, TimeZoneRule *TZrule=NULL, bool readOnly=false, int iEllips=4 );
/** @return pointer to the longitude dms object */
const dms* lng() const { return &Longitude; }
const CachingDms* lng() const { return &Longitude; }
/** @return pointer to the latitude dms object */
const dms* lat() const { return &Latitude; }
const CachingDms* lat() const { return &Latitude; }
/** @return elevation above seal level (meters) */
double height() const { return Height; }
......@@ -263,7 +263,7 @@ public:
void setReadOnly(bool value);
private:
dms Longitude, Latitude;
CachingDms Longitude, Latitude;
QString Name, Province, Country;
TimeZoneRule *TZrule;
double TimeZone, Height;
......
......@@ -168,7 +168,7 @@ public:
Q_INVOKABLE SimClock *clock() { return &Clock; }
/** @return pointer to the local sidereal time: a dms object */
dms *lst() { return &LST; }
CachingDms *lst() { return &LST; }
/** @return pointer to the GeoLocation object*/
GeoLocation *geo() { return &m_Geo; }
......@@ -370,7 +370,7 @@ private:
//KLocale *locale;
dms LST;
CachingDms LST;
QKeySequence resumeKey;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment