libcruft-util/region.cpp
Danny Robson fdaa5e1392 assert: split CHECK_LIMIT into INCLUSIVE and INDEX
LIMIT hid an off-by-one bug when tests used end iterators. We rename the
assertion to uncover all uses of the flawed implementation, and split it
into an identical assertion, and one intended to protect against
iterator ends.
2020-09-24 08:03:41 +10:00

339 lines
8.2 KiB
C++

/*
* 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-2017 Danny Robson <danny@nerdcruft.net>
*/
#include "region.hpp"
#include "debug/assert.hpp"
#include "coord/iostream.hpp"
#include <array>
using cruft::region;
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>::region (extent_t _extent):
region (point_t::origin (), _extent)
{
CHECK_SANITY (*this);
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>::region (point_t _p,
extent_t _e):
p (_p),
e (_e)
{
CHECK_SANITY (*this);
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>::region (point_t _a,
point_t _b):
region (_a, extent_t { _b - _a })
{
// This check must allow for zero area (but non-zero dimension) regions.
// Some code paths need to support this degenerate case. It's ugly but
// simplifies generalisation. eg, vertical linear bezier curves.
CHECK (all (_a <= _b));
CHECK_SANITY (*this);
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename cruft::region<S,T>::extent_t
cruft::region<S,T>::magnitude (void) const
{
return e;
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename cruft::region<S,T>::extent_t
cruft::region<S,T>::magnitude (extent_t _e)
{
e = _e;
return e;
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
bool
cruft::region<S,T>::empty (void) const
{
return almost_zero (area ());
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename cruft::region<S,T>::point_t
cruft::region<S,T>::base (void) const
{
return p;
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename cruft::region<S,T>::point_t
cruft::region<S,T>::away (void) const
{
return p + e;
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename cruft::region<S,T>::point_t
cruft::region<S,T>::centre (void) const
{
return p + e / T{2};
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename cruft::region<S,T>::point_t
cruft::region<S,T>::closest (point_t q) const
{
return clamp (q, p, p + e);
}
//-----------------------------------------------------------------------------
// FIXME: This will fail with an actual infinite range (NaNs will be generated
// in the conditionals).
template <size_t S, typename T>
bool
cruft::region<S,T>::intersects (region<S,T> rhs) const
{
for (size_t i = 0; i < S; ++i)
if ( p[i] >= rhs.p[i] + rhs.e[i] ||
rhs.p[i] >= p[i] + e[i])
{ return false; }
return true;
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
typename region<S,T>::point_t
region<S,T>::constrain (point_t q) const noexcept
{
return closest (q);
}
//-----------------------------------------------------------------------------
template<size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::intersection (region<S,T> rhs) const
{
// find the intersection corners
point_t a, b;
for (size_t i = 0; i < S; ++i) {
a[i] = cruft::max (p[i], rhs.p[i]);
b[i] = cruft::min (p[i] + e[i], rhs.p[i] + rhs.e[i]);
if (b[i] < a[i])
throw std::logic_error ("no overlap");
}
return { a, b };
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
bool
cruft::region<S,T>::covers (region<S, T> r) const noexcept
{
return all (p <= r.p) && all (p + e >= r.p + r.e);
}
///////////////////////////////////////////////////////////////////////////////
template <size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::inset (T mag) const
{
return inset (cruft::vector<S,T> {mag});
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::inset (vector<S,T> mag) const
{
// ensure we have enough space to trim off our total extent
CHECK (all (e >= T{2} * mag));
return {
p + mag,
e - T{2} * mag
};
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::expand (vector<S,T> v) const
{
return {
p - v,
e + v * T{2}
};
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::expand (T mag) const
{
return expand (vector<S,T> {mag});
}
///////////////////////////////////////////////////////////////////////////////
template <size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::operator+ (vector<S,T> rhs) const
{
return { p + rhs, e };
}
//-----------------------------------------------------------------------------
template <size_t S, typename T>
cruft::region<S,T>
cruft::region<S,T>::operator- (vector<S,T> rhs) const
{
return { p - rhs, e };
}
///////////////////////////////////////////////////////////////////////////////
template <size_t S, typename T>
bool
cruft::region<S,T>::operator== (region rhs) const
{
return p == rhs.p && e == rhs.e;
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
cruft::region<2,T>
cruft::rotate90 (cruft::region<2,T> obj, int steps)
{
CHECK_INCLUSIVE (steps, 0, 3);
switch (steps) {
case 0:
return obj;
case 1:
return region2<T> {
point2<T> {
T(obj.p.x - obj.e.h),
obj.p.y
},
extent2<T> {
obj.e.h,
obj.e.w,
}
};
case 2:
return region2<T> {
point2<T> {
obj.p.x,
T(obj.p.y - obj.e.h),
},
obj.e,
};
case 3:
return region2<T> {
obj.p,
extent2<T> {
obj.e.h,
obj.e.w,
},
};
}
unreachable();
}
//-----------------------------------------------------------------------------
template cruft::region2<i32> cruft::rotate90 (cruft::region2<i32>, int);
template cruft::region2<i64> cruft::rotate90 (cruft::region2<i64>, int);
template cruft::region2<f32> cruft::rotate90 (cruft::region2<f32>, int);
///////////////////////////////////////////////////////////////////////////////
template <size_t S, typename T>
std::ostream&
cruft::operator<< (std::ostream &os, const cruft::region<S,T> &rhs) {
return os << "{ position: " << rhs.p << ", extent: " << rhs.e << " }";
}
///////////////////////////////////////////////////////////////////////////////
namespace cruft::debug {
template <size_t S, typename T>
struct validator<cruft::region<S,T>> {
static bool is_valid (const cruft::region<S,T> &r)
{
return cruft::debug::is_valid (r.p) && cruft::debug::is_valid (r.e);
}
};
}
///////////////////////////////////////////////////////////////////////////////
#define INSTANTIATE_S_T(S,T) \
template struct cruft::region<S,T>; \
template std::ostream& cruft::operator<< (std::ostream&, const region<S,T>&); \
template struct cruft::debug::validator<cruft::region<S,T>>;
#define INSTANTIATE(T) \
INSTANTIATE_S_T(2,T) \
INSTANTIATE_S_T(3,T)
INSTANTIATE(i16);
INSTANTIATE(i32);
INSTANTIATE(i64);
INSTANTIATE(u16)
INSTANTIATE(u32)
INSTANTIATE(u64)
INSTANTIATE(f32)
INSTANTIATE(f64)