geom/sample: add subregion sampler for extents

This commit is contained in:
Danny Robson 2020-12-15 10:02:26 +10:00
parent 3bf2c2205d
commit 2f3a767285
3 changed files with 112 additions and 0 deletions

View File

@ -360,6 +360,7 @@ list (
geom/sample/edge.hpp
geom/sample/surface.hpp
geom/sample/volume.hpp
geom/sample/subregion.hpp
geom/segment.cpp
geom/segment.hpp
geom/sphere.cpp
@ -697,6 +698,7 @@ if (TESTS)
geom/plane
geom/ray
geom/sample/edge
geom/sample/subregion
geom/segment
geom/sphere
hash/buzhash

63
geom/sample/subregion.hpp Normal file
View File

@ -0,0 +1,63 @@
/*
* 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 2020, Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include <algorithm>
#include "../../extent.hpp"
#include "../../region.hpp"
#include "../../random.hpp"
namespace cruft::geom::sample {
/// Given a desired subregion shape, and a possible field of locations,
/// select a random location for surface placement.
///
/// Valid placements allow for rotations, but are exclusive of far edges
/// for integer types.
///
/// The largest axis of the shape must not be larger than the smallest
/// axis of the placement field otherwise the chosen region may fall
/// outside the field.
class subregion {
public:
subregion (cruft::extent2i _shape, cruft::extent2i _field)
: m_shape (_shape)
, m_field (_field)
{
CHECK (all (m_shape <= m_field));
}
// Choose placements by looking at each axis in turn.
//
// We support rotations by randomising the axis ordering for the shape
// but not the field.
template <typename GeneratorT>
cruft::region2i
operator() (GeneratorT &&gen)
{
int axes[] = { 0, 1 };
std::shuffle (std::begin (axes), std::end (axes), gen);
cruft::region2i res;
for (int i = 0; i < std::ssize (axes); ++i) {
res.e[i] = m_shape[axes[i]];
res.p[i] = cruft::random::uniform (m_field[i] - res.e[i]);
}
return res;
}
private:
cruft::extent2i m_shape;
cruft::extent2i m_field;
};
}

View File

@ -0,0 +1,47 @@
#include <cruft/util/tap.hpp>
#include <cruft/util/extent.hpp>
#include <cruft/util/geom/sample/subregion.hpp>
int
main ()
{
cruft::TAP::logger tap;
static const struct {
cruft::extent2i shape;
cruft::extent2i field;
char const *message;
} TESTS[] = {
{ { 1, 1 }, { 1, 1 }, "Unit shape and unit field" },
{ { 1, 1 }, { 100, 100 }, "Unit shape and large field" },
{ { 10, 10 }, { 100, 100 }, "Square shape and large field" },
{ { 1, 10 }, { 100, 100 }, "Tall shape and large field" },
{ { 10, 1 }, { 100, 100 }, "" },
{ { 2, 2 }, { 100, 100 }, "" },
};
static const int ITERATIONS = 1'000;
for (auto const &[shape, field, msg]: TESTS) {
cruft::rand::xoshiro256plusplus gen (42);
cruft::geom::sample::subregion sampler {shape, field};
cruft::region2i const valid (cruft::point2i (0), field);
bool success = true;
for (int i = 0; i < ITERATIONS; ++i) {
auto const choice = sampler (gen);
if (!valid.covers (choice)) {
success = false;
break;
}
}
tap.expect (success, "subregion coverage validity, %!", msg);
}
return tap.status ();
}