Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/curve_poly.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| /** \file | /** \file | ||||
| * \ingroup bke | * \ingroup bke | ||||
| */ | */ | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include "BLI_math_rotation.hh" | #include "BLI_math_rotation.hh" | ||||
| #include "BLI_math_vector.hh" | #include "BLI_math_vector.hh" | ||||
| #include "BKE_curves.hh" | #include "BKE_curves.hh" | ||||
| namespace blender::bke::curves::poly { | namespace blender::bke::curves::poly { | ||||
| static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next) | static float3 direction_bisect(const float3 &prev, | ||||
| const float3 &middle, | |||||
| const float3 &next, | |||||
| bool &r_used_fallback) | |||||
| { | { | ||||
| const float epsilon = 1e-6f; | |||||
| const bool prev_equal = math::almost_equal_relative(prev, middle, epsilon); | |||||
| const bool next_equal = math::almost_equal_relative(middle, next, epsilon); | |||||
| if (prev_equal && next_equal) { | |||||
| r_used_fallback = true; | |||||
| return {0.0f, 0.0f, 0.0f}; | |||||
| } | |||||
| if (prev_equal) { | |||||
| return math::normalize(next - middle); | |||||
| } | |||||
| if (next_equal) { | |||||
| return math::normalize(middle - prev); | |||||
| } | |||||
| const float3 dir_prev = math::normalize(middle - prev); | const float3 dir_prev = math::normalize(middle - prev); | ||||
| const float3 dir_next = math::normalize(next - middle); | const float3 dir_next = math::normalize(next - middle); | ||||
| const float3 result = math::normalize(dir_prev + dir_next); | const float3 result = math::normalize(dir_prev + dir_next); | ||||
| if (UNLIKELY(math::is_zero(result))) { | |||||
| return float3(0.0f, 0.0f, 1.0f); | |||||
| } | |||||
| return result; | return result; | ||||
| } | } | ||||
| void calculate_tangents(const Span<float3> positions, | void calculate_tangents(const Span<float3> positions, | ||||
| const bool is_cyclic, | const bool is_cyclic, | ||||
| MutableSpan<float3> tangents) | MutableSpan<float3> tangents) | ||||
| { | { | ||||
| BLI_assert(positions.size() == tangents.size()); | BLI_assert(positions.size() == tangents.size()); | ||||
| if (positions.size() == 1) { | if (positions.size() == 1) { | ||||
| tangents.first() = float3(0.0f, 0.0f, 1.0f); | tangents.first() = float3(0.0f, 0.0f, 1.0f); | ||||
| return; | return; | ||||
| } | } | ||||
| bool used_fallback = false; | |||||
| for (const int i : IndexRange(1, positions.size() - 2)) { | for (const int i : IndexRange(1, positions.size() - 2)) { | ||||
| tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]); | tangents[i] = direction_bisect( | ||||
| positions[i - 1], positions[i], positions[i + 1], used_fallback); | |||||
| } | } | ||||
| if (is_cyclic) { | if (is_cyclic) { | ||||
| const float3 &second_to_last = positions[positions.size() - 2]; | const float3 &second_to_last = positions[positions.size() - 2]; | ||||
| const float3 &last = positions.last(); | const float3 &last = positions.last(); | ||||
| const float3 &first = positions.first(); | const float3 &first = positions.first(); | ||||
| const float3 &second = positions[1]; | const float3 &second = positions[1]; | ||||
| tangents.first() = direction_bisect(last, first, second); | tangents.first() = direction_bisect(last, first, second, used_fallback); | ||||
| tangents.last() = direction_bisect(second_to_last, last, first); | tangents.last() = direction_bisect(second_to_last, last, first, used_fallback); | ||||
| } | } | ||||
| else { | else { | ||||
| tangents.first() = math::normalize(positions[1] - positions.first()); | tangents.first() = math::normalize(positions[1] - positions.first()); | ||||
| tangents.last() = math::normalize(positions.last() - positions[positions.size() - 2]); | tangents.last() = math::normalize(positions.last() - positions[positions.size() - 2]); | ||||
| } | } | ||||
| if (!used_fallback) { | |||||
| return; | |||||
| } | |||||
| /* Find the first tangent that does not use the fallback. */ | |||||
| int first_valid_tangent_index = -1; | |||||
| for (const int i : tangents.index_range()) { | |||||
| if (!math::is_zero(tangents[i])) { | |||||
| first_valid_tangent_index = i; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (first_valid_tangent_index == -1) { | |||||
| /* If all tangents used the fallback, it means that all positions are (almost) the same. Just | |||||
| * use the up-vector as default tangent. */ | |||||
| const float3 up_vector{0.0f, 0.0f, 1.0f}; | |||||
| tangents.fill(up_vector); | |||||
| } | |||||
| else { | |||||
| const float3 &first_valid_tangent = tangents[first_valid_tangent_index]; | |||||
HooglyBoogly: I'd replace "close-by" with "previous" which is a bit more accurate IMO. | |||||
| /* If the first few tangents are invalid, use the tangent from the first point with a valid | |||||
| * tangent. */ | |||||
| tangents.take_front(first_valid_tangent_index).fill(first_valid_tangent); | |||||
| /* Use the previous valid tangent for points that had no valid tangent. */ | |||||
| for (const int i : tangents.index_range().drop_front(first_valid_tangent_index + 1)) { | |||||
| float3 &tangent = tangents[i]; | |||||
| if (math::is_zero(tangent)) { | |||||
| const float3 &prev_tangent = tangents[i - 1]; | |||||
| tangent = prev_tangent; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void calculate_normals_z_up(const Span<float3> tangents, MutableSpan<float3> normals) | void calculate_normals_z_up(const Span<float3> tangents, MutableSpan<float3> normals) | ||||
| { | { | ||||
| BLI_assert(normals.size() == tangents.size()); | BLI_assert(normals.size() == tangents.size()); | ||||
| /* Same as in `vec_to_quat`. */ | /* Same as in `vec_to_quat`. */ | ||||
| const float epsilon = 1e-4f; | const float epsilon = 1e-4f; | ||||
| ▲ Show 20 Lines • Show All 78 Lines • Show Last 20 Lines | |||||
I'd replace "close-by" with "previous" which is a bit more accurate IMO.