Blender V4.5
curves_geometry_test.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
9#include "BKE_curves.hh"
10
11#include "testing/testing.h"
12
13namespace blender::bke::tests {
14
15static CurvesGeometry create_basic_curves(const int points_size, const int curves_size)
16{
17 CurvesGeometry curves(points_size, curves_size);
18
19 const int curve_length = points_size / curves_size;
20 for (const int i : curves.curves_range()) {
21 curves.offsets_for_write()[i] = curve_length * i;
22 }
23 curves.offsets_for_write().last() = points_size;
24
25 for (const int i : curves.points_range()) {
26 curves.positions_for_write()[i] = {float(i), float(i % curve_length), 0.0f};
27 }
28
29 return curves;
30}
31
32TEST(curves_geometry, Empty)
33{
34 CurvesGeometry empty(0, 0);
35 empty.cyclic();
36 EXPECT_TRUE(empty.is_empty());
37 EXPECT_FALSE(empty.bounds_min_max());
38}
39
40TEST(curves_geometry, Move)
41{
43
44 const int *offsets_data = curves.offsets().data();
45 const float3 *positions_data = curves.positions().data();
46
47 CurvesGeometry other = std::move(curves);
48
49 /* The old curves should be empty, and the offsets are expected to be null. */
50 EXPECT_TRUE(curves.is_empty()); /* NOLINT: bugprone-use-after-move */
51 EXPECT_EQ(curves.curve_offsets, nullptr); /* NOLINT: bugprone-use-after-move */
52
53 /* Just a basic check that the new curves work okay. */
54 EXPECT_TRUE(other.bounds_min_max());
55
56 curves = std::move(other);
57
58 CurvesGeometry second_other(std::move(curves));
59
60 /* The data should not have been reallocated ever. */
61 EXPECT_EQ(second_other.positions().data(), positions_data);
62 EXPECT_EQ(second_other.offsets().data(), offsets_data);
63}
64
65TEST(curves_geometry, TypeCount)
66{
68 curves.curve_types_for_write().copy_from({
79 });
80 curves.update_curve_types();
81 const std::array<int, CURVE_TYPES_NUM> &counts = curves.curve_type_counts();
83 EXPECT_EQ(counts[CURVE_TYPE_POLY], 3);
84 EXPECT_EQ(counts[CURVE_TYPE_BEZIER], 1);
85 EXPECT_EQ(counts[CURVE_TYPE_NURBS], 3);
86}
87
88TEST(curves_geometry, CatmullRomEvaluation)
89{
91 curves.fill_curve_types(CURVE_TYPE_CATMULL_ROM);
92 curves.resolution_for_write().fill(12);
93 curves.offsets_for_write().last() = 4;
94 curves.cyclic_for_write().fill(false);
95
96 MutableSpan<float3> positions = curves.positions_for_write();
97 positions[0] = {1, 1, 0};
98 positions[1] = {0, 1, 0};
99 positions[2] = {0, 0, 0};
100 positions[3] = {-1, 0, 0};
101
102 Span<float3> evaluated_positions = curves.evaluated_positions();
103 static const Array<float3> result_1{{
104 {1, 1, 0},
105 {0.948495, 1.00318, 0},
106 {0.87963, 1.01157, 0},
107 {0.796875, 1.02344, 0},
108 {0.703704, 1.03704, 0},
109 {0.603588, 1.05064, 0},
110 {0.5, 1.0625, 0},
111 {0.396412, 1.07089, 0},
112 {0.296296, 1.07407, 0},
113 {0.203125, 1.07031, 0},
114 {0.12037, 1.05787, 0},
115 {0.0515046, 1.03501, 0},
116 {0, 1, 0},
117 {-0.0318287, 0.948495, 0},
118 {-0.0462963, 0.87963, 0},
119 {-0.046875, 0.796875, 0},
120 {-0.037037, 0.703704, 0},
121 {-0.0202546, 0.603588, 0},
122 {0, 0.5, 0},
123 {0.0202546, 0.396412, 0},
124 {0.037037, 0.296296, 0},
125 {0.046875, 0.203125, 0},
126 {0.0462963, 0.12037, 0},
127 {0.0318287, 0.0515046, 0},
128 {0, 0, 0},
129 {-0.0515046, -0.0350116, 0},
130 {-0.12037, -0.0578704, 0},
131 {-0.203125, -0.0703125, 0},
132 {-0.296296, -0.0740741, 0},
133 {-0.396412, -0.0708912, 0},
134 {-0.5, -0.0625, 0},
135 {-0.603588, -0.0506366, 0},
136 {-0.703704, -0.037037, 0},
137 {-0.796875, -0.0234375, 0},
138 {-0.87963, -0.0115741, 0},
139 {-0.948495, -0.00318287, 0},
140 {-1, 0, 0},
141 }};
142 for (const int i : evaluated_positions.index_range()) {
143 EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
144 }
145
146 /* Changing the positions shouldn't cause the evaluated positions array to be reallocated. */
147 curves.tag_positions_changed();
148 curves.evaluated_positions();
149 EXPECT_EQ(curves.evaluated_positions().data(), evaluated_positions.data());
150
151 /* Call recalculation (which shouldn't happen because low-level accessors don't tag caches). */
152 EXPECT_EQ(evaluated_positions[12].x, 0.0f);
153 EXPECT_EQ(evaluated_positions[12].y, 1.0f);
154
155 positions[0] = {1, 0, 0};
156 positions[1] = {1, 1, 0};
157 positions[2] = {0, 1, 0};
158 positions[3] = {0, 0, 0};
159 curves.cyclic_for_write().fill(true);
160
161 /* Tag topology changed because the new cyclic value is different. */
162 curves.tag_topology_changed();
163
164 /* Retrieve the data again since the size should be larger than last time (one more segment). */
165 evaluated_positions = curves.evaluated_positions();
166 static const Array<float3> result_2{{
167 {1, 0, 0},
168 {1.03819, 0.0515046, 0},
169 {1.06944, 0.12037, 0},
170 {1.09375, 0.203125, 0},
171 {1.11111, 0.296296, 0},
172 {1.12153, 0.396412, 0},
173 {1.125, 0.5, 0},
174 {1.12153, 0.603588, 0},
175 {1.11111, 0.703704, 0},
176 {1.09375, 0.796875, 0},
177 {1.06944, 0.87963, 0},
178 {1.03819, 0.948495, 0},
179 {1, 1, 0},
180 {0.948495, 1.03819, 0},
181 {0.87963, 1.06944, 0},
182 {0.796875, 1.09375, 0},
183 {0.703704, 1.11111, 0},
184 {0.603588, 1.12153, 0},
185 {0.5, 1.125, 0},
186 {0.396412, 1.12153, 0},
187 {0.296296, 1.11111, 0},
188 {0.203125, 1.09375, 0},
189 {0.12037, 1.06944, 0},
190 {0.0515046, 1.03819, 0},
191 {0, 1, 0},
192 {-0.0381944, 0.948495, 0},
193 {-0.0694444, 0.87963, 0},
194 {-0.09375, 0.796875, 0},
195 {-0.111111, 0.703704, 0},
196 {-0.121528, 0.603588, 0},
197 {-0.125, 0.5, 0},
198 {-0.121528, 0.396412, 0},
199 {-0.111111, 0.296296, 0},
200 {-0.09375, 0.203125, 0},
201 {-0.0694444, 0.12037, 0},
202 {-0.0381944, 0.0515046, 0},
203 {0, 0, 0},
204 {0.0515046, -0.0381944, 0},
205 {0.12037, -0.0694444, 0},
206 {0.203125, -0.09375, 0},
207 {0.296296, -0.111111, 0},
208 {0.396412, -0.121528, 0},
209 {0.5, -0.125, 0},
210 {0.603588, -0.121528, 0},
211 {0.703704, -0.111111, 0},
212 {0.796875, -0.09375, 0},
213 {0.87963, -0.0694444, 0},
214 {0.948495, -0.0381944, 0},
215 }};
216 for (const int i : evaluated_positions.index_range()) {
217 EXPECT_V3_NEAR(evaluated_positions[i], result_2[i], 1e-5f);
218 }
219}
220
221TEST(curves_geometry, CatmullRomTwoPointCyclic)
222{
224 curves.fill_curve_types(CURVE_TYPE_CATMULL_ROM);
225 curves.resolution_for_write().fill(12);
226 curves.offsets_for_write().last() = 2;
227 curves.cyclic_for_write().fill(true);
228
229 /* The curve should still be cyclic when there are only two control points. */
230 EXPECT_EQ(curves.evaluated_points_num(), 24);
231}
232
233TEST(curves_geometry, BezierPositionEvaluation)
234{
236 curves.fill_curve_types(CURVE_TYPE_BEZIER);
237 curves.resolution_for_write().fill(12);
238 curves.offsets_for_write().last() = 2;
239
240 MutableSpan<float3> handles_left = curves.handle_positions_left_for_write();
241 MutableSpan<float3> handles_right = curves.handle_positions_right_for_write();
242 MutableSpan<float3> positions = curves.positions_for_write();
243 positions.first() = {-1, 0, 0};
244 positions.last() = {1, 0, 0};
245 handles_right.first() = {-0.5f, 0.5f, 0.0f};
246 handles_left.last() = {0, 0, 0};
247
248 /* Dangling handles shouldn't be used in a non-cyclic curve. */
249 handles_left.first() = {100, 100, 100};
250 handles_right.last() = {100, 100, 100};
251
252 Span<float3> evaluated_positions = curves.evaluated_positions();
253 static const Array<float3> result_1{{
254 {-1, 0, 0},
255 {-0.874711, 0.105035, 0},
256 {-0.747685, 0.173611, 0},
257 {-0.617188, 0.210937, 0},
258 {-0.481481, 0.222222, 0},
259 {-0.338831, 0.212674, 0},
260 {-0.1875, 0.1875, 0},
261 {-0.0257524, 0.15191, 0},
262 {0.148148, 0.111111, 0},
263 {0.335937, 0.0703125, 0},
264 {0.539352, 0.0347222, 0},
265 {0.760127, 0.00954859, 0},
266 {1, 0, 0},
267 }};
268 for (const int i : evaluated_positions.index_range()) {
269 EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
270 }
271
272 curves.resize(4, 2);
273 curves.fill_curve_types(CURVE_TYPE_BEZIER);
274 curves.resolution_for_write().fill(9);
275 curves.offsets_for_write().last() = 4;
276 handles_left = curves.handle_positions_left_for_write();
277 handles_right = curves.handle_positions_right_for_write();
278 positions = curves.positions_for_write();
279 positions[2] = {-1, 1, 0};
280 positions[3] = {1, 1, 0};
281 handles_right[2] = {-0.5f, 1.5f, 0.0f};
282 handles_left[3] = {0, 1, 0};
283
284 /* Dangling handles shouldn't be used in a non-cyclic curve. */
285 handles_left[2] = {-100, -100, -100};
286 handles_right[3] = {-100, -100, -100};
287
288 evaluated_positions = curves.evaluated_positions();
289 EXPECT_EQ(evaluated_positions.size(), 20);
290 static const Array<float3> result_2{{
291 {-1, 0, 0},
292 {-0.832647, 0.131687, 0},
293 {-0.66118, 0.201646, 0},
294 {-0.481481, 0.222222, 0},
295 {-0.289438, 0.205761, 0},
296 {-0.0809327, 0.164609, 0},
297 {0.148148, 0.111111, 0},
298 {0.40192, 0.0576133, 0},
299 {0.684499, 0.016461, 0},
300 {1, 0, 0},
301 {-1, 1, 0},
302 {-0.832647, 1.13169, 0},
303 {-0.66118, 1.20165, 0},
304 {-0.481481, 1.22222, 0},
305 {-0.289438, 1.20576, 0},
306 {-0.0809327, 1.16461, 0},
307 {0.148148, 1.11111, 0},
308 {0.40192, 1.05761, 0},
309 {0.684499, 1.01646, 0},
310 {1, 1, 0},
311 }};
312 for (const int i : evaluated_positions.index_range()) {
313 EXPECT_V3_NEAR(evaluated_positions[i], result_2[i], 1e-5f);
314 }
315}
316
317TEST(curves_geometry, NURBSEvaluation)
318{
320 curves.fill_curve_types(CURVE_TYPE_NURBS);
321 curves.resolution_for_write().fill(10);
322 curves.offsets_for_write().last() = 4;
323
324 MutableSpan<float3> positions = curves.positions_for_write();
325 positions[0] = {1, 1, 0};
326 positions[1] = {0, 1, 0};
327 positions[2] = {0, 0, 0};
328 positions[3] = {-1, 0, 0};
329
330 Span<float3> evaluated_positions = curves.evaluated_positions();
331 static const Array<float3> result_1{{
332 {0.166667, 0.833333, 0}, {0.150006, 0.815511, 0}, {0.134453, 0.796582, 0},
333 {0.119924, 0.776627, 0}, {0.106339, 0.75573, 0}, {0.0936146, 0.733972, 0},
334 {0.0816693, 0.711434, 0}, {0.0704211, 0.6882, 0}, {0.0597879, 0.66435, 0},
335 {0.0496877, 0.639968, 0}, {0.0400385, 0.615134, 0}, {0.0307584, 0.589931, 0},
336 {0.0217653, 0.564442, 0}, {0.0129772, 0.538747, 0}, {0.00431208, 0.512929, 0},
337 {-0.00431208, 0.487071, 0}, {-0.0129772, 0.461253, 0}, {-0.0217653, 0.435558, 0},
338 {-0.0307584, 0.410069, 0}, {-0.0400385, 0.384866, 0}, {-0.0496877, 0.360032, 0},
339 {-0.0597878, 0.33565, 0}, {-0.0704211, 0.3118, 0}, {-0.0816693, 0.288566, 0},
340 {-0.0936146, 0.266028, 0}, {-0.106339, 0.24427, 0}, {-0.119924, 0.223373, 0},
341 {-0.134453, 0.203418, 0}, {-0.150006, 0.184489, 0}, {-0.166667, 0.166667, 0},
342 }};
343 for (const int i : evaluated_positions.index_range()) {
344 EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
345 }
346
347 /* Test a cyclic curve. */
348 curves.cyclic_for_write().fill(true);
349 curves.tag_topology_changed();
350 evaluated_positions = curves.evaluated_positions();
351 static const Array<float3> result_2{{
352 {0.166667, 0.833333, 0}, {0.121333, 0.778667, 0},
353 {0.084, 0.716, 0}, {0.0526667, 0.647333, 0},
354 {0.0253333, 0.574667, 0}, {0, 0.5, 0},
355 {-0.0253333, 0.425333, 0}, {-0.0526667, 0.352667, 0},
356 {-0.084, 0.284, 0}, {-0.121333, 0.221333, 0},
357 {-0.166667, 0.166667, 0}, {-0.221, 0.121667, 0},
358 {-0.281333, 0.0866667, 0}, {-0.343667, 0.0616666, 0},
359 {-0.404, 0.0466667, 0}, {-0.458333, 0.0416667, 0},
360 {-0.502667, 0.0466667, 0}, {-0.533, 0.0616666, 0},
361 {-0.545333, 0.0866667, 0}, {-0.535667, 0.121667, 0},
362 {-0.5, 0.166667, 0}, {-0.436, 0.221334, 0},
363 {-0.348, 0.284, 0}, {-0.242, 0.352667, 0},
364 {-0.124, 0.425333, 0}, {0, 0.5, 0},
365 {0.124, 0.574667, 0}, {0.242, 0.647333, 0},
366 {0.348, 0.716, 0}, {0.436, 0.778667, 0},
367 {0.5, 0.833333, 0}, {0.535667, 0.878334, 0},
368 {0.545333, 0.913333, 0}, {0.533, 0.938333, 0},
369 {0.502667, 0.953333, 0}, {0.458333, 0.958333, 0},
370 {0.404, 0.953333, 0}, {0.343667, 0.938333, 0},
371 {0.281333, 0.913333, 0}, {0.221, 0.878333, 0},
372 }};
373 for (const int i : evaluated_positions.index_range()) {
374 EXPECT_V3_NEAR(evaluated_positions[i], result_2[i], 1e-5f);
375 }
376
377 /* Test a circular cyclic curve with weights. */
378 positions[0] = {1, 0, 0};
379 positions[1] = {1, 1, 0};
380 positions[2] = {0, 1, 0};
381 positions[3] = {0, 0, 0};
382 curves.nurbs_weights_for_write().fill(1.0f);
383 curves.nurbs_weights_for_write()[0] = 4.0f;
384 curves.tag_positions_changed();
385 static const Array<float3> result_3{{
386 {0.888889, 0.555556, 0}, {0.837792, 0.643703, 0}, {0.773885, 0.727176, 0},
387 {0.698961, 0.800967, 0}, {0.616125, 0.860409, 0}, {0.529412, 0.901961, 0},
388 {0.443152, 0.923773, 0}, {0.361289, 0.925835, 0}, {0.286853, 0.909695, 0},
389 {0.221722, 0.877894, 0}, {0.166667, 0.833333, 0}, {0.122106, 0.778278, 0},
390 {0.0903055, 0.713148, 0}, {0.0741654, 0.638711, 0}, {0.0762274, 0.556847, 0},
391 {0.0980392, 0.470588, 0}, {0.139591, 0.383875, 0}, {0.199032, 0.301039, 0},
392 {0.272824, 0.226114, 0}, {0.356297, 0.162208, 0}, {0.444444, 0.111111, 0},
393 {0.531911, 0.0731388, 0}, {0.612554, 0.0468976, 0}, {0.683378, 0.0301622, 0},
394 {0.74391, 0.0207962, 0}, {0.794872, 0.017094, 0}, {0.837411, 0.017839, 0},
395 {0.872706, 0.0222583, 0}, {0.901798, 0.0299677, 0}, {0.925515, 0.0409445, 0},
396 {0.944444, 0.0555556, 0}, {0.959056, 0.0744855, 0}, {0.970032, 0.0982019, 0},
397 {0.977742, 0.127294, 0}, {0.982161, 0.162589, 0}, {0.982906, 0.205128, 0},
398 {0.979204, 0.256091, 0}, {0.969838, 0.316622, 0}, {0.953102, 0.387446, 0},
399 {0.926861, 0.468089, 0},
400 }};
401 evaluated_positions = curves.evaluated_positions();
402 for (const int i : evaluated_positions.index_range()) {
403 EXPECT_V3_NEAR(evaluated_positions[i], result_3[i], 1e-5f);
404 }
405}
406
407TEST(curves_geometry, BezierGenericEvaluation)
408{
410 curves.fill_curve_types(CURVE_TYPE_BEZIER);
411 curves.resolution_for_write().fill(8);
412 curves.offsets_for_write().last() = 3;
413
414 MutableSpan<float3> handles_left = curves.handle_positions_left_for_write();
415 MutableSpan<float3> handles_right = curves.handle_positions_right_for_write();
416 MutableSpan<float3> positions = curves.positions_for_write();
417 positions.first() = {-1, 0, 0};
418 handles_right.first() = {-1, 1, 0};
419 handles_left[1] = {0, 0, 0};
420 positions[1] = {1, 0, 0};
421 handles_right[1] = {2, 0, 0};
422 handles_left.last() = {1, 1, 0};
423 positions.last() = {2, 1, 0};
424
425 /* Dangling handles shouldn't be used in a non-cyclic curve. */
426 handles_left.first() = {100, 100, 100};
427 handles_right.last() = {100, 100, 100};
428
429 Span<float3> evaluated_positions = curves.evaluated_positions();
430 static const Array<float3> result_1{{
431 {-1.0f, 0.0f, 0.0f},
432 {-0.955078f, 0.287109f, 0.0f},
433 {-0.828125f, 0.421875f, 0.0f},
434 {-0.630859f, 0.439453f, 0.0f},
435 {-0.375f, 0.375f, 0.0f},
436 {-0.0722656f, 0.263672f, 0.0f},
437 {0.265625f, 0.140625f, 0.0f},
438 {0.626953f, 0.0410156f, 0.0f},
439 {1.0f, 0.0f, 0.0f},
440 {1.28906f, 0.0429688f, 0.0f},
441 {1.4375f, 0.15625f, 0.0f},
442 {1.49219f, 0.316406f, 0.0f},
443 {1.5f, 0.5f, 0.0f},
444 {1.50781f, 0.683594f, 0.0f},
445 {1.5625f, 0.84375f, 0.0f},
446 {1.71094f, 0.957031f, 0.0f},
447 {2.0f, 1.0f, 0.0f},
448 }};
449 for (const int i : evaluated_positions.index_range()) {
450 EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
451 }
452
453 Array<float> radii{{0.0f, 1.0f, 2.0f}};
454 Array<float> evaluated_radii(17);
455 curves.interpolate_to_evaluated(0, radii.as_span(), evaluated_radii.as_mutable_span());
456 static const Array<float> result_2{{
457 0.0f,
458 0.125f,
459 0.25f,
460 0.375f,
461 0.5f,
462 0.625f,
463 0.75f,
464 0.875f,
465 1.0f,
466 1.125f,
467 1.25f,
468 1.375f,
469 1.5f,
470 1.625f,
471 1.75f,
472 1.875f,
473 2.0f,
474 }};
475 for (const int i : evaluated_radii.index_range()) {
476 EXPECT_NEAR(evaluated_radii[i], result_2[i], 1e-6f);
477 }
478}
479
480TEST(knot_vector, KnotVectorUniform)
481{
482 constexpr int8_t order = 5;
483 constexpr int points_num = 7;
484 constexpr std::array<int, 12> expectation{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
485
486 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
488 points_num, KnotsMode::NURBS_KNOT_MODE_NORMAL, order, false, knots);
489
491 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
492}
493
494TEST(knot_vector, KnotVectorUniformClamped)
495{
496 constexpr int8_t order = 3;
497 constexpr int points_num = 7;
498 constexpr std::array<int, 6> expectation{3, 1, 1, 1, 1, 3};
499
500 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
502 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT, order, false, knots);
503
505 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
506}
507
508/* -------------------------------------------------------------------- */
511
512TEST(knot_vector, KnotVectorBezierClampedSegmentDeg2)
513{
514 constexpr int8_t order = 3;
515 constexpr int points_num = 3;
516 constexpr std::array<int, 2> expectation{3, 3};
517
518 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
520 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, false, knots);
521
523 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
524}
525
526TEST(knot_vector, KnotVectorBezierClampedSegmentDeg4)
527{
528 constexpr int8_t order = 5;
529 constexpr int points_num = 5;
530 constexpr std::array<int, 2> expectation{5, 5};
531
532 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
534 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, false, knots);
535
537 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
538}
539
540TEST(knot_vector, KnotVectorBezierClampedDeg2)
541{
542 constexpr int8_t order = 3;
543 constexpr int points_num = 9;
544 constexpr std::array<int, 5> expectation{3, 2, 2, 2, 3};
545
546 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
548 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, false, knots);
549
551 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
552}
553
554TEST(knot_vector, KnotVectorBezierClampedUnevenDeg2)
555{
556 constexpr int8_t order = 3;
557 constexpr int points_num = 8;
558 constexpr std::array<int, 4> expectation{3, 2, 2, 4};
559
560 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
562 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, false, knots);
563
565 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
566}
567
568TEST(knot_vector, KnotVectorBezierClampedDeg4)
569{
570 constexpr int8_t order = 5;
571 constexpr int points_num = 13;
572 constexpr std::array<int, 4> expectation{5, 4, 4, 5};
573
574 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
576 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, false, knots);
577
579 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
580}
581
582TEST(knot_vector, KnotVectorBezierClampedUnevenDeg4)
583{
584 constexpr int8_t order = 5;
585 constexpr int points_num[4] = {12, 11, 10, 9};
586 const std::array<std::array<int, 3>, 4> expectation = {std::array<int, 3>{5, 4, 8},
587 std::array<int, 3>{5, 4, 7},
588 std::array<int, 3>{5, 4, 6},
589 std::array<int, 3>{5, 4, 5}};
590
591 for (int i = 0; i < expectation.size(); i++) {
592 Vector<float> knots(curves::nurbs::knots_num(points_num[i], order, false));
594 points_num[i], KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, false, knots);
595
597 EXPECT_EQ_ARRAY(expectation[i].data(), multiplicity.data(), multiplicity.size());
598 }
599}
600
601TEST(knot_vector, KnotVectorCircleCyclicUnevenDeg2)
602{
603 constexpr int8_t order = 3;
604 constexpr int points_num = 8;
605 constexpr std::array<int, 7> expectation{1, 2, 2, 2, 2, 2, 2};
606
607 Vector<float> knots(curves::nurbs::knots_num(points_num, order, true));
609 points_num, KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, true, knots);
610
612 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
613}
614
615TEST(knot_vector, KnotVectorBezierClampedCyclicUnevenDeg4)
616{
617 constexpr int8_t order = 5;
618 constexpr int points_num[4] = {12, 11, 10, 9};
619 const std::array<std::array<int, 6>, 4> expectation = {std::array<int, 6>{1, 4, 4, 4, 4, 4},
620 std::array<int, 6>{1, 4, 4, 3, 4, 4},
621 std::array<int, 6>{1, 4, 4, 2, 4, 4},
622 std::array<int, 6>{1, 4, 4, 1, 4, 4}};
623
624 for (int i = 0; i < expectation.size(); i++) {
625 Vector<float> knots(curves::nurbs::knots_num(points_num[i], order, true));
627 points_num[i], KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER, order, true, knots);
628
630 EXPECT_EQ_ARRAY(expectation[i].data(), multiplicity.data(), multiplicity.size());
631 }
632}
633
635
636/* -------------------------------------------------------------------- */
639
640TEST(knot_vector, KnotVectorBezierSegmentDeg2)
641{
642 constexpr int8_t order = 4;
643 constexpr int points_num = 4;
644 constexpr std::array<int, 3> expectation{2, 3, 3};
645
646 Vector<float> knots(curves::nurbs::knots_num(points_num, order, false));
648 points_num, KnotsMode::NURBS_KNOT_MODE_BEZIER, order, false, knots);
649
651 EXPECT_EQ_ARRAY(expectation.data(), multiplicity.data(), expectation.size());
652}
653
654TEST(knot_vector, KnotVectorBezierUnevenDeg2)
655{
656 constexpr int8_t order = 3;
657 constexpr int points_num[4] = {8, 7, 6, 5};
658 const std::array<std::array<int, 6>, 4> expectation = {std::array<int, 6>{2, 2, 2, 2, 2, 1},
659 std::array<int, 6>{2, 2, 2, 2, 2, -1},
660 std::array<int, 6>{2, 2, 2, 2, 1, -1},
661 std::array<int, 6>{2, 2, 2, 2, -1, -1}};
662
663 for (int i = 0; i < expectation.size(); i++) {
664 Vector<float> knots(curves::nurbs::knots_num(points_num[i], order, false));
666 points_num[i], KnotsMode::NURBS_KNOT_MODE_BEZIER, order, false, knots);
667
669 EXPECT_EQ_ARRAY(expectation[i].data(), multiplicity.data(), multiplicity.size());
670 }
671}
672
673TEST(knot_vector, KnotVectorBezierUnevenDeg4)
674{
675 constexpr int8_t order = 5;
676 constexpr int points_num[6] = {14, 13, 12, 11, 10, 9};
677 const std::array<std::array<int, 6>, 6> expectation = {std::array<int, 6>{2, 4, 4, 4, 4, 1},
678 std::array<int, 6>{2, 4, 4, 4, 4, -1},
679 std::array<int, 6>{2, 4, 4, 4, 3, -1},
680 std::array<int, 6>{2, 4, 4, 4, 2, -1},
681 std::array<int, 6>{2, 4, 4, 4, 1, -1},
682 std::array<int, 6>{2, 4, 4, 4, -1, -1}};
683
684 for (int i = 0; i < expectation.size(); i++) {
685 Vector<float> knots(curves::nurbs::knots_num(points_num[i], order, false));
687 points_num[i], KnotsMode::NURBS_KNOT_MODE_BEZIER, order, false, knots);
688
690 EXPECT_EQ_ARRAY(expectation[i].data(), multiplicity.data(), multiplicity.size());
691 }
692}
693
694TEST(knot_vector, KnotVectorBezierCyclicUnevenDeg4)
695{
696 constexpr int8_t order = 5;
697 constexpr int points_num[4] = {12, 11, 10, 9};
698 const std::array<std::array<int, 6>, 4> expectation = {std::array<int, 6>{2, 4, 4, 4, 4, 3},
699 std::array<int, 6>{2, 4, 4, 3, 4, 3},
700 std::array<int, 6>{2, 4, 4, 2, 4, 3},
701 std::array<int, 6>{2, 4, 5, 4, 3, -1}};
702
703 for (int i = 0; i < expectation.size(); i++) {
704 Vector<float> knots(curves::nurbs::knots_num(points_num[i], order, true));
706 points_num[i], KnotsMode::NURBS_KNOT_MODE_BEZIER, order, true, knots);
707
709 EXPECT_EQ_ARRAY(expectation[i].data(), multiplicity.data(), multiplicity.size());
710 }
711}
712
714
715} // namespace blender::bke::tests
Low-level operations for curves.
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CURVE_TYPE_CATMULL_ROM
BMesh const char void * data
constexpr const T * data() const
Definition BLI_span.hh:215
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
IndexRange index_range() const
Definition BLI_array.hh:349
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 * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
Span< float3 > positions() const
std::optional< Bounds< float3 > > bounds_min_max(bool use_radius=true) const
VArray< bool > cyclic() const
Vector< int > calculate_multiplicity_sequence(Span< float > knots)
void calculate_knots(int points_num, KnotsMode mode, int8_t order, bool cyclic, MutableSpan< float > knots)
int knots_num(int points_num, int8_t order, bool cyclic)
TEST(action_groups, ReconstructGroupsWithReordering)
static CurvesGeometry create_basic_curves(const int points_size, const int curves_size)
VecBase< float, 3 > float3
i
Definition text_draw.cc:230