/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2022 Blender Foundation. */ #pragma once /** \file * \ingroup bli */ #include #include #include "BLI_math_base.hh" #include "BLI_math_vec_types.hh" #include "BLI_span.hh" #include "BLI_utildefines.h" namespace blender::math { #ifndef NDEBUG # define BLI_ASSERT_UNIT(v) \ { \ const float _test_unit = length_squared(v); \ BLI_assert(!(std::abs(_test_unit - 1.0f) >= BLI_ASSERT_UNIT_EPSILON) || \ !(std::abs(_test_unit) >= BLI_ASSERT_UNIT_EPSILON)); \ } \ (void)0 #else # define BLI_ASSERT_UNIT(v) (void)(v) #endif template inline bool is_zero(const vec_base &a) { for (int i = 0; i < Size; i++) { if (a[i] != T(0)) { return false; } } return true; } template inline bool is_any_zero(const vec_base &a) { for (int i = 0; i < Size; i++) { if (a[i] == T(0)) { return true; } } return false; } template inline bool almost_equal_relative(const vec_base &a, const vec_base &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 inline vec_base abs(const vec_base &a) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = a[i] >= 0 ? a[i] : -a[i]; } return result; } template inline vec_base min(const vec_base &a, const vec_base &b) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = a[i] < b[i] ? a[i] : b[i]; } return result; } template inline vec_base max(const vec_base &a, const vec_base &b) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = a[i] > b[i] ? a[i] : b[i]; } return result; } template inline vec_base clamp(const vec_base &a, const vec_base &min, const vec_base &max) { vec_base result = a; for (int i = 0; i < Size; i++) { result[i] = std::clamp(result[i], min[i], max[i]); } return result; } template inline vec_base clamp(const vec_base &a, const T &min, const T &max) { vec_base result = a; for (int i = 0; i < Size; i++) { result[i] = std::clamp(result[i], min, max); } return result; } template))> inline vec_base mod(const vec_base &a, const vec_base &b) { vec_base result; for (int i = 0; i < Size; i++) { BLI_assert(b[i] != 0); result[i] = std::fmod(a[i], b[i]); } return result; } template))> inline vec_base mod(const vec_base &a, const T &b) { BLI_assert(b != 0); vec_base result; for (int i = 0; i < Size; i++) { result[i] = std::fmod(a[i], b); } return result; } template))> inline T safe_mod(const vec_base &a, const vec_base &b) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = (b[i] != 0) ? std::fmod(a[i], b[i]) : 0; } return result; } template))> inline T safe_mod(const vec_base &a, const T &b) { if (b == 0) { return vec_base(0); } vec_base result; for (int i = 0; i < Size; i++) { result[i] = std::fmod(a[i], b); } return result; } /** * 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))> inline vec_base ceil_to_multiple(const vec_base &a, const vec_base &b) { vec_base 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))> inline vec_base divide_ceil(const vec_base &a, const vec_base &b) { vec_base 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 vec_base &vector, vec_base &min, vec_base &max) { min = math::min(vector, min); max = math::max(vector, max); } template))> inline vec_base safe_divide(const vec_base &a, const vec_base &b) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = (b[i] == 0) ? 0 : a[i] / b[i]; } return result; } template))> inline vec_base safe_divide(const vec_base &a, const T &b) { return (b != 0) ? a / b : vec_base(0.0f); } template))> inline vec_base floor(const vec_base &a) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = std::floor(a[i]); } return result; } template))> inline vec_base ceil(const vec_base &a) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = std::ceil(a[i]); } return result; } template))> inline vec_base fract(const vec_base &a) { vec_base result; for (int i = 0; i < Size; i++) { result[i] = a[i] - std::floor(a[i]); } return result; } template))> inline T dot(const vec_base &a, const vec_base &b) { T result = a[0] * b[0]; for (int i = 1; i < Size; i++) { result += a[i] * b[i]; } return result; } template inline T length_manhattan(const vec_base &a) { T result = std::abs(a[0]); for (int i = 1; i < Size; i++) { result += std::abs(a[i]); } return result; } template))> inline T length_squared(const vec_base &a) { return dot(a, a); } template))> inline T length(const vec_base &a) { return std::sqrt(length_squared(a)); } template))> inline T distance_manhattan(const vec_base &a, const vec_base &b) { return length_manhattan(a - b); } template))> inline T distance_squared(const vec_base &a, const vec_base &b) { return length_squared(a - b); } template))> inline T distance(const vec_base &a, const vec_base &b) { return length(a - b); } template))> inline vec_base reflect(const vec_base &incident, const vec_base &normal) { BLI_ASSERT_UNIT(normal); return incident - 2.0 * dot(normal, incident) * normal; } template))> inline vec_base refract(const vec_base &incident, const vec_base &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 vec_base(0.0f); } return eta * incident - (eta * dot_ni + sqrt(k)) * normal; } template))> inline vec_base project(const vec_base &p, const vec_base &v_proj) { if (UNLIKELY(is_zero(v_proj))) { return vec_base(0.0f); } return v_proj * (dot(p, v_proj) / dot(v_proj, v_proj)); } template))> inline vec_base normalize_and_get_length(const vec_base &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 vec_base(0.0); } template))> inline vec_base normalize(const vec_base &v) { T len; return normalize_and_get_length(v, len); } template))> inline vec_base cross(const vec_base &a, const vec_base &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}; } inline vec_base cross_high_precision(const vec_base &a, const vec_base &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))}; } template))> inline vec_base cross_poly(Span> poly) { /* Newell's Method. */ int nv = int(poly.size()); if (nv < 3) { return vec_base(0, 0, 0); } const vec_base *v_prev = &poly[nv - 1]; const vec_base *v_curr = &poly[0]; vec_base 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; } template))> inline vec_base interpolate(const vec_base &a, const vec_base &b, const FactorT &t) { return a * (1 - t) + b * t; } template))> inline vec_base midpoint(const vec_base &a, const vec_base &b) { return (a + b) * 0.5; } template))> inline vec_base faceforward(const vec_base &vector, const vec_base &incident, const vec_base &reference) { return (dot(reference, incident) < 0) ? vector : -vector; } template inline int dominant_axis(const vec_base &a) { vec_base b = abs(a); return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2)); } /** 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))> isect_result> isect_seg_seg(const vec_base &v1, const vec_base &v2, const vec_base &v3, const vec_base &v4); } // namespace blender::math