Blender V5.0
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
8
10#include "BLI_math_vector.hh"
11
12#include "BKE_curves.hh"
13
15
16static bool delta_dir(const float3 &pos, const float3 &next, float3 &r_delta_dir)
17{
18 const float epsilon = 1.0e-9f;
19
20 const float3 delta = next - pos;
21 const float norm = math::length(delta);
22 if (UNLIKELY(norm < epsilon)) {
23 return false;
24 }
25 r_delta_dir = delta / norm;
26 return true;
27}
28
34 const float3 &next,
35 float3 &other_dir,
36 bool &is_equal)
37{
38 const float epsilon = 1.0e-9f;
39 const bool prev_equal = is_equal;
40
41 const float3 next_delta = next - pos;
42 const float next_norm = math::length(next_delta);
43 is_equal = next_norm < epsilon;
44 if (UNLIKELY(is_equal)) {
45 /* Return the direction relative the 'previous' point. If 'prev_equal' is true, this
46 * will return the direction of the last non-zero segment.
47 */
48 return other_dir;
49 }
50
51 const float3 prev_dir = other_dir;
52 other_dir = next_delta / next_norm;
53 if (UNLIKELY(prev_equal)) {
54 /* Return the direction of the next segment as previous direction is not an adjacent segment!
55 */
56 return other_dir;
57 }
58 const float3 tangent = prev_dir + other_dir;
59 const float norm = math::length(tangent);
60 if (norm < 0.6627619f) { /* Approximates angle between segments < 45°) */
61 if (norm < 2e-7) { /* Approximately < sin(1e-5) */
62 return other_dir;
63 }
64 /* Compute using the cross product, as catastrophic cancellation occurs in `tangent`
65 * when the sum approaches 0, leading to significant numerical errors (see #146332).
66 */
67 const float3 binormal = math::cross(other_dir, prev_dir);
68 const float3 normal = other_dir - prev_dir;
69 return math::normalize(math::cross(binormal, normal));
70 }
71 return tangent / norm;
72}
73
74void calculate_tangents(const Span<float3> positions,
75 const bool is_cyclic,
76 MutableSpan<float3> tangents)
77{
78 BLI_assert(positions.size() == tangents.size());
79
80 if (positions.is_empty()) {
81 return;
82 }
83
84 if (positions.size() == 1) {
85 tangents.first() = float3(0.0f, 0.0f, 1.0f);
86 return;
87 }
88
89 /* Find an initial valid tangent. */
90 int first_valid_index = -1;
91 for (const int i : IndexRange(0, positions.size() - 1)) {
92 if (delta_dir(positions[i], positions[i + 1], tangents[i])) {
93 first_valid_index = i;
94 break;
95 }
96 }
97
98 if (first_valid_index == -1) {
99 /* If all tangents used the fallback, it means that all positions are (almost) the same. Just
100 * use the up-vector as default tangent. */
101 const float3 up_vector{0.0f, 0.0f, 1.0f};
102 tangents.fill(up_vector);
103 return;
104 }
105 if (first_valid_index > 0) {
106 tangents.slice(0, first_valid_index).fill(tangents[first_valid_index]);
107 }
108
109 /* Calculate curve tangents using the delta from previous iteration(s). */
110 float3 prev_delta = tangents[first_valid_index];
111 bool prev_equal = false;
112 for (const int i : positions.index_range().drop_front(first_valid_index + 1).drop_back(1)) {
113 tangents[i] = direction_bisect(positions[i], positions[i + 1], prev_delta, prev_equal);
114 }
115
116 if (is_cyclic) {
117 const float3 &first = positions.first();
118 tangents.last() = direction_bisect(positions.last(), first, prev_delta, prev_equal);
119 tangents.first() = direction_bisect(first, positions[1], prev_delta, prev_equal);
120 }
121 else if (!delta_dir(positions.last(1), positions.last(), tangents.last())) {
122 tangents.last() = prev_delta;
123 }
124}
125
127{
128 BLI_assert(normals.size() == tangents.size());
129
130 /* Same as in `vec_to_quat`. */
131 const float epsilon = 1e-4f;
132 for (const int i : normals.index_range()) {
133 const float3 &tangent = tangents[i];
134 if (std::abs(tangent.x) + std::abs(tangent.y) < epsilon) {
135 normals[i] = {1.0f, 0.0f, 0.0f};
136 }
137 else {
138 normals[i] = math::normalize(float3(tangent.y, -tangent.x, 0.0f));
139 }
140 }
141}
142
146static float3 calculate_next_normal(const float3 &last_normal,
147 const float3 &last_tangent,
148 const float3 &current_tangent)
149{
150 if (math::is_zero(last_tangent) || math::is_zero(current_tangent)) {
151 return last_normal;
152 }
153 const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
154 if (angle != 0.0f) {
155 const float3 axis = math::normalize(math::cross(last_tangent, current_tangent));
156 if (LIKELY(!math::is_zero(axis))) {
157 /* The iterative process here (computing the current normal by rotating the previous one) can
158 * accumulate small floating point errors, leading to 'not enough' normalized results at some
159 * point (see #121169). */
160 return math::normalize(math::rotate_direction_around_axis(last_normal, axis, angle));
161 }
162 }
163 return last_normal;
164}
165
167 const bool cyclic,
169{
170 BLI_assert(normals.size() == tangents.size());
171
172 if (normals.is_empty()) {
173 return;
174 }
175
176 const float epsilon = 1e-4f;
177
178 /* Set initial normal. */
179 const float3 &first_tangent = tangents.first();
180 if (UNLIKELY(fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon)) {
181 normals.first() = {1.0f, 0.0f, 0.0f};
182 }
183 else {
184 normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f));
185 }
186
187 /* Forward normal with minimum twist along the entire curve. */
188 for (const int i : IndexRange(1, normals.size() - 1)) {
189 normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]);
190 }
191
192 if (!cyclic) {
193 return;
194 }
195
196 /* Compute how much the first normal deviates from the normal that has been forwarded along the
197 * entire cyclic curve. */
198 const float3 uncorrected_last_normal = calculate_next_normal(
199 normals.last(), tangents.last(), tangents.first());
200 float correction_angle = angle_signed_on_axis_v3v3_v3(
201 normals.first(), uncorrected_last_normal, tangents.first());
202 if (correction_angle > M_PI) {
203 correction_angle = correction_angle - 2 * M_PI;
204 }
205
206 /* Gradually apply correction by rotating all normals slightly around their tangents. */
207 const float angle_step = correction_angle / normals.size();
208 for (const int i : normals.index_range()) {
209 const float3 axis = tangents[i];
210 if (UNLIKELY(math::is_zero(axis))) {
211 continue;
212 }
213 const float angle = angle_step * i;
215 }
216}
217
218} // namespace blender::bke::curves::poly
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:46
#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)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
constexpr IndexRange drop_back(int64_t n) const
constexpr IndexRange drop_front(int64_t n) const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr T & first() const
Definition BLI_span.hh:679
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
static bool is_cyclic(const Nurb *nu)
static float normals[][3]
uint pos
ccl_device_inline float2 fabs(const float2 a)
static ulong * next
void calculate_normals_z_up(Span< float3 > tangents, MutableSpan< float3 > normals)
static bool delta_dir(const float3 &pos, const float3 &next, float3 &r_delta_dir)
Definition curve_poly.cc:16
static float3 direction_bisect(const float3 &pos, const float3 &next, float3 &other_dir, bool &is_equal)
Definition curve_poly.cc:33
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:74
T length(const VecBase< T, Size > &a)
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)
VecBase< float, 3 > float3
i
Definition text_draw.cc:230