geom: remove template templates from geom::sampler

This commit is contained in:
Danny Robson 2018-11-19 15:36:29 +11:00
parent e26165cea9
commit 210c963d9f
4 changed files with 142 additions and 99 deletions

View File

@ -3,12 +3,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* Copyright 2015-2017 Danny Robson <danny@nerdcruft.net> * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#pragma once
#ifndef CRUFT_UTIL_GEOM_AABB_HPP
#define CRUFT_UTIL_GEOM_AABB_HPP
#include "../debug.hpp" #include "../debug.hpp"
#include "../extent.hpp" #include "../extent.hpp"
@ -16,6 +14,7 @@
#include <cstdint> #include <cstdint>
namespace cruft::geom { namespace cruft::geom {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// represents an axis-aligned bounding-box through two opposing corners. /// represents an axis-aligned bounding-box through two opposing corners.
@ -144,19 +143,18 @@ namespace cruft::geom {
#include <random> #include <random>
namespace cruft::geom { namespace cruft::geom {
template <size_t S, typename T, typename G> template <size_t S, typename T>
struct sampler<S,T,aabb,G> { struct sampler<aabb<S,T>> {
static point<S,T> template <typename GeneratorT>
fn (aabb<S,T> b, G &g) static auto
eval (aabb<S,T> shape, GeneratorT &g)
{ {
std::uniform_real_distribution<T> d; point<S,T> res;
point<S,T> p; for (size_t i = 0; i < S; ++i)
std::generate (p.begin (), p.end (), [&] (void) { return d (g); }); res[i] = cruft::random::uniform (shape.lo[i], shape.hi[i], g);
return p * (b.hi - b.lo) + b.lo.template as<cruft::vector> (); return res;
} }
}; };
} }
#endif

View File

@ -6,8 +6,7 @@
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net> * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef __UTIL_GEOM_ELLIPSE_HPP #pragma once
#define __UTIL_GEOM_ELLIPSE_HPP
#include "fwd.hpp" #include "fwd.hpp"
@ -145,22 +144,54 @@ namespace cruft::geom {
#include <random> #include <random>
namespace cruft::geom { namespace cruft::geom {
template <typename T, template <size_t,typename> class K, typename G> /// Specialisation for uniform sampling of ellipses
struct sampler<2,T,K,G> template <typename T>
struct sampler<ellipse<2,T>>
{ {
static cruft::point<2,T> fn (K<2,T> k, G &g) /// Generate a random point within the ellipse.
template <typename GeneratorT>
static auto
eval (ellipse<2,T> shape, GeneratorT &&g)
{ {
std::uniform_real_distribution<T> dist; // We use a two step process:
// * Generate a point within a unit sphere
// * Transform the point to an ellipse.
float phi = dist (g) * 2 * pi<T>; // TODO: We assume floating point for the time being because it
float rho = std::sqrt (dist (g)); // simplifies interaction with trig routines. There's no
// intrinsic reason for this limitation though.
static_assert (
std::is_floating_point_v<T>,
"The current implementation assumes floating point."
);
return cruft::point<2,T> { // Choose a direction and a distance within the unit circle.
//
// `sqrt` of the distance is used to ensure a uniform
// distribution.
T phi = random::uniform<T> (g) * 2 * pi<T>;
T rho = std::sqrt (random::uniform<T> (g));
cruft::point2<T> const circle_pos {
std::cos (phi), std::cos (phi),
std::sin (phi) std::sin (phi)
} * rho * k.radius + k.origin.template as<cruft::vector> (); };
auto const offset = circle_pos * rho * shape.radius;
return shape.origin + offset.template as<cruft::vector> ();
} }
}; };
}
#if 0
// TODO: We should implement a higher dimensional ellipsoid sampler for
// efficiency gains over rejection sampling that we currently use.
template <typename T>
struct sampler<ellipse<3,T>>
{
template <typename GeneratorT>
static cruft::point<3,T>
eval (ellipse<3,T>, GeneratorT&&);
};
#endif #endif
}

View File

@ -6,17 +6,18 @@
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net> * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef __UTIL_GEOM_SAMPLE_HPP #pragma once
#define __UTIL_GEOM_SAMPLE_HPP
#include "../coord/fwd.hpp" #include "../coord/fwd.hpp"
#include "../extent.hpp" #include "../extent.hpp"
#include "../random.hpp"
#include "ops.hpp" #include "ops.hpp"
#include <cstddef> #include <cstddef>
#include <random> #include <random>
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
namespace cruft::geom { namespace cruft::geom {
/// a function object that selects a uniformly random point inside a shape /// a function object that selects a uniformly random point inside a shape
@ -26,26 +27,19 @@ namespace cruft::geom {
/// may be specialised for arbitrary shapes but uses rejection sampling /// may be specialised for arbitrary shapes but uses rejection sampling
/// as a safe default. this implies that execution may not take a constant /// as a safe default. this implies that execution may not take a constant
/// time. /// time.
/// template <typename ShapeT>
/// \tparam S coordinate type dimension
/// \tparam T value type for the shape/coordinate
/// \tparam K the shape type to test
/// \tparam G a UniformRandomBitGenerator, eg std::min19937
template <
size_t S,
typename T,
template <size_t,typename> class K,
typename G
>
struct sampler { struct sampler {
static point<S,T> /// Returns a point which lies within the supplied shape, inclusive
fn (K<S,T> k, G &g) /// of borders.
template <typename GeneratorT>
static auto
eval (ShapeT const &shape, GeneratorT &&g)
{ {
auto b = bounds (k); auto b = bounds (shape);
while (true) { while (true) {
auto p = sample (b, g); auto p = sample (b, g);
if (intersects (k, p)) if (intersects (shape, p))
return p; return p;
} }
} }
@ -56,15 +50,13 @@ namespace cruft::geom {
/// a convenience function that calls sample::fn to select a random point /// a convenience function that calls sample::fn to select a random point
/// in a provided shape. /// in a provided shape.
template < template <
size_t S, typename ShapeT,
typename T, typename GeneratorT // random generator
template <size_t,typename> class K,
typename G // random generator
> >
point<S,T> auto
sample (K<S,T> k, G &g) sample (ShapeT const &shape, GeneratorT &&gen)
{ {
return sampler<S,T,K,G>::fn (k, g); return sampler<ShapeT>::eval (shape, std::forward<GeneratorT> (gen));
} }
@ -95,14 +87,8 @@ namespace cruft::geom {
{ {
cruft::point<S,T> p; cruft::point<S,T> p;
for (size_t i = 0; i < S; ++i) { for (size_t i = 0; i < S; ++i)
if constexpr (std::is_floating_point_v<T>) { p[i] = random::uniform (T{0}, target[i], gen);
p[i] = std::uniform_real_distribution<T> (0, target[i]) (gen);
} else {
CHECK_GE (target[i], T{1});
p[i] = std::uniform_int_distribution<T> (0, target[i] - 1) (gen);
}
}
return p; return p;
} }
@ -182,5 +168,3 @@ namespace cruft::geom {
} }
} }
} }
#endif

View File

@ -3,11 +3,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* Copyright 2016-2017 Danny Robson <danny@nerdcruft.net> * Copyright 2016-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef CRUFT_UTIL_RANDOM_HPP #pragma once
#define CRUFT_UTIL_RANDOM_HPP
#include "coord/traits.hpp" #include "coord/traits.hpp"
@ -19,9 +18,16 @@
namespace cruft::random { namespace cruft::random {
template <typename T> /// A trait describing the internal state size of a given Generator.
///
/// The structure must be specialised.
///
/// \tparam GeneratorT The generator to query
template <typename GeneratorT>
struct state_size; struct state_size;
//-------------------------------------------------------------------------
template < template <
class UIntType, class UIntType,
size_t w, size_t n, size_t m, size_t r, size_t w, size_t n, size_t m, size_t r,
@ -32,17 +38,24 @@ namespace cruft::random {
static constexpr auto value = n * sizeof (UIntType); static constexpr auto value = n * sizeof (UIntType);
}; };
//-------------------------------------------------------------------------
template <typename UIntType, UIntType a, UIntType c, UIntType m> template <typename UIntType, UIntType a, UIntType c, UIntType m>
struct state_size<std::linear_congruential_engine<UIntType,a,c,m>> { struct state_size<std::linear_congruential_engine<UIntType,a,c,m>> {
static constexpr auto value = sizeof (UIntType); static constexpr auto value = sizeof (UIntType);
}; };
//-------------------------------------------------------------------------
template <typename T> template <typename T>
constexpr auto state_size_v = state_size<T>::value; constexpr auto state_size_v = state_size<T>::value;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// return correctly initialised thread-local generator of an unspecified, /// Returns a correctly pre-initialised reference to a thread-local
/// but not entirely useless, type. ie, not LCG. /// generator of an unspecified (but not entirely useless) type.
///
/// ie, not LCG.
inline auto& inline auto&
generator (void) generator (void)
{ {
@ -61,72 +74,91 @@ namespace cruft::random {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// select a value uniformly from the range [lo, hi) /// A convenience typedef that selects between
template <typename T> /// std::uniform_real_distribution and std::uniform_int_distribution
std::enable_if_t<std::is_floating_point_v<T>,T> /// depending on the supplied value type.
uniform (T lo, T hi) template <typename ValueT>
{ using uniform_distribution = std::conditional_t<
return std::uniform_real_distribution<T> { lo, hi } (generator ()); std::is_floating_point<ValueT>::value,
} std::uniform_real_distribution<ValueT>,
std::uniform_int_distribution<ValueT>
>;
template <typename T> ///////////////////////////////////////////////////////////////////////////
std::enable_if_t<std::is_floating_point_v<T>,T> /// Returns a value chosen uniformly at random the supplied range.
uniform (void) ///
/// This is primarily a convenience helper around the uniform_distribution
/// type.
template <typename ValueT, typename GeneratorT>
auto
uniform (ValueT lo, ValueT hi, GeneratorT &&gen)
{ {
return uniform<T> (0, 1); return uniform_distribution<ValueT> { lo, hi } (gen);
} }
///------------------------------------------------------------------------ ///------------------------------------------------------------------------
/// select a value uniformly from the range [lo, hi) using a supplied /// Return a value uniformly random chosen value between lo and hi.
/// generator template <typename T>
auto
uniform (T lo, T hi)
{
return uniform<T> (lo, hi, generator ());
}
///------------------------------------------------------------------------
/// Return a uniformly random value chosen on the interval [0,1)
template < template <
typename T, typename ValueT,
typename GeneratorT, typename GeneratorT,
typename = std::enable_if_t<std::is_integral_v<T>> typename = std::enable_if_t<std::is_floating_point_v<ValueT>>
> >
T auto uniform (GeneratorT &&gen)
uniform (T lo, T hi, GeneratorT &&gen)
{ {
return std::uniform_int_distribution<T> { lo, hi } (gen); return uniform<ValueT> (ValueT{0}, ValueT{1}, std::forward<GeneratorT> (gen));
} }
///------------------------------------------------------------------------ ///------------------------------------------------------------------------
/// select a value uniformly from the range [lo, hi) /// Return a uniformly random chosen value on the interval [0.f, 1.f)
template <typename T> template <
std::enable_if_t<std::is_integral_v<T>,T> typename ValueT,
uniform (T lo, T hi) typename = std::enable_if_t<std::is_floating_point_v<ValueT>>
>
auto uniform (void)
{ {
return uniform (lo, hi, generator ()); return uniform<ValueT> (ValueT{0}, ValueT{1}, generator ());
} }
//------------------------------------------------------------------------- ///------------------------------------------------------------------------
/// Return a uniformly random chosen value on the interval [0, 1]
template <typename T> template <typename T>
std::enable_if_t<std::is_integral_v<T>,T> std::enable_if_t<std::is_integral_v<T>,T>
uniform (void) uniform (void)
{ {
return uniform ( return uniform<T> (
std::numeric_limits<T>::min (), std::numeric_limits<T>::min (),
std::numeric_limits<T>::max () std::numeric_limits<T>::max ()
); );
} }
//------------------------------------------------------------------------- ///------------------------------------------------------------------------
/// Returns a uniformly random initialised coordinate type by value.
template < template <
typename T, typename ValueT,
typename = std::enable_if_t< typename = std::enable_if_t<
is_coord_v<T> && std::is_floating_point_v<typename T::value_type> is_coord_v<ValueT> && std::is_floating_point_v<typename ValueT::value_type>
> >
> >
T ValueT
uniform (void) uniform (void)
{ {
T res {}; ValueT res {};
std::fill (res.begin (), res.end (), uniform<typename T::value_type> ()); std::fill (res.begin (), res.end (), uniform<typename ValueT::value_type> ());
return res; return res;
} }
@ -141,5 +173,3 @@ namespace cruft::random {
return t[dist (generator ())]; return t[dist (generator ())];
} }
}; };
#endif