Blender V4.3
curve_poly.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10
12#include "BLI_math_vector.hh"
13
14#include "BKE_curves.hh"
15
17
18static float3 direction_bisect(const float3 &prev,
19 const float3 &middle,
20 const float3 &next,
21 bool &r_used_fallback)
22{
23 const float epsilon = 1e-6f;
24 const bool prev_equal = math::almost_equal_relative(prev, middle, epsilon);
25 const bool next_equal = math::almost_equal_relative(middle, next, epsilon);
26 if (prev_equal && next_equal) {
27 r_used_fallback = true;
28 return {0.0f, 0.0f, 0.0f};
29 }
30 if (prev_equal) {
31 return math::normalize(next - middle);
32 }
33 if (next_equal) {
34 return math::normalize(middle - prev);
35 }
36
37 const float3 dir_prev = math::normalize(middle - prev);
38 const float3 dir_next = math::normalize(next - middle);
39 const float3 result = math::normalize(dir_prev + dir_next);
40 return result;
41}
42
43void calculate_tangents(const Span<float3> positions,
44 const bool is_cyclic,
45 MutableSpan<float3> tangents)
46{
47 BLI_assert(positions.size() == tangents.size());
48
49 if (positions.size() == 1) {
50 tangents.first() = float3(0.0f, 0.0f, 1.0f);
51 return;
52 }
53
54 bool used_fallback = false;
55
56 for (const int i : IndexRange(1, positions.size() - 2)) {
57 tangents[i] = direction_bisect(
58 positions[i - 1], positions[i], positions[i + 1], used_fallback);
59 }
60
61 if (is_cyclic) {
62 const float3 &second_to_last = positions[positions.size() - 2];
63 const float3 &last = positions.last();
64 const float3 &first = positions.first();
65 const float3 &second = positions[1];
66 tangents.first() = direction_bisect(last, first, second, used_fallback);
67 tangents.last() = direction_bisect(second_to_last, last, first, used_fallback);
68 }
69 else {
70 const float epsilon = 1e-6f;
71 if (math::almost_equal_relative(positions[0], positions[1], epsilon)) {
72 tangents.first() = {0.0f, 0.0f, 0.0f};
73 used_fallback = true;
74 }
75 else {
76 tangents.first() = math::normalize(positions[1] - positions[0]);
77 }
78 if (math::almost_equal_relative(positions.last(0), positions.last(1), epsilon)) {
79 tangents.last() = {0.0f, 0.0f, 0.0f};
80 used_fallback = true;
81 }
82 else {
83 tangents.last() = math::normalize(positions.last(0) - positions.last(1));
84 }
85 }
86
87 if (!used_fallback) {
88 return;
89 }
90
91 /* Find the first tangent that does not use the fallback. */
92 int first_valid_tangent_index = -1;
93 for (const int i : tangents.index_range()) {
94 if (!math::is_zero(tangents[i])) {
95 first_valid_tangent_index = i;
96 break;
97 }
98 }
99 if (first_valid_tangent_index == -1) {
100 /* If all tangents used the fallback, it means that all positions are (almost) the same. Just
101 * use the up-vector as default tangent. */
102 const float3 up_vector{0.0f, 0.0f, 1.0f};
103 tangents.fill(up_vector);
104 }
105 else {
106 const float3 &first_valid_tangent = tangents[first_valid_tangent_index];
107 /* If the first few tangents are invalid, use the tangent from the first point with a valid
108 * tangent. */
109 tangents.take_front(first_valid_tangent_index).fill(first_valid_tangent);
110 /* Use the previous valid tangent for points that had no valid tangent. */
111 for (const int i : tangents.index_range().drop_front(first_valid_tangent_index + 1)) {
112 float3 &tangent = tangents[i];
113 if (math::is_zero(tangent)) {
114 const float3 &prev_tangent = tangents[i - 1];
115 tangent = prev_tangent;
116 }
117 }
118 }
119}
120
122{
123 BLI_assert(normals.size() == tangents.size());
124
125 /* Same as in `vec_to_quat`. */
126 const float epsilon = 1e-4f;
127 for (const int i : normals.index_range()) {
128 const float3 &tangent = tangents[i];
129 if (std::abs(tangent.x) + std::abs(tangent.y) < epsilon) {
130 normals[i] = {1.0f, 0.0f, 0.0f};
131 }
132 else {
133 normals[i] = math::normalize(float3(tangent.y, -tangent.x, 0.0f));
134 }
135 }
136}
137
141static float3 calculate_next_normal(const float3 &last_normal,
142 const float3 &last_tangent,
143 const float3 &current_tangent)
144{
145 if (math::is_zero(last_tangent) || math::is_zero(current_tangent)) {
146 return last_normal;
147 }
148 const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
149 if (angle != 0.0f) {
150 const float3 axis = math::normalize(math::cross(last_tangent, current_tangent));
151 if (LIKELY(!math::is_zero(axis))) {
152 /* The iterative process here (computing the current normal by rotating the previous one) can
153 * accumulate small floating point errors, leading to 'not enough' normalized results at some
154 * point (see #121169). */
155 return math::normalize(math::rotate_direction_around_axis(last_normal, axis, angle));
156 }
157 }
158 return last_normal;
159}
160
162 const bool cyclic,
163 MutableSpan<float3> normals)
164{
165 BLI_assert(normals.size() == tangents.size());
166
167 if (normals.is_empty()) {
168 return;
169 }
170
171 const float epsilon = 1e-4f;
172
173 /* Set initial normal. */
174 const float3 &first_tangent = tangents.first();
175 if (UNLIKELY(fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon)) {
176 normals.first() = {1.0f, 0.0f, 0.0f};
177 }
178 else {
179 normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f));
180 }
181
182 /* Forward normal with minimum twist along the entire curve. */
183 for (const int i : IndexRange(1, normals.size() - 1)) {
184 normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]);
185 }
186
187 if (!cyclic) {
188 return;
189 }
190
191 /* Compute how much the first normal deviates from the normal that has been forwarded along the
192 * entire cyclic curve. */
193 const float3 uncorrected_last_normal = calculate_next_normal(
194 normals.last(), tangents.last(), tangents.first());
195 float correction_angle = angle_signed_on_axis_v3v3_v3(
196 normals.first(), uncorrected_last_normal, tangents.first());
197 if (correction_angle > M_PI) {
198 correction_angle = correction_angle - 2 * M_PI;
199 }
200
201 /* Gradually apply correction by rotating all normals slightly around their tangents. */
202 const float angle_step = correction_angle / normals.size();
203 for (const int i : normals.index_range()) {
204 const float3 axis = tangents[i];
205 if (UNLIKELY(math::is_zero(axis))) {
206 continue;
207 }
208 const float angle = angle_step * i;
209 normals[i] = math::rotate_direction_around_axis(normals[i], axis, angle);
210 }
211}
212
213} // namespace blender::bke::curves::poly
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:50
#define M_PI
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
#define UNLIKELY(x)
#define LIKELY(x)
constexpr IndexRange drop_front(int64_t n) const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr T & first() const
Definition BLI_span.hh:680
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
static bool is_cyclic(const Nurb *nu)
ccl_device_inline float2 fabs(const float2 a)
static ulong * next
void calculate_normals_z_up(Span< float3 > tangents, MutableSpan< float3 > normals)
static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next, bool &r_used_fallback)
Definition curve_poly.cc:18
void calculate_normals_minimum(Span< float3 > tangents, bool cyclic, MutableSpan< float3 > normals)
static float3 calculate_next_normal(const float3 &last_normal, const float3 &last_tangent, const float3 &current_tangent)
void calculate_tangents(Span< float3 > positions, bool is_cyclic, MutableSpan< float3 > tangents)
Definition curve_poly.cc:43
bool is_zero(const T &a)
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
bool almost_equal_relative(const VecBase< T, Size > &a, const VecBase< T, Size > &b, const T &epsilon_factor)
VecBase< float, 3 > float3