2018-11-26 14:08:50 +11:00
|
|
|
/*
|
|
|
|
* 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 <danny@nerdcruft.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "volume.hpp"
|
|
|
|
|
|
|
|
#include "../ops.hpp"
|
|
|
|
|
|
|
|
#include "../../coord/fwd.hpp"
|
|
|
|
#include "../../extent.hpp"
|
2019-08-21 08:27:07 +10:00
|
|
|
#include "../../region.hpp"
|
2018-11-26 14:08:50 +11:00
|
|
|
#include "../../random.hpp"
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <random>
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
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.
|
|
|
|
///
|
2020-10-22 10:29:22 +11:00
|
|
|
/// \tparam SamplerT A surface sampler
|
|
|
|
/// \tparam DistanceT The type of point-to-point distances
|
|
|
|
/// \tparam GeneratorT The random generator passed to the sampler
|
|
|
|
/// \tparam AcceptT A unary bool function that returns true if a sampled
|
|
|
|
/// point is permissible. The point is counted in the candidate set
|
|
|
|
/// regardless.
|
|
|
|
///
|
2018-11-26 14:08:50 +11:00
|
|
|
/// \return A vector of the computed points
|
2020-10-22 10:29:22 +11:00
|
|
|
template <
|
|
|
|
typename SamplerT,
|
|
|
|
typename DistanceT,
|
|
|
|
typename GeneratorT,
|
|
|
|
typename AcceptT
|
|
|
|
>
|
2018-11-26 14:08:50 +11:00
|
|
|
auto
|
2020-10-22 10:29:22 +11:00
|
|
|
poisson (
|
|
|
|
SamplerT const &sampler,
|
|
|
|
GeneratorT &&gen,
|
|
|
|
AcceptT &&accept,
|
|
|
|
DistanceT &&minimum_distance,
|
|
|
|
std::size_t candidates_count
|
|
|
|
) {
|
|
|
|
using point_type = decltype (sampler.eval (gen));
|
2018-11-26 14:08:50 +11:00
|
|
|
using value_type = typename point_type::value_type;
|
|
|
|
|
|
|
|
std::vector<point_type> selected;
|
|
|
|
std::vector<point_type> candidates;
|
|
|
|
|
|
|
|
// prime the found elements list with an initial point we can
|
|
|
|
// perform distance calculations on
|
2020-10-22 10:29:22 +11:00
|
|
|
selected.push_back (sampler.eval (gen));
|
2018-11-26 14:08:50 +11:00
|
|
|
|
|
|
|
// 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) {
|
2020-10-22 10:29:22 +11:00
|
|
|
return sampler.eval (gen);
|
2018-11-26 14:08:50 +11:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-10-22 10:29:22 +11:00
|
|
|
// Remove points that aren't acceptable
|
|
|
|
std::erase_if (
|
|
|
|
candidates,
|
|
|
|
[&] (auto const &p) { return !accept (p); }
|
|
|
|
);
|
|
|
|
|
2018-11-26 14:08:50 +11:00
|
|
|
// 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<value_type>::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<value_type>::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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-22 10:29:22 +11:00
|
|
|
/// A convenience function that forwards to poisson with unconditional
|
|
|
|
/// point acceptance.
|
|
|
|
template <
|
|
|
|
typename SamplerT,
|
|
|
|
typename DistanceT,
|
|
|
|
typename GeneratorT
|
|
|
|
>
|
|
|
|
auto
|
|
|
|
poisson (
|
|
|
|
SamplerT &&sampler,
|
|
|
|
GeneratorT &&gen,
|
|
|
|
DistanceT &&minimum_distance,
|
|
|
|
std::size_t candidates_count
|
|
|
|
) {
|
|
|
|
return poisson (
|
|
|
|
std::forward<SamplerT> (sampler),
|
|
|
|
std::forward<GeneratorT> (gen),
|
|
|
|
[] (auto&&...) { return true; },
|
|
|
|
std::forward<DistanceT> (minimum_distance),
|
|
|
|
candidates_count
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-26 14:08:50 +11:00
|
|
|
/// 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 <typename T>
|
|
|
|
class surface<extent<2,T>> :
|
|
|
|
public volume<extent<2,T>>
|
|
|
|
{ using volume<extent<2,T>>::volume; };
|
2019-08-21 08:27:07 +10:00
|
|
|
|
|
|
|
|
|
|
|
/// 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 <typename T>
|
|
|
|
class surface<region<2,T>> {
|
|
|
|
public:
|
|
|
|
using shape_type = region<2,T>;
|
|
|
|
|
|
|
|
surface (shape_type const &_shape)
|
|
|
|
: m_extent (_shape.e)
|
|
|
|
, m_offset (_shape.p.template as<vector> ())
|
|
|
|
{ ; }
|
|
|
|
|
|
|
|
|
|
|
|
template <typename GeneratorT>
|
|
|
|
decltype(auto)
|
|
|
|
eval (GeneratorT &&gen)
|
|
|
|
{
|
|
|
|
return m_extent.eval (
|
|
|
|
std::forward<GeneratorT> (gen)
|
|
|
|
) + m_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
surface<extent<2,T>> m_extent;
|
|
|
|
cruft::vector<2,T> m_offset;
|
|
|
|
};
|
2018-11-26 14:08:50 +11:00
|
|
|
}
|