Blender V4.5
animrig/intern/action_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
5#include "ANIM_action.hh"
6
7#include "BKE_action.hh"
8#include "BKE_anim_data.hh"
9#include "BKE_fcurve.hh"
10#include "BKE_idtype.hh"
11#include "BKE_lib_id.hh"
12#include "BKE_main.hh"
13#include "BKE_object.hh"
14
15#include "DNA_action_defaults.h"
16#include "DNA_anim_types.h"
17#include "DNA_object_types.h"
18
19#include "RNA_access.hh"
20
21#include "BLI_listbase.h"
22#include "BLI_string.h"
23#include "BLI_string_utf8.h"
24
25#include <limits>
26
27#include "CLG_log.h"
28#include "testing/testing.h"
29
31
32TEST(action, low_level_initialisation)
33{
34 bAction *action = BKE_id_new_nomain<bAction>("NewAction");
35
36 EXPECT_NE(action->last_slot_handle, 0)
37 << "bAction::last_slot_handle should not be initialised to 0";
38
39 BKE_id_free(nullptr, action);
40}
41
42class ActionLayersTest : public testing::Test {
43 public:
49
50 static void SetUpTestSuite()
51 {
52 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
53 CLG_init();
54
55 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
57 }
58
59 static void TearDownTestSuite()
60 {
61 CLG_exit();
62 }
63
64 void SetUp() override
65 {
67 action = BKE_id_new<Action>(bmain, "ACÄnimåtië");
71 }
72
73 void TearDown() override
74 {
76 }
77};
78
80{
81 Layer &layer = action->layer_add("layer name");
82
83 EXPECT_EQ(action->layer(0), &layer);
84 EXPECT_EQ("layer name", std::string(layer.name));
85 EXPECT_EQ(1.0f, layer.influence) << "Expected DNA defaults to be used.";
86 EXPECT_EQ(0, action->layer_active_index)
87 << "Expected newly added layer to become the active layer.";
88 ASSERT_EQ(0, layer.strips().size()) << "Expected newly added layer to have no strip.";
89}
90
91TEST_F(ActionLayersTest, add_layer__reset_idroot)
92{
93 /* An empty Action is a valid legacy Action, and thus can have its idroot set to a non-zero
94 * value. If such an Action gets a layer, it no longer is a valid legacy Action, and thus its
95 * idtype should be reset to zero. */
96 action->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
97 ASSERT_NE(0, action->idroot) << "action->idroot should not be zero at the start of this test.";
98
99 action->layer_add("layer name");
100
101 EXPECT_EQ(0, action->idroot)
102 << "action->idroot should get reset when the Action becomes layered.";
103}
104
106{
107 Layer &layer0 = action->layer_add("Test Læür nul");
108 Layer &layer1 = action->layer_add("Test Læür één");
109 Layer &layer2 = action->layer_add("Test Læür twee");
110
111 /* Add some strips to check that they are freed correctly too (implicitly by the
112 * memory leak checker). */
113 layer0.strip_add(*action, Strip::Type::Keyframe);
114 layer1.strip_add(*action, Strip::Type::Keyframe);
115 layer2.strip_add(*action, Strip::Type::Keyframe);
116
117 { /* Test removing a layer that is not owned. */
118 Action *other_anim = BKE_id_new<Action>(bmain, "ACOtherAnim");
119 Layer &other_layer = other_anim->layer_add("Another Layer");
120 EXPECT_FALSE(action->layer_remove(other_layer))
121 << "Removing a layer not owned by the Action should be gracefully rejected";
122 BKE_id_free(bmain, &other_anim->id);
123 }
124
125 EXPECT_TRUE(action->layer_remove(layer1));
126 EXPECT_EQ(2, action->layers().size());
127 EXPECT_STREQ(layer0.name, action->layer(0)->name);
128 EXPECT_STREQ(layer2.name, action->layer(1)->name);
129
130 EXPECT_TRUE(action->layer_remove(layer2));
131 EXPECT_EQ(1, action->layers().size());
132 EXPECT_STREQ(layer0.name, action->layer(0)->name);
133
134 EXPECT_TRUE(action->layer_remove(layer0));
135 EXPECT_EQ(0, action->layers().size());
136}
137
139{
140 Layer &layer = action->layer_add("Test Læür");
141
142 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
143 ASSERT_EQ(1, layer.strips().size());
144 EXPECT_EQ(&strip, layer.strip(0));
145
146 constexpr float inf = std::numeric_limits<float>::infinity();
147 EXPECT_EQ(-inf, strip.frame_start) << "Expected strip to be infinite.";
148 EXPECT_EQ(inf, strip.frame_end) << "Expected strip to be infinite.";
149 EXPECT_EQ(0, strip.frame_offset) << "Expected infinite strip to have no offset.";
150
151 Strip &another_strip = layer.strip_add(*action, Strip::Type::Keyframe);
152 ASSERT_EQ(2, layer.strips().size());
153 EXPECT_EQ(&another_strip, layer.strip(1));
154
155 EXPECT_EQ(-inf, another_strip.frame_start) << "Expected strip to be infinite.";
156 EXPECT_EQ(inf, another_strip.frame_end) << "Expected strip to be infinite.";
157 EXPECT_EQ(0, another_strip.frame_offset) << "Expected infinite strip to have no offset.";
158
159 /* Add some keys to check that also the strip data is freed correctly. */
160 const KeyframeSettings settings = get_keyframe_settings(false);
161 Slot &slot = action->slot_add();
162 strip.data<StripKeyframeData>(*action).keyframe_insert(
163 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
164 another_strip.data<StripKeyframeData>(*action).keyframe_insert(
165 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
166}
167
169{
170 Layer &layer = action->layer_add("Test Læür");
171 Strip &strip0 = layer.strip_add(*action, Strip::Type::Keyframe);
172 Strip &strip1 = layer.strip_add(*action, Strip::Type::Keyframe);
173 Strip &strip2 = layer.strip_add(*action, Strip::Type::Keyframe);
174 Strip &strip3 = layer.strip_add(*action, Strip::Type::Keyframe);
175 StripKeyframeData &strip_data0 = strip0.data<StripKeyframeData>(*action);
176 StripKeyframeData &strip_data1 = strip1.data<StripKeyframeData>(*action);
177 StripKeyframeData &strip_data2 = strip2.data<StripKeyframeData>(*action);
178 StripKeyframeData &strip_data3 = strip3.data<StripKeyframeData>(*action);
179
180 /* Add some keys to check that also the strip data is freed correctly. */
181 const KeyframeSettings settings = get_keyframe_settings(false);
182 Slot &slot = action->slot_add();
183 strip_data0.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
184 strip_data1.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 48.0f}, settings);
185 strip_data2.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 49.0f}, settings);
186 strip_data3.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 50.0f}, settings);
187
188 EXPECT_EQ(4, action->strip_keyframe_data().size());
189 EXPECT_EQ(0, strip0.data_index);
190 EXPECT_EQ(1, strip1.data_index);
191 EXPECT_EQ(2, strip2.data_index);
192 EXPECT_EQ(3, strip3.data_index);
193
194 EXPECT_TRUE(layer.strip_remove(*action, strip1));
195 EXPECT_EQ(3, action->strip_keyframe_data().size());
196 EXPECT_EQ(3, layer.strips().size());
197 EXPECT_EQ(&strip0, layer.strip(0));
198 EXPECT_EQ(&strip2, layer.strip(1));
199 EXPECT_EQ(&strip3, layer.strip(2));
200 EXPECT_EQ(0, strip0.data_index);
201 EXPECT_EQ(2, strip2.data_index);
202 EXPECT_EQ(1, strip3.data_index); /* Swapped in when removing strip 1's data. */
203 EXPECT_EQ(&strip_data0, &strip0.data<StripKeyframeData>(*action));
204 EXPECT_EQ(&strip_data2, &strip2.data<StripKeyframeData>(*action));
205 EXPECT_EQ(&strip_data3, &strip3.data<StripKeyframeData>(*action));
206
207 EXPECT_TRUE(layer.strip_remove(*action, strip2));
208 EXPECT_EQ(2, action->strip_keyframe_data().size());
209 EXPECT_EQ(2, layer.strips().size());
210 EXPECT_EQ(&strip0, layer.strip(0));
211 EXPECT_EQ(&strip3, layer.strip(1));
212 EXPECT_EQ(0, strip0.data_index);
213 EXPECT_EQ(1, strip3.data_index);
214 EXPECT_EQ(&strip_data0, &strip0.data<StripKeyframeData>(*action));
215 EXPECT_EQ(&strip_data3, &strip3.data<StripKeyframeData>(*action));
216
217 EXPECT_TRUE(layer.strip_remove(*action, strip3));
218 EXPECT_EQ(1, action->strip_keyframe_data().size());
219 EXPECT_EQ(1, layer.strips().size());
220 EXPECT_EQ(&strip0, layer.strip(0));
221 EXPECT_EQ(0, strip0.data_index);
222 EXPECT_EQ(&strip_data0, &strip0.data<StripKeyframeData>(*action));
223
224 EXPECT_TRUE(layer.strip_remove(*action, strip0));
225 EXPECT_EQ(0, action->strip_keyframe_data().size());
226 EXPECT_EQ(0, layer.strips().size());
227
228 { /* Test removing a strip that is not owned. */
229 Layer &other_layer = action->layer_add("Another Layer");
230 Strip &other_strip = other_layer.strip_add(*action, Strip::Type::Keyframe);
231
232 EXPECT_FALSE(layer.strip_remove(*action, other_strip))
233 << "Removing a strip not owned by the layer should be gracefully rejected";
234 }
235}
236
237/* NOTE: this test creates strip instances in a bespoke way for the purpose of
238 * exercising the strip removal code, because at the time of writing we don't
239 * have a proper API for creating strip instances. When such an API is added,
240 * this test should be updated to use it. */
241TEST_F(ActionLayersTest, remove_strip_instances)
242{
243 Layer &layer = action->layer_add("Test Læür");
244 Strip &strip0 = layer.strip_add(*action, Strip::Type::Keyframe);
245 Strip &strip1 = layer.strip_add(*action, Strip::Type::Keyframe);
246 Strip &strip2 = layer.strip_add(*action, Strip::Type::Keyframe);
247
248 /* Make on of the strips an instance of another. */
249 strip0.data_index = strip1.data_index;
250
251 StripKeyframeData &strip_data_0_1 = strip0.data<StripKeyframeData>(*action);
252 StripKeyframeData &strip_data_2 = strip2.data<StripKeyframeData>(*action);
253
254 /* Add some keys to check that also the strip data is freed correctly. */
255 const KeyframeSettings settings = get_keyframe_settings(false);
256 Slot &slot = action->slot_add();
257 strip_data_0_1.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
258 strip_data_2.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 48.0f}, settings);
259
260 EXPECT_EQ(3, action->strip_keyframe_data().size());
261 EXPECT_EQ(1, strip0.data_index);
262 EXPECT_EQ(1, strip1.data_index);
263 EXPECT_EQ(2, strip2.data_index);
264
265 /* Removing an instance should not delete the underlying data as long as there
266 * is still another strip using it. */
267 EXPECT_TRUE(layer.strip_remove(*action, strip1));
268 EXPECT_EQ(3, action->strip_keyframe_data().size());
269 EXPECT_EQ(2, layer.strips().size());
270 EXPECT_EQ(&strip0, layer.strip(0));
271 EXPECT_EQ(&strip2, layer.strip(1));
272 EXPECT_EQ(1, strip0.data_index);
273 EXPECT_EQ(2, strip2.data_index);
274 EXPECT_EQ(&strip_data_0_1, &strip0.data<StripKeyframeData>(*action));
275 EXPECT_EQ(&strip_data_2, &strip2.data<StripKeyframeData>(*action));
276
277 /* Removing the last user of strip data should also delete the data. */
278 EXPECT_TRUE(layer.strip_remove(*action, strip0));
279 EXPECT_EQ(2, action->strip_keyframe_data().size());
280 EXPECT_EQ(1, layer.strips().size());
281 EXPECT_EQ(&strip2, layer.strip(0));
282 EXPECT_EQ(1, strip2.data_index);
283 EXPECT_EQ(&strip_data_2, &strip2.data<StripKeyframeData>(*action));
284}
285
287{
288 { /* Creating an 'unused' Slot should just be called 'Slot'. */
289 Slot &slot = action->slot_add();
290 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 1, action->last_slot_handle);
292
293 EXPECT_STREQ("XXSlot", slot.identifier);
294 EXPECT_EQ(0, slot.idtype);
295 }
296
297 { /* Creating a Slot for a specific ID should name it after the ID. */
298 Slot &slot = action->slot_add_for_id(cube->id);
299 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle);
301
302 EXPECT_STREQ(cube->id.name, slot.identifier);
303 EXPECT_EQ(ID_OB, slot.idtype);
304 }
305
306 { /* Creating a Slot for a specific ID that already had a slot assigned before should name it
307 * after that previous slot. This should also ensure that the first two characters are actually
308 * correct for the ID type. */
309 AnimData *adt = BKE_animdata_ensure_id(&cube->id);
310 STRNCPY_UTF8(adt->last_slot_identifier, "$$Kübuš 😹");
311 Slot &slot = action->slot_add_for_id(cube->id);
312 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 3, action->last_slot_handle);
314
315 EXPECT_STREQ("Kübuš 😹", slot.identifier + 2)
316 << "The last-assigned slot name should be reused";
317 EXPECT_STREQ("OBKübuš 😹", slot.identifier)
318 << "The ID type encoded in the slot identifier should be correct";
319 EXPECT_EQ(ID_OB, slot.idtype);
320 }
321}
322
323TEST_F(ActionLayersTest, add_slot__reset_idroot)
324{
325 /* An empty Action is a valid legacy Action, and thus can have its idroot set
326 * to a non-zero value. If such an Action gets a slot, it no longer is a
327 * valid legacy Action, and thus its idtype should be reset to zero. */
328 action->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
329 ASSERT_NE(0, action->idroot) << "action->idroot should not be zero at the start of this test.";
330
331 action->slot_add();
332
333 EXPECT_EQ(0, action->idroot)
334 << "action->idroot should get reset when the Action becomes layered.";
335}
336
337TEST_F(ActionLayersTest, add_slot_multiple)
338{
339 Slot &slot_cube = action->slot_add();
340 Slot &slot_suzanne = action->slot_add();
341 EXPECT_TRUE(assign_action(action, cube->id));
343 EXPECT_TRUE(assign_action(action, suzanne->id));
344 EXPECT_EQ(assign_action_slot(&slot_suzanne, suzanne->id), ActionSlotAssignmentResult::OK);
345
346 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle);
349}
350
352{
353 { /* Canary test: removing a just-created slot on an otherwise empty Action should work. */
354 Slot &slot = action->slot_add();
356 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 1, action->last_slot_handle);
357
358 EXPECT_TRUE(action->slot_remove(slot));
359 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 1, action->last_slot_handle)
360 << "Removing a slot should not change the last-used slot handle.";
361 EXPECT_EQ(0, action->slot_array_num);
362 }
363
364 { /* Removing a non-existing slot should return false. */
365 Slot slot;
366 EXPECT_FALSE(action->slot_remove(slot));
367 }
368
369 { /* Removing a slot should remove its Channelbag. */
370 Slot &slot = action->slot_add();
371 const slot_handle_t slot_handle = slot.handle;
373 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle);
374
375 /* Create an F-Curve in a Channelbag for the slot. */
376 action->layer_keystrip_ensure();
377 StripKeyframeData &strip_data = action->layer(0)->strip(0)->data<StripKeyframeData>(*action);
378 Channelbag &channelbag = strip_data.channelbag_for_slot_ensure(slot);
379 channelbag.fcurve_create_unique(bmain, {"location", 1});
380
381 /* Remove the slot. */
382 EXPECT_TRUE(action->slot_remove(slot));
383 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle)
384 << "Removing a slot should not change the last-used slot handle.";
385 EXPECT_EQ(0, action->slot_array_num);
386
387 /* Check that its channelbag is gone. */
388 Channelbag *found_cbag = strip_data.channelbag_for_slot(slot_handle);
389 EXPECT_EQ(found_cbag, nullptr);
390 }
391
392 { /* Removing one slot should leave the other two in place. */
393 Slot &slot1 = action->slot_add();
394 Slot &slot2 = action->slot_add();
395 Slot &slot3 = action->slot_add();
399 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 5, action->last_slot_handle);
400
401 /* For referencing the slot handle after the slot is removed. */
402 const slot_handle_t slot2_handle = slot2.handle;
403
404 /* Create a Channel-bag for each slot. */
405 action->layer_keystrip_ensure();
406 StripKeyframeData &strip_data = action->layer(0)->strip(0)->data<StripKeyframeData>(*action);
407 strip_data.channelbag_for_slot_ensure(slot1);
408 strip_data.channelbag_for_slot_ensure(slot2);
409 strip_data.channelbag_for_slot_ensure(slot3);
410
411 /* Remove the slot. */
412 EXPECT_TRUE(action->slot_remove(slot2));
413 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 5, action->last_slot_handle);
414
415 /* Check the correct slot + channel-bag are removed. */
416 EXPECT_EQ(action->slot_for_handle(slot1.handle), &slot1);
417 EXPECT_EQ(action->slot_for_handle(slot2_handle), nullptr);
418 EXPECT_EQ(action->slot_for_handle(slot3.handle), &slot3);
419
420 EXPECT_NE(strip_data.channelbag_for_slot(slot1.handle), nullptr);
421 EXPECT_EQ(strip_data.channelbag_for_slot(slot2_handle), nullptr);
422 EXPECT_NE(strip_data.channelbag_for_slot(slot3.handle), nullptr);
423 }
424
425 { /* Removing an in-use slot doesn't un-assign it from its users.
426 * This is not that important, but it covers the current behavior. */
427 Slot &slot = action->slot_add_for_id(cube->id);
428 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
429
430 ASSERT_TRUE(slot.runtime_users().contains(&cube->id));
431 ASSERT_EQ(cube->adt->slot_handle, slot.handle);
432
433 const slot_handle_t removed_slot_handle = slot.handle;
434 ASSERT_TRUE(action->slot_remove(slot));
435 EXPECT_EQ(cube->adt->slot_handle, removed_slot_handle);
436 }
437
438 { /* Creating a slot after removing one should not reuse its handle. */
439 action->last_slot_handle = 3; /* To create independence between sub-tests. */
440 Slot &slot1 = action->slot_add();
441 ASSERT_EQ(4, slot1.handle);
442 ASSERT_EQ(4, action->last_slot_handle);
443 ASSERT_TRUE(action->slot_remove(slot1));
444
445 Slot &slot2 = action->slot_add();
446 EXPECT_EQ(5, slot2.handle);
447 EXPECT_EQ(5, action->last_slot_handle);
448 }
449}
450
451TEST_F(ActionLayersTest, slot_move_to_index)
452{
453 Slot &slot_a = action->slot_add_for_id_type(ID_ME);
454 Slot &slot_b = action->slot_add_for_id_type(ID_CA);
455 Slot &slot_cube = action->slot_add_for_id(cube->id);
456 Slot &slot_suzanne = action->slot_add_for_id(suzanne->id);
457
458 assign_action_and_slot(action, &slot_cube, cube->id);
459 assign_action_and_slot(action, &slot_suzanne, suzanne->id);
460
461 const slot_handle_t handle_a = slot_a.handle;
462 const slot_handle_t handle_b = slot_b.handle;
463 const slot_handle_t handle_cube = slot_cube.handle;
464 const slot_handle_t handle_suzanne = slot_suzanne.handle;
465
466 ASSERT_EQ(action->slot(0)->handle, handle_a);
467 ASSERT_EQ(action->slot(0)->idtype_string(), "ME");
468 ASSERT_EQ(action->slot(1)->handle, handle_b);
469 ASSERT_EQ(action->slot(1)->idtype_string(), "CA");
470 ASSERT_EQ(action->slot(2)->handle, handle_cube);
471 ASSERT_EQ(action->slot(2)->idtype_string(), "OB");
472 ASSERT_EQ(action->slot(2)->users(*bmain)[0], &cube->id);
473 ASSERT_EQ(action->slot(3)->handle, handle_suzanne);
474 ASSERT_EQ(action->slot(3)->idtype_string(), "OB");
475 ASSERT_EQ(action->slot(3)->users(*bmain)[0], &suzanne->id);
476
477 /* First "move" a slot to its own location, which should do nothing. */
478 action->slot_move_to_index(slot_b, 1);
479 EXPECT_EQ(action->slot(0)->handle, handle_a);
480 EXPECT_EQ(action->slot(0)->idtype_string(), "ME");
481 EXPECT_EQ(action->slot(1)->handle, handle_b);
482 EXPECT_EQ(action->slot(1)->idtype_string(), "CA");
483 EXPECT_EQ(action->slot(2)->handle, handle_cube);
484 EXPECT_EQ(action->slot(2)->idtype_string(), "OB");
485 EXPECT_EQ(action->slot(2)->users(*bmain)[0], &cube->id);
486 EXPECT_EQ(action->slot(3)->handle, handle_suzanne);
487 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
488 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &suzanne->id);
489
490 /* Then move slots around in various ways. */
491
492 action->slot_move_to_index(slot_a, 2);
493 EXPECT_EQ(action->slot(0)->handle, handle_b);
494 EXPECT_EQ(action->slot(0)->idtype_string(), "CA");
495 EXPECT_EQ(action->slot(1)->handle, handle_cube);
496 EXPECT_EQ(action->slot(1)->idtype_string(), "OB");
497 EXPECT_EQ(action->slot(1)->users(*bmain)[0], &cube->id);
498 EXPECT_EQ(action->slot(2)->handle, handle_a);
499 EXPECT_EQ(action->slot(2)->idtype_string(), "ME");
500 EXPECT_EQ(action->slot(3)->handle, handle_suzanne);
501 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
502 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &suzanne->id);
503
504 action->slot_move_to_index(slot_suzanne, 1);
505 EXPECT_EQ(action->slot(0)->handle, handle_b);
506 EXPECT_EQ(action->slot(0)->idtype_string(), "CA");
507 EXPECT_EQ(action->slot(1)->handle, handle_suzanne);
508 EXPECT_EQ(action->slot(1)->idtype_string(), "OB");
509 EXPECT_EQ(action->slot(1)->users(*bmain)[0], &suzanne->id);
510 EXPECT_EQ(action->slot(2)->handle, handle_cube);
511 EXPECT_EQ(action->slot(2)->idtype_string(), "OB");
512 EXPECT_EQ(action->slot(2)->users(*bmain)[0], &cube->id);
513 EXPECT_EQ(action->slot(3)->handle, handle_a);
514 EXPECT_EQ(action->slot(3)->idtype_string(), "ME");
515
516 action->slot_move_to_index(slot_cube, 3);
517 EXPECT_EQ(action->slot(0)->handle, handle_b);
518 EXPECT_EQ(action->slot(0)->idtype_string(), "CA");
519 EXPECT_EQ(action->slot(1)->handle, handle_suzanne);
520 EXPECT_EQ(action->slot(1)->idtype_string(), "OB");
521 EXPECT_EQ(action->slot(1)->users(*bmain)[0], &suzanne->id);
522 EXPECT_EQ(action->slot(2)->handle, handle_a);
523 EXPECT_EQ(action->slot(2)->idtype_string(), "ME");
524 EXPECT_EQ(action->slot(3)->handle, handle_cube);
525 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
526 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &cube->id);
527
528 action->slot_move_to_index(slot_suzanne, 0);
529 EXPECT_EQ(action->slot(0)->handle, handle_suzanne);
530 EXPECT_EQ(action->slot(0)->idtype_string(), "OB");
531 EXPECT_EQ(action->slot(0)->users(*bmain)[0], &suzanne->id);
532 EXPECT_EQ(action->slot(1)->handle, handle_b);
533 EXPECT_EQ(action->slot(1)->idtype_string(), "CA");
534 EXPECT_EQ(action->slot(2)->handle, handle_a);
535 EXPECT_EQ(action->slot(2)->idtype_string(), "ME");
536 EXPECT_EQ(action->slot(3)->handle, handle_cube);
537 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
538 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &cube->id);
539}
540
541TEST_F(ActionLayersTest, action_assign_id)
542{
543 /* Assign to the only, 'virgin' Slot, should always work. */
544 Slot &slot_cube = action->slot_add();
545 ASSERT_NE(nullptr, slot_cube.runtime);
546 ASSERT_STREQ(slot_cube.identifier, "XXSlot");
547 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
548
549 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
550 EXPECT_STREQ(slot_cube.identifier, "OBSlot");
551 EXPECT_STREQ(slot_cube.identifier, cube->adt->last_slot_identifier)
552 << "The slot identifier should be copied to the adt";
553
554 EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
555 << "Expecting Cube to be registered as animated by its slot.";
556
557 /* Assign another ID to the same Slot. */
558 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, suzanne->id),
560 EXPECT_STREQ(slot_cube.identifier, "OBSlot");
561 EXPECT_STREQ(slot_cube.identifier, cube->adt->last_slot_identifier)
562 << "The slot identifier should be copied to the adt";
563
564 EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
565 << "Expecting Suzanne to be registered as animated by the Cube slot.";
566
567 { /* Assign Cube to another action+slot without unassigning first. */
568 Action *another_anim = BKE_id_new<Action>(bmain, "ACOtherAnim");
569 Slot &another_slot = another_anim->slot_add();
570 ASSERT_EQ(assign_action_and_slot(another_anim, &another_slot, cube->id),
572 EXPECT_FALSE(slot_cube.users(*bmain).contains(&cube->id))
573 << "Expecting Cube to no longer be registered as user of its old slot.";
574 EXPECT_TRUE(another_slot.users(*bmain).contains(&cube->id))
575 << "Expecting Cube to be registered as user of its new slot.";
576 }
577
578 { /* Assign Cube to another slot of the same Action, this should work. */
579 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id),
581 const int user_count_pre = action->id.us;
582 Slot &slot_cube_2 = action->slot_add();
583 ASSERT_EQ(assign_action_and_slot(action, &slot_cube_2, cube->id),
585 ASSERT_EQ(action->id.us, user_count_pre)
586 << "Assigning to a different slot of the same Action should _not_ change the user "
587 "count of that Action";
588 EXPECT_FALSE(slot_cube.users(*bmain).contains(&cube->id))
589 << "Expecting Cube to no longer be registered as animated by the Cube slot.";
590 EXPECT_TRUE(slot_cube_2.users(*bmain).contains(&cube->id))
591 << "Expecting Cube to be registered as animated by the 'cube_2' slot.";
592 }
593
594 { /* Unassign the Action. */
595 const int user_count_pre = action->id.us;
596 EXPECT_TRUE(unassign_action(cube->id));
597 ASSERT_EQ(action->id.us, user_count_pre - 1)
598 << "Unassigning an Action should lower its user count";
599
600 ASSERT_EQ(2, action->slots().size()) << "Expecting the Action to have two Slots";
601 EXPECT_FALSE(action->slot(0)->users(*bmain).contains(&cube->id))
602 << "Expecting Cube to no longer be registered as animated by any slot.";
603 EXPECT_FALSE(action->slot(1)->users(*bmain).contains(&cube->id))
604 << "Expecting Cube to no longer be registered as animated by any slot.";
605 }
606
607 /* Assign Cube to another 'virgin' slot. This should not cause a name
608 * collision between the Slots. */
609 Slot &another_slot_cube = action->slot_add();
610 ASSERT_EQ(assign_action_and_slot(action, &another_slot_cube, cube->id),
612 EXPECT_EQ(another_slot_cube.handle, cube->adt->slot_handle);
613 EXPECT_STREQ("OBSlot.002", another_slot_cube.identifier) << "The slot should be uniquely named";
614 EXPECT_STREQ("OBSlot.002", cube->adt->last_slot_identifier)
615 << "The slot identifier should be copied to the adt";
616 EXPECT_TRUE(another_slot_cube.users(*bmain).contains(&cube->id))
617 << "Expecting Cube to be registered as animated by the 'another_slot_cube' slot.";
618
619 /* Create an ID of another type. This should not be assignable to this slot. */
620 ID *mesh = static_cast<ID *>(BKE_id_new_nomain(ID_ME, "Mesh"));
621 ASSERT_TRUE(assign_action(action, *mesh));
623 << "Mesh should not be animatable by an Object slot";
624 EXPECT_FALSE(another_slot_cube.users(*bmain).contains(mesh))
625 << "Expecting Mesh to not be registered as animated by the 'slot_cube' slot.";
626 BKE_id_free(nullptr, mesh);
627}
628
630{
631 Slot &slot_cube = action->slot_add();
632 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
633 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
634 EXPECT_STREQ("OBSlot", slot_cube.identifier);
635 EXPECT_STREQ(slot_cube.identifier, cube->adt->last_slot_identifier)
636 << "The slot identifier should be copied to the adt";
637
638 action->slot_identifier_define(slot_cube, "OBNew Slot Name");
639 EXPECT_STREQ("OBNew Slot Name", slot_cube.identifier);
640 /* At this point the slot identifier will not have been copied to the cube
641 * AnimData. However, I don't want to test for that here, as it's not exactly
642 * desirable behavior, but more of a side-effect of the current
643 * implementation. */
644
645 action->slot_identifier_propagate(*bmain, slot_cube);
646 EXPECT_STREQ("OBNew Slot Name", cube->adt->last_slot_identifier);
647
648 /* Rename via the display name, which should propagate to the ADT. */
649 action->slot_display_name_set(*bmain, slot_cube, "Slot's New Display Name");
650 EXPECT_STREQ("OBSlot's New Display Name", slot_cube.identifier);
651 EXPECT_STREQ("OBSlot's New Display Name", cube->adt->last_slot_identifier);
652
653 /* Finally, do another rename, do NOT call the propagate function, then
654 * unassign. This should still result in the correct slot name being stored
655 * on the ADT. */
656 action->slot_identifier_define(slot_cube, "OBEven Newer Name");
657 EXPECT_TRUE(unassign_action(cube->id));
658 EXPECT_STREQ("OBEven Newer Name", cube->adt->last_slot_identifier);
659}
660
661TEST_F(ActionLayersTest, slot_identifier_ensure_prefix)
662{
663 class AccessibleSlot : public Slot {
664 public:
665 void identifier_ensure_prefix()
666 {
668 }
669 };
670
671 Slot &raw_slot = action->slot_add();
672 AccessibleSlot &slot = static_cast<AccessibleSlot &>(raw_slot);
673 ASSERT_STREQ("XXSlot", slot.identifier);
674 ASSERT_EQ(0, slot.idtype);
675
676 /* Check defaults, idtype zeroed. */
677 slot.identifier_ensure_prefix();
678 EXPECT_STREQ("XXSlot", slot.identifier);
679
680 /* idtype CA, default name. */
681 slot.idtype = ID_CA;
682 slot.identifier_ensure_prefix();
683 EXPECT_STREQ("CASlot", slot.identifier);
684
685 /* idtype ME, explicit name of other idtype. */
686 action->slot_identifier_define(slot, "CANewName");
687 slot.idtype = ID_ME;
688 slot.identifier_ensure_prefix();
689 EXPECT_STREQ("MENewName", slot.identifier);
690
691 /* Zeroing out idtype. */
692 slot.idtype = 0;
693 slot.identifier_ensure_prefix();
694 EXPECT_STREQ("XXNewName", slot.identifier);
695}
696
697TEST_F(ActionLayersTest, slot_identifier_prefix)
698{
699 Slot &slot = action->slot_add();
700 EXPECT_EQ("XX", slot.idtype_string());
701 EXPECT_EQ("XX", slot.identifier_prefix());
702
703 slot.idtype = ID_CA;
704 EXPECT_EQ("CA", slot.idtype_string());
705 EXPECT_EQ("XX", slot.identifier_prefix());
706
708 EXPECT_EQ("CA", slot.idtype_string());
709 EXPECT_EQ("CA", slot.identifier_prefix());
710}
711
712TEST_F(ActionLayersTest, rename_slot_identifier_collision)
713{
714 Slot &slot1 = action->slot_add();
715 Slot &slot2 = action->slot_add();
716
717 action->slot_identifier_define(slot1, "New Slot Name");
718 action->slot_identifier_define(slot2, "New Slot Name");
719 EXPECT_STREQ("New Slot Name", slot1.identifier);
720 EXPECT_STREQ("New Slot Name.001", slot2.identifier);
721}
722
724{
725 /* ===
726 * Empty case, no slots exist yet and the ID doesn't even have an AnimData. */
727 EXPECT_EQ(nullptr, generic_slot_for_autoassign(cube->id, *this->action, ""));
728
729 /* ===
730 * Slot exists with the same name & type as the ID, but the ID doesn't have any AnimData yet.
731 * These should nevertheless be matched up. */
732 Slot &slot = action->slot_add();
733 slot.handle = 327;
734 STRNCPY_UTF8(slot.identifier, "OBKüüübus");
735 slot.idtype = GS(cube->id.name);
736 EXPECT_EQ(&slot, generic_slot_for_autoassign(cube->id, *this->action, ""));
737
738 /* ===
739 * Slot exists with the same name & type as the ID, and the ID has an AnimData with the same
740 * slot identifier, but a different slot_handle. Since the Action has not yet been
741 * assigned to this ID, the slot_handle should be ignored, and the slot identifier used for
742 * matching. */
743
744 /* Create a slot with a handle that should be ignored. */
745 Slot &other_slot = action->slot_add();
746 other_slot.handle = 47;
747
748 AnimData *adt = BKE_animdata_ensure_id(&cube->id);
749 adt->action = nullptr;
750 /* Configure adt to use the handle of one slot, and the identifier of the other. */
751 adt->slot_handle = other_slot.handle;
753 EXPECT_EQ(&slot,
754 generic_slot_for_autoassign(cube->id, *this->action, cube->adt->last_slot_identifier));
755
756 /* ===
757 * Assigned slot info exists, but doesn't match anything in the action data of the cube. This
758 * should fall back to using the ID name. */
759 adt->slot_handle = 161;
760 STRNCPY_UTF8(adt->last_slot_identifier, "¿¿What's this??");
761 EXPECT_EQ(&slot,
762 generic_slot_for_autoassign(cube->id, *this->action, cube->adt->last_slot_identifier));
763}
764
765TEST_F(ActionLayersTest, generic_slot_for_autoassign_untyped_wildcarding)
766{
767 /* Test the untyped slot "wildcard" behavior, where OBSlot should be chosen when the last slot
768 * identifier was "XXSlot", and vice versa. */
769
770 /* ===
771 * Action has OBSlot, last-used slot is XXSlot. Should pick OBSlot. */
772 AnimData *adt = BKE_animdata_ensure_id(&cube->id);
773 STRNCPY_UTF8(adt->last_slot_identifier, "XXSlot");
774 Slot &ob_slot = action->slot_add_for_id_type(ID_OB);
775 action->slot_identifier_define(ob_slot, "OBSlot");
776
777 EXPECT_EQ(&ob_slot,
778 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
779
780 /* ===
781 * Action has OBSlot and XXSlot, last-used slot is XXSlot. Should pick OBSlot. */
782 Slot &xx_slot = action->slot_add();
783 action->slot_identifier_define(xx_slot, "XXSlot");
784 ASSERT_FALSE(xx_slot.has_idtype());
785 ASSERT_STREQ("XXSlot", xx_slot.identifier);
786 ASSERT_STREQ("XXSlot", adt->last_slot_identifier);
787 EXPECT_EQ(&ob_slot,
788 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
789
790 /* ===
791 * Action has OBSlot and XXSlot, last-used slot is OBSlot. Should pick OBSlot. */
792 STRNCPY_UTF8(adt->last_slot_identifier, "OBSlot");
793 EXPECT_EQ(&ob_slot,
794 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
795
796 /* ===
797 * Action has XXSlot, last-used slot is OBSlot. Should pick XXSlot. */
798 action->slot_remove(ob_slot);
799 ASSERT_STREQ("OBSlot", adt->last_slot_identifier);
800 EXPECT_EQ(&xx_slot,
801 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
802}
803
805{
806 { /* Empty case, no slots exist yet. */
807 EXPECT_EQ(nullptr, action->slot_active_get());
808
809 action->slot_active_set(Slot::unassigned);
810 EXPECT_EQ(nullptr, action->slot_active_get());
811 }
812
813 { /* Single slot case. */
814 Slot &slot_cube = *assign_action_ensure_slot_for_keying(*action, cube->id);
815 EXPECT_EQ(nullptr, action->slot_active_get())
816 << "Adding the first slot should not change what is the active slot.";
817
818 action->slot_active_set(slot_cube.handle);
819 EXPECT_EQ(&slot_cube, action->slot_active_get())
820 << "It should be possible to activate the only available slot";
821 EXPECT_TRUE(slot_cube.is_active());
822
823 action->slot_active_set(Slot::unassigned);
824 EXPECT_EQ(nullptr, action->slot_active_get())
825 << "It should be possible to de-activate the only available slot";
826 EXPECT_FALSE(slot_cube.is_active());
827 }
828
829 {
830 /* Multiple slots case. */
831 Slot &slot_cube = *action->slot(0);
832 action->slot_active_set(slot_cube.handle);
833
834 Slot &slot_suz = *assign_action_ensure_slot_for_keying(*action, suzanne->id);
835 Slot &slot_bob = *assign_action_ensure_slot_for_keying(*action, bob->id);
836 EXPECT_EQ(&slot_cube, action->slot_active_get())
837 << "Adding a subsequent slot should not change what is the active slot.";
838 EXPECT_TRUE(slot_cube.is_active());
839
840 action->slot_active_set(slot_suz.handle);
841 EXPECT_EQ(&slot_suz, action->slot_active_get());
842 EXPECT_FALSE(slot_cube.is_active());
843 EXPECT_TRUE(slot_suz.is_active());
844 EXPECT_FALSE(slot_bob.is_active());
845
846 action->slot_active_set(slot_bob.handle);
847 EXPECT_EQ(&slot_bob, action->slot_active_get());
848 EXPECT_FALSE(slot_cube.is_active());
849 EXPECT_FALSE(slot_suz.is_active());
850 EXPECT_TRUE(slot_bob.is_active());
851
852 action->slot_active_set(Slot::unassigned);
853 EXPECT_EQ(nullptr, action->slot_active_get());
854 EXPECT_FALSE(slot_cube.is_active());
855 EXPECT_FALSE(slot_suz.is_active());
856 EXPECT_FALSE(slot_bob.is_active());
857 }
858}
859
861{
862 { /* Slotless Action, should create a typed slot. */
863 Action &action = action_add(*this->bmain, "ACEmpty");
864 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
865 ASSERT_NE(nullptr, chosen_slot);
866 EXPECT_EQ(ID_OB, chosen_slot->idtype);
867 EXPECT_STREQ("OBKüüübus", chosen_slot->identifier);
868 }
869
870 { /* Single slot with same name as ID, Action not yet assigned.
871 * Should assign the Action and the slot. */
872 Action &action = action_add(*this->bmain, "ACAction");
873 const Slot &slot_for_id = action.slot_add_for_id(cube->id);
874 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
875 ASSERT_NE(nullptr, chosen_slot);
876 EXPECT_EQ(&slot_for_id, chosen_slot) << "The expected slot should be chosen";
877 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
878 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
879 }
880
881 { /* Single slot with same name as ID, Action already assigned but not the slot.
882 * Should create new slot. */
883 Action &action = action_add(*this->bmain, "ACAction");
884 const Slot &slot_for_id = action.slot_add_for_id(cube->id);
885 ASSERT_EQ(ActionSlotAssignmentResult::OK, assign_action_and_slot(&action, nullptr, cube->id));
886
887 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
888 ASSERT_NE(nullptr, chosen_slot);
889 EXPECT_NE(&slot_for_id, chosen_slot) << "A new slot should be chosen";
890 EXPECT_STREQ("OBKüüübus.001", chosen_slot->identifier);
891 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
892 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
893 }
894
895 { /* Single untyped slot, Action already assigned but not the slot. Should assign the untyped
896 * slot. */
897 Action &action = action_add(*this->bmain, "ACAction");
898
899 /* Assign the Action before adding the untyped slot, otherwise the slot gets assigned & thus
900 * typed. */
901 ASSERT_EQ(ActionSlotAssignmentResult::OK, assign_action_and_slot(&action, nullptr, cube->id));
902
903 Slot &untyped_slot = action.slot_add();
904 action.slot_identifier_define(untyped_slot, "XXJust A Slot");
905
906 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
907
908 ASSERT_NE(nullptr, chosen_slot);
909 EXPECT_EQ(&untyped_slot, chosen_slot) << "The untyped slot should be chosen";
910 EXPECT_TRUE(untyped_slot.has_idtype()) << "Slot should have gotten an ID type";
911 EXPECT_STREQ("OBJust A Slot", untyped_slot.identifier);
912 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
913 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
914 }
915}
916
918{
919 constexpr float inf = std::numeric_limits<float>::infinity();
920 Layer &layer0 = action->layer_add("Test Læür nul");
921 Strip &strip = layer0.strip_add(*action, Strip::Type::Keyframe);
922
923 strip.resize(-inf, inf);
924 EXPECT_TRUE(strip.contains_frame(0.0f));
925 EXPECT_TRUE(strip.contains_frame(-100000.0f));
926 EXPECT_TRUE(strip.contains_frame(100000.0f));
927 EXPECT_TRUE(strip.is_last_frame(inf));
928
929 strip.resize(1.0f, 2.0f);
930 EXPECT_FALSE(strip.contains_frame(0.0f))
931 << "Strip should not contain frames before its first frame";
932 EXPECT_TRUE(strip.contains_frame(1.0f)) << "Strip should contain its first frame.";
933 EXPECT_TRUE(strip.contains_frame(2.0f)) << "Strip should contain its last frame.";
934 EXPECT_FALSE(strip.contains_frame(2.0001f))
935 << "Strip should not contain frames after its last frame";
936
937 EXPECT_FALSE(strip.is_last_frame(1.0f));
938 EXPECT_FALSE(strip.is_last_frame(1.5f));
939 EXPECT_FALSE(strip.is_last_frame(1.9999f));
940 EXPECT_TRUE(strip.is_last_frame(2.0f));
941 EXPECT_FALSE(strip.is_last_frame(2.0001f));
942
943 /* Same test as above, but with much larger end frame number. This is 2 hours at 24 FPS. */
944 strip.resize(1.0f, 172800.0f);
945 EXPECT_TRUE(strip.contains_frame(172800.0f)) << "Strip should contain its last frame.";
946 EXPECT_FALSE(strip.contains_frame(172800.1f))
947 << "Strip should not contain frames after its last frame";
948
949 /* You can't get much closer to the end frame before it's considered equal. */
950 EXPECT_FALSE(strip.is_last_frame(172799.925f));
951 EXPECT_TRUE(strip.is_last_frame(172800.0f));
952 EXPECT_FALSE(strip.is_last_frame(172800.075f));
953}
954
955TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
956{
957 Slot &slot = action->slot_add();
958 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
959 Layer &layer = action->layer_add("Kübus layer");
960
961 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
962 StripKeyframeData &strip_data = strip.data<StripKeyframeData>(*action);
963
964 const KeyframeSettings settings = get_keyframe_settings(false);
965 SingleKeyingResult result_loc_a = strip_data.keyframe_insert(
966 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
967 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_loc_a)
968 << "Expected keyframe insertion to be successful";
969
970 /* Check the strip was created correctly, with the channels for the slot. */
971 ASSERT_EQ(1, strip_data.channelbags().size());
972 Channelbag *channels = strip_data.channelbag(0);
973 EXPECT_EQ(slot.handle, channels->slot_handle);
974
975 /* Insert a second key, should insert into the same FCurve as before. */
976 SingleKeyingResult result_loc_b = strip_data.keyframe_insert(
977 bmain, slot, {"location", 0}, {5.0f, 47.1f}, settings);
979 ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (slot/rna "
980 "path/array index) tuple to go into the same FCurve";
981 EXPECT_EQ(2, channels->fcurves()[0]->totvert)
982 << "Expect insertion with the same (slot/rna path/array index) tuple to go into the same "
983 "FCurve";
984
985 EXPECT_EQ(47.0f, evaluate_fcurve(channels->fcurves()[0], 1.0f));
986 EXPECT_EQ(47.1f, evaluate_fcurve(channels->fcurves()[0], 5.0f));
987
988 /* Insert another key for another property, should create another FCurve. */
989 SingleKeyingResult result_rot = strip_data.keyframe_insert(
990 bmain, slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
992 ASSERT_EQ(2, channels->fcurves().size()) << "Expected a second FCurve to be created.";
993 EXPECT_EQ(2, channels->fcurves()[0]->totvert);
994 EXPECT_EQ(1, channels->fcurves()[1]->totvert);
995}
996
998{
999 EXPECT_TRUE(is_action_assignable_to(nullptr, ID_OB))
1000 << "nullptr Actions should be assignable to any type.";
1001 EXPECT_TRUE(is_action_assignable_to(nullptr, ID_CA))
1002 << "nullptr Actions should be assignable to any type.";
1003
1004 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1005 << "Empty Actions should be assignable to any type.";
1006 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1007 << "Empty Actions should be assignable to any type.";
1008
1009 /* Make the Action a legacy one. */
1010 FCurve fake_fcurve;
1011 BLI_addtail(&action->curves, &fake_fcurve);
1012 ASSERT_FALSE(action->is_empty());
1013 ASSERT_TRUE(action->is_action_legacy());
1014 ASSERT_EQ(0, action->idroot);
1015
1016 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1017 << "Legacy Actions with idroot=0 should be assignable to any type.";
1018 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1019 << "Legacy Actions with idroot=0 should be assignable to any type.";
1020
1021 /* Set the legacy idroot. */
1022 action->idroot = ID_CA;
1023 EXPECT_FALSE(is_action_assignable_to(action, ID_OB))
1024 << "Legacy Actions with idroot=ID_CA should NOT be assignable to ID_OB.";
1025 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1026 << "Legacy Actions with idroot=CA should be assignable to ID_CA.";
1027
1028 /* Make the Action a layered one. */
1029 BLI_poptail(&action->curves);
1030 action->layer_add("layer");
1031 ASSERT_EQ(0, action->idroot) << "Adding a layer should clear the idroot.";
1032
1033 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1034 << "Layered Actions should be assignable to any type.";
1035 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1036 << "Layered Actions should be assignable to any type.";
1037}
1038
1039TEST_F(ActionLayersTest, action_slot_get_id_for_keying__empty_action)
1040{
1041 EXPECT_TRUE(assign_action(action, cube->id));
1042
1043 /* Double-check that the action is considered empty for the test. */
1044 EXPECT_TRUE(action->is_empty());
1045
1046 /* None should return an ID, since there are no slots yet which could have this ID assigned.
1047 * Assignment of the Action itself (cube) shouldn't matter. */
1048 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &cube->id));
1049 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, nullptr));
1050 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &suzanne->id));
1051}
1052
1053TEST_F(ActionLayersTest, action_slot_get_id_for_keying__legacy_action)
1054{
1055 FCurve *fcurve = action_fcurve_ensure_legacy(bmain, action, nullptr, nullptr, {"location", 0});
1056 EXPECT_FALSE(fcurve == nullptr);
1057
1058 EXPECT_TRUE(assign_action(action, cube->id));
1059
1060 /* Double-check that the action is considered legacy for the test. */
1061 EXPECT_TRUE(action->is_action_legacy());
1062
1063 /* A `primary_id` that uses the action should get returned. Every other case
1064 * should return nullptr. */
1065 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, 0, &cube->id));
1066 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, nullptr));
1067 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &suzanne->id));
1068}
1069
1070TEST_F(ActionLayersTest, action_slot_get_id_for_keying__layered_action)
1071{
1072 Slot &slot = action->slot_add();
1073
1074 /* Double-check that the action is considered layered for the test. */
1075 EXPECT_TRUE(action->is_action_layered());
1076
1077 /* A slot with no users should never return a user. */
1078 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1079 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1080
1081 /* A slot with precisely one user should always return that user. */
1082 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
1083 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1084 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1085 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &suzanne->id));
1086
1087 /* A slot with more than one user should return the passed `primary_id` if it
1088 * is among its users, and nullptr otherwise. */
1089 ASSERT_EQ(assign_action_and_slot(action, &slot, suzanne->id), ActionSlotAssignmentResult::OK);
1090 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1091 EXPECT_EQ(&suzanne->id,
1092 action_slot_get_id_for_keying(*bmain, *action, slot.handle, &suzanne->id));
1093 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1094 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &bob->id));
1095}
1096
1097TEST_F(ActionLayersTest, conversion_to_layered)
1098{
1099 EXPECT_TRUE(action->is_empty());
1100 FCurve *legacy_fcu_0 = action_fcurve_ensure_legacy(
1101 bmain, action, "Test", nullptr, {"location", 0});
1102 FCurve *legacy_fcu_1 = action_fcurve_ensure_legacy(
1103 bmain, action, "Test", nullptr, {"location", 1});
1104
1105 KeyframeSettings settings;
1106 settings.handle = HD_AUTO;
1107 settings.interpolation = BEZT_IPO_BEZ;
1108 settings.keyframe_type = BEZT_KEYTYPE_KEYFRAME;
1109 insert_vert_fcurve(legacy_fcu_0, {0, 0}, settings, INSERTKEY_NOFLAGS);
1110 insert_vert_fcurve(legacy_fcu_0, {1, 1}, settings, INSERTKEY_NOFLAGS);
1111 add_fmodifier(&legacy_fcu_1->modifiers, FMODIFIER_TYPE_NOISE, legacy_fcu_1);
1112
1113 Action *converted = convert_to_layered_action(*bmain, *action);
1114 ASSERT_TRUE(converted != action);
1115 EXPECT_STREQ(converted->id.name, "ACACÄnimåtië_layered");
1116 Strip *strip = converted->layer(0)->strip(0);
1117 StripKeyframeData &strip_data = strip->data<StripKeyframeData>(*converted);
1118 Channelbag *bag = strip_data.channelbag(0);
1119 ASSERT_EQ(bag->fcurve_array_num, 2);
1120 ASSERT_EQ(bag->fcurve_array[0]->totvert, 2);
1121
1122 ASSERT_EQ(BLI_listbase_count(&action->groups), 1);
1123 ASSERT_EQ(BLI_listbase_count(&converted->groups), 0);
1124
1125 ASSERT_EQ(bag->channel_groups().size(), 1);
1126 bActionGroup *group = bag->channel_group(0);
1127 ASSERT_EQ(group->fcurve_range_length, 2);
1128 ASSERT_STREQ(group->name, "Test");
1129
1130 ASSERT_TRUE(bag->fcurve_array[0]->modifiers.first == nullptr);
1131 ASSERT_TRUE(bag->fcurve_array[1]->modifiers.first != nullptr);
1132
1133 Action *long_name_action = BKE_id_new<Action>(
1134 bmain, "name_for_an_action_that_is_exactly_64_chars_which_is_MAX_ID_NAME");
1135 action_fcurve_ensure_legacy(bmain, long_name_action, "Long", nullptr, {"location", 0});
1136 converted = convert_to_layered_action(*bmain, *long_name_action);
1137 /* AC gets added automatically by Blender, the long name is shortened to make space for
1138 * "_layered". */
1139 EXPECT_STREQ(converted->id.name,
1140 "ACname_for_an_action_that_is_exactly_64_chars_which_is_MA_layered");
1141}
1142
1143TEST_F(ActionLayersTest, conversion_to_layered_action_groups)
1144{
1145 EXPECT_TRUE(action->is_empty());
1146 action_fcurve_ensure_legacy(bmain, action, "Test", nullptr, {"location", 0});
1147 action_fcurve_ensure_legacy(bmain, action, "Test", nullptr, {"rotation_euler", 1});
1148 action_fcurve_ensure_legacy(bmain, action, "Test_Two", nullptr, {"scale", 1});
1149 action_fcurve_ensure_legacy(bmain, action, "Test_Three", nullptr, {"show_name", 1});
1150 action_fcurve_ensure_legacy(bmain, action, "Test_Rename", nullptr, {"show_axis", 1});
1151
1152 bActionGroup *rename_group = static_cast<bActionGroup *>(BLI_findlink(&action->groups, 3));
1153 ASSERT_NE(rename_group, nullptr);
1154 ASSERT_STREQ(rename_group->name, "Test_Rename");
1155 /* Forcing a duplicate name which was allowed by legacy actions. */
1156 STRNCPY_UTF8(rename_group->name, "Test");
1157
1158 Action *converted = convert_to_layered_action(*bmain, *action);
1159 Strip *strip = converted->layer(0)->strip(0);
1160 StripKeyframeData &strip_data = strip->data<StripKeyframeData>(*converted);
1161 Channelbag *bag = strip_data.channelbag(0);
1162
1163 ASSERT_EQ(BLI_listbase_count(&converted->groups), 0);
1164 ASSERT_EQ(bag->channel_groups().size(), 4);
1165
1166 bActionGroup *test_group = bag->channel_group(0);
1167 EXPECT_STREQ(test_group->name, "Test");
1168 EXPECT_EQ(test_group->fcurve_range_length, 2);
1169
1170 bActionGroup *test_two_group = bag->channel_group(1);
1171 EXPECT_STREQ(test_two_group->name, "Test_Two");
1172 EXPECT_EQ(test_two_group->fcurve_range_length, 1);
1173 EXPECT_STREQ(bag->fcurve_array[test_two_group->fcurve_range_start]->rna_path, "scale");
1174
1175 bActionGroup *test_three_group = bag->channel_group(2);
1176 EXPECT_STREQ(test_three_group->name, "Test_Three");
1177 EXPECT_EQ(test_three_group->fcurve_range_length, 1);
1178 EXPECT_STREQ(bag->fcurve_array[test_three_group->fcurve_range_start]->rna_path, "show_name");
1179
1180 bActionGroup *test_rename_group = bag->channel_group(3);
1181 EXPECT_STREQ(test_rename_group->name, "Test.001");
1182 EXPECT_EQ(test_rename_group->fcurve_range_length, 1);
1183 EXPECT_STREQ(bag->fcurve_array[test_rename_group->fcurve_range_start]->rna_path, "show_axis");
1184
1185 ASSERT_NE(converted, action);
1186}
1187
1188TEST_F(ActionLayersTest, empty_to_layered)
1189{
1190 ASSERT_TRUE(action->is_empty());
1191 Action *converted = convert_to_layered_action(*bmain, *action);
1192 ASSERT_TRUE(converted != action);
1193 ASSERT_TRUE(converted->is_action_layered());
1194 ASSERT_FALSE(converted->is_action_legacy());
1195}
1196
1197TEST_F(ActionLayersTest, action_move_slot)
1198{
1199 Action *action_2 = BKE_id_new<Action>(bmain, "Action 2");
1200 EXPECT_TRUE(action->is_empty());
1201
1202 Slot &slot_cube = action->slot_add();
1203 Slot &slot_suzanne = action_2->slot_add();
1204 EXPECT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1205 EXPECT_EQ(assign_action_and_slot(action_2, &slot_suzanne, suzanne->id),
1207
1208 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1209 PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
1210
1211 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1212 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1213
1214 action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"location", 0});
1215 action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"rotation_euler", 1});
1216
1217 ASSERT_EQ(action->layer_array_num, 1);
1218 ASSERT_EQ(action_2->layer_array_num, 1);
1219
1220 Layer *layer_1 = action->layer(0);
1221 Layer *layer_2 = action_2->layer(0);
1222
1223 ASSERT_EQ(layer_1->strip_array_num, 1);
1224 ASSERT_EQ(layer_2->strip_array_num, 1);
1225
1226 StripKeyframeData &strip_data_1 = layer_1->strip(0)->data<StripKeyframeData>(*action);
1227 StripKeyframeData &strip_data_2 = layer_2->strip(0)->data<StripKeyframeData>(*action_2);
1228
1229 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1230 ASSERT_EQ(strip_data_2.channelbag_array_num, 1);
1231
1232 Channelbag *bag_1 = strip_data_1.channelbag(0);
1233 Channelbag *bag_2 = strip_data_2.channelbag(0);
1234
1235 ASSERT_EQ(bag_1->fcurve_array_num, 2);
1236 ASSERT_EQ(bag_2->fcurve_array_num, 2);
1237
1238 move_slot(*bmain, slot_suzanne, *action_2, *action);
1239
1240 ASSERT_EQ(strip_data_1.channelbag_array_num, 2);
1241 ASSERT_EQ(strip_data_2.channelbag_array_num, 0);
1242
1243 ASSERT_EQ(action->slot_array_num, 2);
1244 ASSERT_EQ(action_2->slot_array_num, 0);
1245
1246 /* Action should have been reassigned. */
1247 ASSERT_EQ(action, cube->adt->action);
1248 ASSERT_EQ(action, suzanne->adt->action);
1249}
1250
1251TEST_F(ActionLayersTest, action_move_slot_without_channelbag)
1252{
1253 Action *action_2 = BKE_id_new<Action>(bmain, "Action 2");
1254 EXPECT_TRUE(action->is_empty());
1255
1256 Slot &slot_cube = action->slot_add();
1257 Slot &slot_suzanne = action_2->slot_add();
1258 EXPECT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1259 EXPECT_EQ(assign_action_and_slot(action_2, &slot_suzanne, suzanne->id),
1261
1262 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1263 PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
1264
1265 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1266 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1267
1268 /* Make sure action_2 has a keyframe strip, but without a channelbag. */
1269 action_2->layer_add("Bagless").strip_add(*action_2, Strip::Type::Keyframe);
1270
1271 ASSERT_EQ(action->layer_array_num, 1);
1272 ASSERT_EQ(action_2->layer_array_num, 1);
1273
1274 Layer *layer_1 = action->layer(0);
1275 Layer *layer_2 = action_2->layer(0);
1276
1277 ASSERT_EQ(layer_1->strip_array_num, 1);
1278 ASSERT_EQ(layer_2->strip_array_num, 1);
1279
1280 StripKeyframeData &strip_data_1 = layer_1->strip(0)->data<StripKeyframeData>(*action);
1281 StripKeyframeData &strip_data_2 = layer_2->strip(0)->data<StripKeyframeData>(*action_2);
1282
1283 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1284 ASSERT_EQ(strip_data_2.channelbag_array_num, 0)
1285 << "the keyframe strip of action_2 should NOT have a channelbag in this test";
1286
1287 Channelbag *bag_1 = strip_data_1.channelbag(0);
1288 ASSERT_EQ(bag_1->fcurve_array_num, 2);
1289
1290 move_slot(*bmain, slot_suzanne, *action_2, *action);
1291
1292 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1293 ASSERT_EQ(strip_data_2.channelbag_array_num, 0);
1294
1295 ASSERT_EQ(action->slot_array_num, 2);
1296 ASSERT_EQ(action_2->slot_array_num, 0);
1297
1298 /* Action should have been reassigned. */
1299 ASSERT_EQ(action, cube->adt->action);
1300 ASSERT_EQ(action, suzanne->adt->action);
1301}
1302
1303TEST_F(ActionLayersTest, action_duplicate_slot)
1304{
1305 ASSERT_TRUE(action->is_empty());
1306
1307 Slot &slot_cube = action->slot_add();
1308 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1309
1310 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1311
1312 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1313 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1314
1315 ASSERT_EQ(action->layer_array_num, 1);
1316 Layer *layer = action->layer(0);
1317
1318 ASSERT_EQ(layer->strip_array_num, 1);
1319 StripKeyframeData &strip_data = layer->strip(0)->data<StripKeyframeData>(*action);
1320
1321 ASSERT_EQ(strip_data.channelbag_array_num, 1);
1322 Channelbag *bag = strip_data.channelbag(0);
1323 ASSERT_EQ(bag->fcurve_array_num, 2);
1324
1325 /* Duplicate the slot and check it for uniqueness within the Action. */
1326 Slot &dupli_slot = duplicate_slot(*action, slot_cube);
1327 EXPECT_NE(dupli_slot.identifier, slot_cube.identifier);
1328 EXPECT_NE(dupli_slot.handle, slot_cube.handle);
1329 ASSERT_EQ(action->slot_array_num, 2);
1330 EXPECT_EQ(&dupli_slot, action->slot(1));
1331
1332 /* Check the channelbag has been duplicated correctly. */
1333 ASSERT_EQ(strip_data.channelbag_array_num, 2);
1334 Channelbag *dupli_bag = strip_data.channelbag(1);
1335 EXPECT_EQ(dupli_bag->slot_handle, dupli_slot.handle);
1336 EXPECT_EQ(dupli_bag->fcurve_array_num, 2);
1337
1338 /* Check the original channelbag is untouched. */
1339 EXPECT_EQ(bag->slot_handle, slot_cube.handle);
1340 EXPECT_EQ(bag->fcurve_array_num, 2);
1341
1342 /* The slot should NOT have been reassigned. */
1343 EXPECT_EQ(action, cube->adt->action);
1344 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
1345}
1346
1347TEST_F(ActionLayersTest, action_duplicate_slot_without_channelbag)
1348{
1349 ASSERT_TRUE(action->is_empty());
1350
1351 Slot &slot_cube = action->slot_add();
1352 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1353
1354 /* Create a keyframe strip, but without any channelbags. */
1355 action->layer_keystrip_ensure();
1356
1357 ASSERT_EQ(action->layer_array_num, 1);
1358 Layer *layer = action->layer(0);
1359
1360 ASSERT_EQ(layer->strip_array_num, 1);
1361 StripKeyframeData &strip_data = layer->strip(0)->data<StripKeyframeData>(*action);
1362
1363 ASSERT_EQ(strip_data.channelbag_array_num, 0);
1364
1365 /* Duplicate the slot and check it for uniqueness within the Action. */
1366 Slot &dupli_slot = duplicate_slot(*action, slot_cube);
1367 EXPECT_NE(dupli_slot.identifier, slot_cube.identifier);
1368 EXPECT_NE(dupli_slot.handle, slot_cube.handle);
1369 ASSERT_EQ(action->slot_array_num, 2);
1370 EXPECT_EQ(&dupli_slot, action->slot(1));
1371
1372 /* Check there are still no channelbags. */
1373 EXPECT_EQ(strip_data.channelbag_array_num, 0);
1374
1375 /* The slot should NOT have been reassigned. */
1376 EXPECT_EQ(action, cube->adt->action);
1377 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
1378}
1379
1380/*-----------------------------------------------------------*/
1381
1382/* Allocate fcu->bezt, and also return a unique_ptr to it for easily freeing the memory. */
1383static void allocate_keyframes(FCurve &fcu, const size_t num_keyframes)
1384{
1385 fcu.bezt = MEM_calloc_arrayN<BezTriple>(num_keyframes, __func__);
1386}
1387
1388/* Append keyframe, assumes that fcu->bezt is allocated and has enough space. */
1389static void add_keyframe(FCurve &fcu, float x, float y)
1390{
1391 /* The insert_keyframe functions are in the editors, so we cannot link to those here. */
1392 BezTriple the_keyframe = {};
1393
1394 /* Copied from insert_vert_fcurve() in `keyframing.cc`. */
1395 the_keyframe.vec[0][0] = x - 1.0f;
1396 the_keyframe.vec[0][1] = y;
1397 the_keyframe.vec[1][0] = x;
1398 the_keyframe.vec[1][1] = y;
1399 the_keyframe.vec[2][0] = x + 1.0f;
1400 the_keyframe.vec[2][1] = y;
1401
1402 memcpy(&fcu.bezt[fcu.totvert], &the_keyframe, sizeof(the_keyframe));
1403 fcu.totvert++;
1404}
1405
1406static void add_fcurve_to_action(Action &action, FCurve &fcu)
1407{
1408 Slot &slot = action.slot_array_num > 0 ? *action.slot(0) : action.slot_add();
1409 action.layer_keystrip_ensure();
1410 StripKeyframeData &strip_data = action.layer(0)->strip(0)->data<StripKeyframeData>(action);
1411 Channelbag &cbag = strip_data.channelbag_for_slot_ensure(slot);
1412 cbag.fcurve_append(fcu);
1413}
1414
1415class ActionQueryTest : public testing::Test {
1416 public:
1418
1419 static void SetUpTestSuite()
1420 {
1421 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
1422 CLG_init();
1423
1424 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
1426 }
1427
1428 static void TearDownTestSuite()
1429 {
1430 CLG_exit();
1431 }
1432
1433 void SetUp() override
1434 {
1435 bmain = BKE_main_new();
1436 }
1437
1438 void TearDown() override
1439 {
1441 }
1442
1444 {
1445 return *BKE_id_new<Action>(bmain, "ACÄnimåtië");
1446 }
1447};
1448
1449TEST_F(ActionQueryTest, BKE_action_frame_range_calc)
1450{
1451 /* No FCurves. */
1452 {
1453 const Action &empty = action_new();
1454 EXPECT_EQ((float2{0.0f, 0.0f}), empty.get_frame_range_of_keys(false));
1455 }
1456
1457 /* One curve with one key. */
1458 {
1459 FCurve &fcu = *MEM_callocN<FCurve>(__func__);
1460 allocate_keyframes(fcu, 1);
1461 add_keyframe(fcu, 1.0f, 2.0f);
1462
1463 Action &action = action_new();
1464 add_fcurve_to_action(action, fcu);
1465
1466 const float2 frame_range = action.get_frame_range_of_keys(false);
1467 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1468 EXPECT_FLOAT_EQ(frame_range[1], 1.0f);
1469 }
1470
1471 /* Two curves with one key each on different frames. */
1472 {
1473 FCurve &fcu1 = *MEM_callocN<FCurve>(__func__);
1474 FCurve &fcu2 = *MEM_callocN<FCurve>(__func__);
1475 allocate_keyframes(fcu1, 1);
1476 allocate_keyframes(fcu2, 1);
1477 add_keyframe(fcu1, 1.0f, 2.0f);
1478 add_keyframe(fcu2, 1.5f, 2.0f);
1479
1480 Action &action = action_new();
1481 add_fcurve_to_action(action, fcu1);
1482 add_fcurve_to_action(action, fcu2);
1483
1484 const float2 frame_range = action.get_frame_range_of_keys(false);
1485 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1486 EXPECT_FLOAT_EQ(frame_range[1], 1.5f);
1487 }
1488
1489 /* One curve with two keys. */
1490 {
1491 FCurve &fcu = *MEM_callocN<FCurve>(__func__);
1492 allocate_keyframes(fcu, 2);
1493 add_keyframe(fcu, 1.0f, 2.0f);
1494 add_keyframe(fcu, 1.5f, 2.0f);
1495
1496 Action &action = action_new();
1497 add_fcurve_to_action(action, fcu);
1498
1499 const float2 frame_range = action.get_frame_range_of_keys(false);
1500 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1501 EXPECT_FLOAT_EQ(frame_range[1], 1.5f);
1502 }
1503
1504 /* TODO: action with fcurve modifiers. */
1505}
1506
1507/*-----------------------------------------------------------*/
1508
1509class ChannelbagTest : public testing::Test {
1510 public:
1512
1513 static void SetUpTestSuite() {}
1514
1515 static void TearDownTestSuite() {}
1516
1517 void SetUp() override
1518 {
1519 channelbag = new Channelbag();
1520 }
1521
1522 void TearDown() override
1523 {
1524 delete channelbag;
1525 }
1526};
1527
1528TEST_F(ChannelbagTest, fcurve_create_many)
1529{
1530 FCurve &existing1 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1531 FCurve &existing2 = channelbag->fcurve_ensure(nullptr, {"fcu0", 1, {}, {}, "group0"});
1532 FCurve &existing3 = channelbag->fcurve_ensure(nullptr, {"fcu1", 1, {}, {}, "group1"});
1533 FCurve &existing4 = channelbag->fcurve_ensure(nullptr, {"fcu_", 0});
1534 ASSERT_EQ(2, channelbag->channel_groups().size());
1535 ASSERT_EQ(4, channelbag->fcurves().size());
1536
1537 FCurveDescriptor desc[] = {
1538 /* New group. */
1539 {"fcu2", 0, {}, {}, "group2"},
1540 {"fcu2", 1, {}, {}, "group2"},
1541 {"fcu2", 2, {}, {}, "group2"},
1542 /* Existing groups. */
1543 {"fcu3", 0, {}, {}, "group1"},
1544 {"fcu4", 0, {}, {}, "group0"},
1545 {"fcu5", 0, {}, {}, "group1"},
1546 {"fcu6", 0, {}, {}, "group0"},
1547 {"fcu7", 0, {}, {}, "group2"},
1548 /* No group. */
1549 {"fcu8", 0},
1550 {"fcu8", 1},
1551 /* Empty rna path, should return null. */
1552 {"", 0, {}, {}, "irrelevant"},
1553 /* Should return null since such curves already exist. */
1554 {"fcu0", 1, {}, {}, "irrelevant"},
1555 {"fcu5", 0, {}, {}, "also unused"},
1556 {"fcu2", 0, {}, {}, "group2"},
1557 {"fcu6", 0},
1558 };
1559 Vector<FCurve *> fcurves = channelbag->fcurve_create_many(nullptr, {desc, ARRAY_SIZE(desc)});
1560 ASSERT_EQ(15, fcurves.size());
1561
1562 EXPECT_STREQ("group2", fcurves[0]->grp->name);
1563 EXPECT_STREQ("group2", fcurves[1]->grp->name);
1564 EXPECT_STREQ("group2", fcurves[2]->grp->name);
1565 EXPECT_STREQ("group1", fcurves[3]->grp->name);
1566 EXPECT_STREQ("group0", fcurves[4]->grp->name);
1567 EXPECT_STREQ("group1", fcurves[5]->grp->name);
1568 EXPECT_STREQ("group0", fcurves[6]->grp->name);
1569 EXPECT_STREQ("group2", fcurves[7]->grp->name);
1570 EXPECT_EQ(nullptr, fcurves[8]->grp);
1571 EXPECT_EQ(nullptr, fcurves[9]->grp);
1572 EXPECT_EQ(nullptr, fcurves[10]);
1573 EXPECT_EQ(nullptr, fcurves[11]);
1574 EXPECT_EQ(nullptr, fcurves[12]);
1575 EXPECT_EQ(nullptr, fcurves[13]);
1576 EXPECT_EQ(nullptr, fcurves[14]);
1577
1578 EXPECT_EQ(3, channelbag->channel_groups().size());
1579 EXPECT_EQ(14, channelbag->fcurves().size());
1580
1581 EXPECT_STREQ("group0", existing1.grp->name);
1582 EXPECT_STREQ("group0", existing2.grp->name);
1583 EXPECT_STREQ("group1", existing3.grp->name);
1584 EXPECT_EQ(nullptr, existing4.grp);
1585}
1586
1587TEST_F(ChannelbagTest, fcurve_move_to_index)
1588{
1589 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1590 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1591 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1592 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
1593 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1594
1595 ASSERT_EQ(5, channelbag->fcurves().size());
1596 ASSERT_EQ(2, channelbag->channel_groups().size());
1597
1598 bActionGroup &group0 = *channelbag->channel_group(0);
1599 bActionGroup &group1 = *channelbag->channel_group(1);
1600
1601 /* Moving an fcurve to where it already is should be fine. */
1602 channelbag->fcurve_move_to_index(fcu0, 0);
1603 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1604 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1605 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1606 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1607 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1608 EXPECT_EQ(&group0, fcu0.grp);
1609 EXPECT_EQ(&group0, fcu1.grp);
1610 EXPECT_EQ(&group1, fcu2.grp);
1611 EXPECT_EQ(&group1, fcu3.grp);
1612 EXPECT_EQ(nullptr, fcu4.grp);
1613
1614 /* Move to first. */
1615 channelbag->fcurve_move_to_index(fcu4, 0);
1616 EXPECT_EQ(0, group0.fcurve_range_start);
1617 EXPECT_EQ(2, group0.fcurve_range_length);
1618 EXPECT_EQ(2, group1.fcurve_range_start);
1619 EXPECT_EQ(2, group1.fcurve_range_length);
1620 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
1621 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1622 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
1623 EXPECT_EQ(&fcu2, channelbag->fcurve(3));
1624 EXPECT_EQ(&fcu3, channelbag->fcurve(4));
1625 EXPECT_EQ(&group0, fcu4.grp);
1626 EXPECT_EQ(&group0, fcu0.grp);
1627 EXPECT_EQ(&group1, fcu1.grp);
1628 EXPECT_EQ(&group1, fcu2.grp);
1629 EXPECT_EQ(nullptr, fcu3.grp);
1630
1631 /* Move to last. */
1632 channelbag->fcurve_move_to_index(fcu1, 4);
1633 EXPECT_EQ(0, group0.fcurve_range_start);
1634 EXPECT_EQ(2, group0.fcurve_range_length);
1635 EXPECT_EQ(2, group1.fcurve_range_start);
1636 EXPECT_EQ(2, group1.fcurve_range_length);
1637 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
1638 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1639 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1640 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1641 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
1642 EXPECT_EQ(&group0, fcu4.grp);
1643 EXPECT_EQ(&group0, fcu0.grp);
1644 EXPECT_EQ(&group1, fcu2.grp);
1645 EXPECT_EQ(&group1, fcu3.grp);
1646 EXPECT_EQ(nullptr, fcu1.grp);
1647
1648 /* Move to middle. */
1649 channelbag->fcurve_move_to_index(fcu4, 2);
1650 EXPECT_EQ(0, group0.fcurve_range_start);
1651 EXPECT_EQ(2, group0.fcurve_range_length);
1652 EXPECT_EQ(2, group1.fcurve_range_start);
1653 EXPECT_EQ(2, group1.fcurve_range_length);
1654 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1655 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
1656 EXPECT_EQ(&fcu4, channelbag->fcurve(2));
1657 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1658 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
1659 EXPECT_EQ(&group0, fcu0.grp);
1660 EXPECT_EQ(&group0, fcu2.grp);
1661 EXPECT_EQ(&group1, fcu4.grp);
1662 EXPECT_EQ(&group1, fcu3.grp);
1663 EXPECT_EQ(nullptr, fcu1.grp);
1664}
1665
1666TEST_F(ChannelbagTest, channel_group_create)
1667{
1668 ASSERT_TRUE(channelbag->channel_groups().is_empty());
1669
1670 bActionGroup &group0 = channelbag->channel_group_create("Foo");
1671 ASSERT_EQ(channelbag->channel_groups().size(), 1);
1672 EXPECT_EQ(StringRef{group0.name}, StringRef{"Foo"});
1673 EXPECT_EQ(group0.fcurve_range_start, 0);
1674 EXPECT_EQ(group0.fcurve_range_length, 0);
1675 EXPECT_EQ(&group0, channelbag->channel_group(0));
1676
1677 /* Set for testing purposes. Does not reflect actual fcurves in this test. */
1678 group0.fcurve_range_length = 2;
1679
1680 bActionGroup &group1 = channelbag->channel_group_create("Bar");
1681 ASSERT_EQ(channelbag->channel_groups().size(), 2);
1682 EXPECT_EQ(StringRef{group1.name}, StringRef{"Bar"});
1683 EXPECT_EQ(group1.fcurve_range_start, 2);
1684 EXPECT_EQ(group1.fcurve_range_length, 0);
1685 EXPECT_EQ(&group0, channelbag->channel_group(0));
1686 EXPECT_EQ(&group1, channelbag->channel_group(1));
1687
1688 /* Set for testing purposes. Does not reflect actual fcurves in this test. */
1689 group1.fcurve_range_length = 1;
1690
1691 bActionGroup &group2 = channelbag->channel_group_create("Yar");
1692 ASSERT_EQ(channelbag->channel_groups().size(), 3);
1693 EXPECT_EQ(StringRef{group2.name}, StringRef{"Yar"});
1694 EXPECT_EQ(group2.fcurve_range_start, 3);
1695 EXPECT_EQ(group2.fcurve_range_length, 0);
1696 EXPECT_EQ(&group0, channelbag->channel_group(0));
1697 EXPECT_EQ(&group1, channelbag->channel_group(1));
1698 EXPECT_EQ(&group2, channelbag->channel_group(2));
1699}
1700
1701TEST_F(ChannelbagTest, channel_group_remove)
1702{
1703 bActionGroup &group0 = channelbag->channel_group_create("Group0");
1704 bActionGroup &group1 = channelbag->channel_group_create("Group1");
1705 bActionGroup &group2 = channelbag->channel_group_create("Group2");
1706
1707 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "Group0"});
1708 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "Group0"});
1709 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "Group2"});
1710 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "Group2"});
1711 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1712
1713 ASSERT_EQ(3, channelbag->channel_groups().size());
1714 ASSERT_EQ(5, channelbag->fcurves().size());
1715
1716 /* Attempt to remove a group that's not in the channel bag. Shouldn't do
1717 * anything. */
1718 bActionGroup bogus;
1719 EXPECT_EQ(false, channelbag->channel_group_remove(bogus));
1720 ASSERT_EQ(3, channelbag->channel_groups().size());
1721 ASSERT_EQ(5, channelbag->fcurves().size());
1722 EXPECT_EQ(&group0, channelbag->channel_group(0));
1723 EXPECT_EQ(&group1, channelbag->channel_group(1));
1724 EXPECT_EQ(&group2, channelbag->channel_group(2));
1725 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1726 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1727 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1728 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1729 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1730 EXPECT_EQ(&group0, fcu0.grp);
1731 EXPECT_EQ(&group0, fcu1.grp);
1732 EXPECT_EQ(&group2, fcu2.grp);
1733 EXPECT_EQ(&group2, fcu3.grp);
1734 EXPECT_EQ(nullptr, fcu4.grp);
1735
1736 /* Removing an empty group shouldn't affect the fcurves at all. */
1737 EXPECT_EQ(true, channelbag->channel_group_remove(group1));
1738 ASSERT_EQ(2, channelbag->channel_groups().size());
1739 ASSERT_EQ(5, channelbag->fcurves().size());
1740 EXPECT_EQ(&group0, channelbag->channel_group(0));
1741 EXPECT_EQ(&group2, channelbag->channel_group(1));
1742 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1743 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1744 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1745 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1746 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1747 EXPECT_EQ(&group0, fcu0.grp);
1748 EXPECT_EQ(&group0, fcu1.grp);
1749 EXPECT_EQ(&group2, fcu2.grp);
1750 EXPECT_EQ(&group2, fcu3.grp);
1751 EXPECT_EQ(nullptr, fcu4.grp);
1752
1753 /* Removing a group that's not at the end of the group array should move its
1754 * fcurves to be just after the grouped fcurves. */
1755 EXPECT_EQ(true, channelbag->channel_group_remove(group0));
1756 ASSERT_EQ(1, channelbag->channel_groups().size());
1757 ASSERT_EQ(5, channelbag->fcurves().size());
1758 EXPECT_EQ(&group2, channelbag->channel_group(0));
1759 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
1760 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1761 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1762 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
1763 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1764 EXPECT_EQ(nullptr, fcu0.grp);
1765 EXPECT_EQ(nullptr, fcu1.grp);
1766 EXPECT_EQ(&group2, fcu2.grp);
1767 EXPECT_EQ(&group2, fcu3.grp);
1768 EXPECT_EQ(nullptr, fcu4.grp);
1769
1770 /* Removing a group at the end of the group array shouldn't move its
1771 * fcurves. */
1772 EXPECT_EQ(true, channelbag->channel_group_remove(group2));
1773 ASSERT_EQ(0, channelbag->channel_groups().size());
1774 ASSERT_EQ(5, channelbag->fcurves().size());
1775 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
1776 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1777 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1778 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
1779 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1780 EXPECT_EQ(nullptr, fcu0.grp);
1781 EXPECT_EQ(nullptr, fcu1.grp);
1782 EXPECT_EQ(nullptr, fcu2.grp);
1783 EXPECT_EQ(nullptr, fcu3.grp);
1784 EXPECT_EQ(nullptr, fcu4.grp);
1785}
1786
1787TEST_F(ChannelbagTest, channel_group_find)
1788{
1789 bActionGroup &group0a = channelbag->channel_group_create("Foo");
1790 bActionGroup &group1a = channelbag->channel_group_create("Bar");
1791 bActionGroup &group2a = channelbag->channel_group_create("Yar");
1792
1793 bActionGroup *group0b = channelbag->channel_group_find("Foo");
1794 bActionGroup *group1b = channelbag->channel_group_find("Bar");
1795 bActionGroup *group2b = channelbag->channel_group_find("Yar");
1796
1797 EXPECT_EQ(&group0a, group0b);
1798 EXPECT_EQ(&group1a, group1b);
1799 EXPECT_EQ(&group2a, group2b);
1800
1801 EXPECT_EQ(nullptr, channelbag->channel_group_find("Wat"));
1802}
1803
1804TEST_F(ChannelbagTest, channel_group_ensure)
1805{
1806 bActionGroup &group0 = channelbag->channel_group_create("Foo");
1807 bActionGroup &group1 = channelbag->channel_group_create("Bar");
1808 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1809
1810 EXPECT_EQ(&group0, &channelbag->channel_group_ensure("Foo"));
1811 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1812
1813 EXPECT_EQ(&group1, &channelbag->channel_group_ensure("Bar"));
1814 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1815
1816 bActionGroup &group2 = channelbag->channel_group_ensure("Yar");
1817 ASSERT_EQ(channelbag->channel_groups().size(), 3);
1818 EXPECT_EQ(&group2, channelbag->channel_group(2));
1819}
1820
1821TEST_F(ChannelbagTest, channel_group_fcurve_creation)
1822{
1823 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0});
1824 EXPECT_EQ(1, channelbag->fcurves().size());
1825 EXPECT_TRUE(channelbag->channel_groups().is_empty());
1826
1827 /* If an fcurve already exists, then ensuring it with a channel group in the
1828 * fcurve descriptor should NOT add it that group, nor should the group be
1829 * created if it doesn't already exist. */
1830 channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1831 EXPECT_EQ(1, channelbag->fcurves().size());
1832 EXPECT_EQ(nullptr, fcu0.grp);
1833 EXPECT_TRUE(channelbag->channel_groups().is_empty());
1834
1835 /* Creating a new fcurve with a channel group in the fcurve descriptor should
1836 * create the group and put the fcurve in it. This also implies that the
1837 * fcurve will be added before any non-grouped fcurves in the array. */
1838 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1839 ASSERT_EQ(2, channelbag->fcurves().size());
1840 ASSERT_EQ(1, channelbag->channel_groups().size());
1841 bActionGroup &group0 = *channelbag->channel_group(0);
1842 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1843 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1844 EXPECT_EQ(&group0, fcu1.grp);
1845 EXPECT_EQ(nullptr, fcu0.grp);
1846 EXPECT_EQ(0, group0.fcurve_range_start);
1847 EXPECT_EQ(1, group0.fcurve_range_length);
1848
1849 /* Creating a new fcurve with a second channel group in the fcurve descriptor
1850 * should create the group and put the fcurve in it. This also implies that
1851 * the fcurve will be added before non-grouped fcurves, but after other
1852 * grouped ones. */
1853 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1854 ASSERT_EQ(3, channelbag->fcurves().size());
1855 ASSERT_EQ(2, channelbag->channel_groups().size());
1856 EXPECT_EQ(&group0, channelbag->channel_group(0));
1857 bActionGroup &group1 = *channelbag->channel_group(1);
1858 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1859 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
1860 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1861 EXPECT_EQ(&group0, fcu1.grp);
1862 EXPECT_EQ(&group1, fcu2.grp);
1863 EXPECT_EQ(nullptr, fcu0.grp);
1864 EXPECT_EQ(0, group0.fcurve_range_start);
1865 EXPECT_EQ(1, group0.fcurve_range_length);
1866 EXPECT_EQ(1, group1.fcurve_range_start);
1867 EXPECT_EQ(1, group1.fcurve_range_length);
1868
1869 /* Creating a new fcurve with the first channel group again should put it at
1870 * the end of that group. */
1871 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group0"});
1872 ASSERT_EQ(4, channelbag->fcurves().size());
1873 ASSERT_EQ(2, channelbag->channel_groups().size());
1874 EXPECT_EQ(&group0, channelbag->channel_group(0));
1875 EXPECT_EQ(&group1, channelbag->channel_group(1));
1876 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1877 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1878 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1879 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
1880 EXPECT_EQ(&group0, fcu1.grp);
1881 EXPECT_EQ(&group0, fcu3.grp);
1882 EXPECT_EQ(&group1, fcu2.grp);
1883 EXPECT_EQ(nullptr, fcu0.grp);
1884 EXPECT_EQ(0, group0.fcurve_range_start);
1885 EXPECT_EQ(2, group0.fcurve_range_length);
1886 EXPECT_EQ(2, group1.fcurve_range_start);
1887 EXPECT_EQ(1, group1.fcurve_range_length);
1888
1889 /* Finally, creating a new fcurve with the second channel group again should
1890 * also put it at the end of that group. */
1891 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0, {}, {}, "group1"});
1892 ASSERT_EQ(5, channelbag->fcurves().size());
1893 ASSERT_EQ(2, channelbag->channel_groups().size());
1894 EXPECT_EQ(&group0, channelbag->channel_group(0));
1895 EXPECT_EQ(&group1, channelbag->channel_group(1));
1896 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1897 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1898 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1899 EXPECT_EQ(&fcu4, channelbag->fcurve(3));
1900 EXPECT_EQ(&fcu0, channelbag->fcurve(4));
1901 EXPECT_EQ(&group0, fcu1.grp);
1902 EXPECT_EQ(&group0, fcu3.grp);
1903 EXPECT_EQ(&group1, fcu2.grp);
1904 EXPECT_EQ(&group1, fcu4.grp);
1905 EXPECT_EQ(nullptr, fcu0.grp);
1906 EXPECT_EQ(0, group0.fcurve_range_start);
1907 EXPECT_EQ(2, group0.fcurve_range_length);
1908 EXPECT_EQ(2, group1.fcurve_range_start);
1909 EXPECT_EQ(2, group1.fcurve_range_length);
1910}
1911
1912TEST_F(ChannelbagTest, channel_group_fcurve_removal)
1913{
1914 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1915 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1916 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1917 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
1918 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1919
1920 ASSERT_EQ(5, channelbag->fcurves().size());
1921 ASSERT_EQ(2, channelbag->channel_groups().size());
1922
1923 bActionGroup &group0 = *channelbag->channel_group(0);
1924 bActionGroup &group1 = *channelbag->channel_group(1);
1925
1926 EXPECT_EQ(0, group0.fcurve_range_start);
1927 EXPECT_EQ(2, group0.fcurve_range_length);
1928 EXPECT_EQ(2, group1.fcurve_range_start);
1929 EXPECT_EQ(2, group1.fcurve_range_length);
1930 EXPECT_EQ(&group0, fcu0.grp);
1931 EXPECT_EQ(&group0, fcu1.grp);
1932 EXPECT_EQ(&group1, fcu2.grp);
1933 EXPECT_EQ(&group1, fcu3.grp);
1934 EXPECT_EQ(nullptr, fcu4.grp);
1935
1936 channelbag->fcurve_remove(fcu3);
1937 ASSERT_EQ(4, channelbag->fcurves().size());
1938 ASSERT_EQ(2, channelbag->channel_groups().size());
1939 EXPECT_EQ(&group0, channelbag->channel_group(0));
1940 EXPECT_EQ(&group1, channelbag->channel_group(1));
1941 EXPECT_EQ(0, group0.fcurve_range_start);
1942 EXPECT_EQ(2, group0.fcurve_range_length);
1943 EXPECT_EQ(2, group1.fcurve_range_start);
1944 EXPECT_EQ(1, group1.fcurve_range_length);
1945 EXPECT_EQ(&group0, fcu0.grp);
1946 EXPECT_EQ(&group0, fcu1.grp);
1947 EXPECT_EQ(&group1, fcu2.grp);
1948 EXPECT_EQ(nullptr, fcu4.grp);
1949
1950 channelbag->fcurve_remove(fcu0);
1951 ASSERT_EQ(3, channelbag->fcurves().size());
1952 ASSERT_EQ(2, channelbag->channel_groups().size());
1953 EXPECT_EQ(&group0, channelbag->channel_group(0));
1954 EXPECT_EQ(&group1, channelbag->channel_group(1));
1955 EXPECT_EQ(0, group0.fcurve_range_start);
1956 EXPECT_EQ(1, group0.fcurve_range_length);
1957 EXPECT_EQ(1, group1.fcurve_range_start);
1958 EXPECT_EQ(1, group1.fcurve_range_length);
1959 EXPECT_EQ(&group0, fcu1.grp);
1960 EXPECT_EQ(&group1, fcu2.grp);
1961 EXPECT_EQ(nullptr, fcu4.grp);
1962
1963 channelbag->fcurve_remove(fcu1);
1964 ASSERT_EQ(2, channelbag->fcurves().size());
1965 ASSERT_EQ(1, channelbag->channel_groups().size());
1966 EXPECT_EQ(&group1, channelbag->channel_group(0));
1967 EXPECT_EQ(0, group1.fcurve_range_start);
1968 EXPECT_EQ(1, group1.fcurve_range_length);
1969 EXPECT_EQ(&group1, fcu2.grp);
1970 EXPECT_EQ(nullptr, fcu4.grp);
1971
1972 channelbag->fcurve_remove(fcu4);
1973 ASSERT_EQ(1, channelbag->fcurves().size());
1974 ASSERT_EQ(1, channelbag->channel_groups().size());
1975 EXPECT_EQ(&group1, channelbag->channel_group(0));
1976 EXPECT_EQ(0, group1.fcurve_range_start);
1977 EXPECT_EQ(1, group1.fcurve_range_length);
1978 EXPECT_EQ(&group1, fcu2.grp);
1979
1980 channelbag->fcurve_remove(fcu2);
1981 ASSERT_EQ(0, channelbag->fcurves().size());
1982 ASSERT_EQ(0, channelbag->channel_groups().size());
1983}
1984
1985TEST_F(ChannelbagTest, channel_group_move_to_index)
1986{
1987 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1988 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group1"});
1989 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1990 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group2"});
1991 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1992
1993 ASSERT_EQ(5, channelbag->fcurves().size());
1994 ASSERT_EQ(3, channelbag->channel_groups().size());
1995
1996 bActionGroup &group0 = *channelbag->channel_group(0);
1997 bActionGroup &group1 = *channelbag->channel_group(1);
1998 bActionGroup &group2 = *channelbag->channel_group(2);
1999
2000 channelbag->channel_group_move_to_index(group0, 2);
2001 EXPECT_EQ(&group1, channelbag->channel_group(0));
2002 EXPECT_EQ(&group2, channelbag->channel_group(1));
2003 EXPECT_EQ(&group0, channelbag->channel_group(2));
2004 EXPECT_EQ(0, group1.fcurve_range_start);
2005 EXPECT_EQ(2, group1.fcurve_range_length);
2006 EXPECT_EQ(2, group2.fcurve_range_start);
2007 EXPECT_EQ(1, group2.fcurve_range_length);
2008 EXPECT_EQ(3, group0.fcurve_range_start);
2009 EXPECT_EQ(1, group0.fcurve_range_length);
2010 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2011 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2012 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2013 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2014 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2015 EXPECT_EQ(&group1, fcu1.grp);
2016 EXPECT_EQ(&group1, fcu2.grp);
2017 EXPECT_EQ(&group2, fcu3.grp);
2018 EXPECT_EQ(&group0, fcu0.grp);
2019 EXPECT_EQ(nullptr, fcu4.grp);
2020
2021 channelbag->channel_group_move_to_index(group1, 1);
2022 EXPECT_EQ(&group2, channelbag->channel_group(0));
2023 EXPECT_EQ(&group1, channelbag->channel_group(1));
2024 EXPECT_EQ(&group0, channelbag->channel_group(2));
2025 EXPECT_EQ(0, group2.fcurve_range_start);
2026 EXPECT_EQ(1, group2.fcurve_range_length);
2027 EXPECT_EQ(1, group1.fcurve_range_start);
2028 EXPECT_EQ(2, group1.fcurve_range_length);
2029 EXPECT_EQ(3, group0.fcurve_range_start);
2030 EXPECT_EQ(1, group0.fcurve_range_length);
2031 EXPECT_EQ(&fcu3, channelbag->fcurve(0));
2032 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
2033 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2034 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2035 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2036 EXPECT_EQ(&group2, fcu3.grp);
2037 EXPECT_EQ(&group1, fcu1.grp);
2038 EXPECT_EQ(&group1, fcu2.grp);
2039 EXPECT_EQ(&group0, fcu0.grp);
2040 EXPECT_EQ(nullptr, fcu4.grp);
2041
2042 channelbag->channel_group_move_to_index(group0, 0);
2043 EXPECT_EQ(&group0, channelbag->channel_group(0));
2044 EXPECT_EQ(&group2, channelbag->channel_group(1));
2045 EXPECT_EQ(&group1, channelbag->channel_group(2));
2046 EXPECT_EQ(0, group0.fcurve_range_start);
2047 EXPECT_EQ(1, group0.fcurve_range_length);
2048 EXPECT_EQ(1, group2.fcurve_range_start);
2049 EXPECT_EQ(1, group2.fcurve_range_length);
2050 EXPECT_EQ(2, group1.fcurve_range_start);
2051 EXPECT_EQ(2, group1.fcurve_range_length);
2052 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2053 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
2054 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2055 EXPECT_EQ(&fcu2, channelbag->fcurve(3));
2056 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2057 EXPECT_EQ(&group0, fcu0.grp);
2058 EXPECT_EQ(&group2, fcu3.grp);
2059 EXPECT_EQ(&group1, fcu1.grp);
2060 EXPECT_EQ(&group1, fcu2.grp);
2061 EXPECT_EQ(nullptr, fcu4.grp);
2062}
2063
2064TEST_F(ChannelbagTest, channel_group_move_fcurve_into)
2065{
2066 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0});
2067 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0});
2068 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0});
2069 bActionGroup &group0 = channelbag->channel_group_create("group0");
2070 bActionGroup &group1 = channelbag->channel_group_create("group1");
2071
2072 ASSERT_EQ(3, channelbag->fcurves().size());
2073 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2074 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
2075 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2076 ASSERT_EQ(2, channelbag->channel_groups().size());
2077 EXPECT_EQ(&group0, channelbag->channel_group(0));
2078 EXPECT_EQ(&group1, channelbag->channel_group(1));
2079 EXPECT_EQ(0, group0.fcurve_range_start);
2080 EXPECT_EQ(0, group0.fcurve_range_length);
2081 EXPECT_EQ(0, group1.fcurve_range_start);
2082 EXPECT_EQ(0, group1.fcurve_range_length);
2083
2084 channelbag->fcurve_assign_to_channel_group(fcu2, group1);
2085 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
2086 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2087 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2088 EXPECT_EQ(0, group0.fcurve_range_start);
2089 EXPECT_EQ(0, group0.fcurve_range_length);
2090 EXPECT_EQ(0, group1.fcurve_range_start);
2091 EXPECT_EQ(1, group1.fcurve_range_length);
2092
2093 channelbag->fcurve_assign_to_channel_group(fcu1, group0);
2094 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2095 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2096 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2097 EXPECT_EQ(0, group0.fcurve_range_start);
2098 EXPECT_EQ(1, group0.fcurve_range_length);
2099 EXPECT_EQ(1, group1.fcurve_range_start);
2100 EXPECT_EQ(1, group1.fcurve_range_length);
2101
2102 channelbag->fcurve_assign_to_channel_group(fcu0, group1);
2103 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2104 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2105 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2106 EXPECT_EQ(0, group0.fcurve_range_start);
2107 EXPECT_EQ(1, group0.fcurve_range_length);
2108 EXPECT_EQ(1, group1.fcurve_range_start);
2109 EXPECT_EQ(2, group1.fcurve_range_length);
2110
2111 channelbag->fcurve_assign_to_channel_group(fcu0, group0);
2112 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2113 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2114 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2115 EXPECT_EQ(0, group0.fcurve_range_start);
2116 EXPECT_EQ(2, group0.fcurve_range_length);
2117 EXPECT_EQ(2, group1.fcurve_range_start);
2118 EXPECT_EQ(1, group1.fcurve_range_length);
2119
2120 channelbag->fcurve_assign_to_channel_group(fcu1, group1);
2121 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2122 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2123 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2124 EXPECT_EQ(0, group0.fcurve_range_start);
2125 EXPECT_EQ(1, group0.fcurve_range_length);
2126 EXPECT_EQ(1, group1.fcurve_range_start);
2127 EXPECT_EQ(2, group1.fcurve_range_length);
2128}
2129
2130TEST_F(ChannelbagTest, channel_group_fcurve_ungroup)
2131{
2132 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
2133 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
2134 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
2135 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
2136 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
2137
2138 ASSERT_EQ(5, channelbag->fcurves().size());
2139 ASSERT_EQ(2, channelbag->channel_groups().size());
2140
2141 bActionGroup &group0 = *channelbag->channel_group(0);
2142 bActionGroup &group1 = *channelbag->channel_group(1);
2143
2144 /* Attempting to ungroup an fcurve that's not in the channel bag should fail. */
2145 FCurve bogus = {};
2146 EXPECT_FALSE(channelbag->fcurve_ungroup(bogus));
2147
2148 /* Attempting to ungroup an fcurve that's already ungrouped is fine. */
2149 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu4));
2150
2151 /* Ungroup each fcurve until all are ungrouped. */
2152
2153 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu0));
2154 EXPECT_EQ(0, group0.fcurve_range_start);
2155 EXPECT_EQ(1, group0.fcurve_range_length);
2156 EXPECT_EQ(1, group1.fcurve_range_start);
2157 EXPECT_EQ(2, group1.fcurve_range_length);
2158 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2159 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2160 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2161 EXPECT_EQ(&fcu4, channelbag->fcurve(3));
2162 EXPECT_EQ(&fcu0, channelbag->fcurve(4));
2163 EXPECT_EQ(&group0, fcu1.grp);
2164 EXPECT_EQ(&group1, fcu2.grp);
2165 EXPECT_EQ(&group1, fcu3.grp);
2166 EXPECT_EQ(nullptr, fcu4.grp);
2167 EXPECT_EQ(nullptr, fcu0.grp);
2168
2169 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu3));
2170 EXPECT_EQ(0, group0.fcurve_range_start);
2171 EXPECT_EQ(1, group0.fcurve_range_length);
2172 EXPECT_EQ(1, group1.fcurve_range_start);
2173 EXPECT_EQ(1, group1.fcurve_range_length);
2174 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2175 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2176 EXPECT_EQ(&fcu4, channelbag->fcurve(2));
2177 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2178 EXPECT_EQ(&fcu3, channelbag->fcurve(4));
2179 EXPECT_EQ(&group0, fcu1.grp);
2180 EXPECT_EQ(&group1, fcu2.grp);
2181 EXPECT_EQ(nullptr, fcu4.grp);
2182 EXPECT_EQ(nullptr, fcu0.grp);
2183 EXPECT_EQ(nullptr, fcu3.grp);
2184
2185 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu1));
2186 EXPECT_EQ(1, channelbag->channel_groups().size());
2187 EXPECT_EQ(&group1, channelbag->channel_group(0));
2188 EXPECT_EQ(0, group1.fcurve_range_start);
2189 EXPECT_EQ(1, group1.fcurve_range_length);
2190 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
2191 EXPECT_EQ(&fcu4, channelbag->fcurve(1));
2192 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2193 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
2194 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
2195 EXPECT_EQ(&group1, fcu2.grp);
2196 EXPECT_EQ(nullptr, fcu4.grp);
2197 EXPECT_EQ(nullptr, fcu0.grp);
2198 EXPECT_EQ(nullptr, fcu3.grp);
2199 EXPECT_EQ(nullptr, fcu1.grp);
2200
2201 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu2));
2202 EXPECT_EQ(0, channelbag->channel_groups().size());
2203 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
2204 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2205 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2206 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
2207 EXPECT_EQ(&fcu2, channelbag->fcurve(4));
2208 EXPECT_EQ(nullptr, fcu4.grp);
2209 EXPECT_EQ(nullptr, fcu0.grp);
2210 EXPECT_EQ(nullptr, fcu3.grp);
2211 EXPECT_EQ(nullptr, fcu1.grp);
2212 EXPECT_EQ(nullptr, fcu2.grp);
2213}
2214
2215/*-----------------------------------------------------------*/
2216
2217class ActionFCurveMoveTest : public testing::Test {
2218 public:
2220
2221 static void SetUpTestSuite()
2222 {
2223 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
2224 CLG_init();
2225
2226 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
2228 }
2229
2230 static void TearDownTestSuite()
2231 {
2232 CLG_exit();
2233 }
2234
2235 void SetUp() override
2236 {
2237 bmain = BKE_main_new();
2238 }
2239
2240 void TearDown() override
2241 {
2243 }
2244
2245 static FCurve *fcurve_create(const StringRefNull rna_path, const int array_index)
2246 {
2247 FCurve *fcurve = BKE_fcurve_create();
2248 fcurve->rna_path = BLI_strdupn(rna_path.c_str(), array_index);
2249 return fcurve;
2250 };
2251};
2252
2253TEST_F(ActionFCurveMoveTest, test_fcurve_move_legacy)
2254{
2255 Action &action_src = action_add(*this->bmain, "SourceAction");
2256 Action &action_dst = action_add(*this->bmain, "DestinationAction");
2257
2258 /* Add F-Curves to source Action. */
2259 BLI_addtail(&action_src.curves, fcurve_create("source_prop", 0));
2260 FCurve *fcurve_to_move = fcurve_create("source_prop", 2);
2261 BLI_addtail(&action_src.curves, fcurve_to_move);
2262
2263 /* Add F-Curves to destination Action. */
2264 BLI_addtail(&action_dst.curves, fcurve_create("dest_prop", 0));
2265
2266 ASSERT_TRUE(action_src.is_action_legacy());
2267 ASSERT_TRUE(action_dst.is_action_legacy());
2268
2269 action_fcurve_move(action_dst, Slot::unassigned, action_src, *fcurve_to_move);
2270
2271 EXPECT_TRUE(action_src.is_action_legacy());
2272 EXPECT_TRUE(action_dst.is_action_legacy());
2273
2274 EXPECT_EQ(-1, BLI_findindex(&action_src.curves, fcurve_to_move))
2275 << "F-Curve should no longer exist in source Action";
2276 EXPECT_EQ(1, BLI_findindex(&action_dst.curves, fcurve_to_move))
2277 << "F-Curve should exist in destination Action";
2278
2279 EXPECT_EQ(1, BLI_listbase_count(&action_src.curves))
2280 << "Source Action should still have the other F-Curve";
2281 EXPECT_EQ(2, BLI_listbase_count(&action_dst.curves))
2282 << "Destination Action should have its original and the moved F-Curve";
2283}
2284
2285TEST_F(ActionFCurveMoveTest, test_fcurve_move_layered)
2286{
2287 Action &action_src = action_add(*this->bmain, "SourceAction");
2288 Action &action_dst = action_add(*this->bmain, "DestinationAction");
2289
2290 /* Add F-Curves to source Action. */
2291 Slot &slot_src = action_src.slot_add();
2292 action_src.layer_keystrip_ensure();
2293 StripKeyframeData &strip_data_src = action_src.layer(0)->strip(0)->data<StripKeyframeData>(
2294 action_src);
2295 Channelbag &cbag_src = strip_data_src.channelbag_for_slot_ensure(slot_src);
2296
2297 cbag_src.fcurve_ensure(this->bmain, {"source_prop", 0});
2298 FCurve &fcurve_to_move = cbag_src.fcurve_ensure(this->bmain, {"source_prop", 2});
2299 bActionGroup &group_src = cbag_src.channel_group_create("Gröpje");
2300 cbag_src.fcurve_assign_to_channel_group(fcurve_to_move, group_src);
2301
2302 /* Add F-Curves to destination Action. */
2303 Slot &slot_dst = action_dst.slot_add();
2304 action_dst.layer_keystrip_ensure();
2305 StripKeyframeData &strip_data_dst = action_dst.layer(0)->strip(0)->data<StripKeyframeData>(
2306 action_dst);
2307 Channelbag &cbag_dst = strip_data_dst.channelbag_for_slot_ensure(slot_dst);
2308
2309 cbag_dst.fcurve_ensure(this->bmain, {"dest_prop", 0});
2310
2311 ASSERT_TRUE(action_src.is_action_layered());
2312 ASSERT_TRUE(action_dst.is_action_layered());
2313
2314 action_fcurve_move(action_dst, slot_dst.handle, action_src, fcurve_to_move);
2315
2316 EXPECT_TRUE(action_src.is_action_layered());
2317 EXPECT_TRUE(action_dst.is_action_layered());
2318
2319 EXPECT_EQ(nullptr, cbag_src.fcurve_find({fcurve_to_move.rna_path, fcurve_to_move.array_index}))
2320 << "F-Curve should no longer exist in source Action";
2321 EXPECT_EQ(&fcurve_to_move,
2322 cbag_dst.fcurve_find({fcurve_to_move.rna_path, fcurve_to_move.array_index}))
2323 << "F-Curve should exist in destination Action";
2324
2325 EXPECT_EQ(1, cbag_src.fcurves().size()) << "Source Action should still have the other F-Curve";
2326 EXPECT_EQ(2, cbag_dst.fcurves().size())
2327 << "Destination Action should have its original and the moved F-Curve";
2328
2329 bActionGroup *group_dst = cbag_dst.channel_group_find("Gröpje");
2330 ASSERT_NE(nullptr, group_dst) << "Expected channel group to be created";
2331 ASSERT_EQ(group_dst, fcurve_to_move.grp) << "Expected group membership to move as well";
2332}
2333
2334} // namespace blender::animrig::tests
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:96
FCurve * BKE_fcurve_create()
FModifier * add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
float evaluate_fcurve(const FCurve *fcu, float evaltime)
void BKE_idtype_init()
Definition idtype.cc:122
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1495
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1500
Main * BKE_main_new()
Definition main.cc:48
void BKE_main_free(Main *bmain)
Definition main.cc:175
General operations, lookup, etc. for blender objects.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
void * BLI_poptail(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:261
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
#define STRNCPY_UTF8(dst, src)
#define ARRAY_SIZE(arr)
void CLG_exit(void)
Definition clog.c:704
void CLG_init(void)
Definition clog.c:697
@ ID_CA
@ ID_ME
@ ID_OB
#define DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE
@ INSERTKEY_NOFLAGS
@ FMODIFIER_TYPE_NOISE
@ HD_AUTO
@ BEZT_IPO_BEZ
@ BEZT_KEYTYPE_KEYFRAME
Object is a sort of wrapper for general info.
@ OB_EMPTY
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool contains(const T &value) const
constexpr const char * c_str() const
int64_t size() const
Slot & slot_add_for_id(const ID &animated_id)
const Layer * layer(int64_t index) const
const Slot * slot(int64_t index) const
float2 get_frame_range_of_keys(bool include_modifiers) const ATTR_WARN_UNUSED_RESULT
void slot_identifier_define(Slot &slot, StringRefNull new_identifier)
Layer & layer_add(std::optional< StringRefNull > name)
const bActionGroup * channel_group(int64_t index) const
FCurve & fcurve_ensure(Main *bmain, const FCurveDescriptor &fcurve_descriptor)
FCurve * fcurve_create_unique(Main *bmain, const FCurveDescriptor &fcurve_descriptor)
blender::Span< const FCurve * > fcurves() const
blender::Span< const bActionGroup * > channel_groups() const
bool strip_remove(Action &owning_action, Strip &strip)
blender::Span< const Strip * > strips() const
const Strip * strip(int64_t index) const
Strip & strip_add(Action &owning_action, Strip::Type strip_type)
std::string idtype_string() const
Span< ID * > users(Main &bmain) const
static constexpr slot_handle_t unassigned
const Channelbag * channelbag_for_slot(const Slot &slot) const
SingleKeyingResult keyframe_insert(Main *bmain, const Slot &slot, const FCurveDescriptor &fcurve_descriptor, float2 time_value, const KeyframeSettings &settings, eInsertKeyFlags insert_key_flags=INSERTKEY_NOFLAGS, std::optional< float2 > cycle_range=std::nullopt)
Channelbag & channelbag_for_slot_ensure(const Slot &slot)
blender::Span< const Channelbag * > channelbags() const
const Channelbag * channelbag(int64_t index) const
bool is_last_frame(float frame_time) const
void resize(float frame_start, float frame_end)
const T & data(const Action &owning_action) const
bool contains_frame(float frame_time) const
static FCurve * fcurve_create(const StringRefNull rna_path, const int array_index)
#define this
#define GS(a)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static void add_fcurve_to_action(Action &action, FCurve &fcu)
static void allocate_keyframes(FCurve &fcu, const size_t num_keyframes)
TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
TEST(action, low_level_initialisation)
static void add_keyframe(FCurve &fcu, float x, float y)
KeyframeSettings get_keyframe_settings(bool from_userprefs)
Slot * assign_action_ensure_slot_for_keying(Action &action, ID &animated_id)
void action_fcurve_move(Action &action_dst, slot_handle_t action_slot_dst, Action &action_src, FCurve &fcurve)
Action & action_add(Main &bmain, StringRefNull name)
Slot & duplicate_slot(Action &action, const Slot &slot)
FCurve * action_fcurve_ensure_legacy(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, const FCurveDescriptor &fcurve_descriptor)
ActionSlotAssignmentResult assign_action_and_slot(Action *action, Slot *slot_to_assign, ID &animated_id)
decltype(::ActionSlot::handle) slot_handle_t
bool is_action_assignable_to(const bAction *dna_action, ID_Type id_code)
SingleKeyingResult insert_vert_fcurve(FCurve *fcu, const float2 position, const KeyframeSettings &settings, eInsertKeyFlags flag)
Main Key-framing API call.
ID * action_slot_get_id_for_keying(Main &bmain, Action &action, slot_handle_t slot_handle, ID *primary_id)
FCurve * action_fcurve_ensure_ex(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, const FCurveDescriptor &fcurve_descriptor)
Action * convert_to_layered_action(Main &bmain, const Action &legacy_action)
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
ActionSlotAssignmentResult assign_action_slot(Slot *slot_to_assign, ID &animated_id)
void move_slot(Main &bmain, Slot &slot, Action &from_action, Action &to_action)
Slot * generic_slot_for_autoassign(const ID &animated_id, Action &action, StringRefNull last_slot_identifier)
VecBase< float, 2 > float2
PointerRNA RNA_id_pointer_create(ID *id)
struct FCurve ** fcurve_array
char identifier[66]
ActionSlotRuntimeHandle * runtime
bAction * action
int32_t slot_handle
char last_slot_identifier[66]
float vec[3][3]
bActionGroup * grp
char * rna_path
BezTriple * bezt
unsigned int totvert
ListBase modifiers
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * first
ListBase curves
int32_t last_slot_handle
ListBase groups