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
* 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>
*/
#ifndef CRUFT_UTIL_GEOM_AABB_HPP
#define CRUFT_UTIL_GEOM_AABB_HPP
#pragma once
#include "../debug.hpp"
#include "../extent.hpp"
@ -16,6 +14,7 @@
#include <cstdint>
namespace cruft::geom {
///////////////////////////////////////////////////////////////////////////
/// represents an axis-aligned bounding-box through two opposing corners.
@ -144,19 +143,18 @@ namespace cruft::geom {
#include <random>
namespace cruft::geom {
template <size_t S, typename T, typename G>
struct sampler<S,T,aabb,G> {
static point<S,T>
fn (aabb<S,T> b, G &g)
template <size_t S, typename T>
struct sampler<aabb<S,T>> {
template <typename GeneratorT>
static auto
eval (aabb<S,T> shape, GeneratorT &g)
{
std::uniform_real_distribution<T> d;
point<S,T> res;
point<S,T> p;
std::generate (p.begin (), p.end (), [&] (void) { return d (g); });
for (size_t i = 0; i < S; ++i)
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>
*/
#ifndef __UTIL_GEOM_ELLIPSE_HPP
#define __UTIL_GEOM_ELLIPSE_HPP
#pragma once
#include "fwd.hpp"
@ -145,22 +144,54 @@ namespace cruft::geom {
#include <random>
namespace cruft::geom {
template <typename T, template <size_t,typename> class K, typename G>
struct sampler<2,T,K,G>
/// Specialisation for uniform sampling of ellipses
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>;
float rho = std::sqrt (dist (g));
// TODO: We assume floating point for the time being because it
// 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::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
}

View File

@ -6,17 +6,18 @@
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_GEOM_SAMPLE_HPP
#define __UTIL_GEOM_SAMPLE_HPP
#pragma once
#include "../coord/fwd.hpp"
#include "../extent.hpp"
#include "../random.hpp"
#include "ops.hpp"
#include <cstddef>
#include <random>
///////////////////////////////////////////////////////////////////////////////
namespace cruft::geom {
/// 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
/// as a safe default. this implies that execution may not take a constant
/// time.
///
/// \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
>
template <typename ShapeT>
struct sampler {
static point<S,T>
fn (K<S,T> k, G &g)
/// Returns a point which lies within the supplied shape, inclusive
/// of borders.
template <typename GeneratorT>
static auto
eval (ShapeT const &shape, GeneratorT &&g)
{
auto b = bounds (k);
auto b = bounds (shape);
while (true) {
auto p = sample (b, g);
if (intersects (k, p))
if (intersects (shape, p))
return p;
}
}
@ -56,15 +50,13 @@ namespace cruft::geom {
/// a convenience function that calls sample::fn to select a random point
/// in a provided shape.
template <
size_t S,
typename T,
template <size_t,typename> class K,
typename G // random generator
typename ShapeT,
typename GeneratorT // random generator
>
point<S,T>
sample (K<S,T> k, G &g)
auto
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;
for (size_t i = 0; i < S; ++i) {
if constexpr (std::is_floating_point_v<T>) {
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);
}
}
for (size_t i = 0; i < S; ++i)
p[i] = random::uniform (T{0}, target[i], gen);
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
* 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
#define CRUFT_UTIL_RANDOM_HPP
#pragma once
#include "coord/traits.hpp"
@ -19,9 +18,16 @@
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;
//-------------------------------------------------------------------------
template <
class UIntType,
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);
};
//-------------------------------------------------------------------------
template <typename UIntType, UIntType a, UIntType c, UIntType m>
struct state_size<std::linear_congruential_engine<UIntType,a,c,m>> {
static constexpr auto value = sizeof (UIntType);
};
//-------------------------------------------------------------------------
template <typename T>
constexpr auto state_size_v = state_size<T>::value;
///////////////////////////////////////////////////////////////////////////
/// return correctly initialised thread-local generator of an unspecified,
/// but not entirely useless, type. ie, not LCG.
/// Returns a correctly pre-initialised reference to a thread-local
/// generator of an unspecified (but not entirely useless) type.
///
/// ie, not LCG.
inline auto&
generator (void)
{
@ -61,72 +74,91 @@ namespace cruft::random {
///////////////////////////////////////////////////////////////////////////
/// select a value uniformly from the range [lo, hi)
template <typename T>
std::enable_if_t<std::is_floating_point_v<T>,T>
uniform (T lo, T hi)
{
return std::uniform_real_distribution<T> { lo, hi } (generator ());
}
/// A convenience typedef that selects between
/// std::uniform_real_distribution and std::uniform_int_distribution
/// depending on the supplied value type.
template <typename ValueT>
using uniform_distribution = std::conditional_t<
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>
uniform (void)
///////////////////////////////////////////////////////////////////////////
/// Returns a value chosen uniformly at random the supplied range.
///
/// 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
/// generator
/// Return a value uniformly random chosen value between lo and hi.
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 <
typename T,
typename ValueT,
typename GeneratorT,
typename = std::enable_if_t<std::is_integral_v<T>>
typename = std::enable_if_t<std::is_floating_point_v<ValueT>>
>
T
uniform (T lo, T hi, GeneratorT &&gen)
auto uniform (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)
template <typename T>
std::enable_if_t<std::is_integral_v<T>,T>
uniform (T lo, T hi)
/// Return a uniformly random chosen value on the interval [0.f, 1.f)
template <
typename ValueT,
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>
std::enable_if_t<std::is_integral_v<T>,T>
uniform (void)
{
return uniform (
return uniform<T> (
std::numeric_limits<T>::min (),
std::numeric_limits<T>::max ()
);
}
//-------------------------------------------------------------------------
///------------------------------------------------------------------------
/// Returns a uniformly random initialised coordinate type by value.
template <
typename T,
typename ValueT,
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)
{
T res {};
std::fill (res.begin (), res.end (), uniform<typename T::value_type> ());
ValueT res {};
std::fill (res.begin (), res.end (), uniform<typename ValueT::value_type> ());
return res;
}
@ -141,5 +173,3 @@ namespace cruft::random {
return t[dist (generator ())];
}
};
#endif