/* * 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 2010-2019 Danny Robson */ #pragma once #include "./extent.hpp" #include "./point.hpp" #include "./vector.hpp" #include "./types/traits.hpp" #include namespace cruft { /** * A two-dimensional rectangle, with size and position. */ template struct region { using extent_t = cruft::extent; using point_t = cruft::point; using value_type = T; //--------------------------------------------------------------------- static constexpr size_t dimension = S; static constexpr size_t elements = extent_t::elements + point_t::elements; point_t p; extent_t e; //--------------------------------------------------------------------- region () = default; constexpr region (point_t _p, extent_t _e) : p (_p) , e (_e) { ; } constexpr region (point_t _a, point_t _b) : region (_a, extent_t (_b - _a)) { ; } //--------------------------------------------------------------------- template constexpr region cast (void) const { return { p.template cast (), e.template cast () }; } //--------------------------------------------------------------------- constexpr T area (void) const { return e.area (); } constexpr T diameter (void) const { return e.diameter (); } extent_t magnitude (void) const; extent_t magnitude (extent_t); bool empty (void) const; //--------------------------------------------------------------------- point_t base (void) const; point_t away (void) const; point_t centre (void) const; point_t closest (point_t) const; //--------------------------------------------------------------------- // exclusive of borders bool intersects (region) const; // Compute binary region combinations region intersection (region) const; // Test if a region lies completely within our space bool covers (region) const noexcept; /// Test if a point lies within our space. Inclusive of borders constexpr bool inclusive (point q) const noexcept { return all (p <= q && p + e >= q); } /// test if a point lies within our space, exclusive of the /// bottom-right border constexpr bool exclusive (point q) const noexcept { return all (p <= q && p + e > q); } // Move a point to be within the region bounds point_t constrain (point_t) const noexcept; //--------------------------------------------------------------------- // Compute a region `mag` units into the region region inset (T mag) const; region inset (vector mag) const; region expand (T mag) const; region expand (vector) const; // arithmetic operators region operator+ (vector) const; region operator- (vector) const; // Logical comparison operators bool operator ==(region rhs) const; bool operator !=(region rhs) const { return !(*this == rhs); } // Utility constants static constexpr region max (void) { return { cruft::point {std::numeric_limits::lowest () / 2}, cruft::extent {std::numeric_limits::max ()} }; } static constexpr region unit (void) { return { point_t::origin (), extent_t {1} }; } static constexpr region zero (void) { return { point_t {0}, extent_t {0} }; } class iterator { public: using iterator_category = std::forward_iterator_tag; using difference_type = std::size_t; using value_type = point_t; using pointer = value_type*; using reference = value_type&; iterator (point_t _lo, point_t _hi): cursor (_lo), lo (_lo), hi (_hi) { ; } iterator () : cursor {} , lo {} , hi {} { ; } const point_t& operator* (void) const& { return cursor; } iterator& operator++ (void) { cursor[0] += 1; for (size_t s = 0; s < S-1; ++s) { if (cursor[s] < hi[s]) return *this; cursor[s] = lo[s]; cursor[s+1]++; } return *this; } bool operator== (const iterator &rhs) const { return cursor == rhs.cursor; } bool operator!= (const iterator &rhs) const { return cursor != rhs.cursor; } private: point_t cursor, lo, hi; }; /// Returns an iterator that provides successive points across the /// region. /// /// The points are in the half open range [p, p+e). ie, the /// 'bottom-right' corner will never be returned. If you need this /// behaviour then construct a larger range. auto step (void) const { point_t last = p; last[S-1] = (p + e)[S-1]; return cruft::view { iterator { p, p + e }, iterator { last, p + e } }; }; void sanity (void) const; }; /////////////////////////////////////////////////////////////////////////// template using region2 = region<2,T>; template using region3 = region<3,T>; //------------------------------------------------------------------------- using region2u = region2; using region2i = region2; using region2f = region2; using region2d = region2; //------------------------------------------------------------------------- extern template struct region<2, unsigned>; extern template struct region<2, int>; extern template struct region<2, float>; extern template struct region<2, double>; /////////////////////////////////////////////////////////////////////////// /// constructs the minimal region that encompasses a region and a point. template region operator| (region const r, point const p) { const auto p0 = select (r.p < p, r.p, p); const auto p1 = select (r.away () > p, r.away (), p); return { p0, p1 }; } //------------------------------------------------------------------------- template auto operator| (point const p, region const r) { return r | p; } //------------------------------------------------------------------------- // construct a minimal bounding region over two supplied regions template region operator| (region const a, region const b) { return a | b.base () | b.away (); } /////////////////////////////////////////////////////////////////////////// /// Construct a region that consists of the overlapping portions of two /// supplied regions. /// /// The behaviour is undefined if there is no overlap. The caller should /// test using `intersects` if required beforehand. template region intersection (region const a, region const b) { // Find the two corners of the new region. auto const lo = cruft::max (a.base (), b.base ()); auto const hi = cruft::min (a.away (), b.away ()); CHECK (all (lo <= hi)); return { lo, hi }; } /////////////////////////////////////////////////////////////////////////// /// returns the squared minimum distance from a region to a given point template T distance2 (region r, point p) { auto const clamped = cruft::max ( r.p - p, vector (0), p - (r.p + r.e) ); return sum (clamped * clamped); } ///------------------------------------------------------------------------ /// returns the squared minimum distance from a region to a given point template T distance2 (point p, region r) { return distance2 (r, p); } /////////////////////////////////////////////////////////////////////////// template bool intersects ( cruft::region const &a, cruft::region const &b ) { return a.intersects (b); } /// returns true if the supplied point lies within the supplied region /// inclusive of borders. template bool intersects (cruft::region const area, cruft::point const query) { return area.inclusive (query); } ///------------------------------------------------------------------------ /// returns true if the supplied point lies within the supplied region /// inclusive of borders. template bool intersects (cruft::point const query, cruft::region const area) { return intersects (area, query); } /////////////////////////////////////////////////////////////////////////// /// returns a uniformly randomly sampled point within the supplied region template cruft::point sample (region shape, GeneratorT &&gen) { return shape.p + sample ( shape.e, std::forward (gen) ).template as (); } ///------------------------------------------------------------------------ /// Returns a uniformly randomly sampled point within the supplied region /// using the default thread-local generator. template decltype(auto) sample (region const &shape) { return sample (shape, cruft::random::generator ()); } /////////////////////////////////////////////////////////////////////////// /// Returns a region rotated clockwise about the base point in `steps` /// multiples of 90 degrees. /// /// `steps` must lie in the range [0, 4) so we can avoid an expensive /// modulus in the typical case. template cruft::region2 rotate90 (cruft::region2 obj, int steps); /////////////////////////////////////////////////////////////////////////// /// Returns the vertices of the region (excluding last->first vertex) in /// CCW winding. /// /// It assumes the base position is the min / extent is strictly positive. template std::array, 4> path (cruft::region2 const &obj) { CHECK (all (obj.e > 0)); return { obj.p, obj.p + vector2 { 0, obj.e.h }, obj.p + vector2 {obj.e.w, obj.e.h }, obj.p + vector2 {obj.e.w, 0 }, }; } /////////////////////////////////////////////////////////////////////////// template std::ostream& operator<< (std::ostream&, const cruft::region&); }