geom/sample: add subregion sampler for extents
This commit is contained in:
parent
3bf2c2205d
commit
2f3a767285
@ -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
63
geom/sample/subregion.hpp
Normal 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;
|
||||
};
|
||||
}
|
47
test/geom/sample/subregion.cpp
Normal file
47
test/geom/sample/subregion.cpp
Normal 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 ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user