/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2022 Blender Foundation. */ #pragma once /** \file * \ingroup bli */ #include #include #include #include #include "BLI_utildefines.h" namespace blender { /* clang-format off */ template using as_uint_type = std::conditional_t>>>; /* clang-format on */ template struct vec_struct_base { std::array values; }; template struct vec_struct_base { T x, y; }; template struct vec_struct_base { T x, y, z; }; template struct vec_struct_base { T x, y, z, w; }; template void unroll_impl(Fn fn, std::index_sequence /*indices*/) { (fn(I), ...); } /** * Variadic templates are used to unroll loops manually. This helps GCC avoid branching during math * operations and makes the code generation more explicit and predictable. Unrolling should always * be worth it because the vector size is expected to be small. */ template void unroll(Fn fn) { unroll_impl(fn, std::make_index_sequence()); } namespace math { template uint64_t vector_hash(const T &vec) { BLI_STATIC_ASSERT(T::type_length <= 4, "Longer types need to implement vector_hash themself."); const typename T::uint_type &uvec = *reinterpret_cast(&vec); uint64_t result; result = uvec[0] * uint64_t(435109); if constexpr (T::type_length > 1) { result ^= uvec[1] * uint64_t(380867); } if constexpr (T::type_length > 2) { result ^= uvec[2] * uint64_t(1059217); } if constexpr (T::type_length > 3) { result ^= uvec[3] * uint64_t(2002613); } return result; } } // namespace math template struct vec_base : public vec_struct_base { static constexpr int type_length = Size; using base_type = T; using uint_type = vec_base, Size>; vec_base() = default; explicit vec_base(uint value) { for (int i = 0; i < Size; i++) { (*this)[i] = T(value); } } explicit vec_base(int value) { for (int i = 0; i < Size; i++) { (*this)[i] = T(value); } } explicit vec_base(float value) { for (int i = 0; i < Size; i++) { (*this)[i] = T(value); } } explicit vec_base(double value) { for (int i = 0; i < Size; i++) { (*this)[i] = T(value); } } /* Workaround issue with template BLI_ENABLE_IF((Size == 2)) not working. */ #define BLI_ENABLE_IF_VEC(_size, _test) int S = _size, BLI_ENABLE_IF((S _test)) template vec_base(T _x, T _y) { (*this)[0] = _x; (*this)[1] = _y; } template vec_base(T _x, T _y, T _z) { (*this)[0] = _x; (*this)[1] = _y; (*this)[2] = _z; } template vec_base(T _x, T _y, T _z, T _w) { (*this)[0] = _x; (*this)[1] = _y; (*this)[2] = _z; (*this)[3] = _w; } /** Mixed scalar-vector constructors. */ template constexpr vec_base(const vec_base &xy, T z) : vec_base(T(xy.x), T(xy.y), z) { } template constexpr vec_base(T x, const vec_base &yz) : vec_base(x, T(yz.x), T(yz.y)) { } template vec_base(vec_base xyz, T w) : vec_base(T(xyz.x), T(xyz.y), T(xyz.z), T(w)) { } template vec_base(T x, vec_base yzw) : vec_base(T(x), T(yzw.x), T(yzw.y), T(yzw.z)) { } template vec_base(vec_base xy, vec_base zw) : vec_base(T(xy.x), T(xy.y), T(zw.x), T(zw.y)) { } template vec_base(vec_base xy, T z, T w) : vec_base(T(xy.x), T(xy.y), T(z), T(w)) { } template vec_base(T x, vec_base yz, T w) : vec_base(T(x), T(yz.x), T(yz.y), T(w)) { } template vec_base(T x, T y, vec_base zw) : vec_base(T(x), T(y), T(zw.x), T(zw.y)) { } /** Masking. */ template Size)> explicit vec_base(const vec_base &other) { for (int i = 0; i < Size; i++) { (*this)[i] = T(other[i]); } } #undef BLI_ENABLE_IF_VEC /** Conversion from pointers (from C-style vectors). */ vec_base(const T *ptr) { unroll([&](auto i) { (*this)[i] = ptr[i]; }); } template))> explicit vec_base(const U *ptr) { unroll([&](auto i) { (*this)[i] = ptr[i]; }); } vec_base(const T (*ptr)[Size]) : vec_base(static_cast(ptr[0])) { } /** Conversion from other vector types. */ template explicit vec_base(const vec_base &vec) { unroll([&](auto i) { (*this)[i] = T(vec[i]); }); } /** C-style pointer dereference. */ operator const T *() const { return reinterpret_cast(this); } operator T *() { return reinterpret_cast(this); } /** Array access. */ const T &operator[](int index) const { BLI_assert(index >= 0); BLI_assert(index < Size); return reinterpret_cast(this)[index]; } T &operator[](int index) { BLI_assert(index >= 0); BLI_assert(index < Size); return reinterpret_cast(this)[index]; } /** Internal Operators Macro. */ #define BLI_INT_OP(_T) template))> /** Arithmetic operators. */ friend vec_base operator+(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] + b[i]; }); return result; } friend vec_base operator+(const vec_base &a, const T &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] + b; }); return result; } friend vec_base operator+(const T &a, const vec_base &b) { return b + a; } vec_base &operator+=(const vec_base &b) { unroll([&](auto i) { (*this)[i] += b[i]; }); return *this; } vec_base &operator+=(const T &b) { vec_base result; unroll([&](auto i) { (*this)[i] += b; }); return result; } friend vec_base operator-(const vec_base &a) { vec_base result; unroll([&](auto i) { result[i] = -a[i]; }); return result; } friend vec_base operator-(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] - b[i]; }); return result; } friend vec_base operator-(const vec_base &a, const T &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] - b; }); return result; } friend vec_base operator-(const T &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a - b[i]; }); return result; } vec_base &operator-=(const vec_base &b) { unroll([&](auto i) { (*this)[i] -= b[i]; }); return *this; } vec_base &operator-=(const T &b) { unroll([&](auto i) { (*this)[i] -= b; }); return *this; } friend vec_base operator*(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] * b[i]; }); return result; } template friend vec_base operator*(const vec_base &a, FactorT b) { vec_base result; unroll([&](auto i) { result[i] = a[i] * b; }); return result; } friend vec_base operator*(T a, const vec_base &b) { return b * a; } vec_base &operator*=(T b) { unroll([&](auto i) { (*this)[i] *= b; }); return *this; } vec_base &operator*=(const vec_base &b) { unroll([&](auto i) { (*this)[i] *= b[i]; }); return *this; } friend vec_base operator/(const vec_base &a, const vec_base &b) { for (int i = 0; i < Size; i++) { BLI_assert(b[i] != T(0)); } vec_base result; unroll([&](auto i) { result[i] = a[i] / b[i]; }); return result; } friend vec_base operator/(const vec_base &a, T b) { BLI_assert(b != T(0)); vec_base result; unroll([&](auto i) { result[i] = a[i] / b; }); return result; } friend vec_base operator/(T a, const vec_base &b) { for (int i = 0; i < Size; i++) { BLI_assert(b[i] != T(0)); } vec_base result; unroll([&](auto i) { result[i] = a / b[i]; }); return result; } vec_base &operator/=(T b) { BLI_assert(b != T(0)); unroll([&](auto i) { (*this)[i] /= b; }); return *this; } vec_base &operator/=(const vec_base &b) { BLI_assert(b != T(0)); unroll([&](auto i) { (*this)[i] /= b[i]; }); return *this; } /** Binary operators. */ BLI_INT_OP(T) friend vec_base operator&(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] & b[i]; }); return result; } BLI_INT_OP(T) friend vec_base operator&(const vec_base &a, T b) { vec_base result; unroll([&](auto i) { result[i] = a[i] & b; }); return result; } BLI_INT_OP(T) friend vec_base operator&(T a, const vec_base &b) { return b & a; } BLI_INT_OP(T) vec_base &operator&=(T b) { unroll([&](auto i) { (*this)[i] &= b; }); return *this; } BLI_INT_OP(T) vec_base &operator&=(const vec_base &b) { unroll([&](auto i) { (*this)[i] &= b[i]; }); return *this; } BLI_INT_OP(T) friend vec_base operator|(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] | b[i]; }); return result; } BLI_INT_OP(T) friend vec_base operator|(const vec_base &a, T b) { vec_base result; unroll([&](auto i) { result[i] = a[i] | b; }); return result; } BLI_INT_OP(T) friend vec_base operator|(T a, const vec_base &b) { return b | a; } BLI_INT_OP(T) vec_base &operator|=(T b) { unroll([&](auto i) { (*this)[i] |= b; }); return *this; } BLI_INT_OP(T) vec_base &operator|=(const vec_base &b) { unroll([&](auto i) { (*this)[i] |= b[i]; }); return *this; } BLI_INT_OP(T) friend vec_base operator^(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] ^ b[i]; }); return result; } BLI_INT_OP(T) friend vec_base operator^(const vec_base &a, T b) { vec_base result; unroll([&](auto i) { result[i] = a[i] ^ b; }); return result; } BLI_INT_OP(T) friend vec_base operator^(T a, const vec_base &b) { return b ^ a; } BLI_INT_OP(T) vec_base &operator^=(T b) { unroll([&](auto i) { (*this)[i] ^= b; }); return *this; } BLI_INT_OP(T) vec_base &operator^=(const vec_base &b) { unroll([&](auto i) { (*this)[i] ^= b[i]; }); return *this; } BLI_INT_OP(T) friend vec_base operator~(const vec_base &a) { vec_base result; unroll([&](auto i) { result[i] = ~a[i]; }); return result; } /** Bit-shift operators. */ BLI_INT_OP(T) friend vec_base operator<<(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] << b[i]; }); return result; } BLI_INT_OP(T) friend vec_base operator<<(const vec_base &a, T b) { vec_base result; unroll([&](auto i) { result[i] = a[i] << b; }); return result; } BLI_INT_OP(T) vec_base &operator<<=(T b) { unroll([&](auto i) { (*this)[i] <<= b; }); return *this; } BLI_INT_OP(T) vec_base &operator<<=(const vec_base &b) { unroll([&](auto i) { (*this)[i] <<= b[i]; }); return *this; } BLI_INT_OP(T) friend vec_base operator>>(const vec_base &a, const vec_base &b) { vec_base result; unroll([&](auto i) { result[i] = a[i] >> b[i]; }); return result; } BLI_INT_OP(T) friend vec_base operator>>(const vec_base &a, T b) { vec_base result; unroll([&](auto i) { result[i] = a[i] >> b; }); return result; } BLI_INT_OP(T) vec_base &operator>>=(T b) { unroll([&](auto i) { (*this)[i] >>= b; }); return *this; } BLI_INT_OP(T) vec_base &operator>>=(const vec_base &b) { unroll([&](auto i) { (*this)[i] >>= b[i]; }); return *this; } /** Modulo operators. */ BLI_INT_OP(T) friend vec_base operator%(const vec_base &a, const vec_base &b) { for (int i = 0; i < Size; i++) { BLI_assert(b[i] != T(0)); } vec_base result; unroll([&](auto i) { result[i] = a[i] % b[i]; }); return result; } BLI_INT_OP(T) friend vec_base operator%(const vec_base &a, T b) { BLI_assert(b != 0); vec_base result; unroll([&](auto i) { result[i] = a[i] % b; }); return result; } BLI_INT_OP(T) friend vec_base operator%(T a, const vec_base &b) { BLI_assert(b != T(0)); vec_base result; unroll([&](auto i) { result[i] = a % b[i]; }); return result; } #undef BLI_INT_OP /** Compare. */ friend bool operator==(const vec_base &a, const vec_base &b) { for (int i = 0; i < Size; i++) { if (a[i] != b[i]) { return false; } } return true; } friend bool operator!=(const vec_base &a, const vec_base &b) { return !(a == b); } /** Misc. */ uint64_t hash() const { return math::vector_hash(*this); } friend std::ostream &operator<<(std::ostream &stream, const vec_base &v) { stream << "("; for (int i = 0; i < Size; i++) { stream << v[i]; if (i != Size - 1) { stream << ", "; } } stream << ")"; return stream; } }; using char3 = blender::vec_base; using uchar3 = blender::vec_base; using uchar4 = blender::vec_base; using int2 = vec_base; using int3 = vec_base; using int4 = vec_base; using uint2 = vec_base; using uint3 = vec_base; using uint4 = vec_base; using short3 = blender::vec_base; using ushort2 = vec_base; using ushort3 = blender::vec_base; using ushort4 = blender::vec_base; using float2 = vec_base; using float3 = vec_base; using float4 = vec_base; using double2 = vec_base; using double3 = vec_base; using double4 = vec_base; } // namespace blender