Blender V4.5
GEO_interpolate_curves_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6
7#include "BKE_curves.hh"
8#include "BKE_idtype.hh"
9#include "BKE_lib_id.hh"
10
12
13#include "testing/testing.h"
14
15namespace blender::bke::tests {
16
17class GreasePencilInterpolate : public testing::Test {
18 public:
25
26 static void create_test_shape(const TestCurveShape shape, MutableSpan<float3> positions)
27 {
28 switch (shape) {
30 positions.fill(float3(0.0f));
31 break;
33 for (const int point_i : positions.index_range()) {
34 const float angle = 2.0f * M_PI * float(point_i) / float(positions.size());
35 positions[point_i] = float3(math::cos(angle), math::sin(angle), 0.0f);
36 }
37 break;
39 for (const int point_i : positions.index_range()) {
40 const float angle = 2.0f * M_PI * float(point_i) / float(positions.size());
41 positions[point_i] = float3(math::cos(angle), math::sin(angle * 2.0f), 0.0f);
42 }
43 break;
45 const int turns = 3;
46 const float pitch = 0.3f;
47 for (const int point_i : positions.index_range()) {
48 const float factor = float(turns) * float(point_i) / float(positions.size() - 1);
49 const float angle = 2.0f * M_PI * factor;
50 const float height = pitch * factor;
51 positions[point_i] = float3(math::cos(angle), math::sin(angle), height);
52 }
53 break;
54 }
55 }
56
58 Span<bool> cyclic,
59 TestCurveShape shape)
60 {
61 BLI_assert(!offsets.is_empty());
62 const int curves_num = offsets.size() - 1;
63 BLI_assert(cyclic.size() == curves_num);
64 const int points_num = offsets.last();
65
66 bke::CurvesGeometry curves(points_num, curves_num);
67 curves.offsets_for_write().copy_from(offsets);
68 curves.cyclic_for_write().copy_from(cyclic);
69
70 MutableSpan<float3> positions = curves.positions_for_write();
71 for (const int curve_i : curves.curves_range()) {
72 const IndexRange points = curves.points_by_curve()[curve_i];
73 create_test_shape(shape, positions.slice(points));
74 }
75
76 /* Attribute storing original indices to test point remapping. */
77 SpanAttributeWriter<int> test_indices_writer =
78 curves.attributes_for_write().lookup_or_add_for_write_span<int>(
80 array_utils::fill_index_range(test_indices_writer.span);
81 test_indices_writer.finish();
82
83 return curves;
84 }
85
87 const int curve_index,
88 const bool reverse,
89 const Span<int> expected_indices,
90 const Span<float> expected_factors,
91 const float threshold = 1e-4f)
92 {
93 const int num_dst_points = expected_indices.size();
94 BLI_assert(expected_factors.size() == num_dst_points);
95
96 const bool cyclic = curves.cyclic()[curve_index];
97
98 Array<int> indices(num_dst_points, -9999);
99 Array<float> factors(num_dst_points, -12345.6f);
100 geometry::sample_curve_padded(curves, curve_index, cyclic, reverse, indices, factors);
101
102 EXPECT_EQ(expected_indices.size(), indices.size());
103 EXPECT_EQ_ARRAY(expected_indices.data(), indices.data(), indices.size());
104
105 EXPECT_EQ(expected_factors.size(), factors.size());
106 if (expected_factors.size() == factors.size()) {
107 for (const int i : expected_factors.index_range()) {
108 EXPECT_NEAR(expected_factors[i], factors[i], threshold)
109 << "Element mismatch at index " << i;
110 }
111 }
112 }
113};
114
115TEST_F(GreasePencilInterpolate, sample_curve_empty_output)
116{
117 bke::CurvesGeometry curves = create_test_curves(
118 {0, 1, 3}, {false, false}, TestCurveShape::Eight);
119
120 test_sample_curve(curves, 0, false, {}, {});
121 test_sample_curve(curves, 1, false, {}, {});
122}
123
124TEST_F(GreasePencilInterpolate, sample_curve_same_length)
125{
126 bke::CurvesGeometry curves = create_test_curves(
127 {0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
128
129 test_sample_curve(curves, 0, false, {0}, {0.0f});
130 test_sample_curve(curves, 0, true, {0}, {0.0f});
131
132 test_sample_curve(curves, 1, false, {0, 1}, {0.0f, 0.0f});
133 test_sample_curve(curves, 1, true, {1, 0}, {0.0f, 0.0f});
134
135 test_sample_curve(curves,
136 2,
137 false,
138 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
139 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
140 test_sample_curve(curves,
141 2,
142 true,
143 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
144 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
145
146 test_sample_curve(curves, 3, false, {0}, {0.0f});
147 test_sample_curve(curves, 3, true, {0}, {0.0f});
148
149 test_sample_curve(curves, 4, false, {0, 1}, {0.0f, 0.0f});
150 test_sample_curve(curves, 4, true, {1, 0}, {0.0f, 0.0f});
151
152 test_sample_curve(curves,
153 5,
154 false,
155 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
156 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
157 test_sample_curve(curves,
158 5,
159 true,
160 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
161 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
162}
163
164TEST_F(GreasePencilInterpolate, sample_curve_shorter)
165{
166 bke::CurvesGeometry curves = create_test_curves(
167 {0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
168
169 test_sample_curve(curves, 1, false, {0}, {0.0f});
170 test_sample_curve(curves, 1, true, {1}, {0.0f});
171
172 test_sample_curve(curves, 2, false, {0, 2, 5, 9}, {0.0f, 0.82178f, 0.88113f, 0.0f});
173 test_sample_curve(curves, 2, true, {9, 5, 2, 0}, {0.0f, 0.88113f, 0.82178f, 0.0f});
174
175 test_sample_curve(curves, 4, false, {0}, {0.0f});
176 test_sample_curve(curves, 4, true, {1}, {0.0f});
177
178 test_sample_curve(curves, 5, false, {0, 2, 5, 7}, {0.0f, 0.5f, 0.0f, 0.5f});
179 test_sample_curve(curves, 5, true, {9, 6, 4, 1}, {0.0f, 0.50492f, 0.0f, 0.50492f});
180}
181
182TEST_F(GreasePencilInterpolate, sample_curve_longer)
183{
184 bke::CurvesGeometry curves = create_test_curves(
185 {0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
186
187 test_sample_curve(curves,
188 1,
189 false,
190 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
191 {0.0f,
192 0.09091f,
193 0.18182f,
194 0.27273f,
195 0.36364f,
196 0.45455f,
197 0.54545f,
198 0.63636f,
199 0.72727f,
200 0.81818f,
201 0.90909f,
202 0.0f});
203 test_sample_curve(curves,
204 1,
205 true,
206 {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
207 {0.0f,
208 0.90909f,
209 0.81818f,
210 0.72727f,
211 0.63636f,
212 0.54545f,
213 0.45455f,
214 0.36364f,
215 0.27273f,
216 0.18182f,
217 0.09091f,
218 0.0f});
219
220 test_sample_curve(curves,
221 2,
222 false,
223 {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9},
224 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f});
225 test_sample_curve(curves,
226 2,
227 true,
228 {9, 8, 7, 6, 6, 5, 4, 3, 2, 2, 1, 0},
229 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f});
230
231 test_sample_curve(curves,
232 4,
233 false,
234 {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
235 {0.0f,
236 0.16667f,
237 0.33333f,
238 0.5f,
239 0.66667f,
240 0.83333f,
241 0.0f,
242 0.16667f,
243 0.33333f,
244 0.5f,
245 0.66667f,
246 0.83333f});
247 test_sample_curve(curves,
248 4,
249 true,
250 {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
251 {0.83333f,
252 0.66667f,
253 0.5f,
254 0.33333f,
255 0.16667f,
256 0.0f,
257 0.83333f,
258 0.66667f,
259 0.5f,
260 0.33333f,
261 0.16667f,
262 0.0f});
263
264 test_sample_curve(curves,
265 5,
266 false,
267 {0, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 9},
268 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f});
269 test_sample_curve(curves,
270 5,
271 true,
272 {9, 8, 7, 6, 6, 5, 4, 3, 2, 1, 1, 0},
273 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f});
274}
275
276TEST_F(GreasePencilInterpolate, sample_zero_length_curve)
277{
278 bke::CurvesGeometry curves = create_test_curves(
279 {0, 10, 20}, {false, true}, TestCurveShape::Zero);
280
281 test_sample_curve(curves,
282 0,
283 false,
284 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
285 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
286 test_sample_curve(curves,
287 1,
288 false,
289 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
290 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
291}
292
293} // namespace blender::bke::tests
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:46
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define M_PI
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
int64_t size() const
Definition BLI_array.hh:245
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 IndexRange index_range() const
Definition BLI_span.hh:670
constexpr const T * data() const
Definition BLI_span.hh:215
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 void create_test_shape(const TestCurveShape shape, MutableSpan< float3 > positions)
void test_sample_curve(const bke::CurvesGeometry &curves, const int curve_index, const bool reverse, const Span< int > expected_indices, const Span< float > expected_factors, const float threshold=1e-4f)
static bke::CurvesGeometry create_test_curves(Span< int > offsets, Span< bool > cyclic, TestCurveShape shape)
static ushort indices[]
void fill_index_range(MutableSpan< T > span, const T start=0)
TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected)
void sample_curve_padded(const Span< float3 > positions, bool cyclic, MutableSpan< int > r_indices, MutableSpan< float > r_factors)
T cos(const AngleRadianBase< T > &a)
T sin(const AngleRadianBase< T > &a)
VecBase< float, 3 > float3
i
Definition text_draw.cc:230