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>
matrix<S,T>::perspective (T fov, T aspect, range<T> Z)
{
CHECK_GE (Z.min, 0);
CHECK_GE (Z.max, 0);
CHECK_GE (Z.lo, 0);
CHECK_GE (Z.hi, 0);
T f = 1 / std::tan (fov / 2);
T x = f / aspect;
T y = f;
T z1 = (Z.max + Z.min) / (Z.min - Z.max);
T z2 = (2 * Z.max * Z.min) / (Z.min - Z.max);
T z1 = (Z.hi + Z.lo) / (Z.lo - Z.hi);
T z2 = (2 * Z.hi * Z.lo) / (Z.lo - Z.hi);
return { {
{ x, 0, 0, 0 },

View File

@ -21,37 +21,15 @@
#include "json/tree.hpp"
#include "maths.hpp"
#include <limits>
#include <cmath>
#include <cstdint>
///////////////////////////////////////////////////////////////////////////////
template <typename T>
const util::range<T>
util::range<T>::UNLIMITED (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>
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)
util::range<T>::range (T _lo, T _hi):
lo (_lo),
hi (_hi)
{
sanity ();
}
@ -62,7 +40,7 @@ template <typename T>
T
util::range<T>::magnitude (void) const
{
return max - min;
return hi - lo;
}
@ -71,7 +49,7 @@ template <typename T>
bool
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
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
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
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
// values should change both min and max to be that value.
min = std::min (val, min);
max = std::max (val, max);
lo = std::min (val, lo);
hi = std::max (val, hi);
}
@ -119,8 +97,8 @@ template <typename T>
util::range<T>&
util::range<T>::operator*= (T val)
{
min *= val;
max *= val;
lo *= val;
hi *= val;
return *this;
}
@ -131,7 +109,7 @@ template <typename T>
util::range<T>
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>::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
{
double pos = ::rand () / (double)(RAND_MAX);
return (max - min) * pos + min;
return (hi - lo) * pos + lo;
}
template <>
@ -159,7 +137,7 @@ namespace util {
range<float>::random (void) const
{
float pos = ::rand () / (float)(RAND_MAX);
return (max - min) * pos + min;
return (hi - lo) * pos + lo;
}
}
@ -169,7 +147,7 @@ template <typename T>
T
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
range<float>::operator ==(const range<float> &rhs) const
{
return almost_equal (min, rhs.min) &&
almost_equal (max, rhs.max);
return almost_equal (lo, rhs.lo) &&
almost_equal (hi, rhs.hi);
}
@ -188,8 +166,8 @@ namespace util {
bool
range<double>::operator ==(const range<double> &rhs) const
{
return almost_equal (min, rhs.min) &&
almost_equal (max, rhs.max);
return almost_equal (lo, rhs.lo) &&
almost_equal (hi, rhs.hi);
}
}
@ -199,7 +177,7 @@ template <typename T>
bool
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
util::range<T>::sanity (void) const
{
CHECK (min <= max);
CHECK (lo <= hi);
}
@ -218,9 +196,9 @@ namespace util {
void
range<double>::sanity (void) const
{
if (std::isnan (min) || std::isnan (max))
if (std::isnan (lo) || std::isnan (hi))
return;
CHECK (min <= max);
CHECK (lo <= hi);
}
}
@ -237,17 +215,17 @@ namespace util {
//-----------------------------------------------------------------------------
namespace json { namespace tree {
namespace json::tree {
template <>
util::range<double>
io<util::range<double>>::deserialise (const json::tree::node &node)
{
if (node.is_string () && (node == "UNIT" ||
node == "unit")) {
return util::range<double>::UNIT;
return util::range<double>::unit ();
} else if (node.is_string () && (node == "UNLIMITED" ||
node == "unlimited")) {
return util::range<double>::UNLIMITED;
return util::range<double>::unlimited ();
} else {
return {
node[0].as_number (),
@ -255,4 +233,4 @@ namespace json { namespace tree {
};
}
}
} }
}

View File

@ -28,8 +28,8 @@ namespace util {
*/
template <typename T>
struct range {
T min;
T max;
T lo;
T hi;
range (T _min, T _max);
@ -78,10 +78,10 @@ namespace util {
{ return !(*this == rhs); }
/// A range which is guaranteed to contain all elements type T
static const range<T> UNLIMITED;
static const range<T> MAX;
static constexpr range<T> unlimited (void);
static constexpr range<T> max (void);
/// 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;
};

View File

@ -20,19 +20,59 @@
#define __UTIL_RANGE_IPP
#endif
#include <limits>
#include <type_traits>
//-----------------------------------------------------------------------------
namespace util {
template <typename T>
template <typename U>
U
range<T>::normalise (T val) const {
static_assert (std::is_floating_point<U>::value,
"normalise isn't implemented for integer types");
template <typename T>
template <typename U>
U
util::range<T>::normalise (T val) const
{
static_assert (std::is_floating_point<U>::value,
"normalise isn't implemented for integer types");
return static_cast<U> (val - min) /
static_cast<U> (max - min);
}
return static_cast<U> (val - lo) /
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;
// 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 ( 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 (-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 ( 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 (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 ( 1.00001), "doesn't contain fractionally high value");
// 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 (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 (std::numeric_limits <uint16_t>::max ()), "unsigned unit doesn't contain max");
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 (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");
// 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 (+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");
// 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 (+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");
// 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>::max()), "floating unlimited contains max");
// 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>::max()), "floating unlimited contains 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");
// 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<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>::min()), "unsigned max contains min");
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
{
@ -46,8 +46,8 @@ main (int, char **)
std::numeric_limits<double>::quiet_NaN ());
initial_nan.expand (1.0);
tap.expect_eq (initial_nan.min, 1.0, "NAN expansion noop for min");
tap.expect_eq (initial_nan.max, 1.0, "NAN expansion noop for max");
tap.expect_eq (initial_nan.lo, 1.0, "NAN expansion noop for lo");
tap.expect_eq (initial_nan.hi, 1.0, "NAN expansion noop for hi");
}
return tap.status ();