/****************************************************************************** * * MantaFlow fluid solver framework * Copyright 2011-2016 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 * * Basic vector class * ******************************************************************************/ #ifndef _VECTORBASE_H #define _VECTORBASE_H // get rid of windos min/max defines #if (defined(WIN32) || defined(_WIN32)) && !defined(NOMINMAX) # define NOMINMAX #endif #include #include #include #include #include "general.h" // if min/max are still around... #if defined(WIN32) || defined(_WIN32) # undef min # undef max #endif // redefine usage of some windows functions #if defined(WIN32) || defined(_WIN32) # ifndef snprintf # define snprintf _snprintf # endif #endif // use which fp-precision? 1=float, 2=double #ifndef FLOATINGPOINT_PRECISION # define FLOATINGPOINT_PRECISION 1 #endif // VECTOR_EPSILON is the minimal vector length // In order to be able to discriminate floating point values near zero, and // to be sure not to fail a comparison because of roundoff errors, use this // value as a threshold. #if FLOATINGPOINT_PRECISION == 1 typedef float Real; # define VECTOR_EPSILON (1e-6f) #else typedef double Real; # define VECTOR_EPSILON (1e-10) #endif #ifndef M_PI # define M_PI 3.1415926536 #endif #ifndef M_E # define M_E 2.7182818284 #endif namespace Manta { //! Basic inlined vector class template class Vector3D { public: //! Constructor inline Vector3D() : x(0), y(0), z(0) { } //! Copy-Constructor inline Vector3D(const Vector3D &v) : x(v.x), y(v.y), z(v.z) { } //! Copy-Constructor inline Vector3D(const int *v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) { } //! Copy-Constructor inline Vector3D(const float *v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) { } //! Copy-Constructor inline Vector3D(const double *v) : x((S)v[0]), y((S)v[1]), z((S)v[2]) { } //! Construct a vector from one S inline Vector3D(S v) : x(v), y(v), z(v) { } //! Construct a vector from three Ss inline Vector3D(S vx, S vy, S vz) : x(vx), y(vy), z(vz) { } // Operators //! Assignment operator inline const Vector3D &operator=(const Vector3D &v) { x = v.x; y = v.y; z = v.z; return *this; } //! Assignment operator inline const Vector3D &operator=(S s) { x = y = z = s; return *this; } //! Assign and add operator inline const Vector3D &operator+=(const Vector3D &v) { x += v.x; y += v.y; z += v.z; return *this; } //! Assign and add operator inline const Vector3D &operator+=(S s) { x += s; y += s; z += s; return *this; } //! Assign and sub operator inline const Vector3D &operator-=(const Vector3D &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } //! Assign and sub operator inline const Vector3D &operator-=(S s) { x -= s; y -= s; z -= s; return *this; } //! Assign and mult operator inline const Vector3D &operator*=(const Vector3D &v) { x *= v.x; y *= v.y; z *= v.z; return *this; } //! Assign and mult operator inline const Vector3D &operator*=(S s) { x *= s; y *= s; z *= s; return *this; } //! Assign and div operator inline const Vector3D &operator/=(const Vector3D &v) { x /= v.x; y /= v.y; z /= v.z; return *this; } //! Assign and div operator inline const Vector3D &operator/=(S s) { x /= s; y /= s; z /= s; return *this; } //! Negation operator inline Vector3D operator-() const { return Vector3D(-x, -y, -z); } //! 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; } //! 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[3]; struct { S x; S y; S z; }; struct { S X; S Y; S Z; }; }; //! zero element static const Vector3D Zero, Invalid; //! For compatibility with 4d vectors (discards 4th comp) inline Vector3D(S vx, S vy, S vz, S vDummy) : x(vx), y(vy), z(vz) { } protected: }; //! helper to check whether value is non-zero template inline bool notZero(S v) { return (std::abs(v) > VECTOR_EPSILON); } template inline bool notZero(Vector3D v) { return (std::abs(norm(v)) > VECTOR_EPSILON); } //************************************************************************ // Additional operators //************************************************************************ //! Addition operator template inline Vector3D operator+(const Vector3D &v1, const Vector3D &v2) { return Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); } //! Addition operator template inline Vector3D operator+(const Vector3D &v, S2 s) { return Vector3D(v.x + s, v.y + s, v.z + s); } //! Addition operator template inline Vector3D operator+(S2 s, const Vector3D &v) { return Vector3D(v.x + s, v.y + s, v.z + s); } //! Subtraction operator template inline Vector3D operator-(const Vector3D &v1, const Vector3D &v2) { return Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); } //! Subtraction operator template inline Vector3D operator-(const Vector3D &v, S2 s) { return Vector3D(v.x - s, v.y - s, v.z - s); } //! Subtraction operator template inline Vector3D operator-(S2 s, const Vector3D &v) { return Vector3D(s - v.x, s - v.y, s - v.z); } //! Multiplication operator template inline Vector3D operator*(const Vector3D &v1, const Vector3D &v2) { return Vector3D(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); } //! Multiplication operator template inline Vector3D operator*(const Vector3D &v, S2 s) { return Vector3D(v.x * s, v.y * s, v.z * s); } //! Multiplication operator template inline Vector3D operator*(S2 s, const Vector3D &v) { return Vector3D(s * v.x, s * v.y, s * v.z); } //! Division operator template inline Vector3D operator/(const Vector3D &v1, const Vector3D &v2) { return Vector3D(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z); } //! Division operator template inline Vector3D operator/(const Vector3D &v, S2 s) { return Vector3D(v.x / s, v.y / s, v.z / s); } //! Division operator template inline Vector3D operator/(S2 s, const Vector3D &v) { return Vector3D(s / v.x, s / v.y, s / v.z); } //! Comparison operator template inline bool operator==(const Vector3D &s1, const Vector3D &s2) { return s1.x == s2.x && s1.y == s2.y && s1.z == s2.z; } //! Comparison operator template inline bool operator!=(const Vector3D &s1, const Vector3D &s2) { return s1.x != s2.x || s1.y != s2.y || s1.z != s2.z; } //************************************************************************ // External functions //************************************************************************ //! Min operator template inline Vector3D vmin(const Vector3D &s1, const Vector3D &s2) { return Vector3D(std::min(s1.x, s2.x), std::min(s1.y, s2.y), std::min(s1.z, s2.z)); } //! Min operator template inline Vector3D vmin(const Vector3D &s1, S2 s2) { return Vector3D(std::min(s1.x, s2), std::min(s1.y, s2), std::min(s1.z, s2)); } //! Min operator template inline Vector3D vmin(S1 s1, const Vector3D &s2) { return Vector3D(std::min(s1, s2.x), std::min(s1, s2.y), std::min(s1, s2.z)); } //! Max operator template inline Vector3D vmax(const Vector3D &s1, const Vector3D &s2) { return Vector3D(std::max(s1.x, s2.x), std::max(s1.y, s2.y), std::max(s1.z, s2.z)); } //! Max operator template inline Vector3D vmax(const Vector3D &s1, S2 s2) { return Vector3D(std::max(s1.x, s2), std::max(s1.y, s2), std::max(s1.z, s2)); } //! Max operator template inline Vector3D vmax(S1 s1, const Vector3D &s2) { return Vector3D(std::max(s1, s2.x), std::max(s1, s2.y), std::max(s1, s2.z)); } //! Dot product template inline S dot(const Vector3D &t, const Vector3D &v) { return t.x * v.x + t.y * v.y + t.z * v.z; } //! Cross product template inline Vector3D cross(const Vector3D &t, const Vector3D &v) { Vector3D 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; } //! Project a vector into a plane, defined by its normal /*! Projects a vector into a plane normal to the given vector, which must have unit length. Self is modified. \param v The vector to project \param n The plane normal \return The projected vector */ template inline const Vector3D &projectNormalTo(const Vector3D &v, const Vector3D &n) { S sprod = dot(v, n); return v - n * dot(v, n); } //! Compute the magnitude (length) of the vector //! (clamps to 0 and 1 with VECTOR_EPSILON) template inline S norm(const Vector3D &v) { S l = v.x * v.x + v.y * v.y + v.z * v.z; if (l <= VECTOR_EPSILON * VECTOR_EPSILON) return (0.); return (fabs(l - 1.) < VECTOR_EPSILON * VECTOR_EPSILON) ? 1. : sqrt(l); } //! Compute squared magnitude template inline S normSquare(const Vector3D &v) { return v.x * v.x + v.y * v.y + v.z * v.z; } //! compatibility, allow use of int, Real and Vec inputs with norm/normSquare inline Real norm(const Real v) { return fabs(v); } inline Real normSquare(const Real v) { return square(v); } inline Real norm(const int v) { return abs(v); } inline Real normSquare(const int v) { return square(v); } //! Returns a normalized vector template inline Vector3D getNormalized(const Vector3D &v) { S l = v.x * v.x + v.y * v.y + v.z * v.z; if (fabs(l - 1.) < VECTOR_EPSILON * VECTOR_EPSILON) return v; /* normalized "enough"... */ else if (l > VECTOR_EPSILON * VECTOR_EPSILON) { S fac = 1. / sqrt(l); return Vector3D(v.x * fac, v.y * fac, v.z * fac); } else return Vector3D((S)0); } //! Compute the norm of the vector and normalize it. /*! \return The value of the norm */ template inline S normalize(Vector3D &v) { S norm; S l = v.x * v.x + v.y * v.y + v.z * v.z; if (fabs(l - 1.) < VECTOR_EPSILON * VECTOR_EPSILON) { norm = 1.; } else if (l > VECTOR_EPSILON * VECTOR_EPSILON) { norm = sqrt(l); v *= 1. / norm; } else { v = Vector3D::Zero; norm = 0.; } return (S)norm; } //! Obtain an orthogonal vector /*! Compute a vector that is orthonormal to the given vector. * Nothing else can be assumed for the direction of the new vector. * \return The orthonormal vector */ template Vector3D getOrthogonalVector(const Vector3D &v) { // Determine the component with max. absolute value int maxIndex = (fabs(v.x) > fabs(v.y)) ? 0 : 1; maxIndex = (fabs(v[maxIndex]) > fabs(v.z)) ? maxIndex : 2; // Choose another axis than the one with max. component and project // orthogonal to self Vector3D o(0.0); o[(maxIndex + 1) % 3] = 1; Vector3D c = cross(v, o); normalize(c); return c; } //! Convert vector to polar coordinates /*! Stable vector to angle conversion *\param v vector to convert \param phi unique angle [0,2PI] \param theta unique angle [0,PI] */ template inline void vecToAngle(const Vector3D &v, S &phi, S &theta) { if (fabs(v.y) < VECTOR_EPSILON) theta = M_PI / 2; else if (fabs(v.x) < VECTOR_EPSILON && fabs(v.z) < VECTOR_EPSILON) theta = (v.y >= 0) ? 0 : M_PI; else theta = atan(sqrt(v.x * v.x + v.z * v.z) / v.y); if (theta < 0) theta += M_PI; if (fabs(v.x) < VECTOR_EPSILON) phi = M_PI / 2; else phi = atan(v.z / v.x); if (phi < 0) phi += M_PI; if (fabs(v.z) < VECTOR_EPSILON) phi = (v.x >= 0) ? 0 : M_PI; else if (v.z < 0) phi += M_PI; } //! Compute vector reflected at a surface /*! Compute a vector, that is self (as an incoming vector) * reflected at a surface with a distinct normal vector. * Note that the normal is reversed, if the scalar product with it is positive. \param t The incoming vector \param n The surface normal \return The new reflected vector */ template inline Vector3D reflectVector(const Vector3D &t, const Vector3D &n) { Vector3D nn = (dot(t, n) > 0.0) ? (n * -1.0) : n; return (t - nn * (2.0 * dot(nn, t))); } //! Compute vector refracted at a surface /*! \param t The incoming vector * \param n The surface normal * \param nt The "inside" refraction index * \param nair The "outside" refraction index * \param refRefl Set to 1 on total reflection * \return The refracted vector */ template inline Vector3D refractVector( const Vector3D &t, const Vector3D &normal, S nt, S nair, int &refRefl) { // from Glassner's book, section 5.2 (Heckberts method) S eta = nair / nt; S n = -dot(t, normal); S tt = 1.0 + eta * eta * (n * n - 1.0); if (tt < 0.0) { // we have total reflection! refRefl = 1; } else { // normal reflection tt = eta * n - sqrt(tt); return (t * eta + normal * tt); } return t; } //! Outputs the object in human readable form as string template std::string Vector3D::toString() const { char buf[256]; snprintf(buf, 256, "[%+4.6f,%+4.6f,%+4.6f]", (double)(*this)[0], (double)(*this)[1], (double)(*this)[2]); // for debugging, optionally increase precision: // snprintf ( buf,256,"[%+4.16f,%+4.16f,%+4.16f]", ( double ) ( *this ) [0], ( double ) ( *this ) // [1], ( double ) ( *this ) [2] ); return std::string(buf); } template<> std::string Vector3D::toString() const; //! Outputs the object in human readable form to stream /*! Output format [x,y,z] */ template std::ostream &operator<<(std::ostream &os, const Vector3D &i) { os << i.toString(); return os; } //! Reads the contents of the object from a stream /*! Input format [x,y,z] */ template std::istream &operator>>(std::istream &is, Vector3D &i) { char c; char dummy[3]; is >> c >> i[0] >> dummy >> i[1] >> dummy >> i[2] >> c; return is; } /**************************************************************************/ // Define default vector alias /**************************************************************************/ //! 3D vector class of type Real (typically float) typedef Vector3D Vec3; //! 3D vector class of type int typedef Vector3D Vec3i; //! convert to Real Vector template inline Vec3 toVec3(T v) { return Vec3(v[0], v[1], v[2]); } //! convert to int Vector template inline Vec3i toVec3i(T v) { return Vec3i((int)v[0], (int)v[1], (int)v[2]); } //! convert to int Vector template inline Vec3i toVec3i(T v0, T v1, T v2) { return Vec3i((int)v0, (int)v1, (int)v2); } //! round, and convert to int Vector template inline Vec3i toVec3iRound(T v) { return Vec3i((int)round(v[0]), (int)round(v[1]), (int)round(v[2])); } //! convert to int Vector if values are close enough to an int template inline Vec3i toVec3iChecked(T v) { Vec3i ret; for (size_t i = 0; i < 3; i++) { Real a = v[i]; if (fabs(a - floor(a + 0.5)) > 1e-5) errMsg("argument is not an int, cannot convert"); ret[i] = (int)(a + 0.5); } return ret; } //! convert to double Vector template inline Vector3D toVec3d(T v) { return Vector3D(v[0], v[1], v[2]); } //! convert to float Vector template inline Vector3D toVec3f(T v) { return Vector3D(v[0], v[1], v[2]); } /**************************************************************************/ // Specializations for common math functions /**************************************************************************/ template<> inline Vec3 clamp(const Vec3 &a, const Vec3 &b, const Vec3 &c) { return Vec3(clamp(a.x, b.x, c.x), clamp(a.y, b.y, c.y), clamp(a.z, b.z, c.z)); } template<> inline Vec3 safeDivide(const Vec3 &a, const Vec3 &b) { return Vec3(safeDivide(a.x, b.x), safeDivide(a.y, b.y), safeDivide(a.z, b.z)); } template<> inline Vec3 nmod(const Vec3 &a, const Vec3 &b) { return Vec3(nmod(a.x, b.x), nmod(a.y, b.y), nmod(a.z, b.z)); } }; // namespace Manta #endif