/* SPDX-FileCopyrightText: 2022 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once /** \file * \ingroup bli */ #include #include "BLI_math_base.hh" #include "BLI_math_vector_types.hh" #include "BLI_span.hh" #include "BLI_utildefines.h" namespace blender::math { /** * Returns true if the given vectors are equal within the given epsilon. * The epsilon is scaled for each component by magnitude of the matching component of `a`. */ template [[nodiscard]] inline bool almost_equal_relative(const VecBase &a, const VecBase &b, const T &epsilon_factor) { for (int i = 0; i < Size; i++) { const float epsilon = epsilon_factor * math::abs(a[i]); if (math::distance(a[i], b[i]) > epsilon) { return false; } } return true; } template [[nodiscard]] inline VecBase abs(const VecBase &a) { VecBase result; for (int i = 0; i < Size; i++) { result[i] = a[i] >= 0 ? a[i] : -a[i]; } return result; } /** * Returns -1 if \a a is less than 0, 0 if \a a is equal to 0, and +1 if \a a is greater than 0. */ template [[nodiscard]] inline VecBase sign(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::sign, a); } template [[nodiscard]] inline VecBase min(const VecBase &a, const VecBase &b) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::min, a, b); } /** * Element-wise minimum of the passed vectors. */ template [[nodiscard]] inline VecBase min(Span> values) { BLI_assert(!values.is_empty()); VecBase result = values[0]; for (const VecBase &v : values.drop_front(1)) { result = min(result, v); } return result; } /** * Element-wise minimum of the passed vectors. */ template [[nodiscard]] inline VecBase min(std::initializer_list> values) { return min(Span(values)); } template [[nodiscard]] inline VecBase max(const VecBase &a, const VecBase &b) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::max, a, b); } /** * Element-wise maximum of the passed vectors. */ template [[nodiscard]] inline VecBase max(Span> values) { BLI_assert(!values.is_empty()); VecBase result = values[0]; for (const VecBase &v : values.drop_front(1)) { result = max(result, v); } return result; } /** * Element-wise maximum of the passed vectors. */ template [[nodiscard]] inline VecBase max(std::initializer_list> values) { return max(Span(values)); } template [[nodiscard]] inline VecBase clamp(const VecBase &a, const VecBase &min, const VecBase &max) { VecBase result = a; for (int i = 0; i < Size; i++) { result[i] = math::clamp(result[i], min[i], max[i]); } return result; } template [[nodiscard]] inline VecBase clamp(const VecBase &a, const T &min, const T &max) { VecBase result = a; for (int i = 0; i < Size; i++) { result[i] = math::clamp(result[i], min, max); } return result; } template [[nodiscard]] inline VecBase step(const VecBase &edge, const VecBase &value) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::step, edge, value); } template [[nodiscard]] inline VecBase step(const T &edge, const VecBase &value) { VecBase result = value; for (int i = 0; i < Size; i++) { result[i] = math::step(edge, result[i]); } return result; } template [[nodiscard]] inline VecBase mod(const VecBase &a, const VecBase &b) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::mod, a, b); } template [[nodiscard]] inline VecBase mod(const VecBase &a, const T &b) { BLI_assert(b != 0); VecBase result; for (int i = 0; i < Size; i++) { result[i] = math::mod(a[i], b); } return result; } /** * Safe version of mod(a, b) that returns 0 if b is equal to 0. */ template [[nodiscard]] inline VecBase safe_mod(const VecBase &a, const VecBase &b) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::safe_mod, a, b); } /** * Safe version of mod(a, b) that returns 0 if b is equal to 0. */ template [[nodiscard]] inline VecBase safe_mod(const VecBase &a, const T &b) { if (b == 0) { return VecBase(0); } VecBase result; for (int i = 0; i < Size; i++) { result[i] = math::mod(a[i], b); } return result; } template [[nodiscard]] inline VecBase floored_mod(const VecBase &a, const VecBase &b) { VecBase result; for (int i = 0; i < Size; i++) { BLI_assert(b[i] != 0); result[i] = math::floored_mod(a[i], b[i]); } return result; } template [[nodiscard]] inline VecBase floored_mod(const VecBase &a, const T &b) { BLI_assert(b != 0); VecBase result; for (int i = 0; i < Size; i++) { result[i] = math::floored_mod(a[i], b); } return result; } /** * Return the value of x raised to the y power. * The result is undefined if x < 0 or if x = 0 and y <= 0. */ template [[nodiscard]] inline VecBase pow(const VecBase &x, const T &y) { VecBase result; for (int i = 0; i < Size; i++) { result[i] = math::pow(x[i], y); } return result; } /** * Return the value of x raised to the y power. * The result is x if x < 0 or if x = 0 and y <= 0. */ template [[nodiscard]] inline VecBase safe_pow(const VecBase &x, const T &y) { VecBase result; for (int i = 0; i < Size; i++) { result[i] = math::safe_pow(x[i], y); } return result; } /** * Return the value of x raised to the y power. * The result is the given fallback if x < 0 or if x = 0 and y <= 0. */ template [[nodiscard]] inline VecBase fallback_pow(const VecBase &x, const T &y, const VecBase &fallback) { VecBase result; for (int i = 0; i < Size; i++) { result[i] = math::fallback_pow(x[i], y, fallback[i]); } return result; } /** * Return the value of x raised to the y power. * The result is undefined if x < 0 or if x = 0 and y <= 0. */ template [[nodiscard]] inline VecBase pow(const VecBase &x, const VecBase &y) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::pow, x, y); } /** Per-element square. */ template [[nodiscard]] inline VecBase square(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::square, a); } /* Per-element exponent. */ template [[nodiscard]] inline VecBase exp(const VecBase &x) { BLI_UNROLL_MATH_VEC_OP_VEC(math::exp, x); } /** * Returns \a a if it is a multiple of \a b or the next multiple or \a b after \b a . * In other words, it is equivalent to `divide_ceil(a, b) * b`. * It is undefined if \a a is negative or \b b is not strictly positive. */ template [[nodiscard]] inline VecBase ceil_to_multiple(const VecBase &a, const VecBase &b) { VecBase result; for (int i = 0; i < Size; i++) { BLI_assert(a[i] >= 0); BLI_assert(b[i] > 0); result[i] = ((a[i] + b[i] - 1) / b[i]) * b[i]; } return result; } /** * Integer division that returns the ceiling, instead of flooring like normal C division. * It is undefined if \a a is negative or \b b is not strictly positive. */ template [[nodiscard]] inline VecBase divide_ceil(const VecBase &a, const VecBase &b) { VecBase result; for (int i = 0; i < Size; i++) { BLI_assert(a[i] >= 0); BLI_assert(b[i] > 0); result[i] = (a[i] + b[i] - 1) / b[i]; } return result; } template inline void min_max(const VecBase &vector, VecBase &min, VecBase &max) { min = math::min(vector, min); max = math::max(vector, max); } /** * Returns 0 if denominator is exactly equal to 0. */ template [[nodiscard]] inline VecBase safe_divide(const VecBase &a, const VecBase &b) { BLI_UNROLL_MATH_VEC_FUNC_VEC_VEC(math::safe_divide, a, b); } /** * Returns 0 if denominator is exactly equal to 0. */ template [[nodiscard]] inline VecBase safe_divide(const VecBase &a, const T &b) { return (b != 0) ? a / b : VecBase(0.0f); } template [[nodiscard]] inline VecBase floor(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::floor, a); } template [[nodiscard]] inline VecBase round(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::round, a); } template [[nodiscard]] inline VecBase ceil(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::ceil, a); } /** * Per-element square root. * Negative elements are evaluated to NaN. */ template [[nodiscard]] inline VecBase sqrt(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::sqrt, a); } /** * Per-element square root. * Negative elements are evaluated to zero. */ template [[nodiscard]] inline VecBase safe_sqrt(const VecBase &a) { VecBase result; for (int i = 0; i < Size; i++) { result[i] = a[i] >= T(0) ? math ::sqrt(a[i]) : T(0); } return result; } /** * Per-element inverse. * Zero elements are evaluated to NaN. */ template [[nodiscard]] inline VecBase rcp(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::rcp, a); } /** * Per-element inverse. * Zero elements are evaluated to zero. */ template [[nodiscard]] inline VecBase safe_rcp(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::safe_rcp, a); } template [[nodiscard]] inline VecBase fract(const VecBase &a) { BLI_UNROLL_MATH_VEC_OP_VEC(math::fract, a); } /** * Dot product between two vectors. * Equivalent to component wise multiplication followed by summation of the result. * Equivalent to the cosine of the angle between the two vectors if the vectors are normalized. * \note prefer using `length_manhattan(a)` than `dot(a, vec(1))` to get the sum of all components. */ template [[nodiscard]] inline T dot(const VecBase &a, const VecBase &b) { T result = a[0] * b[0]; for (int i = 1; i < Size; i++) { result += a[i] * b[i]; } return result; } /** * Returns summation of all components. */ template [[nodiscard]] inline T length_manhattan(const VecBase &a) { T result = math::abs(a[0]); for (int i = 1; i < Size; i++) { result += math::abs(a[i]); } return result; } template [[nodiscard]] inline T length_squared(const VecBase &a) { return dot(a, a); } template [[nodiscard]] inline T length(const VecBase &a) { return math::sqrt(length_squared(a)); } /** Return true if each individual column is unit scaled. Mainly for assert usage. */ template [[nodiscard]] inline bool is_unit_scale(const VecBase &v) { /* Checks are flipped so NAN doesn't assert because we're making sure the value was * normalized and in the case we don't want NAN to be raising asserts since there * is nothing to be done in that case. */ const T test_unit = math::length_squared(v); return (!(math::abs(test_unit - T(1)) >= AssertUnitEpsilon::value) || !(math::abs(test_unit) >= AssertUnitEpsilon::value)); } template [[nodiscard]] inline T distance_manhattan(const VecBase &a, const VecBase &b) { return length_manhattan(a - b); } template [[nodiscard]] inline T distance_squared(const VecBase &a, const VecBase &b) { return length_squared(a - b); } template [[nodiscard]] inline T distance(const VecBase &a, const VecBase &b) { return length(a - b); } template [[nodiscard]] inline VecBase reflect(const VecBase &incident, const VecBase &normal) { BLI_assert(is_unit_scale(normal)); return incident - 2.0 * dot(normal, incident) * normal; } template [[nodiscard]] inline VecBase refract(const VecBase &incident, const VecBase &normal, const T &eta) { float dot_ni = dot(normal, incident); float k = 1.0f - eta * eta * (1.0f - dot_ni * dot_ni); if (k < 0.0f) { return VecBase(0.0f); } return eta * incident - (eta * dot_ni + sqrt(k)) * normal; } /** * Project \a p onto \a v_proj . * Returns zero vector if \a v_proj is degenerate (zero length). */ template [[nodiscard]] inline VecBase project(const VecBase &p, const VecBase &v_proj) { if (UNLIKELY(is_zero(v_proj))) { return VecBase(0.0f); } return v_proj * (dot(p, v_proj) / dot(v_proj, v_proj)); } template [[nodiscard]] inline VecBase normalize_and_get_length(const VecBase &v, T &out_length) { out_length = length_squared(v); /* A larger value causes normalize errors in a scaled down models with camera extreme close. */ constexpr T threshold = std::is_same_v ? 1.0e-70 : 1.0e-35f; if (out_length > threshold) { out_length = sqrt(out_length); return v / out_length; } /* Either the vector is small or one of it's values contained `nan`. */ out_length = 0.0; return VecBase(0.0); } template [[nodiscard]] inline VecBase normalize(const VecBase &v) { T len; return normalize_and_get_length(v, len); } template [[nodiscard]] inline T cross(const VecBase &a, const VecBase &b) { return a.x * b.y - a.y * b.x; } /** * \return cross perpendicular vector to \a a and \a b. * \note Return zero vector if \a a and \a b are collinear. * \note The length of the resulting vector is equal to twice the area of the triangle between \a a * and \a b ; and it is equal to the sine of the angle between \a a and \a b if they are * normalized. * \note Blender 3D space uses right handedness: \a a = thumb, \a b = index, return = middle. */ template [[nodiscard]] inline VecBase cross(const VecBase &a, const VecBase &b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; } /** * Same as `cross(a, b)` but use double float precision for the computation. */ [[nodiscard]] inline VecBase cross_high_precision(const VecBase &a, const VecBase &b) { return {float(double(a.y) * double(b.z) - double(a.z) * double(b.y)), float(double(a.z) * double(b.x) - double(a.x) * double(b.z)), float(double(a.x) * double(b.y) - double(a.y) * double(b.x))}; } /** * \param poly: Array of points around a polygon. They don't have to be co-planar. * \return Best fit plane normal for the given polygon loop or zero vector if point * array is too short. Not normalized. */ template [[nodiscard]] inline VecBase cross_poly(Span> poly) { /* Newell's Method. */ int nv = int(poly.size()); if (nv < 3) { return VecBase(0, 0, 0); } const VecBase *v_prev = &poly[nv - 1]; const VecBase *v_curr = &poly[0]; VecBase n(0, 0, 0); for (int i = 0; i < nv;) { n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]); v_prev = v_curr; ++i; if (i < nv) { v_curr = &poly[i]; } } return n; } /** * Return normal vector to a triangle. * The result is not normalized and can be degenerate. */ template [[nodiscard]] inline VecBase cross_tri(const VecBase &v1, const VecBase &v2, const VecBase &v3) { return cross(v1 - v2, v2 - v3); } /** * Return normal vector to a triangle. * The result is normalized but can still be degenerate. */ template [[nodiscard]] inline VecBase normal_tri(const VecBase &v1, const VecBase &v2, const VecBase &v3) { return normalize(cross_tri(v1, v2, v3)); } /** * Per component linear interpolation. * \param t: interpolation factor. Return \a a if equal 0. Return \a b if equal 1. * Outside of [0..1] range, use linear extrapolation. */ template [[nodiscard]] inline VecBase interpolate(const VecBase &a, const VecBase &b, const FactorT &t) { return a * (1 - t) + b * t; } /** * \return Point halfway between \a a and \a b. */ template [[nodiscard]] inline VecBase midpoint(const VecBase &a, const VecBase &b) { return (a + b) * 0.5; } /** * Return `vector` if `incident` and `reference` are pointing in the same direction. */ template [[nodiscard]] inline VecBase faceforward(const VecBase &vector, const VecBase &incident, const VecBase &reference) { return (dot(reference, incident) < 0) ? vector : -vector; } /** * \return Index of the component with the greatest magnitude. */ template [[nodiscard]] inline int dominant_axis(const VecBase &a) { VecBase b = abs(a); return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2)); } /** * \return the maximum component of a vector. */ template [[nodiscard]] inline T reduce_max(const VecBase &a) { T result = a[0]; for (int i = 1; i < Size; i++) { if (a[i] > result) { result = a[i]; } } return result; } /** * \return the minimum component of a vector. */ template [[nodiscard]] inline T reduce_min(const VecBase &a) { T result = a[0]; for (int i = 1; i < Size; i++) { if (a[i] < result) { result = a[i]; } } return result; } /** * \return the sum of the components of a vector. */ template [[nodiscard]] inline T reduce_add(const VecBase &a) { T result = a[0]; for (int i = 1; i < Size; i++) { result += a[i]; } return result; } /** * \return the product of the components of a vector. */ template [[nodiscard]] inline T reduce_mul(const VecBase &a) { T result = a[0]; for (int i = 1; i < Size; i++) { result *= a[i]; } return result; } /** * \return the average of the components of a vector. */ template [[nodiscard]] inline T average(const VecBase &a) { return reduce_add(a) * (T(1) / T(Size)); } /** * Calculates a perpendicular vector to \a v. * \note Returned vector can be in any perpendicular direction. * \note Returned vector might not the same length as \a v. */ template [[nodiscard]] inline VecBase orthogonal(const VecBase &v) { const int axis = dominant_axis(v); switch (axis) { case 0: return {-v.y - v.z, v.x, v.x}; case 1: return {v.y, -v.x - v.z, v.y}; case 2: return {v.z, v.z, -v.x - v.y}; } return v; } /** * Calculates a perpendicular vector to \a v. * \note Returned vector can be in any perpendicular direction. */ template [[nodiscard]] inline VecBase orthogonal(const VecBase &v) { return {-v.y, v.x}; } /** * Returns true if vectors are equal within the given epsilon. */ template [[nodiscard]] inline bool is_equal(const VecBase &a, const VecBase &b, const T epsilon = T(0)) { for (int i = 0; i < Size; i++) { if (math::abs(a[i] - b[i]) > epsilon) { return false; } } return true; } /** * Return true if the absolute values of all components are smaller than given epsilon (0 by * default). * * \note Does not compute the actual length of the vector, for performance. */ template [[nodiscard]] inline bool is_zero(const VecBase &a, const T epsilon = T(0)) { for (int i = 0; i < Size; i++) { if (math::abs(a[i]) > epsilon) { return false; } } return true; } /** * Returns true if at least one component is exactly equal to 0. */ template [[nodiscard]] inline bool is_any_zero(const VecBase &a) { for (int i = 0; i < Size; i++) { if (a[i] == T(0)) { return true; } } return false; } /** * Return true if the squared length of the vector is (almost) equal to 1 (with a * `10 * std::numeric_limits::epsilon()` epsilon error by default). */ template [[nodiscard]] inline bool is_unit(const VecBase &a, const T epsilon = T(10) * std::numeric_limits::epsilon()) { const T length = length_squared(a); return math::abs(length - T(1)) <= epsilon; } /** Intersections. */ template struct isect_result { enum { LINE_LINE_COLINEAR = -1, LINE_LINE_NONE = 0, LINE_LINE_EXACT = 1, LINE_LINE_CROSS = 2, } kind; typename T::base_type lambda; }; template [[nodiscard]] isect_result> isect_seg_seg(const VecBase &v1, const VecBase &v2, const VecBase &v3, const VecBase &v4); } // namespace blender::math