More accurately normalize 4D vectors, to avoid potential numerical stability issues in Mantaflow. For consistency with changes from #136966. Pull Request: https://projects.blender.org/blender/blender/pulls/137413
544 lines
14 KiB
C++
544 lines
14 KiB
C++
/******************************************************************************
|
|
*
|
|
* MantaFlow fluid solver framework
|
|
* Copyright 2011 Tobias Pfaff, Nils Thuerey
|
|
*
|
|
* This program is free software, distributed under the terms of the
|
|
* Apache License, Version 2.0
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* 4D vector class
|
|
*
|
|
******************************************************************************/
|
|
|
|
#ifndef _VECTOR4D_H
|
|
#define _VECTOR4D_H
|
|
|
|
#include "vectorbase.h"
|
|
|
|
namespace Manta {
|
|
|
|
//! Basic inlined vector class
|
|
template<class S> class Vector4D {
|
|
public:
|
|
//! Constructor
|
|
inline Vector4D() : x(0), y(0), z(0), t(0)
|
|
{
|
|
}
|
|
|
|
//! Copy-Constructor
|
|
inline Vector4D(const Vector4D<S> &v) : x(v.x), y(v.y), z(v.z), t(v.t)
|
|
{
|
|
}
|
|
|
|
//! Copy-Constructor
|
|
inline Vector4D(const float *v) : x((S)v[0]), y((S)v[1]), z((S)v[2]), t((S)v[3])
|
|
{
|
|
}
|
|
|
|
//! Copy-Constructor
|
|
inline Vector4D(const double *v) : x((S)v[0]), y((S)v[1]), z((S)v[2]), t((S)v[3])
|
|
{
|
|
}
|
|
|
|
//! Construct a vector from one S
|
|
inline Vector4D(S v) : x(v), y(v), z(v), t(v)
|
|
{
|
|
}
|
|
|
|
//! Construct a vector from three Ss
|
|
inline Vector4D(S vx, S vy, S vz, S vw) : x(vx), y(vy), z(vz), t(vw)
|
|
{
|
|
}
|
|
|
|
// Operators
|
|
|
|
//! Assignment operator
|
|
inline const Vector4D<S> &operator=(const Vector4D<S> &v)
|
|
{
|
|
x = v.x;
|
|
y = v.y;
|
|
z = v.z;
|
|
t = v.t;
|
|
return *this;
|
|
}
|
|
//! Assignment operator
|
|
inline const Vector4D<S> &operator=(S s)
|
|
{
|
|
x = y = z = t = s;
|
|
return *this;
|
|
}
|
|
//! Assign and add operator
|
|
inline const Vector4D<S> &operator+=(const Vector4D<S> &v)
|
|
{
|
|
x += v.x;
|
|
y += v.y;
|
|
z += v.z;
|
|
t += v.t;
|
|
return *this;
|
|
}
|
|
//! Assign and add operator
|
|
inline const Vector4D<S> &operator+=(S s)
|
|
{
|
|
x += s;
|
|
y += s;
|
|
z += s;
|
|
t += s;
|
|
return *this;
|
|
}
|
|
//! Assign and sub operator
|
|
inline const Vector4D<S> &operator-=(const Vector4D<S> &v)
|
|
{
|
|
x -= v.x;
|
|
y -= v.y;
|
|
z -= v.z;
|
|
t -= v.t;
|
|
return *this;
|
|
}
|
|
//! Assign and sub operator
|
|
inline const Vector4D<S> &operator-=(S s)
|
|
{
|
|
x -= s;
|
|
y -= s;
|
|
z -= s;
|
|
t -= s;
|
|
return *this;
|
|
}
|
|
//! Assign and mult operator
|
|
inline const Vector4D<S> &operator*=(const Vector4D<S> &v)
|
|
{
|
|
x *= v.x;
|
|
y *= v.y;
|
|
z *= v.z;
|
|
t *= v.t;
|
|
return *this;
|
|
}
|
|
//! Assign and mult operator
|
|
inline const Vector4D<S> &operator*=(S s)
|
|
{
|
|
x *= s;
|
|
y *= s;
|
|
z *= s;
|
|
t *= s;
|
|
return *this;
|
|
}
|
|
//! Assign and div operator
|
|
inline const Vector4D<S> &operator/=(const Vector4D<S> &v)
|
|
{
|
|
x /= v.x;
|
|
y /= v.y;
|
|
z /= v.z;
|
|
t /= v.t;
|
|
return *this;
|
|
}
|
|
//! Assign and div operator
|
|
inline const Vector4D<S> &operator/=(S s)
|
|
{
|
|
x /= s;
|
|
y /= s;
|
|
z /= s;
|
|
t /= s;
|
|
return *this;
|
|
}
|
|
//! Negation operator
|
|
inline Vector4D<S> operator-() const
|
|
{
|
|
return Vector4D<S>(-x, -y, -z, -t);
|
|
}
|
|
|
|
//! Get smallest component
|
|
// inline S min() const { return ( x<y ) ? ( ( x<z ) ? x:z ) : ( ( y<z ) ? y:z ); }
|
|
//! Get biggest component
|
|
// inline S max() const { return ( x>y ) ? ( ( x>z ) ? x:z ) : ( ( y>z ) ? y:z ); }
|
|
|
|
//! Test if all components are zero
|
|
inline bool empty()
|
|
{
|
|
return x == 0 && y == 0 && z == 0 && t == 0;
|
|
}
|
|
|
|
//! access operator
|
|
inline S &operator[](unsigned int i)
|
|
{
|
|
return value[i];
|
|
}
|
|
//! constant access operator
|
|
inline const S &operator[](unsigned int i) const
|
|
{
|
|
return value[i];
|
|
}
|
|
|
|
//! debug output vector to a string
|
|
std::string toString() const;
|
|
|
|
//! test if nans are present
|
|
bool isValid() const;
|
|
|
|
//! actual values
|
|
union {
|
|
S value[4];
|
|
struct {
|
|
S x;
|
|
S y;
|
|
S z;
|
|
S t;
|
|
};
|
|
struct {
|
|
S X;
|
|
S Y;
|
|
S Z;
|
|
S T;
|
|
};
|
|
};
|
|
|
|
// zero element
|
|
static const Vector4D<S> Zero, Invalid;
|
|
|
|
protected:
|
|
};
|
|
|
|
//************************************************************************
|
|
// Additional operators
|
|
//************************************************************************
|
|
|
|
//! Addition operator
|
|
template<class S> inline Vector4D<S> operator+(const Vector4D<S> &v1, const Vector4D<S> &v2)
|
|
{
|
|
return Vector4D<S>(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z, v1.t + v2.t);
|
|
}
|
|
//! Addition operator
|
|
template<class S, class S2> inline Vector4D<S> operator+(const Vector4D<S> &v, S2 s)
|
|
{
|
|
return Vector4D<S>(v.x + s, v.y + s, v.z + s, v.t + s);
|
|
}
|
|
//! Addition operator
|
|
template<class S, class S2> inline Vector4D<S> operator+(S2 s, const Vector4D<S> &v)
|
|
{
|
|
return Vector4D<S>(v.x + s, v.y + s, v.z + s, v.t + s);
|
|
}
|
|
|
|
//! Subtraction operator
|
|
template<class S> inline Vector4D<S> operator-(const Vector4D<S> &v1, const Vector4D<S> &v2)
|
|
{
|
|
return Vector4D<S>(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z, v1.t - v2.t);
|
|
}
|
|
//! Subtraction operator
|
|
template<class S, class S2> inline Vector4D<S> operator-(const Vector4D<S> &v, S2 s)
|
|
{
|
|
return Vector4D<S>(v.x - s, v.y - s, v.z - s, v.t - s);
|
|
}
|
|
//! Subtraction operator
|
|
template<class S, class S2> inline Vector4D<S> operator-(S2 s, const Vector4D<S> &v)
|
|
{
|
|
return Vector4D<S>(s - v.x, s - v.y, s - v.z, s - v.t);
|
|
}
|
|
|
|
//! Multiplication operator
|
|
template<class S> inline Vector4D<S> operator*(const Vector4D<S> &v1, const Vector4D<S> &v2)
|
|
{
|
|
return Vector4D<S>(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z, v1.t * v2.t);
|
|
}
|
|
//! Multiplication operator
|
|
template<class S, class S2> inline Vector4D<S> operator*(const Vector4D<S> &v, S2 s)
|
|
{
|
|
return Vector4D<S>(v.x * s, v.y * s, v.z * s, v.t * s);
|
|
}
|
|
//! Multiplication operator
|
|
template<class S, class S2> inline Vector4D<S> operator*(S2 s, const Vector4D<S> &v)
|
|
{
|
|
return Vector4D<S>(s * v.x, s * v.y, s * v.z, s * v.t);
|
|
}
|
|
|
|
//! Division operator
|
|
template<class S> inline Vector4D<S> operator/(const Vector4D<S> &v1, const Vector4D<S> &v2)
|
|
{
|
|
return Vector4D<S>(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z, v1.t / v2.t);
|
|
}
|
|
//! Division operator
|
|
template<class S, class S2> inline Vector4D<S> operator/(const Vector4D<S> &v, S2 s)
|
|
{
|
|
return Vector4D<S>(v.x / s, v.y / s, v.z / s, v.t / s);
|
|
}
|
|
//! Division operator
|
|
template<class S, class S2> inline Vector4D<S> operator/(S2 s, const Vector4D<S> &v)
|
|
{
|
|
return Vector4D<S>(s / v.x, s / v.y, s / v.z, s / v.t);
|
|
}
|
|
|
|
//! Comparison operator
|
|
template<class S> inline bool operator==(const Vector4D<S> &s1, const Vector4D<S> &s2)
|
|
{
|
|
return s1.x == s2.x && s1.y == s2.y && s1.z == s2.z && s1.t == s2.t;
|
|
}
|
|
|
|
//! Comparison operator
|
|
template<class S> inline bool operator!=(const Vector4D<S> &s1, const Vector4D<S> &s2)
|
|
{
|
|
return s1.x != s2.x || s1.y != s2.y || s1.z != s2.z || s1.t != s2.t;
|
|
}
|
|
|
|
//************************************************************************
|
|
// External functions
|
|
//************************************************************************
|
|
|
|
//! Dot product
|
|
template<class S> inline S dot(const Vector4D<S> &t, const Vector4D<S> &v)
|
|
{
|
|
return t.x * v.x + t.y * v.y + t.z * v.z + t.t * v.t;
|
|
}
|
|
|
|
/* Based on libstdc++ implementation from:
|
|
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/c_global/cmath#L3769
|
|
*/
|
|
template<typename _Tp>
|
|
inline _Tp
|
|
__hypot4(_Tp __x, _Tp __y, _Tp __z, _Tp __t)
|
|
{
|
|
__x = std::abs(__x);
|
|
__y = std::abs(__y);
|
|
__z = std::abs(__z);
|
|
__t = std::abs(__t);
|
|
if (_Tp __a = __x < __y ? (__y < __z ? (__z < __t ? __t : __z ) : (__y < __t ? __t : __y )) : __x < __z ? (__z < __t ? __t : __z ) : (__x < __t ? __t : __x ))
|
|
return __a * std::sqrt((__x / __a) * (__x / __a)
|
|
+ (__y / __a) * (__y / __a)
|
|
+ (__z / __a) * (__z / __a)
|
|
+ (__t / __a) * (__t / __a));
|
|
else
|
|
return {};
|
|
}
|
|
|
|
inline float
|
|
hypot4(float __x, float __y, float __z, float __t)
|
|
{ return __hypot4<float>(__x, __y, __z, __t); }
|
|
|
|
inline double
|
|
hypot4(double __x, double __y, double __z, double __t)
|
|
{ return __hypot4<double>(__x, __y, __z, __t); }
|
|
|
|
//! Cross product
|
|
/*template<class S>
|
|
inline Vector4D<S> cross ( const Vector4D<S> &t, const Vector4D<S> &v ) {
|
|
NYI Vector4D<S> cp (
|
|
( ( t.y*v.z ) - ( t.z*v.y ) ),
|
|
( ( t.z*v.x ) - ( t.x*v.z ) ),
|
|
( ( t.x*v.y ) - ( t.y*v.x ) ) );
|
|
return cp;
|
|
}*/
|
|
|
|
//! Compute the magnitude (length) of the vector
|
|
template<class S> inline S norm(const Vector4D<S> &v)
|
|
{
|
|
S l = hypot4(v.x, v.y, v.z, v.t);
|
|
return (fabs(l - 1.) < VECTOR_EPSILON) ? 1. : l;
|
|
}
|
|
|
|
//! Compute squared magnitude
|
|
template<class S> inline S normSquare(const Vector4D<S> &v)
|
|
{
|
|
return v.x * v.x + v.y * v.y + v.z * v.z + v.t * v.t;
|
|
}
|
|
|
|
//! Returns a normalized vector
|
|
template<class S> inline Vector4D<S> getNormalized(const Vector4D<S> &v)
|
|
{
|
|
S l = hypot4(v.x, v.y, v.z, v.t);
|
|
if (fabs(l - 1.) < VECTOR_EPSILON)
|
|
return v; /* normalized "enough"... */
|
|
else if (l > VECTOR_EPSILON) {
|
|
S fac = 1. / l;
|
|
return Vector4D<S>(v.x * fac, v.y * fac, v.z * fac, v.t * fac);
|
|
}
|
|
else
|
|
return Vector4D<S>((S)0);
|
|
}
|
|
|
|
//! Compute the norm of the vector and normalize it.
|
|
/*! \return The value of the norm */
|
|
template<class S> inline S normalize(Vector4D<S> &v)
|
|
{
|
|
S norm;
|
|
S l = hypot4(v.x, v.y, v.z, v.t);
|
|
if (fabs(l - 1.) < VECTOR_EPSILON) {
|
|
norm = 1.;
|
|
}
|
|
else if (l > VECTOR_EPSILON) {
|
|
norm = l;
|
|
v *= 1. / norm;
|
|
}
|
|
else {
|
|
v = Vector4D<S>::Zero;
|
|
norm = 0.;
|
|
}
|
|
return (S)norm;
|
|
}
|
|
|
|
//! Outputs the object in human readable form as string
|
|
template<class S> std::string Vector4D<S>::toString() const
|
|
{
|
|
char buf[256];
|
|
snprintf(buf,
|
|
256,
|
|
"[%+4.6f,%+4.6f,%+4.6f,%+4.6f]",
|
|
(double)(*this)[0],
|
|
(double)(*this)[1],
|
|
(double)(*this)[2],
|
|
(double)(*this)[3]);
|
|
// for debugging, optionally increase precision:
|
|
// snprintf ( buf,256,"[%+4.16f,%+4.16f,%+4.16f,%+4.16f]", ( double ) ( *this ) [0], ( double ) (
|
|
// *this ) [1], ( double ) ( *this ) [2], ( double ) ( *this ) [3] );
|
|
return std::string(buf);
|
|
}
|
|
|
|
template<> std::string Vector4D<int>::toString() const;
|
|
|
|
//! Outputs the object in human readable form to stream
|
|
template<class S> std::ostream &operator<<(std::ostream &os, const Vector4D<S> &i)
|
|
{
|
|
os << i.toString();
|
|
return os;
|
|
}
|
|
|
|
//! Reads the contents of the object from a stream
|
|
template<class S> std::istream &operator>>(std::istream &is, Vector4D<S> &i)
|
|
{
|
|
char c;
|
|
char dummy[4];
|
|
is >> c >> i[0] >> dummy >> i[1] >> dummy >> i[2] >> dummy >> i[3] >> c;
|
|
return is;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
// Define default vector alias
|
|
/**************************************************************************/
|
|
|
|
//! 3D vector class of type Real (typically float)
|
|
typedef Vector4D<Real> Vec4;
|
|
|
|
//! 3D vector class of type int
|
|
typedef Vector4D<int> Vec4i;
|
|
|
|
//! convert to Real Vector
|
|
template<class T> inline Vec4 toVec4(T v)
|
|
{
|
|
return Vec4(v[0], v[1], v[2], v[3]);
|
|
}
|
|
template<class T> inline Vec4i toVec4i(T v)
|
|
{
|
|
return Vec4i(v[0], v[1], v[2], v[3]);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
// Specializations for common math functions
|
|
/**************************************************************************/
|
|
|
|
template<> inline Vec4 clamp<Vec4>(const Vec4 &a, const Vec4 &b, const Vec4 &c)
|
|
{
|
|
return Vec4(
|
|
clamp(a.x, b.x, c.x), clamp(a.y, b.y, c.y), clamp(a.z, b.z, c.z), clamp(a.t, b.t, c.t));
|
|
}
|
|
template<> inline Vec4 safeDivide<Vec4>(const Vec4 &a, const Vec4 &b)
|
|
{
|
|
return Vec4(
|
|
safeDivide(a.x, b.x), safeDivide(a.y, b.y), safeDivide(a.z, b.z), safeDivide(a.t, b.t));
|
|
}
|
|
template<> inline Vec4 nmod<Vec4>(const Vec4 &a, const Vec4 &b)
|
|
{
|
|
return Vec4(nmod(a.x, b.x), nmod(a.y, b.y), nmod(a.z, b.z), nmod(a.t, b.t));
|
|
}
|
|
|
|
/**************************************************************************/
|
|
// 4d interpolation (note only 4d here, 2d/3d interpolations are in interpol.h)
|
|
/**************************************************************************/
|
|
|
|
#define BUILD_INDEX_4D \
|
|
Real px = pos.x - 0.5f, py = pos.y - 0.5f, pz = pos.z - 0.5f, pt = pos.t - 0.5f; \
|
|
int xi = (int)px; \
|
|
int yi = (int)py; \
|
|
int zi = (int)pz; \
|
|
int ti = (int)pt; \
|
|
Real s1 = px - (Real)xi, s0 = 1. - s1; \
|
|
Real t1 = py - (Real)yi, t0 = 1. - t1; \
|
|
Real f1 = pz - (Real)zi, f0 = 1. - f1; \
|
|
Real g1 = pt - (Real)ti, g0 = 1. - g1; \
|
|
/* clamp to border */ \
|
|
if (px < 0.) { \
|
|
xi = 0; \
|
|
s0 = 1.0; \
|
|
s1 = 0.0; \
|
|
} \
|
|
if (py < 0.) { \
|
|
yi = 0; \
|
|
t0 = 1.0; \
|
|
t1 = 0.0; \
|
|
} \
|
|
if (pz < 0.) { \
|
|
zi = 0; \
|
|
f0 = 1.0; \
|
|
f1 = 0.0; \
|
|
} \
|
|
if (pt < 0.) { \
|
|
ti = 0; \
|
|
g0 = 1.0; \
|
|
g1 = 0.0; \
|
|
} \
|
|
if (xi >= size.x - 1) { \
|
|
xi = size.x - 2; \
|
|
s0 = 0.0; \
|
|
s1 = 1.0; \
|
|
} \
|
|
if (yi >= size.y - 1) { \
|
|
yi = size.y - 2; \
|
|
t0 = 0.0; \
|
|
t1 = 1.0; \
|
|
} \
|
|
if (zi >= size.z - 1) { \
|
|
zi = size.z - 2; \
|
|
f0 = 0.0; \
|
|
f1 = 1.0; \
|
|
} \
|
|
if (ti >= size.t - 1) { \
|
|
ti = size.t - 2; \
|
|
g0 = 0.0; \
|
|
g1 = 1.0; \
|
|
} \
|
|
const int sX = 1; \
|
|
const int sY = size.x;
|
|
|
|
static inline void checkIndexInterpol4d(const Vec4i &size, int idx)
|
|
{
|
|
if (idx < 0 || idx > size.x * size.y * size.z * size.t) {
|
|
std::ostringstream s;
|
|
s << "Grid interpol4d dim " << size << " : index " << idx << " out of bound ";
|
|
errMsg(s.str());
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
inline T interpol4d(
|
|
const T *data, const Vec4i &size, const IndexInt sZ, const IndexInt sT, const Vec4 &pos)
|
|
{
|
|
BUILD_INDEX_4D
|
|
IndexInt idx = (IndexInt)xi + sY * (IndexInt)yi + sZ * (IndexInt)zi + sT * (IndexInt)ti;
|
|
DEBUG_ONLY(checkIndexInterpol4d(size, idx));
|
|
DEBUG_ONLY(checkIndexInterpol4d(size, idx + sX + sY + sZ + sT));
|
|
|
|
return (((data[idx] * t0 + data[idx + sY] * t1) * s0 +
|
|
(data[idx + sX] * t0 + data[idx + sX + sY] * t1) * s1) *
|
|
f0 +
|
|
((data[idx + sZ] * t0 + data[idx + sY + sZ] * t1) * s0 +
|
|
(data[idx + sX + sZ] * t0 + data[idx + sX + sY + sZ] * t1) * s1) *
|
|
f1) *
|
|
g0 +
|
|
(((data[idx + sT] * t0 + data[idx + sT + sY] * t1) * s0 +
|
|
(data[idx + sT + sX] * t0 + data[idx + sT + sX + sY] * t1) * s1) *
|
|
f0 +
|
|
((data[idx + sT + sZ] * t0 + data[idx + sT + sY + sZ] * t1) * s0 +
|
|
(data[idx + sT + sX + sZ] * t0 + data[idx + sT + sX + sY + sZ] * t1) * s1) *
|
|
f1) *
|
|
g1;
|
|
}
|
|
|
|
}; // namespace Manta
|
|
|
|
#endif
|