From 2f3a767285745feeeb36a078cf78b90e16097d40 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 15 Dec 2020 10:02:26 +1000 Subject: [PATCH] geom/sample: add subregion sampler for extents --- CMakeLists.txt | 2 ++ geom/sample/subregion.hpp | 63 ++++++++++++++++++++++++++++++++++ test/geom/sample/subregion.cpp | 47 +++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 geom/sample/subregion.hpp create mode 100644 test/geom/sample/subregion.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dd94cdd..f83e2ab8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/geom/sample/subregion.hpp b/geom/sample/subregion.hpp new file mode 100644 index 00000000..188c4e97 --- /dev/null +++ b/geom/sample/subregion.hpp @@ -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 + */ + +#pragma once + +#include + +#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 + 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; + }; +} \ No newline at end of file diff --git a/test/geom/sample/subregion.cpp b/test/geom/sample/subregion.cpp new file mode 100644 index 00000000..aee65bd1 --- /dev/null +++ b/test/geom/sample/subregion.cpp @@ -0,0 +1,47 @@ + +#include + +#include +#include + + +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 (); +} \ No newline at end of file