From 210c963d9f6366ebfbdd63ffea6edf74db3ea117 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Mon, 19 Nov 2018 15:36:29 +1100 Subject: [PATCH] geom: remove template templates from geom::sampler --- geom/aabb.hpp | 26 ++++++------ geom/ellipse.hpp | 53 ++++++++++++++++++----- geom/sample.hpp | 54 +++++++++--------------- random.hpp | 108 ++++++++++++++++++++++++++++++----------------- 4 files changed, 142 insertions(+), 99 deletions(-) diff --git a/geom/aabb.hpp b/geom/aabb.hpp index 4906b518..b7a6c3ad 100644 --- a/geom/aabb.hpp +++ b/geom/aabb.hpp @@ -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 + * Copyright 2015-2018 Danny Robson */ - -#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 + namespace cruft::geom { /////////////////////////////////////////////////////////////////////////// /// represents an axis-aligned bounding-box through two opposing corners. @@ -144,19 +143,18 @@ namespace cruft::geom { #include namespace cruft::geom { - template - struct sampler { - static point - fn (aabb b, G &g) + template + struct sampler> { + template + static auto + eval (aabb shape, GeneratorT &g) { - std::uniform_real_distribution d; + point res; - point 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 (); + return res; } }; } - -#endif diff --git a/geom/ellipse.hpp b/geom/ellipse.hpp index 86ca89ec..c98e2164 100644 --- a/geom/ellipse.hpp +++ b/geom/ellipse.hpp @@ -6,8 +6,7 @@ * Copyright 2015-2018 Danny Robson */ -#ifndef __UTIL_GEOM_ELLIPSE_HPP -#define __UTIL_GEOM_ELLIPSE_HPP +#pragma once #include "fwd.hpp" @@ -145,22 +144,54 @@ namespace cruft::geom { #include namespace cruft::geom { - template class K, typename G> - struct sampler<2,T,K,G> + /// Specialisation for uniform sampling of ellipses + template + struct sampler> { - static cruft::point<2,T> fn (K<2,T> k, G &g) + /// Generate a random point within the ellipse. + template + static auto + eval (ellipse<2,T> shape, GeneratorT &&g) { - std::uniform_real_distribution 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; - 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, + "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 (g) * 2 * pi; + T rho = std::sqrt (random::uniform (g)); + + cruft::point2 const circle_pos { std::cos (phi), std::sin (phi) - } * rho * k.radius + k.origin.template as (); + }; + + auto const offset = circle_pos * rho * shape.radius; + return shape.origin + offset.template as (); } }; -} + +#if 0 + // TODO: We should implement a higher dimensional ellipsoid sampler for + // efficiency gains over rejection sampling that we currently use. + template + struct sampler> + { + template + static cruft::point<3,T> + eval (ellipse<3,T>, GeneratorT&&); + }; #endif +} diff --git a/geom/sample.hpp b/geom/sample.hpp index 199fa47f..345ea5a8 100644 --- a/geom/sample.hpp +++ b/geom/sample.hpp @@ -6,17 +6,18 @@ * Copyright 2015-2018 Danny Robson */ -#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 - #include + /////////////////////////////////////////////////////////////////////////////// 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 class K, - typename G - > + template struct sampler { - static point - fn (K k, G &g) + /// Returns a point which lies within the supplied shape, inclusive + /// of borders. + template + 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 class K, - typename G // random generator + typename ShapeT, + typename GeneratorT // random generator > - point - sample (K k, G &g) + auto + sample (ShapeT const &shape, GeneratorT &&gen) { - return sampler::fn (k, g); + return sampler::eval (shape, std::forward (gen)); } @@ -95,14 +87,8 @@ namespace cruft::geom { { cruft::point p; - for (size_t i = 0; i < S; ++i) { - if constexpr (std::is_floating_point_v) { - p[i] = std::uniform_real_distribution (0, target[i]) (gen); - } else { - CHECK_GE (target[i], T{1}); - p[i] = std::uniform_int_distribution (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 diff --git a/random.hpp b/random.hpp index 98f3d9be..1bc5267f 100644 --- a/random.hpp +++ b/random.hpp @@ -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 + * Copyright 2016-2018 Danny Robson */ -#ifndef CRUFT_UTIL_RANDOM_HPP -#define CRUFT_UTIL_RANDOM_HPP +#pragma once #include "coord/traits.hpp" @@ -19,9 +18,16 @@ namespace cruft::random { - template + /// A trait describing the internal state size of a given Generator. + /// + /// The structure must be specialised. + /// + /// \tparam GeneratorT The generator to query + template 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 struct state_size> { static constexpr auto value = sizeof (UIntType); }; + + //------------------------------------------------------------------------- template constexpr auto state_size_v = state_size::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 - std::enable_if_t,T> - uniform (T lo, T hi) - { - return std::uniform_real_distribution { 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 + using uniform_distribution = std::conditional_t< + std::is_floating_point::value, + std::uniform_real_distribution, + std::uniform_int_distribution + >; - template - std::enable_if_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 + auto + uniform (ValueT lo, ValueT hi, GeneratorT &&gen) { - return uniform (0, 1); + return uniform_distribution { 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 + auto + uniform (T lo, T hi) + { + return uniform (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> + typename = std::enable_if_t> > - T - uniform (T lo, T hi, GeneratorT &&gen) + auto uniform (GeneratorT &&gen) { - return std::uniform_int_distribution { lo, hi } (gen); + return uniform (ValueT{0}, ValueT{1}, std::forward (gen)); } ///------------------------------------------------------------------------ - /// select a value uniformly from the range [lo, hi) - template - std::enable_if_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> + > + auto uniform (void) { - return uniform (lo, hi, generator ()); + return uniform (ValueT{0}, ValueT{1}, generator ()); } - //------------------------------------------------------------------------- + ///------------------------------------------------------------------------ + /// Return a uniformly random chosen value on the interval [0, 1] template std::enable_if_t,T> uniform (void) { - return uniform ( + return uniform ( std::numeric_limits::min (), std::numeric_limits::max () ); } - //------------------------------------------------------------------------- + ///------------------------------------------------------------------------ + /// Returns a uniformly random initialised coordinate type by value. template < - typename T, + typename ValueT, typename = std::enable_if_t< - is_coord_v && std::is_floating_point_v + is_coord_v && std::is_floating_point_v > > - T + ValueT uniform (void) { - T res {}; - std::fill (res.begin (), res.end (), uniform ()); + ValueT res {}; + std::fill (res.begin (), res.end (), uniform ()); return res; } @@ -141,5 +173,3 @@ namespace cruft::random { return t[dist (generator ())]; } }; - -#endif