range: convert static members to constexpr methods

This commit is contained in:
Danny Robson 2016-12-21 16:44:48 +11:00
parent 2dcb315ce6
commit 2e0fa64494
5 changed files with 112 additions and 94 deletions

View File

@ -261,15 +261,15 @@ template <size_t S, typename T>
matrix4<T> matrix4<T>
matrix<S,T>::perspective (T fov, T aspect, range<T> Z) matrix<S,T>::perspective (T fov, T aspect, range<T> Z)
{ {
CHECK_GE (Z.min, 0); CHECK_GE (Z.lo, 0);
CHECK_GE (Z.max, 0); CHECK_GE (Z.hi, 0);
T f = 1 / std::tan (fov / 2); T f = 1 / std::tan (fov / 2);
T x = f / aspect; T x = f / aspect;
T y = f; T y = f;
T z1 = (Z.max + Z.min) / (Z.min - Z.max); T z1 = (Z.hi + Z.lo) / (Z.lo - Z.hi);
T z2 = (2 * Z.max * Z.min) / (Z.min - Z.max); T z2 = (2 * Z.hi * Z.lo) / (Z.lo - Z.hi);
return { { return { {
{ x, 0, 0, 0 }, { x, 0, 0, 0 },

View File

@ -21,37 +21,15 @@
#include "json/tree.hpp" #include "json/tree.hpp"
#include "maths.hpp" #include "maths.hpp"
#include <limits>
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
const util::range<T> util::range<T>::range (T _lo, T _hi):
util::range<T>::UNLIMITED (std::numeric_limits<T>::has_infinity ? -std::numeric_limits<T>::infinity () : lo (_lo),
std::numeric_limits<T>::lowest (), hi (_hi)
std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity () :
std::numeric_limits<T>::max ());
//-----------------------------------------------------------------------------
template <typename T>
const util::range<T>
util::range<T>::MAX (std::numeric_limits<T>::lowest (),
std::numeric_limits<T>::max ());
//-----------------------------------------------------------------------------
template <typename T>
const util::range<T>
util::range<T>::UNIT (0.0, 1.0);
///////////////////////////////////////////////////////////////////////////////
template <typename T>
util::range<T>::range (T _min, T _max):
min (_min),
max (_max)
{ {
sanity (); sanity ();
} }
@ -62,7 +40,7 @@ template <typename T>
T T
util::range<T>::magnitude (void) const util::range<T>::magnitude (void) const
{ {
return max - min; return hi - lo;
} }
@ -71,7 +49,7 @@ template <typename T>
bool bool
util::range<T>::contains (T val) const util::range<T>::contains (T val) const
{ {
return val >= min && val <= max; return val >= lo && val <= hi;
} }
@ -80,7 +58,7 @@ template <typename T>
bool bool
util::range<T>::contains (const range <T> &r) const util::range<T>::contains (const range <T> &r) const
{ {
return r.min >= min && r.max <= max; return r.lo >= lo && r.hi <= hi;
} }
@ -89,7 +67,7 @@ template <typename T>
T T
util::range<T>::at (float t) const util::range<T>::at (float t) const
{ {
return static_cast<T> (min + (max - min) * t); return static_cast<T> (lo + (hi - lo) * t);
} }
@ -98,7 +76,7 @@ template <typename T>
T T
util::range<T>::clamp (T val) const util::range<T>::clamp (T val) const
{ {
return std::max (min, std::min (val, max)); return std::max (lo, std::min (val, hi));
} }
@ -109,8 +87,8 @@ util::range<T>::expand (T val)
{ {
// The arguments to min and max are such that expansion from initial NaN // The arguments to min and max are such that expansion from initial NaN
// values should change both min and max to be that value. // values should change both min and max to be that value.
min = std::min (val, min); lo = std::min (val, lo);
max = std::max (val, max); hi = std::max (val, hi);
} }
@ -119,8 +97,8 @@ template <typename T>
util::range<T>& util::range<T>&
util::range<T>::operator*= (T val) util::range<T>::operator*= (T val)
{ {
min *= val; lo *= val;
max *= val; hi *= val;
return *this; return *this;
} }
@ -131,7 +109,7 @@ template <typename T>
util::range<T> util::range<T>
util::range<T>::operator* (T val) const util::range<T>::operator* (T val) const
{ {
return util::range<T> (min * val, max * val); return util::range<T> (lo * val, hi * val);
} }
@ -140,7 +118,7 @@ template <typename T>
util::range<T> util::range<T>
util::range<T>::operator- (T val) const util::range<T>::operator- (T val) const
{ {
return util::range<T> (min - val, max - val); return util::range<T> (lo - val, hi - val);
} }
@ -151,7 +129,7 @@ namespace util {
range<double>::random (void) const range<double>::random (void) const
{ {
double pos = ::rand () / (double)(RAND_MAX); double pos = ::rand () / (double)(RAND_MAX);
return (max - min) * pos + min; return (hi - lo) * pos + lo;
} }
template <> template <>
@ -159,7 +137,7 @@ namespace util {
range<float>::random (void) const range<float>::random (void) const
{ {
float pos = ::rand () / (float)(RAND_MAX); float pos = ::rand () / (float)(RAND_MAX);
return (max - min) * pos + min; return (hi - lo) * pos + lo;
} }
} }
@ -169,7 +147,7 @@ template <typename T>
T T
util::range<T>::random (void) const util::range<T>::random (void) const
{ {
return min + (T)::rand () % (max - min); return lo + (T)::rand () % (hi - lo);
} }
@ -179,8 +157,8 @@ namespace util {
bool bool
range<float>::operator ==(const range<float> &rhs) const range<float>::operator ==(const range<float> &rhs) const
{ {
return almost_equal (min, rhs.min) && return almost_equal (lo, rhs.lo) &&
almost_equal (max, rhs.max); almost_equal (hi, rhs.hi);
} }
@ -188,8 +166,8 @@ namespace util {
bool bool
range<double>::operator ==(const range<double> &rhs) const range<double>::operator ==(const range<double> &rhs) const
{ {
return almost_equal (min, rhs.min) && return almost_equal (lo, rhs.lo) &&
almost_equal (max, rhs.max); almost_equal (hi, rhs.hi);
} }
} }
@ -199,7 +177,7 @@ template <typename T>
bool bool
util::range<T>::operator ==(const util::range<T> &rhs) const util::range<T>::operator ==(const util::range<T> &rhs) const
{ {
return min == rhs.min && max == rhs.max; return lo == rhs.lo && hi == rhs.hi;
} }
@ -208,7 +186,7 @@ template <typename T>
void void
util::range<T>::sanity (void) const util::range<T>::sanity (void) const
{ {
CHECK (min <= max); CHECK (lo <= hi);
} }
@ -218,9 +196,9 @@ namespace util {
void void
range<double>::sanity (void) const range<double>::sanity (void) const
{ {
if (std::isnan (min) || std::isnan (max)) if (std::isnan (lo) || std::isnan (hi))
return; return;
CHECK (min <= max); CHECK (lo <= hi);
} }
} }
@ -237,17 +215,17 @@ namespace util {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
namespace json { namespace tree { namespace json::tree {
template <> template <>
util::range<double> util::range<double>
io<util::range<double>>::deserialise (const json::tree::node &node) io<util::range<double>>::deserialise (const json::tree::node &node)
{ {
if (node.is_string () && (node == "UNIT" || if (node.is_string () && (node == "UNIT" ||
node == "unit")) { node == "unit")) {
return util::range<double>::UNIT; return util::range<double>::unit ();
} else if (node.is_string () && (node == "UNLIMITED" || } else if (node.is_string () && (node == "UNLIMITED" ||
node == "unlimited")) { node == "unlimited")) {
return util::range<double>::UNLIMITED; return util::range<double>::unlimited ();
} else { } else {
return { return {
node[0].as_number (), node[0].as_number (),
@ -255,4 +233,4 @@ namespace json { namespace tree {
}; };
} }
} }
} } }

View File

@ -28,8 +28,8 @@ namespace util {
*/ */
template <typename T> template <typename T>
struct range { struct range {
T min; T lo;
T max; T hi;
range (T _min, T _max); range (T _min, T _max);
@ -78,10 +78,10 @@ namespace util {
{ return !(*this == rhs); } { return !(*this == rhs); }
/// A range which is guaranteed to contain all elements type T /// A range which is guaranteed to contain all elements type T
static const range<T> UNLIMITED; static constexpr range<T> unlimited (void);
static const range<T> MAX; static constexpr range<T> max (void);
/// A range which only contains elements between 0 and 1 inclusive /// A range which only contains elements between 0 and 1 inclusive
static const range<T> UNIT; static constexpr range<T> unit (void);
void sanity (void) const; void sanity (void) const;
}; };

View File

@ -20,19 +20,59 @@
#define __UTIL_RANGE_IPP #define __UTIL_RANGE_IPP
#endif #endif
#include <limits>
#include <type_traits> #include <type_traits>
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
namespace util { template <typename T>
template <typename T> template <typename U>
template <typename U> U
U util::range<T>::normalise (T val) const
range<T>::normalise (T val) const { {
static_assert (std::is_floating_point<U>::value, static_assert (std::is_floating_point<U>::value,
"normalise isn't implemented for integer types"); "normalise isn't implemented for integer types");
return static_cast<U> (val - min) / return static_cast<U> (val - lo) /
static_cast<U> (max - min); static_cast<U> ( hi - lo);
} }
///////////////////////////////////////////////////////////////////////////////
template <typename T>
constexpr
util::range<T>
util::range<T>::unlimited (void)
{
return {
std::numeric_limits<T>::has_infinity ? -std::numeric_limits<T>::infinity () :
std::numeric_limits<T>::lowest (),
std::numeric_limits<T>::has_infinity ? std::numeric_limits<T>::infinity () :
std::numeric_limits<T>::max ()
};
}
//-----------------------------------------------------------------------------
template <typename T>
constexpr
util::range<T>
util::range<T>::max (void)
{
return {
std::numeric_limits<T>::lowest (),
std::numeric_limits<T>::max ()
};
}
//-----------------------------------------------------------------------------
template <typename T>
constexpr
util::range<T>
util::range<T>::unit (void)
{
return {
T {0}, T {1}
};
} }

View File

@ -11,34 +11,34 @@ main (int, char **)
util::TAP::logger tap; util::TAP::logger tap;
// Check some simple cases close to the edges of a unit range. Tests float rounding. // Check some simple cases close to the edges of a unit range. Tests float rounding.
tap.expect ( util::range<double>::UNIT.contains ( 0.0), "floating unit contains 0"); tap.expect ( util::range<double>::unit ().contains ( 0.0), "floating unit contains 0");
tap.expect ( util::range<double>::UNIT.contains ( 1.0), "floating unit contains 1"); tap.expect ( util::range<double>::unit ().contains ( 1.0), "floating unit contains 1");
tap.expect ( util::range<double>::UNIT.contains (std::numeric_limits<double>::min ()), "floating unit contains min"); tap.expect ( util::range<double>::unit ().contains (std::numeric_limits<double>::min ()), "floating unit contains min");
tap.expect (!util::range<double>::UNIT.contains (-0.00001), "doesn't contain fractionally low value"); tap.expect (!util::range<double>::unit ().contains (-0.00001), "doesn't contain fractionally low value");
tap.expect (!util::range<double>::UNIT.contains ( 1.00001), "doesn't contain fractionally high value"); tap.expect (!util::range<double>::unit ().contains ( 1.00001), "doesn't contain fractionally high value");
// Check edge cases of unit with integer values // Check edge cases of unit with integer values
tap.expect ( util::range<uint16_t>::UNIT.contains (0), "unsigned unit contains 0"); tap.expect ( util::range<uint16_t>::unit ().contains (0), "unsigned unit contains 0");
tap.expect ( util::range<uint16_t>::UNIT.contains (1), "unsigned unit contains 1"); tap.expect ( util::range<uint16_t>::unit ().contains (1), "unsigned unit contains 1");
tap.expect (!util::range<uint16_t>::UNIT.contains (2), "unsigned unit doesn't contain 2"); tap.expect (!util::range<uint16_t>::unit ().contains (2), "unsigned unit doesn't contain 2");
tap.expect (!util::range<uint16_t>::UNIT.contains (std::numeric_limits <uint16_t>::max ()), "unsigned unit doesn't contain max"); tap.expect (!util::range<uint16_t>::unit ().contains (std::numeric_limits <uint16_t>::max ()), "unsigned unit doesn't contain max");
// Check the inclusivity of UNLIMITED with special floating values // Check the inclusivity of unlimited with special floating values
tap.expect ( util::range<double>::UNLIMITED.contains (0.0), "floating unlimited contains 0"); tap.expect ( util::range<double>::unlimited ().contains (0.0), "floating unlimited contains 0");
tap.expect ( util::range<double>::UNLIMITED.contains (+std::numeric_limits<double>::infinity ()), "floating unlimited contains +INF"); tap.expect ( util::range<double>::unlimited ().contains (+std::numeric_limits<double>::infinity ()), "floating unlimited contains +INF");
tap.expect ( util::range<double>::UNLIMITED.contains (-std::numeric_limits<double>::infinity ()), "floating unlimited contains -INF"); tap.expect ( util::range<double>::unlimited ().contains (-std::numeric_limits<double>::infinity ()), "floating unlimited contains -INF");
tap.expect (!util::range<double>::UNLIMITED.contains ( std::numeric_limits<double>::quiet_NaN ()), "floating unlimited doesn't contain NAN"); tap.expect (!util::range<double>::unlimited ().contains ( std::numeric_limits<double>::quiet_NaN ()), "floating unlimited doesn't contain NAN");
// Check the inclusivity of UNLIMITED with some large numbers // Check the inclusivity of unlimited with some large numbers
tap.expect ( util::range<uint16_t>::UNLIMITED.contains (std::numeric_limits<uint16_t>::min()), "floating unlimited contains min"); tap.expect ( util::range<uint16_t>::unlimited ().contains (std::numeric_limits<uint16_t>::min()), "floating unlimited contains min");
tap.expect ( util::range<uint16_t>::UNLIMITED.contains (std::numeric_limits<uint16_t>::max()), "floating unlimited contains max"); tap.expect ( util::range<uint16_t>::unlimited ().contains (std::numeric_limits<uint16_t>::max()), "floating unlimited contains max");
// Check inclusivity of MAX // Check inclusivity of max
tap.expect (!util::range<double>::MAX.contains ( std::numeric_limits<double>::infinity ()), "floating max contains +INF"); tap.expect (!util::range<double>::max ().contains ( std::numeric_limits<double>::infinity ()), "floating max contains +INF");
tap.expect (!util::range<double>::MAX.contains (-std::numeric_limits<double>::infinity ()), "floating max contains -INF"); tap.expect (!util::range<double>::max ().contains (-std::numeric_limits<double>::infinity ()), "floating max contains -INF");
tap.expect ( util::range<uint16_t>::MAX.contains (std::numeric_limits<uint16_t>::min()), "unsigned max contains min"); tap.expect ( util::range<uint16_t>::max ().contains (std::numeric_limits<uint16_t>::min()), "unsigned max contains min");
tap.expect ( util::range<uint16_t>::MAX.contains (std::numeric_limits<uint16_t>::max()), "unsigned max contains max"); tap.expect ( util::range<uint16_t>::max ().contains (std::numeric_limits<uint16_t>::max()), "unsigned max contains max");
// Check that expansion via NaN is a noop // Check that expansion via NaN is a noop
{ {
@ -46,8 +46,8 @@ main (int, char **)
std::numeric_limits<double>::quiet_NaN ()); std::numeric_limits<double>::quiet_NaN ());
initial_nan.expand (1.0); initial_nan.expand (1.0);
tap.expect_eq (initial_nan.min, 1.0, "NAN expansion noop for min"); tap.expect_eq (initial_nan.lo, 1.0, "NAN expansion noop for lo");
tap.expect_eq (initial_nan.max, 1.0, "NAN expansion noop for max"); tap.expect_eq (initial_nan.hi, 1.0, "NAN expansion noop for hi");
} }
return tap.status (); return tap.status ();