/* * 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 "../../region.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; }; /// A surface sampler specialisation for 2d regions /// /// We encapsulate an extent sampler and an offset, then mainly pass off /// the work to the extent sampler. template class surface> { public: using shape_type = region<2,T>; surface (shape_type const &_shape) : m_extent (_shape.e) , m_offset (_shape.p.template as ()) { ; } template decltype(auto) eval (GeneratorT &&gen) { return m_extent.eval ( std::forward (gen) ) + m_offset; } private: surface> m_extent; cruft::vector<2,T> m_offset; }; }