/* * This Source Code Form is subject to the terms of the Mozilla Public * 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-2018 Danny Robson */ #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 /// using a provided random generator. The point will lie within the shape, /// inclusive of boundaries. /// /// May be specialised for arbitrary shapes but uses rejection sampling /// as a safe default. This implies that execution may not take a constant /// time. template struct sampler { /// Returns a point which lies within the supplied shape, inclusive /// of borders. template static auto eval (ShapeT const &shape, GeneratorT &&g) { auto b = bounds (shape); while (true) { auto p = sample (b, g); if (intersects (shape, p)) return p; } } }; /////////////////////////////////////////////////////////////////////////// /// A convenience function that calls sample::fn to select a random point /// in a provided shape. template < typename ShapeT, typename GeneratorT // random generator > auto sample (ShapeT const &shape, GeneratorT &&gen) { return sampler::eval (shape, std::forward (gen)); } /////////////////////////////////////////////////////////////////////////// std::vector poisson_sample (cruft::extent2i, float distance, int samples); /////////////////////////////////////////////////////////////////////////// namespace surface { /// A generator of samples that lie on the surface of a shape template class sampler; template sampler (ShapeT const&) -> sampler>; //--------------------------------------------------------------------- template class sampler> { public: sampler (cruft::extent _target): target (_target) { ; } template cruft::point operator() (GeneratorT &&gen) const noexcept { cruft::point p; for (size_t i = 0; i < S; ++i) p[i] = random::uniform (T{0}, target[i], gen); return p; } private: cruft::extent target; }; /// Approximate a poisson disc sampling through the "Mitchell's Best /// Candidate" algorithm. /// /// Try to keep adding a new point to a set. Each new point is the /// best of a set of candidates. The 'best' is the point that is /// furthest from all selected points. /// /// \return A vector of the computed points template auto poisson (SamplerT const &target, GeneratorT &&gen, DistanceT &&minimum_distance, size_t candidates_count) { using point_type = decltype (target (gen)); using value_type = typename point_type::value_type; std::vector selected; std::vector candidates; // prime the found elements list with an initial point we can // perform distance calculations on selected.push_back (target (gen)); // keep trying to add one more new point while (1) { // generate a group of candidate points candidates.clear (); std::generate_n ( std::back_inserter (candidates), candidates_count, [&] (void) { return target (gen); } ); // find the point whose minimum distance to the existing // points is the greatest (while also being greater than the // required minimum distance); auto best_distance2 = std::numeric_limits::lowest (); size_t best_index = 0; for (size_t i = 0; i < candidates.size (); ++i) { auto const p = candidates[i]; auto d2 = std::numeric_limits::max (); // find the minimum distance from this candidate to the // selected points for (auto q: selected) d2 = cruft::min (d2, cruft::distance2 (p, q)); // record if it's the furthest away if (d2 > best_distance2 && d2 > cruft::pow (minimum_distance (p), 2)) { best_distance2 = d2; best_index = i; } } // if we didn't find a suitable point then we give up and // return the points we found, otherwise record the best point if (best_distance2 <= 0) break; selected.push_back (candidates[best_index]); } return selected; } } }