libcruft-util/cruft/util/coord/ops.hpp

1621 lines
45 KiB
C++
Raw Normal View History

/*
2018-08-04 15:14:06 +10:00
* 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 2012-2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "fwd.hpp"
#include "traits.hpp"
#include "../array/varray.hpp"
2016-08-11 14:22:29 +10:00
2017-11-23 17:24:11 +11:00
// we specifically rely on vector<bool> to compute a few logical operations
#include "../vector.hpp"
#include "../tuple/value.hpp"
#include "../debug/assert.hpp"
#include "../maths.hpp"
#include <cruft/util/preprocessor.hpp>
#include <algorithm>
2015-09-29 18:06:52 +10:00
#include <cmath>
2016-08-15 18:46:04 +10:00
#include <cstdlib>
#include <iterator>
#include <functional>
namespace cruft {
/// returns the data at a templated index in a coordinate.
///
/// specifically required for structured bindings support.
///
/// \tparam I index of the requested data
/// \tparam S dimensionality of the coordinate
/// \tparam T underlying data type of the coordinate
/// \tparam K coordinate data type to operate on
template <
std::size_t I,
typename K,
typename = std::enable_if_t<
is_coord_v<K>, void
>
>
const auto&
get (const K &k)
{
static_assert (I < K::elements);
return k[I];
};
/// returns the data at a templated index in a coordinate.
///
/// specifically required for structured bindings support.
///
/// \tparam I index of the requested data
/// \tparam S dimensionality of the coordinate
/// \tparam T underlying data type of the coordinate
/// \tparam K coordinate data type to operate on
template <
std::size_t I,
typename K,
typename = std::enable_if_t<
is_coord_v<K> && I < K::elements, void
>
>
auto &
get (K &k)
{
static_assert (I < K::elements);
return k[I];
};
2017-03-23 14:36:39 +11:00
///////////////////////////////////////////////////////////////////////////
// a templated functor that exposes arithmetic and assignment maths
// functions for vector-vector or vector-scalar operations.
//
// we implement the operations this way because it (somewhat) simplifies
// ambiguity resolution in the various operators we need to provide.
// eg, operator+(vec,vec) vs operator+(vec,int).
//
// it used to be directly implemented with a series of templated free
// functions when we could restrict the arguments more easily with quite
// specific template template parameters. but the introduction of
// coordinate types that do not expose size or type information as template
// parameters we can't rely on this mechanism anymore.
template <typename, typename, typename=void>
struct assignment {};
2017-03-23 14:36:39 +11:00
//-------------------------------------------------------------------------
template <typename CoordA, typename CoordB>
struct assignment<
CoordA,
CoordB,
std::enable_if_t<
is_coord_v<CoordA> &&
is_coord_v<CoordB> &&
arity_v<CoordA> == arity_v<CoordB> &&
std::is_same_v<
typename CoordA::value_type,
std::common_type_t<
typename CoordA::value_type,
typename CoordB::value_type
>
>
,
void
2016-08-11 14:23:32 +10:00
>
> {
template <typename OperationT>
static constexpr CoordA&
eval (OperationT &&op, CoordA &a, const CoordB &b)
{
for (std::size_t i = 0; i < CoordA::elements; ++i)
a[i] = op (a[i], b[i]);
return a;
}
};
2017-03-23 14:36:39 +11:00
//-------------------------------------------------------------------------
// vector-scalar operations
template <
typename CoordT,
typename ScalarT
>
struct assignment<
CoordT,
ScalarT,
std::enable_if_t<
is_coord_v<CoordT> &&
!is_coord_v<ScalarT> &&
has_scalar_op_v<CoordT> &&
std::is_same_v<
typename CoordT::value_type,
std::common_type_t<
typename CoordT::value_type,
ScalarT
>
>
,
void
>
> {
// we allow scalar types which can be naturally promoted to the vector's
// value_type
template <typename OperationT>
static constexpr CoordT&
eval (OperationT &&op, CoordT &coord, const ScalarT scalar)
{
for (size_t i = 0; i < CoordT::elements; ++i)
coord[i] = op (coord[i], scalar);
return coord;
}
};
///////////////////////////////////////////////////////////////////////////
/// create a coord from supplied arguments, optionally specifying the
/// underlying type.
///
/// much like experimental::make_array we use a void type to signal we
/// need to deduce the underlying type.
#define MAKE_COORD(KLASS) \
template < \
typename _T = void, \
typename ...Args \
> \
constexpr auto \
make_##KLASS (Args &&...args) \
{ \
using T = std::conditional_t< \
std::is_void_v<_T>, \
std::common_type_t<Args...>, \
_T \
>; \
\
return KLASS<sizeof...(Args),T> { \
std::forward<Args> (args)... \
}; \
}
MAKE_COORD(extent)
MAKE_COORD(point)
MAKE_COORD(vector)
#undef MAKE_COORD
template <
template <std::size_t,typename> class K,
typename ...Args
>
constexpr auto
2017-11-23 17:24:11 +11:00
make_coord (Args &&...args)
{
using T = std::common_type_t<Args...>;
return K<sizeof...(Args),T> { std::forward<Args> (args)... };
}
///////////////////////////////////////////////////////////////////////////
template <typename ValueA, typename ValueB, typename=void>
struct arithmetic {};
//-------------------------------------------------------------------------
template <typename CoordA, typename CoordB>
struct arithmetic<
CoordA,
CoordB,
std::enable_if_t<
is_coord_v<CoordA> &&
is_coord_v<CoordB> &&
arity_v<CoordA> == arity_v<CoordB> &&
has_result_v<CoordA,CoordB>
,
void
>
> {
template <typename OperationT>
static constexpr auto
eval (OperationT &&op, const CoordA &a, const CoordB &b)
{
using common_t = std::common_type_t<
typename CoordA::value_type,
typename CoordB::value_type
>;
revalue_t<result_t<CoordA,CoordB>,common_t> out {};
for (size_t i = 0; i < CoordA::elements; ++i)
out[i] = op (a[i], b[i]);
return out;
}
};
//-------------------------------------------------------------------------
template <typename CoordT, typename ScalarT>
struct arithmetic<
CoordT,
ScalarT,
std::enable_if_t<
is_coord_v<CoordT> && std::is_arithmetic_v<ScalarT> && has_scalar_op_v<CoordT>,
void
>
> {
template <typename OperationT>
static constexpr auto
eval (OperationT &&op, const CoordT &coord, const ScalarT &scalar)
{
using common_t = std::common_type_t<typename CoordT::value_type, ScalarT>;
revalue_t<CoordT,common_t> out {};
for (size_t i = 0; i < CoordT::elements; ++i)
out[i] = op (coord[i], scalar);
return out;
}
};
//-------------------------------------------------------------------------
template <typename ScalarT, typename CoordT>
struct arithmetic<
ScalarT,
CoordT,
std::enable_if_t<
is_coord_v<CoordT> && std::is_arithmetic_v<ScalarT> && has_scalar_op_v<CoordT>,
void
>
> {
template <typename OperationT>
static constexpr auto
eval (OperationT &&op, const ScalarT &scalar, const CoordT &coord)
{
using common_t = std::common_type_t<typename CoordT::value_type, ScalarT>;
revalue_t<CoordT,common_t> out {};
for (size_t i = 0; i < CoordT::elements; ++i)
out[i] = op (scalar, coord[i]);
return out;
}
};
///////////////////////////////////////////////////////////////////////////
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator+ (A &&a, B &&b)
{
return arithmetic<std::decay_t<A>, std::decay_t<B>>::eval (std::plus{}, a, b);
}
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator- (A &&a, B &&b)
{
return arithmetic<std::decay_t<A>, std::decay_t<B>>::eval (std::minus{}, a, b);
}
2015-11-04 23:22:49 +11:00
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator* (A &&a, B &&b)
{
return arithmetic<
2017-11-23 17:24:11 +11:00
std::decay_t<A>,
std::decay_t<B>
>::eval (std::multiplies{}, a, b);
}
2017-03-23 14:36:39 +11:00
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator/ (A &&a, B &&b)
2017-03-23 14:36:39 +11:00
{
return arithmetic<std::decay_t<A>, std::decay_t<B>>::eval (std::divides{}, a, b);
2017-03-23 14:36:39 +11:00
}
2015-03-07 03:18:32 +11:00
//-------------------------------------------------------------------------
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator += (A &&a, B &&b)
2017-03-23 14:36:39 +11:00
{
return assignment<
2017-11-23 17:24:11 +11:00
std::decay_t<A>,
std::decay_t<B>
>::eval (std::plus{}, a, b);
}
2017-03-23 14:36:39 +11:00
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator -= (A &&a, B &&b)
{
return assignment<
2017-11-23 17:24:11 +11:00
std::decay_t<A>,
std::decay_t<B>
>::eval (std::minus{}, a, b);
}
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator *= (A &&a, B &&b)
{
return assignment<
2017-11-23 17:24:11 +11:00
std::decay_t<A>,
std::decay_t<B>
>::eval (std::multiplies{}, a, b);
}
template <
typename A,
typename B,
typename = std::enable_if_t<
(is_coord_v<std::decay_t<A>> || is_coord_v<std::decay_t<B>>) &&
(is_coord_v<std::decay_t<A>> || std::is_arithmetic_v<std::decay_t<A>>) &&
(is_coord_v<std::decay_t<B>> || std::is_arithmetic_v<std::decay_t<B>>)
>
>
constexpr auto
operator /= (A &&a, B &&b)
{
return assignment<
2017-11-23 17:24:11 +11:00
std::decay_t<A>,
std::decay_t<B>
>::eval (std::divides{}, a, b);
}
///////////////////////////////////////////////////////////////////////////
// unary operators
2017-11-23 17:24:11 +11:00
#define UNARY_OP(OP) \
template < \
typename K, \
typename = std::enable_if_t< \
is_coord_v<K>, void \
> \
> \
constexpr \
auto \
operator OP (K k) \
{ \
using value_type = decltype( \
OP std::declval<typename K::value_type> () \
); \
\
revalue_t<K,value_type> out {}; \
\
for (std::size_t i = 0; i < K::elements; ++i) \
out[i] = OP k[i]; \
\
return out; \
}
UNARY_OP(!)
UNARY_OP(~)
UNARY_OP(+)
UNARY_OP(-)
#undef UNARY_OP
///////////////////////////////////////////////////////////////////////////
namespace detail {
2018-03-13 14:34:58 +11:00
/// invoke a function elementwise to the arguments elementwise.
///
/// \tparam ArgsT a tuple containing the (coord) arguments for the func
template <
typename RetT,
typename FuncT,
2018-03-13 14:34:58 +11:00
typename ArgsT,
std::size_t ...Indices
>
constexpr auto
apply (const std::index_sequence<Indices...>,
FuncT &&func,
2018-03-13 14:34:58 +11:00
ArgsT args) noexcept
{
2018-03-13 14:34:58 +11:00
using part_t = std::tuple_element_t<0,ArgsT>;
using value_t = typename part_t::value_type;
return RetT {
2018-03-13 14:34:58 +11:00
std::apply (
func,
::cruft::tuple::value::map (
2018-03-13 14:34:58 +11:00
static_cast<
const value_t& (&)(const part_t&)
> (
get<Indices,RetT>
), args
)
)...
};
}
}
2018-03-13 14:34:58 +11:00
//-------------------------------------------------------------------------
// invokes a function elementwise using elementwise parameters from the
// supplied arguments.
//
// equivalent to this pseduocode:
// for (int i: indices (ReturnT))
// res[i] = func (args[i]...);
// return res;
//
// forwards the arguments as a tuple to a helper function that has access
// to indices as a template parameter.
template <
2018-03-13 14:34:58 +11:00
typename ReturnT,
typename FuncT,
2018-03-13 14:34:58 +11:00
typename ...ArgT,
typename = std::enable_if_t<
2018-03-13 14:34:58 +11:00
// return type and arguments must be coordinates
(is_coord_v<ReturnT> && ... && is_coord_v<std::decay_t<ArgT>>) &&
// all types must be the same arity
((ReturnT::elements == std::decay_t<ArgT>::elements) && ...) &&
// all the arguments must be the same type
(std::is_same_v<
std::tuple_element_t<0,std::tuple<std::decay_t<ArgT>...>>,
std::decay_t<ArgT>
> && ...),
void
>,
2018-03-13 14:34:58 +11:00
typename Indices = std::make_index_sequence<ReturnT::elements>
>
constexpr auto
2018-03-13 14:34:58 +11:00
invoke (FuncT &&func, ArgT &&...args) noexcept
{
2018-03-13 14:34:58 +11:00
return detail::apply<ReturnT> (
Indices{},
std::forward<FuncT> (func),
std::tuple (std::forward<ArgT> (args)...)
);
}
///////////////////////////////////////////////////////////////////////////
// logic operators
namespace detail {
template <
2017-11-23 17:24:11 +11:00
typename K,
typename FuncT,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>,
std::size_t ...Indices
>
constexpr auto
2017-11-23 17:24:11 +11:00
compare (FuncT &&func, std::index_sequence<Indices...>, const K a, const K b)
{
2017-11-23 17:24:11 +11:00
return vector<K::elements,bool> {
std::invoke (func, a[Indices], b[Indices])...
};
}
}
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename FuncT,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>,
2017-11-23 17:24:11 +11:00
typename Indices = std::make_index_sequence<K::elements>
>
constexpr auto
2017-11-23 17:24:11 +11:00
compare (const K a, const K b, FuncT &&func)
{
return detail::compare (std::forward<FuncT> (func), Indices{}, a, b);
}
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
constexpr auto
2017-11-23 17:24:11 +11:00
compare (const K a, const K b)
{
2017-11-23 17:24:11 +11:00
return compare (a, b, std::equal_to<typename K::value_type> {});
}
2015-03-07 03:18:32 +11:00
/// elementwise equality operator
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
constexpr bool
2017-11-23 17:24:11 +11:00
operator== (const K a, const K b)
{
2017-11-23 17:24:11 +11:00
return all (compare (a, b, std::equal_to<typename K::value_type> {}));
}
2015-03-07 03:18:32 +11:00
///------------------------------------------------------------------------
/// elementwise inquality operator
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
constexpr bool
2017-11-23 17:24:11 +11:00
operator!= (const K a, const K b)
{
2017-11-23 17:24:11 +11:00
return any (compare (a, b, std::not_equal_to<typename K::value_type> {}));
}
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
constexpr
bool
2017-11-23 17:24:11 +11:00
almost_zero (const K &k)
{
return std::all_of (
std::cbegin (k),
std::cend (k),
2017-11-23 17:24:11 +11:00
[] (auto t) { return almost_zero (t); }
);
}
///////////////////////////////////////////////////////////////////////////
// special operators
2015-03-07 03:18:32 +11:00
/// point-point subtraction giving a vector difference
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
typename T,
typename U
>
constexpr
vector<S,std::common_type_t<T,U>>
operator- (point<S,T> a, point<S,U> b)
{
vector<S,std::common_type_t<T,U>> out {};
for (std::size_t i = 0; i < S; ++i)
out[i] = a[i] - b[i];
return out;
}
2015-03-07 03:18:32 +11:00
//-------------------------------------------------------------------------
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
typename T,
typename U,
typename = std::enable_if_t<
std::is_arithmetic<T>::value && std::is_arithmetic<U>::value,
void
>
2015-03-07 03:18:32 +11:00
>
constexpr
vector<S,std::common_type_t<T,U>>
operator- (U u, point<S,T> p)
2015-03-07 03:18:32 +11:00
{
return point<S,U> {u} - p;
2015-03-07 03:18:32 +11:00
}
2016-08-11 14:23:32 +10:00
///////////////////////////////////////////////////////////////////////////
template <std::size_t S, typename T>
constexpr T
dot (
cruft::varray<S,T> const &a,
cruft::varray<S,T> const &b
) {
T sum = 0;
2017-08-28 12:25:23 +10:00
for (std::size_t i = 0; i < S; ++i)
sum += a[i] * b[i];
return sum;
}
2016-08-11 14:23:32 +10:00
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
typename T,
typename K,
typename = std::enable_if_t<
is_coord_v<K> && std::is_same_v<typename K::value_type, T> && K::elements == S,
void
>
>
constexpr
T
dot (const T (&a)[S], K k)
{
return dot (a, k.data);
}
2016-08-11 14:23:32 +10:00
//-------------------------------------------------------------------------
template <
2017-11-24 13:08:56 +11:00
typename A,
typename B,
typename = std::enable_if_t<
2017-11-24 13:08:56 +11:00
is_coord_v<A> && is_coord_v<B>, void
>
>
2017-11-24 13:08:56 +11:00
constexpr auto
dot (A a, B b)
{
return dot (varray (a.data), varray (b.data));
}
2016-08-11 14:23:32 +10:00
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename T,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K> && std::is_same_v<T,typename K::value_type>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
dot (K a, const T (&b)[K::elements])
{
2017-11-23 17:24:11 +11:00
return dot (a.data, b);
}
2016-08-11 14:23:32 +10:00
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
dot (const typename K::value_type (&a)[K::elements], K b)
{
2017-11-23 17:24:11 +11:00
return dot (a, b.data);
}
///////////////////////////////////////////////////////////////////////////
template <
typename K,
typename = std::enable_if_t<has_norm_v<K>,void>
>
constexpr
auto
norm2 (const K &k)
{
2016-12-21 20:21:40 +11:00
return dot (k, k);
}
2015-05-29 15:51:08 +10:00
//-------------------------------------------------------------------------
template <
typename K,
typename = std::enable_if_t<
has_norm_v<K>,
void
>
>
constexpr
auto
norm (const K &k)
{
return std::sqrt (norm2 (k));
}
//-------------------------------------------------------------------------
template <
typename K,
typename = std::enable_if_t<
has_norm_v<K>,
void
>
>
constexpr
auto
normalised (const K &k)
{
CHECK_NEZ (norm (k));
return k / norm (k);
}
//-------------------------------------------------------------------------
template <
typename K,
typename = std::enable_if_t<
has_norm_v<K>,
void
>
>
constexpr
bool
is_normalised (const K &k)
{
using value_type = typename K::value_type;
constexpr auto hi = value_type (1.00001);
constexpr auto lo = value_type (0.99999);
const auto n2 = norm2 (k);
return n2 < hi && n2 > lo;
}
///////////////////////////////////////////////////////////////////////////
2015-05-29 15:51:08 +10:00
template <
typename K,
typename = std::enable_if_t<
is_coord_v<K>, void
>
2015-05-29 15:51:08 +10:00
>
constexpr
K
abs (K k)
2015-05-29 15:51:08 +10:00
{
for (auto &v: k)
v = std::abs (v);
return k;
}
2016-08-11 14:23:32 +10:00
///////////////////////////////////////////////////////////////////////////
2015-10-12 23:56:41 +11:00
template <
typename BaseT,
typename = std::enable_if_t<is_coord_v<BaseT>>
2015-10-12 23:56:41 +11:00
>
constexpr
auto
pow (BaseT k, float p)
2015-10-12 23:56:41 +11:00
{
for (auto &v: k)
v = std::pow (v, p);
2015-10-12 23:56:41 +11:00
return k;
}
///////////////////////////////////////////////////////////////////////////
// root of sum of squares
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr typename K::value_type
hypot (K k)
{
return std::sqrt (sum (k * k));
}
2015-10-12 23:56:41 +11:00
2016-09-14 17:38:30 +10:00
///////////////////////////////////////////////////////////////////////////
template <
2017-11-23 17:24:11 +11:00
typename K,
2016-09-14 17:38:30 +10:00
typename T,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K> && std::is_same_v<T, typename K::value_type>, void
2016-09-14 17:38:30 +10:00
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
mod (K k, T t)
2016-09-14 17:38:30 +10:00
{
2016-09-14 17:42:11 +10:00
std::transform (
std::cbegin (k),
std::cend (k),
std::begin (k),
[t] (auto v) { return mod (v, t);
2019-05-12 14:34:32 +10:00
});
2017-11-23 17:24:11 +11:00
2016-09-14 17:38:30 +10:00
return k;
}
2019-05-12 14:34:32 +10:00
//-------------------------------------------------------------------------
template <
typename A,
typename B,
typename = std::enable_if_t<
is_coord_v<std::decay_t<A>> && is_coord_v<std::decay_t<B>>
>
>
constexpr auto
mod (A &&a, B &&b)
{
return arithmetic<
std::decay_t<A>,
std::decay_t<B>
>::eval (
2019-05-12 14:34:32 +10:00
std::modulus{},
std::forward<A> (a),
std::forward<B> (b)
);
}
2016-09-14 17:39:32 +10:00
///////////////////////////////////////////////////////////////////////////
// trigonometric functions
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<is_coord_v<K>,void>
2016-09-14 17:39:32 +10:00
>
2017-11-23 17:24:11 +11:00
constexpr auto
sin (K k)
2016-09-14 17:39:32 +10:00
{
std::transform (
std::cbegin (k),
std::cend (k),
std::begin (k),
[] (auto v) { return std::sin (v); }
);
return k;
}
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<is_coord_v<K>,void>
2016-09-14 17:39:32 +10:00
>
2017-11-23 17:24:11 +11:00
constexpr auto
cos (K k)
2016-09-14 17:39:32 +10:00
{
std::transform (
std::cbegin (k),
std::cend (k),
std::begin (k),
[] (auto v) { return std::cos (v); }
);
return k;
}
2016-09-14 17:38:30 +10:00
2015-03-07 03:18:32 +11:00
///////////////////////////////////////////////////////////////////////////
// logical element operators
/// return a coord type containing the max element at each offset
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2017-07-31 16:16:54 +10:00
>,
typename ...Args
>
2017-11-23 17:24:11 +11:00
constexpr auto
min (K a, K b, Args &&...args)
{
2017-11-23 17:24:11 +11:00
// the varargs must be the same types as the first two arguments
static_assert ((
... && std::is_same_v<
K,
std::decay_t<Args>
>
));
2017-07-31 16:16:54 +10:00
2017-11-23 17:24:11 +11:00
K out {};
for (std::size_t i = 0; i < K::elements; ++i)
2017-07-31 16:16:54 +10:00
out[i] = min (a[i], b[i], args[i]...);
return out;
}
2015-03-07 03:18:32 +11:00
///------------------------------------------------------------------------
// /return a coord type containing the max element at each offset
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2017-07-31 16:16:54 +10:00
>,
typename ...Args
>
2017-11-23 17:24:11 +11:00
constexpr auto
max (K a, K b, Args &&...args)
{
2017-11-23 17:24:11 +11:00
static_assert ((
... && std::is_same_v<
K,
std::decay_t<Args>
>
));
2017-07-31 16:16:54 +10:00
2017-11-23 17:24:11 +11:00
K out {};
for (std::size_t i = 0; i < K::elements; ++i)
2017-07-31 16:16:54 +10:00
out[i] = max (a[i], b[i], args[i]...);
return out;
}
//-------------------------------------------------------------------------
2017-08-24 12:49:59 +10:00
/// returns a coordinate type where each element has been clamped to the
/// range [lo,hi].
///
/// we specifically do not allow different coordinate types for val, lo,
/// and hi because the min and max calls are ill definied for varying
/// types (not because varying types would not be useful).
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
2018-05-03 21:43:48 +10:00
clamp (K k, K lo, K hi)
{
assert (all (lo <= hi));
return max (min (k, hi), lo);
}
2017-08-24 12:49:59 +10:00
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
2017-08-24 12:49:59 +10:00
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2017-08-24 12:49:59 +10:00
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
2018-05-03 21:43:48 +10:00
clamp (K k, typename K::value_type lo, K hi)
2017-08-24 12:49:59 +10:00
{
2018-05-03 21:43:48 +10:00
return clamp (k, K {lo}, hi);
2017-08-24 12:49:59 +10:00
}
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
2017-08-24 12:49:59 +10:00
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2017-08-24 12:49:59 +10:00
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
2018-05-03 21:43:48 +10:00
clamp (K k, K lo, typename K::value_type hi)
2017-08-24 12:49:59 +10:00
{
2018-05-03 21:43:48 +10:00
return clamp (k, lo, K {hi});
2017-08-24 12:49:59 +10:00
}
//-------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
2017-08-24 12:49:59 +10:00
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2017-08-24 12:49:59 +10:00
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
2018-05-03 21:43:48 +10:00
clamp (K k, typename K::value_type lo, typename K::value_type hi)
2017-08-24 12:49:59 +10:00
{
2018-05-03 21:43:48 +10:00
return clamp (k, K {lo}, K {hi});
2017-08-24 12:49:59 +10:00
}
2015-03-07 03:18:32 +11:00
///------------------------------------------------------------------------
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
min (const K &k)
{
return *std::min_element (std::cbegin (k), std::cend (k));
}
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
max (const K &k)
{
return *std::max_element (std::cbegin (k), std::cend (k));
}
2015-05-26 16:25:41 +10:00
2016-08-15 18:46:04 +10:00
///////////////////////////////////////////////////////////////////////////
template <
2017-11-23 17:24:11 +11:00
typename K,
2016-08-15 18:46:04 +10:00
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2016-08-15 18:46:04 +10:00
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
sum (const K &k)
2016-08-15 18:46:04 +10:00
{
// DO NOT USE cruft::sum(begin, end) from maths.hpp
//
// It would be nice to use kahan summation from maths.hpp but speed
// and simplicity is more important for these fixed sized
// coordinates. Infinities tend to crop up using these classes and
// they cause a few headaches in the kahan code.
//
// So, if the user wants kahan summation they can request it
// explicitly.
2017-11-23 17:24:11 +11:00
return std::accumulate (std::cbegin (k), std::cend (k), typename K::value_type{0});
2016-08-15 18:46:04 +10:00
}
2018-04-17 17:01:53 +10:00
//-------------------------------------------------------------------------
template <
typename K,
typename = std::enable_if_t<is_coord_v<K>>
>
auto
product (K const& k)
{
typename K::value_type accum = 1;
for (auto i: k)
accum *= i;
return accum;
}
2016-08-15 18:46:04 +10:00
///////////////////////////////////////////////////////////////////////////
2017-11-23 17:24:11 +11:00
#define VECTOR_OP(OP) \
template < \
typename A, \
typename B, \
typename = std::enable_if_t< \
is_coord_v<A> && \
is_coord_v<B> && \
A::elements == B::elements && \
std::is_same_v< \
typename A::value_type, \
typename B::value_type \
>, \
void \
> \
> \
constexpr auto \
operator OP (const A a, const B b) \
{ \
vector<A::elements,bool> out {}; \
for (std::size_t i = 0; i < A::elements; ++i) \
out[i] = a[i] OP b[i]; \
return out; \
2015-10-20 12:37:08 +11:00
}
VECTOR_OP(<)
VECTOR_OP(>)
VECTOR_OP(<=)
VECTOR_OP(>=)
VECTOR_OP(&&)
VECTOR_OP(||)
2015-10-20 12:37:08 +11:00
#undef VECTOR_OP
2017-11-23 17:24:11 +11:00
#define SCALAR_OP(OP) \
template < \
typename K, \
typename U, \
typename = std::enable_if_t< \
is_coord_v<K> && \
std::is_arithmetic_v<U>, \
void \
> \
> \
constexpr auto \
operator OP (const K &k, const U u) \
{ \
vector<K::elements,bool> out {}; \
for (std::size_t i = 0; i < K::elements; ++i) \
out[i] = k[i] OP u; \
return out; \
} \
\
template < \
typename K, \
typename U, \
typename = std::enable_if_t< \
is_coord_v<K> && \
std::is_arithmetic_v<U>, \
void \
> \
> \
constexpr auto \
operator OP (const U u, const K &k) \
{ \
vector<K::elements,bool> out {}; \
for (std::size_t i = 0; i < K::elements; ++i) \
out[i] = u OP k[i]; \
return out; \
}
2015-05-26 16:25:41 +10:00
SCALAR_OP(<)
SCALAR_OP(>)
SCALAR_OP(<=)
SCALAR_OP(>=)
SCALAR_OP(==)
SCALAR_OP(&&)
SCALAR_OP(||)
2015-05-26 16:25:41 +10:00
#undef SCALAR_OP
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <
std::size_t S,
template <std::size_t,typename> class K,
std::size_t ...I,
typename = std::enable_if_t<
is_coord_v<K<S,bool>>,
void
>
>
constexpr bool
any (const K<S,bool> k, std::index_sequence<I...>)
{
return (k[I] || ...);
}
};
///---------------------------------------------------------------------------
/// returns true if any element is true.
///
/// this function must be suitable for use in static_assert, so it must remain
/// constexpr.
///
/// we would ideally use std::any_of, but it is not constexpr.
/// we would ideally use range-for, but cbegin is not constexpr.
/// so... moar templates.
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
template <std::size_t,typename> class K,
typename = std::enable_if_t<
is_coord_v<K<S,bool>>, void
>,
typename Indices = std::make_index_sequence<S>
>
constexpr
2015-05-26 16:25:41 +10:00
bool
any (const K<S,bool> k)
{
return detail::any (k, Indices{});
}
///------------------------------------------------------------------------
/// returns true if the value is true.
///
/// provided so that templates may operate with the same calls for vectors
/// and scalars. eg, any (t >= 0 && t <= 1) should work for arbitrary
/// types.
constexpr
bool any (const bool val)
{
return val;
}
///////////////////////////////////////////////////////////////////////////
namespace detail {
template <
std::size_t S,
2017-08-28 12:25:23 +10:00
template <std::size_t,typename> class K,
std::size_t ...I,
typename = std::enable_if_t<
is_coord_v<K<S,bool>>,
void
>
>
constexpr bool
all (const K<S,bool> k, std::index_sequence<I...>)
{
return (k[I] && ...);
}
}
//-------------------------------------------------------------------------
/// returns true if all elements are true.
///
/// this function must be suitable for use in static_assert, so it must be
/// constexpr.
///
/// we would ideally use std::all_of, but it is not constexpr.
/// we would ideally use range-for, but cbegin is not constexpr.
/// so... moar templates.
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
template <std::size_t,typename> class K,
typename = std::enable_if_t<
is_coord_v<K<S,bool>>, void
>,
typename Indices = std::make_index_sequence<S>
>
constexpr
bool
all (const K<S,bool> k)
{
return detail::all (k, Indices {});
}
2015-09-29 18:06:52 +10:00
///------------------------------------------------------------------------
/// returns true if the value is true.
///
/// provided so that templates may operate with the same calls for vectors
/// and scalars. eg, all (t >= 0 && t <= 1) should work for either type.
constexpr bool
all (bool val)
{
return val;
}
2017-07-31 15:41:33 +10:00
///------------------------------------------------------------------------
/// returns an instance of K elementwise using a when s is true, and b
/// otherwise. ie, k[i] = s[i] ? a[i] : b[i];
///
/// corresponds to the function `select' from OpenCL.
template <
2017-11-23 17:24:11 +11:00
typename K,
2017-07-31 15:41:33 +10:00
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
2017-07-31 15:41:33 +10:00
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
select (vector<K::elements,bool> s, K a, K b)
2017-07-31 15:41:33 +10:00
{
2017-11-23 17:24:11 +11:00
K k {};
for (std::size_t i = 0; i < K::elements; ++i)
2017-07-31 15:41:33 +10:00
k[i] = s[i] ? a[i] : b[i];
return k;
}
2018-05-03 18:32:08 +10:00
template <
size_t S,
typename T,
typename K,
typename = std::enable_if_t<
is_coord_v<K> && !is_coord_v<T>
>
>
constexpr auto
select (vector<S,bool> s, K a, T b)
{
return select (s, a, K {b});
}
template <
size_t S,
typename T,
typename = std::enable_if_t<!is_coord_v<T>>
>
constexpr auto
select (vector<S,bool> s, T a, T b)
{
return select (s, vector<S,T> {a}, vector<S,T> {b});
}
2015-09-29 18:06:52 +10:00
///////////////////////////////////////////////////////////////////////////
// return the componentwise floor of the coordinate type
//
// don't use std::floor, it's _really_ slow in comparison.
//
// ideally we'd use SIMD or other more useful instructions here.
template <
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
is_coord_v<K> && std::is_floating_point_v<typename K::value_type>
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
floor (const K &k)
2015-09-29 18:06:52 +10:00
{
2017-11-23 17:24:11 +11:00
K out {};
std::transform (
std::cbegin (k),
std::cend (k),
std::begin (out),
[] (auto i) {
return i >= 0 ? static_cast<intmax_t> (i) : static_cast<intmax_t> (i) - 1;
}
);
return out;
2015-09-29 18:06:52 +10:00
}
2017-06-15 16:24:44 +10:00
2020-06-22 12:58:21 +10:00
//-------------------------------------------------------------------------
template <
typename K,
typename = std::enable_if_t<
is_coord_v<K> && std::is_floating_point_v<typename K::value_type>
>
> constexpr auto
ceil (K const &k)
{
K res {};
for (std::size_t i = 0; i < K::elements; ++i)
res[i] = std::ceil (k[i]);
return res;
}
///------------------------------------------------------------------------
/// Return the fractional part of a real value.
///
2020-06-22 12:58:21 +10:00
/// This is an extraordinarily naive implementation. We avoid doing
/// explicit casts here in the hope that floor and sub is more efficient
/// (ie, keeps floats as floats in registers).
template <
typename K,
typename = std::enable_if_t<
is_coord_v<K> &&
std::is_floating_point_v<
typename K::value_type
>
>
>
constexpr auto
frac (const K k)
{
return k - floor (k);
}
///////////////////////////////////////////////////////////////////////////
/// shifts all elements `num' indices to the right, setting the left-most
/// `num' indices to the value `fill'.
///
/// num must be between 0 and S. when 0 it is equivalent to an ordinary
/// fill, when S it is equivalent to a noop.
template<
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
rshift (const K k, const int num, const K fill)
{
CHECK_INCLUSIVE (num, 0, int (K::elements));
2017-11-23 17:24:11 +11:00
K res {};
2017-11-23 17:24:11 +11:00
std::copy_n (std::cbegin (k), K::elements - num, std::begin (res) + num);
std::copy_n (std::cbegin (fill), num, std::begin (res));
return res;
}
//-------------------------------------------------------------------------
template<
2017-11-23 17:24:11 +11:00
typename K,
typename = std::enable_if_t<
2017-11-23 17:24:11 +11:00
is_coord_v<K>, void
>
>
2017-11-23 17:24:11 +11:00
constexpr auto
rshift (const K k, const int num, typename K::value_type fill)
{
2017-11-23 17:24:11 +11:00
return rshift (k, num, K {fill});
}
2018-05-04 17:06:18 +10:00
template <
typename K,
typename = std::enable_if_t<
is_coord_v<K>
>
>
constexpr auto
lshift (const K k, const int places, typename K::value_type fill)
{
K res {};
std::copy_n (std::cbegin (k) + places, K::elements - places, std::begin (res));
std::fill_n (std::begin (res) + K::elements - places, places, fill);
return res;
}
2019-12-18 10:52:10 +11:00
//-------------------------------------------------------------------------
template <
std::size_t S,
typename T
>
vector<S,T>
to_radians (vector<S,T> const &val)
{
return ::cruft::invoke<vector<S,T>> (
::cruft::sin<T>,
val
);
}
//-------------------------------------------------------------------------
template <
std::size_t S,
typename T,
typename = std::enable_if_t<std::is_floating_point_v<T>>
>
vector<S,bool>
isfinite (vector<S,T> const &val)
{
vector<S,bool> res;
for (std::size_t i = 0; i < S; ++i)
res[i] = std::isfinite (val[i]);
return res;
}
}
2017-06-15 16:24:44 +10:00
///////////////////////////////////////////////////////////////////////////////
2017-06-15 16:24:44 +10:00
#include <tuple>
namespace std {
/// returns the dimensions of a coordinate type.
///
/// specifically required for structured bindings support.
///
/// \tparam S dimensions
/// \tparam T data type
/// \tparam K coordinate class
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
2017-06-15 16:24:44 +10:00
typename T,
2017-11-23 17:24:11 +11:00
template <std::size_t,typename> typename K
2017-06-15 16:24:44 +10:00
>
class tuple_size<K<S,T>> : public std::enable_if_t<
::cruft::is_coord_v<K<S,T>>,
2017-11-23 17:24:11 +11:00
std::integral_constant<std::size_t, S>
2017-06-15 16:24:44 +10:00
> { };
/// indicates the type at a given index of a coordinate type
///
/// specifically required for structured bindings support.
///
/// \tparam I data index
/// \tparam S dimensionality of the coordinate
/// \tparam T data type for the coordinate
/// \tparam K the underlying coordinate class
template <
2017-08-28 12:25:23 +10:00
std::size_t I,
std::size_t S,
2017-06-15 16:24:44 +10:00
typename T,
2017-11-23 17:24:11 +11:00
template <std::size_t,typename> typename K
2017-06-15 16:24:44 +10:00
>
2017-11-23 17:24:11 +11:00
class tuple_element<I,K<S,T>> : public std::enable_if<
::cruft::is_coord_v<K<S,T>>,
2017-06-15 16:24:44 +10:00
T
2017-11-23 17:24:11 +11:00
> {};
2017-06-15 16:24:44 +10:00
}
///////////////////////////////////////////////////////////////////////////////
#include "../hash.hpp"
namespace std {
//-------------------------------------------------------------------------
template <
2017-08-28 12:25:23 +10:00
std::size_t S,
typename T,
2017-11-23 17:24:11 +11:00
template <std::size_t,typename> typename K
>
2017-11-23 17:24:11 +11:00
struct hash<K<S,T>> : enable_if<
::cruft::is_coord_v<K<S,T>>
> {
constexpr std::size_t
operator() (K<S,T> k) const noexcept
{
std::size_t v = 0xdeadbeef;
for (T const &t: k)
v = ::cruft::hash::mix (t, v);
return v;
}
};
2018-03-12 23:04:17 +11:00
//-------------------------------------------------------------------------
2018-03-12 23:04:17 +11:00
template <
typename CoordT,
typename = std::enable_if_t<
::cruft::is_coord_v<CoordT>, void
2018-03-12 23:04:17 +11:00
>
>
auto cos (CoordT val)
{
return ::cruft::invoke<CoordT> (::cruft::cos<typename CoordT::value_type>, val);
}
2018-03-12 23:04:17 +11:00
//-------------------------------------------------------------------------
template <
typename CoordT,
typename = std::enable_if_t<
::cruft::is_coord_v<CoordT>, void
>
>
auto sin (CoordT val)
{
return ::cruft::invoke<CoordT> (::cruft::sin<typename CoordT::value_type>, val);
2018-03-12 23:04:17 +11:00
}
};