libcruft-util/extent.hpp

210 lines
5.9 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-2020, Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "coord/fwd.hpp"
#include "coord/base.hpp"
#include "vector.hpp"
#include "point.hpp"
#include "random.hpp"
#include <cstddef>
namespace cruft {
/**
* A pure n-dimensional size, without positioning
*/
template <size_t S, typename T>
struct extent : public ::cruft::coord::base<S,T,::cruft::extent<S,T>>
{
using ::cruft::coord::base<S,T,::cruft::extent<S,T>>::base;
extent () = default;
explicit extent (::cruft::vector<S,T>);
constexpr T
area (void) const
{
return std::accumulate (std::begin (this->data),
std::end (this->data),
T {1},
std::multiplies<T> ());
}
constexpr T
diameter (void) const
{
return static_cast<T> (
std::sqrt (
std::accumulate (std::begin (this->data),
std::end (this->data),
T {0},
[] (auto a, auto b) { return a + b * b; })
)
);
}
template <typename U = float>
constexpr
U aspect (void) const
{
return static_cast<U> (this->w) / this->h;
}
/// tests whether a point would lie within:
/// region { origin, *this }, inclusive of borders.
///
/// included for parity with cruft::region.
constexpr bool
inclusive (cruft::point<S,T> p) const
{
return all (p >= T{0} && p <= *this);
}
/// tests whether a point would like within:
/// region { origin, *this }, exclusive of the bottom-right border
/// included for parity with cruft::region
constexpr bool
exclusive (point<S,T> p) const
{
return all (p >= T{0} && p < *this);
}
::cruft::extent<S,T> expanded (::cruft::vector<S,T>) const;
::cruft::extent<S,T> expanded (T) const;
::cruft::extent<S,T> contracted (::cruft::vector<S,T>) const;
::cruft::extent<S,T> contracted (T) const;
bool empty (void) const;
static constexpr
::cruft::extent<S,T> max (void)
{
return extent<S,T> {
std::numeric_limits<T>::max ()
};
}
static constexpr
::cruft::extent<S,T> min (void)
{
return extent<S,T> { 0 };
}
///////////////////////////////////////////////////////////////////////
class iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = point<S,T>;
using difference_type = size_t;
using pointer = value_type*;
using reference = value_type&;
iterator (point<S,T> _cursor, extent<S,T> _target):
m_cursor (_cursor),
m_target (_target)
{ ; }
point<S,T>& operator* () & { return m_cursor; }
const point<S,T>& operator* () const & { return m_cursor; }
iterator& operator++ (void)&
{
++m_cursor[0];
for (size_t i = 0; i < S - 1; ++i) {
if (m_cursor[i] < m_target[i])
break;
m_cursor[i] = 0;
m_cursor[i+1]++;
}
return *this;
}
bool operator!= (const iterator &rhs) const { return m_cursor != rhs.m_cursor; }
bool operator== (const iterator &rhs) const { return m_cursor == rhs.m_cursor; }
private:
point<S,T> m_cursor;
extent<S,T> m_target;
};
auto step (void) const
{
point<S,T> last {0};
last[S-1] = this->data[S-1];
return cruft::view {
iterator {point<S,T> {0}, *this},
iterator {last, *this}
};
}
};
///////////////////////////////////////////////////////////////////////////
// convenience typedefs
template <typename T> using extent2 = extent<2,T>;
template <typename T> using extent3 = extent<3,T>;
template <size_t S> using extentu = extent<S,unsigned>;
template <size_t S> using extenti = extent<S,int>;
template <size_t S> using extentf = extent<S,float>;
template <size_t S> using extentd = extent<S,double>;
typedef extent2<int> extent2i;
typedef extent2<unsigned> extent2u;
typedef extent2<float> extent2f;
typedef extent2<double> extent2d;
typedef extent3<unsigned> extent3u;
typedef extent3<float> extent3f;
///////////////////////////////////////////////////////////////////////////
template <size_t S, typename T>
cruft::point<S,T>
sample (cruft::extent<S,T> shape)
{
return sample (shape, cruft::random::generator ());
}
/// Return a uniform random point that lies within the extent.
///
/// The interval conventions are the same as for the std library; close for
/// integers, half-open for reals.
template <size_t S, typename T, typename GeneratorT>
cruft::point<S,T>
sample (cruft::extent<S,T> shape, GeneratorT &&gen)
{
cruft::point<S,T> p;
for (size_t i = 0; i < S; ++i)
p[i] = cruft::random::uniform (T{0}, shape[i], gen);
return p;
}
//-------------------------------------------------------------------------
template <typename T>
extent2<T>
rotate90 (extent2<T> val, int steps);
}