Blender V5.0
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_SPAN(expected_indices, indices.as_span());
103
104 EXPECT_EQ(expected_factors.size(), factors.size());
105 if (expected_factors.size() == factors.size()) {
106 for (const int i : expected_factors.index_range()) {
107 EXPECT_NEAR(expected_factors[i], factors[i], threshold)
108 << "Element mismatch at index " << i;
109 }
110 }
111 }
112};
113
114TEST_F(GreasePencilInterpolate, sample_curve_empty_output)
115{
116 bke::CurvesGeometry curves = create_test_curves(
117 {0, 1, 3}, {false, false}, TestCurveShape::Eight);
118
119 test_sample_curve(curves, 0, false, {}, {});
120 test_sample_curve(curves, 1, false, {}, {});
121}
122
123TEST_F(GreasePencilInterpolate, sample_curve_same_length)
124{
125 bke::CurvesGeometry curves = create_test_curves(
126 {0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
127
128 test_sample_curve(curves, 0, false, {0}, {0.0f});
129 test_sample_curve(curves, 0, true, {0}, {0.0f});
130
131 test_sample_curve(curves, 1, false, {0, 1}, {0.0f, 0.0f});
132 test_sample_curve(curves, 1, true, {1, 0}, {0.0f, 0.0f});
133
134 test_sample_curve(curves,
135 2,
136 false,
137 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
138 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
139 test_sample_curve(curves,
140 2,
141 true,
142 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
143 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
144
145 test_sample_curve(curves, 3, false, {0}, {0.0f});
146 test_sample_curve(curves, 3, true, {0}, {0.0f});
147
148 test_sample_curve(curves, 4, false, {0, 1}, {0.0f, 0.0f});
149 test_sample_curve(curves, 4, true, {1, 0}, {0.0f, 0.0f});
150
151 test_sample_curve(curves,
152 5,
153 false,
154 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
155 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
156 test_sample_curve(curves,
157 5,
158 true,
159 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
160 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
161}
162
163TEST_F(GreasePencilInterpolate, sample_curve_shorter)
164{
165 bke::CurvesGeometry curves = create_test_curves(
166 {0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
167
168 test_sample_curve(curves, 1, false, {0}, {0.0f});
169 test_sample_curve(curves, 1, true, {1}, {0.0f});
170
171 test_sample_curve(curves, 2, false, {0, 2, 5, 9}, {0.0f, 0.82178f, 0.88113f, 0.0f});
172 test_sample_curve(curves, 2, true, {9, 5, 2, 0}, {0.0f, 0.88113f, 0.82178f, 0.0f});
173
174 test_sample_curve(curves, 4, false, {0}, {0.0f});
175 test_sample_curve(curves, 4, true, {1}, {0.0f});
176
177 test_sample_curve(curves, 5, false, {0, 2, 5, 7}, {0.0f, 0.5f, 0.0f, 0.5f});
178 test_sample_curve(curves, 5, true, {9, 6, 4, 1}, {0.0f, 0.50492f, 0.0f, 0.50492f});
179}
180
181TEST_F(GreasePencilInterpolate, sample_curve_longer)
182{
183 bke::CurvesGeometry curves = create_test_curves(
184 {0, 1, 3, 13, 14, 16, 26}, {false, false, false, true, true, true}, TestCurveShape::Eight);
185
186 test_sample_curve(curves,
187 1,
188 false,
189 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
190 {0.0f,
191 0.09091f,
192 0.18182f,
193 0.27273f,
194 0.36364f,
195 0.45455f,
196 0.54545f,
197 0.63636f,
198 0.72727f,
199 0.81818f,
200 0.90909f,
201 0.0f});
202 test_sample_curve(curves,
203 1,
204 true,
205 {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
206 {0.0f,
207 0.90909f,
208 0.81818f,
209 0.72727f,
210 0.63636f,
211 0.54545f,
212 0.45455f,
213 0.36364f,
214 0.27273f,
215 0.18182f,
216 0.09091f,
217 0.0f});
218
219 test_sample_curve(curves,
220 2,
221 false,
222 {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9},
223 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f});
224 test_sample_curve(curves,
225 2,
226 true,
227 {9, 8, 7, 6, 6, 5, 4, 3, 2, 2, 1, 0},
228 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f});
229
230 test_sample_curve(curves,
231 4,
232 false,
233 {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
234 {0.0f,
235 0.16667f,
236 0.33333f,
237 0.5f,
238 0.66667f,
239 0.83333f,
240 0.0f,
241 0.16667f,
242 0.33333f,
243 0.5f,
244 0.66667f,
245 0.83333f});
246 test_sample_curve(curves,
247 4,
248 true,
249 {1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
250 {0.83333f,
251 0.66667f,
252 0.5f,
253 0.33333f,
254 0.16667f,
255 0.0f,
256 0.83333f,
257 0.66667f,
258 0.5f,
259 0.33333f,
260 0.16667f,
261 0.0f});
262
263 test_sample_curve(curves,
264 5,
265 false,
266 {0, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 9},
267 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f});
268 test_sample_curve(curves,
269 5,
270 true,
271 {9, 8, 7, 6, 6, 5, 4, 3, 2, 1, 1, 0},
272 {0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f});
273}
274
275TEST_F(GreasePencilInterpolate, sample_zero_length_curve)
276{
277 bke::CurvesGeometry curves = create_test_curves(
278 {0, 10, 20}, {false, true}, TestCurveShape::Zero);
279
280 test_sample_curve(curves,
281 0,
282 false,
283 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
284 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
285 test_sample_curve(curves,
286 1,
287 false,
288 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
289 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
290}
291
292} // 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:256
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 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)
nullptr float
static ushort indices[]
void fill_index_range(MutableSpan< T > span, const T start=0)
TEST_F(ArmatureDeformTest, MeshDeform)
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