Hello,
Could you please critique this code please? It's for creating sine/cosine/tan
ratio lookup tables. Any ideas on how to improve it in terms of efficiency, any
problems forseen in its use, etc?
I've ommitted various header includes and comments that I thought wouldn't do
any good to list by the way.
I realise now that the name 'BinLookup' would perhaps have been more meaningful
than 'BiLookup'...
========= 222 LINES OF CODE FOLLOW - BE WARNED ========
/** Compare d1 to d2, using error as max difference between d1 and d2
for which both are equal. */
template<typename T>
bool
compare_eq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) <= error;
}
/** Compare d1 to d2, using error as min difference between d1 and d2
for which both are not equal. */
template<typename T>
bool
compare_neq(const T& d1, const T& d2, const T& error)
{
return std::abs((d1 > d2) ? d1-d2 : d2-d1) > error;
}
/**Generates and contains trigonometric ratio lookup tables (sin, cos,
tan).
Q: "Why don't you make them static, so only one copy of them can
possibly exist?"
A: "Some platforms have different memory locations for different
things (e.g. ROM and RAM areas on consoles.) Doing it this way
allows these tables to exist in different memory locations at the
choice of the programmer. For example, they might always exist in
ROM, and only sometimes in RAM depending on the game mode that is
active.
NOTE: According to the gcc manual page (and confirmed by my own
frustrating experience), the x86 and the Motorola 68000 keep more
precision for doubles in registers they do in memory. So comparing
a looked up value to a non-looked up value with '==' is just asking
for trouble.
*/
template<typename T, uint_fast32_t units>
class Trig_Tables
{
public:
class Lookup;
class BiLookup;
/** Initialise the static triglookup tables. */
Trig_Tables()
{
init_tables_();
}
/** Because It Should Be There (tm) */
virtual
~Trig_Tables()
{
}
/** Converts the given positive argument (units in a circle)
into radians. Not optimised, so only useful for
initialisation! Note: shouldn't go over the limit (won't
be wrapped around.) */
static inline T
to_rads(uint_fast32_t arg);
/// The sin table
T sin_[units];
/// The cos table
T cos_[units];
/// The tan table
T tan_[units];
/// initialise the tables
void
init_tables_()
{
for(uint_fast32_t i = 0; i < units; ++i) {
sin_[i] = std::sin(to_rads(i));
cos_[i] = std::cos(to_rads(i));
tan_[i] = std::tan(to_rads(i));
}
}
}; // class Trig_Tables
template<typename T, uint_fast32_t units>
inline T
Trig_Tables<T, units>::to_rads(uint_fast32_t arg)
{
return static_cast<T>(arg)/static_cast<T>(units)
* static_cast<T>(2.0*M_PI);
}
/** Provides generic support for getting trig table values from a
Trig_Tables object. */
template<typename T, uint_fast32_t units>
class Trig_Tables<T, units>::Lookup
{
public:
Lookup(const Trig_Tables<T, units>& arg)
: tables_(arg)
{
}
/** Gets sin value from table WITHOUT a bounds check. */
T
sin(uint_fast32_t arg) const
{
return tables_.sin_[arg];
}
/** Gets sin value after truncating the argument to the
smallest value that measures the same angle (i.e. if
units is 360 (degrees), then if 361 is provided, actual
value gotten from sin table is sin(1). Also absolutes the
value (i.e. sin(-7) becomes -sin(7).) */
T
get_sin(int_fast32_t arg) const
{
return (arg < 0)
? -tables_.sin_[std::abs(arg)%units]
: tables_.sin_[std::abs(arg)%units];
}
/** Gets cos value from table WITHOUT a bounds check. */
T
cos(uint_fast32_t arg) const
{
return tables_.cos_[arg];
}
/** Gets cos value as get_sin (see above). Note: As cos(x) is
the same as cos(-x), this eliminates an extra check.
Groovy. eh?
@see get_sin. */
T
get_cos(int_fast32_t arg) const
{
return tables_.cos_[std::abs(arg)%units];
}
/** Gets tan value from table WITHOUT a bounds check. */
T
tan(uint_fast32_t arg) const
{
return tables_.tan_[arg];
}
/** Gets tan value as get_sin (see above).
@see get_sin. */
T
get_tan(int_fast32_t arg) const
{
return (arg < 0)
? -tables_.tan_[std::abs(arg)%units]
: tables_.tan_[std::abs(arg)%units];
}
protected:
const Trig_Tables<T, units>& tables_;
}; // class Trig_Tables::Lookup
/** Only for use with Trig_Tables whose units a power of 2, where x is
an unsigned integer. If this is NOT the case, throws a
std::logic_error, and the object is NOT created.
Provides faster access to the get_* functions of Lookup, by using
'&' to wrap the requested angles around.
What is 'wrap around'? Wrap-around is changing angle 361 to 1,
which both give the correct results. */
template<typename T, uint_fast32_t units>
class Trig_Tables<T, units>::BiLookup : public Trig_Tables<T, units>::Lookup
{
public:
/** Simply uses Lookup's default constructor with a check for
binary angle wrap-around compatibility.
@throw std::logic_error */
BiLookup(const Trig_Tables<T, units>& arg)
: Lookup(arg), mask_(units-1)
{
if( ((units&(units-1)) != 0) || units < 2)
throw std::logic_error("BiLookup used for unit "
"size not a power of 2");
}
/** Uses '&' instead of '%' to wrap around values. */
T
get_sin(int_fast32_t arg) const
{
return (arg < 0)
? -this->tables_.sin_[arg&mask_]
:this-> tables_.sin_[arg&mask_];
}
/** Uses '&' instead of '%' to wrap around values. */
T
get_cos(int_fast32_t arg) const
{
return this->tables_.cos_[arg&mask_];
}
/** Uses '&' instead of '%' to wrap around values. */
T
get_tan(int_fast32_t arg) const
{
return (arg < 0)
? -this->tables_.tan_[arg&mask_]
: this->tables_.tan_[arg&mask_];
}
protected:
const uint_fast32_t mask_;
}; // class Trig_Tables::BiLookup
========= END OF CODE ========
--
Entry in RollerCoaster Tycoon 2 readme.txt file:
RollerCoaster Tycoon2 must be played on a video card capable of 640x480 screen
resolution at a bit depth setting of 256 bits.
And the proof that playing too many strategy games causes loss of humour:
http://tinyurl.com/dyrtt