/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2012-2017 Danny Robson */ #ifndef __UTIL_COORDS_OPS #define __UTIL_COORDS_OPS #include "fwd.hpp" #include "traits.hpp" #include "../debug.hpp" #include "../maths.hpp" #include "../preprocessor.hpp" #include "../types/bits.hpp" #include #include #include #include #include namespace util { /////////////////////////////////////////////////////////////////////////// // 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 struct ops { }; //------------------------------------------------------------------------- // vector operators template struct ops< ValueA, ValueB, std::enable_if_t< is_coord_v && is_coord_v && arity::value == arity::value && std::is_same_v< typename ValueA::value_type, typename ValueB::value_type >, void > > { template static constexpr auto arithmetic (OpT op, ValueA a, ValueB b) { result_t out {}; for (std::size_t i = 0; i < ValueA::elements; ++i) out[i] = op (a[i], b[i]); return out; } template static constexpr ValueA& assignment (OpT op, ValueA &a, ValueB b) { for (std::size_t i = 0; i < ValueA::elements; ++i) a[i] = op (a[i], b[i]); return a; } }; //------------------------------------------------------------------------- // vector-scalar operations // // we allow scalar types which can be naturally promoted to the vector's // value_type template < typename CoordT, typename ScalarT > struct ops< CoordT, ScalarT, std::enable_if_t< has_scalar_op_v && is_coord_v && std::is_same_v< typename CoordT::value_type, std::common_type_t< typename CoordT::value_type, ScalarT > >, void > > { template static constexpr CoordT& assignment (OpT &&op, CoordT &c, ScalarT s) { for (size_t i = 0; i < CoordT::elements; ++i) c[i] = op (c[i], s); return c; } template static constexpr CoordT arithmetic (OpT &&op, CoordT c, ScalarT s) { CoordT out {}; for (size_t i = 0; i < CoordT::elements; ++i) out[i] = op (c[i], s); return out; } }; //------------------------------------------------------------------------- // scalar-vector operations template struct ops< ScalarT, CoordT, std::enable_if_t< has_scalar_op_v && std::is_arithmetic_v && std::is_same_v, void > > { template static constexpr CoordT arithmetic (OpT op, ScalarT s, CoordT c) { CoordT out {}; for (size_t i = 0; i < CoordT::elements; ++i) out[i] = op (s, c[i]); return out; } }; /////////////////////////////////////////////////////////////////////////// template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator+ (A &&a, B &&b) { return ops,std::decay_t>::template arithmetic (std::plus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator- (A &&a, B &&b) { return ops,std::decay_t>::template arithmetic (std::minus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator* (A &&a, B &&b) { return ops,std::decay_t>::template arithmetic (std::multiplies{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator/ (A &&a, B &&b) { return ops,std::decay_t>::template arithmetic (std::divides{}, a, b); } //------------------------------------------------------------------------- template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator += (A &&a, B &&b) { return ops, std::decay_t>::template assignment (std::plus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator -= (A &&a, B &&b) { return ops, std::decay_t>::template assignment (std::plus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator *= (A &&a, B &&b) { return ops, std::decay_t>::template assignment (std::plus{}, a, b); } template < typename A, typename B, typename = std::enable_if_t< (is_coord_v> || is_coord_v>) && (is_coord_v> || std::is_arithmetic_v>) && (is_coord_v> || std::is_arithmetic_v>) > > constexpr auto operator /= (A &&a, B &&b) { return ops, std::decay_t>::template assignment (std::plus{}, a, b); } /////////////////////////////////////////////////////////////////////////// // unary operators #define UNARY_OP(OP) \ template < \ std::size_t S, \ typename T, \ template class K, \ typename = std::enable_if_t< \ is_coord_v>, void \ > \ > \ constexpr \ auto \ operator OP (K k) \ { \ K ())> out{}; \ \ for (std::size_t i = 0; i < S; ++i) \ out[i] = OP k[i]; \ \ return out; \ } UNARY_OP(!) UNARY_OP(~) UNARY_OP(+) UNARY_OP(-) #undef UNARY_OP /////////////////////////////////////////////////////////////////////////// // logic operators /// elementwise equality operator template < typename ValueT, typename = std::enable_if_t< is_coord_v, void > > constexpr bool operator== (const ValueT a, const ValueT b) { using value_type = typename ValueT::value_type; bool (*predicate)(const value_type&, const value_type&) = almost_equal; return std::equal (std::cbegin (a), std::cend (a), std::cbegin (b), predicate); } ///------------------------------------------------------------------------ /// elementwise inquality operator template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr bool operator!= (K a, K b) { return !(a == b); } //------------------------------------------------------------------------- template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr bool almost_zero (const K &k) { return std::all_of ( std::cbegin (k), std::cend (k), [] (T t) { return almost_equal (t); } ); } /////////////////////////////////////////////////////////////////////////// // special operators /// point-point subtraction giving a vector difference template < std::size_t S, typename T, typename U > constexpr vector> operator- (point a, point b) { vector> out {}; for (std::size_t i = 0; i < S; ++i) out[i] = a[i] - b[i]; return out; } //------------------------------------------------------------------------- template < std::size_t S, typename T, typename U, typename = std::enable_if_t< std::is_arithmetic::value && std::is_arithmetic::value, void > > constexpr vector> operator- (U u, point p) { return point {u} - p; } /////////////////////////////////////////////////////////////////////////// template < std::size_t S, typename T > constexpr T dot (const T (&a)[S], const T (&b)[S]) { T sum = 0; for (std::size_t i = 0; i < S; ++i) sum += a[i] * b[i]; return sum; } template < std::size_t S, typename T, typename K, typename = std::enable_if_t< is_coord_v && std::is_same_v && K::elements == S, void > > constexpr T dot (const T (&a)[S], K k) { return dot (a, k.data); } template < std::size_t S, typename T, typename K, typename = std::enable_if_t< is_coord_v && std::is_same_v && K::elements == S, void > > constexpr T dot (K k, const T (&a)[S]) { return dot (k.data, a); } //------------------------------------------------------------------------- template < std::size_t S, typename T, template class A, template class B, typename = std::enable_if_t< is_coord_v> && is_coord_v>, void > > constexpr T dot (A a, B b) { return dot (a.data, b.data); } //------------------------------------------------------------------------- template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr T dot (K a, const T (&b)[S]) { return dot (a.data, b); } //------------------------------------------------------------------------- template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr T dot (const T (&a)[S], K b) { return dot (a, b.data); } /////////////////////////////////////////////////////////////////////////// template < typename K, typename = std::enable_if_t,void> > constexpr auto norm2 (const K &k) { return dot (k, k); } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< has_norm_v, void > > constexpr auto norm (const K &k) { return std::sqrt (norm2 (k)); } //------------------------------------------------------------------------- template < typename K, typename = std::enable_if_t< has_norm_v, 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, void > > constexpr bool is_normalised (const K &k) { return almost_equal (norm2 (k), 1); } /////////////////////////////////////////////////////////////////////////// template < typename K, typename = std::enable_if_t< is_coord_v, void > > constexpr K abs (K k) { for (auto &v: k) v = std::abs (v); return k; } /////////////////////////////////////////////////////////////////////////// template < typename K, typename = std::enable_if_t< is_coord_v, void > > constexpr K pow (K k) { for (auto &v: k) v = pow (v); return k; } /////////////////////////////////////////////////////////////////////////// // root of sum of squares template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr T hypot (K k) { return std::sqrt (sum (k * k)); } /////////////////////////////////////////////////////////////////////////// template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr K mod (K k, T t) { std::transform ( std::cbegin (k), std::cend (k), std::begin (k), [t] (auto v) { return mod (v, t); }); return k; } /////////////////////////////////////////////////////////////////////////// // trigonometric functions template < std::size_t S, typename T, template class K, typename = std::enable_if_t>,void> > constexpr K sin (K k) { std::transform ( std::cbegin (k), std::cend (k), std::begin (k), [] (auto v) { return std::sin (v); } ); return k; } //------------------------------------------------------------------------- template < std::size_t S, typename T, template class K, typename = std::enable_if_t>,void> > constexpr K cos (K k) { std::transform ( std::cbegin (k), std::cend (k), std::begin (k), [] (auto v) { return std::cos (v); } ); return k; } /////////////////////////////////////////////////////////////////////////// // logical element operators /// return a coord type containing the max element at each offset template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr K min (K a, K b) { K out {}; for (std::size_t i = 0; i < S; ++i) out[i] = min (a[i], b[i]); return out; } ///------------------------------------------------------------------------ // /return a coord type containing the max element at each offset template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr K max (K a, K b) { K out {}; for (std::size_t i = 0; i < S; ++i) out[i] = max (a[i], b[i]); return out; } ///------------------------------------------------------------------------ template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr T min (const K k) { return *std::min_element (std::cbegin (k), std::cend (k)); } template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr T max (const K k) { return *std::max_element (std::cbegin (k), std::cend (k)); } /////////////////////////////////////////////////////////////////////////// template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr T sum (const K k) { // DO NOT USE util::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. return std::accumulate (std::cbegin (k), std::cend (k), T{0}); } /////////////////////////////////////////////////////////////////////////// #define VECTOR_OP(OP) \ template < \ std::size_t S, \ typename T, \ typename U, \ template class A, \ template class B, \ typename = std::enable_if_t< \ is_coord_v> && is_coord_v>, void \ > \ > \ constexpr \ vector \ operator OP (const A a, const B b) \ { \ vector out {}; \ for (std::size_t i = 0; i < S; ++i) \ out[i] = a[i] OP b[i]; \ return out; \ } VECTOR_OP(<) VECTOR_OP(>) VECTOR_OP(<=) VECTOR_OP(>=) VECTOR_OP(&&) VECTOR_OP(||) #undef VECTOR_OP #define SCALAR_OP(OP) \ template < \ std::size_t S, \ typename T, \ typename U, \ template class K, \ typename = std::enable_if_t< \ is_coord_v>, void \ > \ > \ constexpr \ vector \ operator OP (const K k, const U u) \ { \ vector out {}; \ for (std::size_t i = 0; i < S; ++i) \ out[i] = k[i] OP u; \ return out; \ } SCALAR_OP(<) SCALAR_OP(>) SCALAR_OP(<=) SCALAR_OP(>=) SCALAR_OP(&&) SCALAR_OP(||) #undef SCALAR_OP //------------------------------------------------------------------------- template < std::size_t S, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr bool any (const K k) { return std::any_of (std::cbegin (k), std::cend (k), identity); } //------------------------------------------------------------------------- template < std::size_t S, template class K, typename = std::enable_if_t< is_coord_v>, void > > constexpr bool all (const K k) { return std::all_of (std::cbegin (k), std::cend (k), identity); } /////////////////////////////////////////////////////////////////////////// template < std::size_t S, typename T, template class K, typename = std::enable_if_t< is_coord_v> && std::is_floating_point::value, void > > constexpr K floor (const K k) { T (*floor_func)(T) = std::floor; K out {}; std::transform (std::cbegin (k), std::cend (k), std::begin (out), floor_func); return out; } /// 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, std::size_t S, typename T, template< std::size_t, typename > class K > const std::enable_if_t< is_coord_v>, T >& get (const K &k) { static_assert (I < S); 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, std::size_t S, typename T, template< std::size_t, typename > class K > std::enable_if_t< is_coord_v>, T >& get (K &k) { static_assert (I < S); return k[I]; }; } /////////////////////////////////////////////////////////////////////////////// #include 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 < std::size_t S, typename T, template< std::size_t, typename > class K > class tuple_size> : public std::enable_if_t< ::util::is_coord_v>, std::integral_constant > { }; /// 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 < std::size_t I, std::size_t S, typename T, template< std::size_t, typename > class K > class tuple_element< I, K > : public enable_if< ::util::is_coord_v>, T > { }; } /////////////////////////////////////////////////////////////////////////////// #include #include "../hash.hpp" namespace std { template < std::size_t S, typename T, template < std::size_t,typename > class K > struct hash< K > : public ::std::enable_if< ::util::is_coord_v> > { uint32_t operator() (K k) const { uint32_t v = 0xdeadbeef; for (auto t: k) v = ::util::hash::mix (t, v); return v; } }; } #endif