Blender V4.5
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 = 1e-6f;
20 return false;
21 }
22 r_delta_dir = math::normalize(next - pos);
23 return true;
24}
25
31 const float3 &next,
32 float3 &other_dir,
33 bool &is_equal)
34{
35 const float epsilon = 1e-6f;
36 const bool prev_equal = is_equal;
37 is_equal = math::almost_equal_relative(pos, next, epsilon);
38 if (UNLIKELY(is_equal)) {
39 /* Return the direction relative the 'previous' point. If 'prev_equal' is true this is not
40 * the direction from previous point (it would be from the previous 'non-zero' segment).
41 */
42 return other_dir;
43 }
44
45 const float3 prev_dir = other_dir;
46 other_dir = math::normalize(next - pos);
47 if (UNLIKELY(prev_equal)) {
48 /* Return direction to next point as previous direction is not from the adjacent point! */
49 return other_dir;
50 }
51 return math::normalize(prev_dir + other_dir);
52}
53
54void calculate_tangents(const Span<float3> positions,
55 const bool is_cyclic,
56 MutableSpan<float3> tangents)
57{
58 BLI_assert(positions.size() == tangents.size());
59
60 if (positions.is_empty()) {
61 return;
62 }
63
64 if (positions.size() == 1) {
65 tangents.first() = float3(0.0f, 0.0f, 1.0f);
66 return;
67 }
68
69 /* Find an initial valid tangent. */
70 int first_valid_index = -1;
71 for (const int i : IndexRange(0, positions.size() - 1)) {
72 if (delta_dir(positions[i], positions[i + 1], tangents[i])) {
73 first_valid_index = i;
74 break;
75 }
76 }
77
78 if (first_valid_index == -1) {
79 /* If all tangents used the fallback, it means that all positions are (almost) the same. Just
80 * use the up-vector as default tangent. */
81 const float3 up_vector{0.0f, 0.0f, 1.0f};
82 tangents.fill(up_vector);
83 return;
84 }
85 if (first_valid_index > 0) {
86 tangents.slice(0, first_valid_index).fill(tangents[first_valid_index]);
87 }
88
89 /* Calculate curve tangents using the delta from previous iteration(s). */
90 float3 prev_delta = tangents[first_valid_index];
91 bool prev_equal = false;
92 for (const int i : positions.index_range().drop_front(first_valid_index + 1).drop_back(1)) {
93 tangents[i] = direction_bisect(positions[i], positions[i + 1], prev_delta, prev_equal);
94 }
95
96 if (is_cyclic) {
97 const float3 &first = positions.first();
98 tangents.last() = direction_bisect(positions.last(), first, prev_delta, prev_equal);
99 tangents.first() = direction_bisect(first, positions[1], prev_delta, prev_equal);
100 }
101 else if (!delta_dir(positions.last(1), positions.last(), tangents.last())) {
102 tangents.last() = prev_delta;
103 }
104}
105
107{
108 BLI_assert(normals.size() == tangents.size());
109
110 /* Same as in `vec_to_quat`. */
111 const float epsilon = 1e-4f;
112 for (const int i : normals.index_range()) {
113 const float3 &tangent = tangents[i];
114 if (std::abs(tangent.x) + std::abs(tangent.y) < epsilon) {
115 normals[i] = {1.0f, 0.0f, 0.0f};
116 }
117 else {
118 normals[i] = math::normalize(float3(tangent.y, -tangent.x, 0.0f));
119 }
120 }
121}
122
126static float3 calculate_next_normal(const float3 &last_normal,
127 const float3 &last_tangent,
128 const float3 &current_tangent)
129{
130 if (math::is_zero(last_tangent) || math::is_zero(current_tangent)) {
131 return last_normal;
132 }
133 const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
134 if (angle != 0.0f) {
135 const float3 axis = math::normalize(math::cross(last_tangent, current_tangent));
136 if (LIKELY(!math::is_zero(axis))) {
137 /* The iterative process here (computing the current normal by rotating the previous one) can
138 * accumulate small floating point errors, leading to 'not enough' normalized results at some
139 * point (see #121169). */
140 return math::normalize(math::rotate_direction_around_axis(last_normal, axis, angle));
141 }
142 }
143 return last_normal;
144}
145
147 const bool cyclic,
149{
150 BLI_assert(normals.size() == tangents.size());
151
152 if (normals.is_empty()) {
153 return;
154 }
155
156 const float epsilon = 1e-4f;
157
158 /* Set initial normal. */
159 const float3 &first_tangent = tangents.first();
160 if (UNLIKELY(fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon)) {
161 normals.first() = {1.0f, 0.0f, 0.0f};
162 }
163 else {
164 normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f));
165 }
166
167 /* Forward normal with minimum twist along the entire curve. */
168 for (const int i : IndexRange(1, normals.size() - 1)) {
169 normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]);
170 }
171
172 if (!cyclic) {
173 return;
174 }
175
176 /* Compute how much the first normal deviates from the normal that has been forwarded along the
177 * entire cyclic curve. */
178 const float3 uncorrected_last_normal = calculate_next_normal(
179 normals.last(), tangents.last(), tangents.first());
180 float correction_angle = angle_signed_on_axis_v3v3_v3(
181 normals.first(), uncorrected_last_normal, tangents.first());
182 if (correction_angle > M_PI) {
183 correction_angle = correction_angle - 2 * M_PI;
184 }
185
186 /* Gradually apply correction by rotating all normals slightly around their tangents. */
187 const float angle_step = correction_angle / normals.size();
188 for (const int i : normals.index_range()) {
189 const float3 axis = tangents[i];
190 if (UNLIKELY(math::is_zero(axis))) {
191 continue;
192 }
193 const float angle = angle_step * i;
195 }
196}
197
198} // 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
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:30
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:54
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
i
Definition text_draw.cc:230