Danny Robson
fdaa5e1392
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.
339 lines
8.2 KiB
C++
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)
|