/* * 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 "volume.hpp" #include "../ops.hpp" #include "../../coord/fwd.hpp" #include "../../extent.hpp" #include "../../random.hpp" #include #include /////////////////////////////////////////////////////////////////////////////// namespace cruft::geom::sample { /// 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.eval (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.eval (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.eval (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; } /// A surface sampler specialisation for 2d extents. /// /// The actual work is handed off to the volume sampler, as it's /// equivalent in 2 dimensions. template class surface> : public volume> { using volume>::volume; }; }