671 lines
15 KiB
C++
671 lines
15 KiB
C++
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <ostream>
|
|
#include <type_traits>
|
|
|
|
#include "BLI_unroll.hh"
|
|
#include "BLI_utildefines.h"
|
|
|
|
namespace blender {
|
|
|
|
/* clang-format off */
|
|
template<typename T>
|
|
using as_uint_type = std::conditional_t<sizeof(T) == sizeof(uint8_t), uint8_t,
|
|
std::conditional_t<sizeof(T) == sizeof(uint16_t), uint16_t,
|
|
std::conditional_t<sizeof(T) == sizeof(uint32_t), uint32_t,
|
|
std::conditional_t<sizeof(T) == sizeof(uint64_t), uint64_t, void>>>>;
|
|
/* clang-format on */
|
|
|
|
template<typename T, int Size> struct vec_struct_base {
|
|
std::array<T, Size> values;
|
|
};
|
|
|
|
template<typename T> struct vec_struct_base<T, 2> {
|
|
T x, y;
|
|
};
|
|
|
|
template<typename T> struct vec_struct_base<T, 3> {
|
|
T x, y, z;
|
|
};
|
|
|
|
template<typename T> struct vec_struct_base<T, 4> {
|
|
T x, y, z, w;
|
|
};
|
|
|
|
namespace math {
|
|
|
|
template<typename T> 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<const typename T::uint_type *>(&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<typename T, int Size> struct VecBase : public vec_struct_base<T, Size> {
|
|
|
|
BLI_STATIC_ASSERT(alignof(T) <= sizeof(T),
|
|
"VecBase is not compatible with aligned type for now.");
|
|
|
|
/* 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))
|
|
|
|
static constexpr int type_length = Size;
|
|
|
|
using base_type = T;
|
|
using uint_type = VecBase<as_uint_type<T>, Size>;
|
|
|
|
VecBase() = default;
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, > 1)> explicit VecBase(T value)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
(*this)[i] = value;
|
|
}
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))>
|
|
explicit VecBase(U value) : VecBase(T(value))
|
|
{
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, == 1)> VecBase(T _x)
|
|
{
|
|
(*this)[0] = _x;
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, == 2)> VecBase(T _x, T _y)
|
|
{
|
|
(*this)[0] = _x;
|
|
(*this)[1] = _y;
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, == 3)> VecBase(T _x, T _y, T _z)
|
|
{
|
|
(*this)[0] = _x;
|
|
(*this)[1] = _y;
|
|
(*this)[2] = _z;
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, == 4)> VecBase(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<typename U, BLI_ENABLE_IF_VEC(Size, == 3)>
|
|
constexpr VecBase(const VecBase<U, 2> &xy, T z) : VecBase(T(xy.x), T(xy.y), z)
|
|
{
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF_VEC(Size, == 3)>
|
|
constexpr VecBase(T x, const VecBase<U, 2> &yz) : VecBase(x, T(yz.x), T(yz.y))
|
|
{
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF_VEC(Size, == 4)>
|
|
VecBase(VecBase<U, 3> xyz, T w) : VecBase(T(xyz.x), T(xyz.y), T(xyz.z), T(w))
|
|
{
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF_VEC(Size, == 4)>
|
|
VecBase(T x, VecBase<U, 3> yzw) : VecBase(T(x), T(yzw.x), T(yzw.y), T(yzw.z))
|
|
{
|
|
}
|
|
|
|
template<typename U, typename V, BLI_ENABLE_IF_VEC(Size, == 4)>
|
|
VecBase(VecBase<U, 2> xy, VecBase<V, 2> zw) : VecBase(T(xy.x), T(xy.y), T(zw.x), T(zw.y))
|
|
{
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF_VEC(Size, == 4)>
|
|
VecBase(VecBase<U, 2> xy, T z, T w) : VecBase(T(xy.x), T(xy.y), T(z), T(w))
|
|
{
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF_VEC(Size, == 4)>
|
|
VecBase(T x, VecBase<U, 2> yz, T w) : VecBase(T(x), T(yz.x), T(yz.y), T(w))
|
|
{
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF_VEC(Size, == 4)>
|
|
VecBase(T x, T y, VecBase<U, 2> zw) : VecBase(T(x), T(y), T(zw.x), T(zw.y))
|
|
{
|
|
}
|
|
|
|
/** Masking. */
|
|
|
|
template<typename U, int OtherSize, BLI_ENABLE_IF(OtherSize > Size)>
|
|
explicit VecBase(const VecBase<U, OtherSize> &other)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
(*this)[i] = T(other[i]);
|
|
}
|
|
}
|
|
|
|
/** Swizzling. */
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, >= 2)> VecBase<T, 2> xy() const
|
|
{
|
|
return *reinterpret_cast<const VecBase<T, 2> *>(this);
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, >= 3)> VecBase<T, 2> yz() const
|
|
{
|
|
return *reinterpret_cast<const VecBase<T, 2> *>(&((*this)[1]));
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, >= 4)> VecBase<T, 2> zw() const
|
|
{
|
|
return *reinterpret_cast<const VecBase<T, 2> *>(&((*this)[2]));
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, >= 3)> VecBase<T, 3> xyz() const
|
|
{
|
|
return *reinterpret_cast<const VecBase<T, 3> *>(this);
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, >= 4)> VecBase<T, 3> yzw() const
|
|
{
|
|
return *reinterpret_cast<const VecBase<T, 3> *>(&((*this)[1]));
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_VEC(Size, >= 4)> VecBase<T, 4> xyzw() const
|
|
{
|
|
return *reinterpret_cast<const VecBase<T, 4> *>(this);
|
|
}
|
|
|
|
#undef BLI_ENABLE_IF_VEC
|
|
|
|
/** Conversion from pointers (from C-style vectors). */
|
|
|
|
VecBase(const T *ptr)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] = ptr[i]; });
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))> explicit VecBase(const U *ptr)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] = ptr[i]; });
|
|
}
|
|
|
|
VecBase(const T (*ptr)[Size]) : VecBase(static_cast<const T *>(ptr[0])) {}
|
|
|
|
/** Conversion from other vector types. */
|
|
|
|
template<typename U> explicit VecBase(const VecBase<U, Size> &vec)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] = T(vec[i]); });
|
|
}
|
|
|
|
/** C-style pointer dereference. */
|
|
|
|
operator const T *() const
|
|
{
|
|
return reinterpret_cast<const T *>(this);
|
|
}
|
|
|
|
operator T *()
|
|
{
|
|
return reinterpret_cast<T *>(this);
|
|
}
|
|
|
|
/** Array access. */
|
|
|
|
const T &operator[](int index) const
|
|
{
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < Size);
|
|
return reinterpret_cast<const T *>(this)[index];
|
|
}
|
|
|
|
T &operator[](int index)
|
|
{
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < Size);
|
|
return reinterpret_cast<T *>(this)[index];
|
|
}
|
|
|
|
/** Internal Operators Macro. */
|
|
|
|
#define BLI_INT_OP(_T) template<typename U = _T, BLI_ENABLE_IF((std::is_integral_v<U>))>
|
|
|
|
/** Arithmetic operators. */
|
|
|
|
friend VecBase operator+(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] + b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator+(const VecBase &a, const T &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] + b; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator+(const T &a, const VecBase &b)
|
|
{
|
|
return b + a;
|
|
}
|
|
|
|
VecBase &operator+=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] += b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
VecBase &operator+=(const T &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] += b; });
|
|
return *this;
|
|
}
|
|
|
|
friend VecBase operator-(const VecBase &a)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = -a[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator-(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator-(const VecBase &a, const T &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] - b; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator-(const T &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
VecBase &operator-=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] -= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
VecBase &operator-=(const T &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] -= b; });
|
|
return *this;
|
|
}
|
|
|
|
friend VecBase operator*(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] * b[i]; });
|
|
return result;
|
|
}
|
|
|
|
template<typename FactorT> friend VecBase operator*(const VecBase &a, FactorT b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] * b; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator*(T a, const VecBase &b)
|
|
{
|
|
return b * a;
|
|
}
|
|
|
|
VecBase &operator*=(T b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] *= b; });
|
|
return *this;
|
|
}
|
|
|
|
VecBase &operator*=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] *= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
friend VecBase operator/(const VecBase &a, const VecBase &b)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
BLI_assert(b[i] != T(0));
|
|
}
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] / b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator/(const VecBase &a, T b)
|
|
{
|
|
BLI_assert(b != T(0));
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] / b; });
|
|
return result;
|
|
}
|
|
|
|
friend VecBase operator/(T a, const VecBase &b)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
BLI_assert(b[i] != T(0));
|
|
}
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a / b[i]; });
|
|
return result;
|
|
}
|
|
|
|
VecBase &operator/=(T b)
|
|
{
|
|
BLI_assert(b != T(0));
|
|
unroll<Size>([&](auto i) { (*this)[i] /= b; });
|
|
return *this;
|
|
}
|
|
|
|
VecBase &operator/=(const VecBase &b)
|
|
{
|
|
BLI_assert(b != T(0));
|
|
unroll<Size>([&](auto i) { (*this)[i] /= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
/** Binary operators. */
|
|
|
|
BLI_INT_OP(T) friend VecBase operator&(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] & b[i]; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator&(const VecBase &a, T b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] & b; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator&(T a, const VecBase &b)
|
|
{
|
|
return b & a;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator&=(T b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] &= b; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator&=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] &= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator|(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] | b[i]; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator|(const VecBase &a, T b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] | b; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator|(T a, const VecBase &b)
|
|
{
|
|
return b | a;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator|=(T b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] |= b; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator|=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] |= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator^(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] ^ b[i]; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator^(const VecBase &a, T b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] ^ b; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator^(T a, const VecBase &b)
|
|
{
|
|
return b ^ a;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator^=(T b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] ^= b; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator^=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] ^= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator~(const VecBase &a)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = ~a[i]; });
|
|
return result;
|
|
}
|
|
|
|
/** Bit-shift operators. */
|
|
|
|
BLI_INT_OP(T) friend VecBase operator<<(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] << b[i]; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator<<(const VecBase &a, T b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] << b; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator<<=(T b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] <<= b; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator<<=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] <<= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator>>(const VecBase &a, const VecBase &b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] >> b[i]; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator>>(const VecBase &a, T b)
|
|
{
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] >> b; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator>>=(T b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] >>= b; });
|
|
return *this;
|
|
}
|
|
|
|
BLI_INT_OP(T) VecBase &operator>>=(const VecBase &b)
|
|
{
|
|
unroll<Size>([&](auto i) { (*this)[i] >>= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
/** Modulo operators. */
|
|
|
|
BLI_INT_OP(T) friend VecBase operator%(const VecBase &a, const VecBase &b)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
BLI_assert(b[i] != T(0));
|
|
}
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] % b[i]; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator%(const VecBase &a, T b)
|
|
{
|
|
BLI_assert(b != 0);
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a[i] % b; });
|
|
return result;
|
|
}
|
|
|
|
BLI_INT_OP(T) friend VecBase operator%(T a, const VecBase &b)
|
|
{
|
|
BLI_assert(b != T(0));
|
|
VecBase result;
|
|
unroll<Size>([&](auto i) { result[i] = a % b[i]; });
|
|
return result;
|
|
}
|
|
|
|
#undef BLI_INT_OP
|
|
|
|
/** Compare. */
|
|
|
|
friend bool operator==(const VecBase &a, const VecBase &b)
|
|
{
|
|
for (int i = 0; i < Size; i++) {
|
|
if (a[i] != b[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
friend bool operator!=(const VecBase &a, const VecBase &b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
/** Misc. */
|
|
|
|
uint64_t hash() const
|
|
{
|
|
return math::vector_hash(*this);
|
|
}
|
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const VecBase &v)
|
|
{
|
|
stream << "(";
|
|
for (int i = 0; i < Size; i++) {
|
|
stream << v[i];
|
|
if (i != Size - 1) {
|
|
stream << ", ";
|
|
}
|
|
}
|
|
stream << ")";
|
|
return stream;
|
|
}
|
|
};
|
|
|
|
namespace math {
|
|
|
|
template<typename T> struct AssertUnitEpsilon {
|
|
/** \note Copy of BLI_ASSERT_UNIT_EPSILON_DB to avoid dragging the entire header. */
|
|
static constexpr T value = T(0.0002);
|
|
};
|
|
|
|
} // namespace math
|
|
|
|
using char2 = blender::VecBase<int8_t, 2>;
|
|
using char3 = blender::VecBase<int8_t, 3>;
|
|
using char4 = blender::VecBase<int8_t, 4>;
|
|
|
|
using uchar3 = blender::VecBase<uint8_t, 3>;
|
|
using uchar4 = blender::VecBase<uint8_t, 4>;
|
|
|
|
using int2 = VecBase<int32_t, 2>;
|
|
using int3 = VecBase<int32_t, 3>;
|
|
using int4 = VecBase<int32_t, 4>;
|
|
|
|
using uint2 = VecBase<uint32_t, 2>;
|
|
using uint3 = VecBase<uint32_t, 3>;
|
|
using uint4 = VecBase<uint32_t, 4>;
|
|
|
|
using short2 = blender::VecBase<int16_t, 2>;
|
|
using short3 = blender::VecBase<int16_t, 3>;
|
|
using short4 = blender::VecBase<int16_t, 4>;
|
|
|
|
using ushort2 = VecBase<uint16_t, 2>;
|
|
using ushort3 = blender::VecBase<uint16_t, 3>;
|
|
using ushort4 = blender::VecBase<uint16_t, 4>;
|
|
|
|
using float1 = VecBase<float, 1>;
|
|
using float2 = VecBase<float, 2>;
|
|
using float3 = VecBase<float, 3>;
|
|
using float4 = VecBase<float, 4>;
|
|
|
|
using double2 = VecBase<double, 2>;
|
|
using double3 = VecBase<double, 3>;
|
|
using double4 = VecBase<double, 4>;
|
|
|
|
} // namespace blender
|