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 EXPECT_EQ(action.id.us, 0);
865 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
866 EXPECT_EQ(action.id.us, 1);
867 ASSERT_NE(nullptr, chosen_slot);
868 EXPECT_EQ(ID_OB, chosen_slot->idtype);
869 EXPECT_STREQ("OBKüüübus", chosen_slot->identifier);
870 }
871
872 { /* Single slot with same name as ID, Action not yet assigned.
873 * Should assign the Action and the slot. */
874 Action &action = action_add(*this->bmain, "ACAction");
875 const Slot &slot_for_id = action.slot_add_for_id(cube->id);
876 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
877 ASSERT_NE(nullptr, chosen_slot);
878 EXPECT_EQ(&slot_for_id, chosen_slot) << "The expected slot should be chosen";
879 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
880 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
881 }
882
883 { /* Single slot with same name as ID, Action already assigned but not the slot.
884 * Should create new slot. */
885 Action &action = action_add(*this->bmain, "ACAction");
886 const Slot &slot_for_id = action.slot_add_for_id(cube->id);
887 ASSERT_EQ(ActionSlotAssignmentResult::OK, assign_action_and_slot(&action, nullptr, cube->id));
888
889 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
890 ASSERT_NE(nullptr, chosen_slot);
891 EXPECT_NE(&slot_for_id, chosen_slot) << "A new slot should be chosen";
892 EXPECT_STREQ("OBKüüübus.001", chosen_slot->identifier);
893 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
894 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
895 }
896
897 { /* Single untyped slot, Action already assigned but not the slot. Should assign the untyped
898 * slot. */
899 Action &action = action_add(*this->bmain, "ACAction");
900
901 /* Assign the Action before adding the untyped slot, otherwise the slot gets assigned & thus
902 * typed. */
903 ASSERT_EQ(ActionSlotAssignmentResult::OK, assign_action_and_slot(&action, nullptr, cube->id));
904
905 Slot &untyped_slot = action.slot_add();
906 action.slot_identifier_define(untyped_slot, "XXJust A Slot");
907
908 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
909
910 ASSERT_NE(nullptr, chosen_slot);
911 EXPECT_EQ(&untyped_slot, chosen_slot) << "The untyped slot should be chosen";
912 EXPECT_TRUE(untyped_slot.has_idtype()) << "Slot should have gotten an ID type";
913 EXPECT_STREQ("OBJust A Slot", untyped_slot.identifier);
914 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
915 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
916 }
917}
918
920{
921 constexpr float inf = std::numeric_limits<float>::infinity();
922 Layer &layer0 = action->layer_add("Test Læür nul");
923 Strip &strip = layer0.strip_add(*action, Strip::Type::Keyframe);
924
925 strip.resize(-inf, inf);
926 EXPECT_TRUE(strip.contains_frame(0.0f));
927 EXPECT_TRUE(strip.contains_frame(-100000.0f));
928 EXPECT_TRUE(strip.contains_frame(100000.0f));
929 EXPECT_TRUE(strip.is_last_frame(inf));
930
931 strip.resize(1.0f, 2.0f);
932 EXPECT_FALSE(strip.contains_frame(0.0f))
933 << "Strip should not contain frames before its first frame";
934 EXPECT_TRUE(strip.contains_frame(1.0f)) << "Strip should contain its first frame.";
935 EXPECT_TRUE(strip.contains_frame(2.0f)) << "Strip should contain its last frame.";
936 EXPECT_FALSE(strip.contains_frame(2.0001f))
937 << "Strip should not contain frames after its last frame";
938
939 EXPECT_FALSE(strip.is_last_frame(1.0f));
940 EXPECT_FALSE(strip.is_last_frame(1.5f));
941 EXPECT_FALSE(strip.is_last_frame(1.9999f));
942 EXPECT_TRUE(strip.is_last_frame(2.0f));
943 EXPECT_FALSE(strip.is_last_frame(2.0001f));
944
945 /* Same test as above, but with much larger end frame number. This is 2 hours at 24 FPS. */
946 strip.resize(1.0f, 172800.0f);
947 EXPECT_TRUE(strip.contains_frame(172800.0f)) << "Strip should contain its last frame.";
948 EXPECT_FALSE(strip.contains_frame(172800.1f))
949 << "Strip should not contain frames after its last frame";
950
951 /* You can't get much closer to the end frame before it's considered equal. */
952 EXPECT_FALSE(strip.is_last_frame(172799.925f));
953 EXPECT_TRUE(strip.is_last_frame(172800.0f));
954 EXPECT_FALSE(strip.is_last_frame(172800.075f));
955}
956
957TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
958{
959 Slot &slot = action->slot_add();
960 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
961 Layer &layer = action->layer_add("Kübus layer");
962
963 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
964 StripKeyframeData &strip_data = strip.data<StripKeyframeData>(*action);
965
966 const KeyframeSettings settings = get_keyframe_settings(false);
967 SingleKeyingResult result_loc_a = strip_data.keyframe_insert(
968 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
969 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_loc_a)
970 << "Expected keyframe insertion to be successful";
971
972 /* Check the strip was created correctly, with the channels for the slot. */
973 ASSERT_EQ(1, strip_data.channelbags().size());
974 Channelbag *channels = strip_data.channelbag(0);
975 EXPECT_EQ(slot.handle, channels->slot_handle);
976
977 /* Insert a second key, should insert into the same FCurve as before. */
978 SingleKeyingResult result_loc_b = strip_data.keyframe_insert(
979 bmain, slot, {"location", 0}, {5.0f, 47.1f}, settings);
981 ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (slot/rna "
982 "path/array index) tuple to go into the same FCurve";
983 EXPECT_EQ(2, channels->fcurves()[0]->totvert)
984 << "Expect insertion with the same (slot/rna path/array index) tuple to go into the same "
985 "FCurve";
986
987 EXPECT_EQ(47.0f, evaluate_fcurve(channels->fcurves()[0], 1.0f));
988 EXPECT_EQ(47.1f, evaluate_fcurve(channels->fcurves()[0], 5.0f));
989
990 /* Insert another key for another property, should create another FCurve. */
991 SingleKeyingResult result_rot = strip_data.keyframe_insert(
992 bmain, slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
994 ASSERT_EQ(2, channels->fcurves().size()) << "Expected a second FCurve to be created.";
995 EXPECT_EQ(2, channels->fcurves()[0]->totvert);
996 EXPECT_EQ(1, channels->fcurves()[1]->totvert);
997}
998
1000{
1001 EXPECT_TRUE(is_action_assignable_to(nullptr, ID_OB))
1002 << "nullptr Actions should be assignable to any type.";
1003 EXPECT_TRUE(is_action_assignable_to(nullptr, ID_CA))
1004 << "nullptr Actions should be assignable to any type.";
1005
1006 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1007 << "Empty Actions should be assignable to any type.";
1008 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1009 << "Empty Actions should be assignable to any type.";
1010
1011 /* Make the Action a legacy one. */
1012 FCurve fake_fcurve;
1013 BLI_addtail(&action->curves, &fake_fcurve);
1014 ASSERT_FALSE(action->is_empty());
1015 ASSERT_TRUE(action->is_action_legacy());
1016 ASSERT_EQ(0, action->idroot);
1017
1018 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1019 << "Legacy Actions with idroot=0 should be assignable to any type.";
1020 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1021 << "Legacy Actions with idroot=0 should be assignable to any type.";
1022
1023 /* Set the legacy idroot. */
1024 action->idroot = ID_CA;
1025 EXPECT_FALSE(is_action_assignable_to(action, ID_OB))
1026 << "Legacy Actions with idroot=ID_CA should NOT be assignable to ID_OB.";
1027 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1028 << "Legacy Actions with idroot=CA should be assignable to ID_CA.";
1029
1030 /* Make the Action a layered one. */
1031 BLI_poptail(&action->curves);
1032 action->layer_add("layer");
1033 ASSERT_EQ(0, action->idroot) << "Adding a layer should clear the idroot.";
1034
1035 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1036 << "Layered Actions should be assignable to any type.";
1037 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1038 << "Layered Actions should be assignable to any type.";
1039}
1040
1041TEST_F(ActionLayersTest, action_slot_get_id_for_keying__empty_action)
1042{
1043 EXPECT_TRUE(assign_action(action, cube->id));
1044
1045 /* Double-check that the action is considered empty for the test. */
1046 EXPECT_TRUE(action->is_empty());
1047
1048 /* None should return an ID, since there are no slots yet which could have this ID assigned.
1049 * Assignment of the Action itself (cube) shouldn't matter. */
1050 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &cube->id));
1051 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, nullptr));
1052 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &suzanne->id));
1053}
1054
1055TEST_F(ActionLayersTest, action_slot_get_id_for_keying__legacy_action)
1056{
1057 FCurve *fcurve = action_fcurve_ensure_legacy(bmain, action, nullptr, nullptr, {"location", 0});
1058 EXPECT_FALSE(fcurve == nullptr);
1059
1060 EXPECT_TRUE(assign_action(action, cube->id));
1061
1062 /* Double-check that the action is considered legacy for the test. */
1063 EXPECT_TRUE(action->is_action_legacy());
1064
1065 /* A `primary_id` that uses the action should get returned. Every other case
1066 * should return nullptr. */
1067 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, 0, &cube->id));
1068 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, nullptr));
1069 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &suzanne->id));
1070}
1071
1072TEST_F(ActionLayersTest, action_slot_get_id_for_keying__layered_action)
1073{
1074 Slot &slot = action->slot_add();
1075
1076 /* Double-check that the action is considered layered for the test. */
1077 EXPECT_TRUE(action->is_action_layered());
1078
1079 /* A slot with no users should never return a user. */
1080 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1081 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1082
1083 /* A slot with precisely one user should always return that user. */
1084 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
1085 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1086 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1087 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &suzanne->id));
1088
1089 /* A slot with more than one user should return the passed `primary_id` if it
1090 * is among its users, and nullptr otherwise. */
1091 ASSERT_EQ(assign_action_and_slot(action, &slot, suzanne->id), ActionSlotAssignmentResult::OK);
1092 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1093 EXPECT_EQ(&suzanne->id,
1094 action_slot_get_id_for_keying(*bmain, *action, slot.handle, &suzanne->id));
1095 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1096 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &bob->id));
1097}
1098
1099TEST_F(ActionLayersTest, conversion_to_layered)
1100{
1101 EXPECT_TRUE(action->is_empty());
1102 FCurve *legacy_fcu_0 = action_fcurve_ensure_legacy(
1103 bmain, action, "Test", nullptr, {"location", 0});
1104 FCurve *legacy_fcu_1 = action_fcurve_ensure_legacy(
1105 bmain, action, "Test", nullptr, {"location", 1});
1106
1107 KeyframeSettings settings;
1108 settings.handle = HD_AUTO;
1109 settings.interpolation = BEZT_IPO_BEZ;
1110 settings.keyframe_type = BEZT_KEYTYPE_KEYFRAME;
1111 insert_vert_fcurve(legacy_fcu_0, {0, 0}, settings, INSERTKEY_NOFLAGS);
1112 insert_vert_fcurve(legacy_fcu_0, {1, 1}, settings, INSERTKEY_NOFLAGS);
1113 add_fmodifier(&legacy_fcu_1->modifiers, FMODIFIER_TYPE_NOISE, legacy_fcu_1);
1114
1115 Action *converted = convert_to_layered_action(*bmain, *action);
1116 ASSERT_TRUE(converted != action);
1117 EXPECT_STREQ(converted->id.name, "ACACÄnimåtië_layered");
1118 Strip *strip = converted->layer(0)->strip(0);
1119 StripKeyframeData &strip_data = strip->data<StripKeyframeData>(*converted);
1120 Channelbag *bag = strip_data.channelbag(0);
1121 ASSERT_EQ(bag->fcurve_array_num, 2);
1122 ASSERT_EQ(bag->fcurve_array[0]->totvert, 2);
1123
1124 ASSERT_EQ(BLI_listbase_count(&action->groups), 1);
1125 ASSERT_EQ(BLI_listbase_count(&converted->groups), 0);
1126
1127 ASSERT_EQ(bag->channel_groups().size(), 1);
1128 bActionGroup *group = bag->channel_group(0);
1129 ASSERT_EQ(group->fcurve_range_length, 2);
1130 ASSERT_STREQ(group->name, "Test");
1131
1132 ASSERT_TRUE(bag->fcurve_array[0]->modifiers.first == nullptr);
1133 ASSERT_TRUE(bag->fcurve_array[1]->modifiers.first != nullptr);
1134
1135 Action *long_name_action = BKE_id_new<Action>(
1136 bmain, "name_for_an_action_that_is_exactly_64_chars_which_is_MAX_ID_NAME");
1137 action_fcurve_ensure_legacy(bmain, long_name_action, "Long", nullptr, {"location", 0});
1138 converted = convert_to_layered_action(*bmain, *long_name_action);
1139 /* AC gets added automatically by Blender, the long name is shortened to make space for
1140 * "_layered". */
1141 EXPECT_STREQ(converted->id.name,
1142 "ACname_for_an_action_that_is_exactly_64_chars_which_is_MA_layered");
1143}
1144
1145TEST_F(ActionLayersTest, conversion_to_layered_action_groups)
1146{
1147 EXPECT_TRUE(action->is_empty());
1148 action_fcurve_ensure_legacy(bmain, action, "Test", nullptr, {"location", 0});
1149 action_fcurve_ensure_legacy(bmain, action, "Test", nullptr, {"rotation_euler", 1});
1150 action_fcurve_ensure_legacy(bmain, action, "Test_Two", nullptr, {"scale", 1});
1151 action_fcurve_ensure_legacy(bmain, action, "Test_Three", nullptr, {"show_name", 1});
1152 action_fcurve_ensure_legacy(bmain, action, "Test_Rename", nullptr, {"show_axis", 1});
1153
1154 bActionGroup *rename_group = static_cast<bActionGroup *>(BLI_findlink(&action->groups, 3));
1155 ASSERT_NE(rename_group, nullptr);
1156 ASSERT_STREQ(rename_group->name, "Test_Rename");
1157 /* Forcing a duplicate name which was allowed by legacy actions. */
1158 STRNCPY_UTF8(rename_group->name, "Test");
1159
1160 Action *converted = convert_to_layered_action(*bmain, *action);
1161 Strip *strip = converted->layer(0)->strip(0);
1162 StripKeyframeData &strip_data = strip->data<StripKeyframeData>(*converted);
1163 Channelbag *bag = strip_data.channelbag(0);
1164
1165 ASSERT_EQ(BLI_listbase_count(&converted->groups), 0);
1166 ASSERT_EQ(bag->channel_groups().size(), 4);
1167
1168 bActionGroup *test_group = bag->channel_group(0);
1169 EXPECT_STREQ(test_group->name, "Test");
1170 EXPECT_EQ(test_group->fcurve_range_length, 2);
1171
1172 bActionGroup *test_two_group = bag->channel_group(1);
1173 EXPECT_STREQ(test_two_group->name, "Test_Two");
1174 EXPECT_EQ(test_two_group->fcurve_range_length, 1);
1175 EXPECT_STREQ(bag->fcurve_array[test_two_group->fcurve_range_start]->rna_path, "scale");
1176
1177 bActionGroup *test_three_group = bag->channel_group(2);
1178 EXPECT_STREQ(test_three_group->name, "Test_Three");
1179 EXPECT_EQ(test_three_group->fcurve_range_length, 1);
1180 EXPECT_STREQ(bag->fcurve_array[test_three_group->fcurve_range_start]->rna_path, "show_name");
1181
1182 bActionGroup *test_rename_group = bag->channel_group(3);
1183 EXPECT_STREQ(test_rename_group->name, "Test.001");
1184 EXPECT_EQ(test_rename_group->fcurve_range_length, 1);
1185 EXPECT_STREQ(bag->fcurve_array[test_rename_group->fcurve_range_start]->rna_path, "show_axis");
1186
1187 ASSERT_NE(converted, action);
1188}
1189
1190TEST_F(ActionLayersTest, empty_to_layered)
1191{
1192 ASSERT_TRUE(action->is_empty());
1193 Action *converted = convert_to_layered_action(*bmain, *action);
1194 ASSERT_TRUE(converted != action);
1195 ASSERT_TRUE(converted->is_action_layered());
1196 ASSERT_FALSE(converted->is_action_legacy());
1197}
1198
1199TEST_F(ActionLayersTest, action_move_slot)
1200{
1201 Action *action_2 = BKE_id_new<Action>(bmain, "Action 2");
1202 EXPECT_TRUE(action->is_empty());
1203
1204 Slot &slot_cube = action->slot_add();
1205 Slot &slot_suzanne = action_2->slot_add();
1206 EXPECT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1207 EXPECT_EQ(assign_action_and_slot(action_2, &slot_suzanne, suzanne->id),
1209
1210 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1211 PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
1212
1213 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1214 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1215
1216 action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"location", 0});
1217 action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"rotation_euler", 1});
1218
1219 ASSERT_EQ(action->layer_array_num, 1);
1220 ASSERT_EQ(action_2->layer_array_num, 1);
1221
1222 Layer *layer_1 = action->layer(0);
1223 Layer *layer_2 = action_2->layer(0);
1224
1225 ASSERT_EQ(layer_1->strip_array_num, 1);
1226 ASSERT_EQ(layer_2->strip_array_num, 1);
1227
1228 StripKeyframeData &strip_data_1 = layer_1->strip(0)->data<StripKeyframeData>(*action);
1229 StripKeyframeData &strip_data_2 = layer_2->strip(0)->data<StripKeyframeData>(*action_2);
1230
1231 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1232 ASSERT_EQ(strip_data_2.channelbag_array_num, 1);
1233
1234 Channelbag *bag_1 = strip_data_1.channelbag(0);
1235 Channelbag *bag_2 = strip_data_2.channelbag(0);
1236
1237 ASSERT_EQ(bag_1->fcurve_array_num, 2);
1238 ASSERT_EQ(bag_2->fcurve_array_num, 2);
1239
1240 move_slot(*bmain, slot_suzanne, *action_2, *action);
1241
1242 ASSERT_EQ(strip_data_1.channelbag_array_num, 2);
1243 ASSERT_EQ(strip_data_2.channelbag_array_num, 0);
1244
1245 ASSERT_EQ(action->slot_array_num, 2);
1246 ASSERT_EQ(action_2->slot_array_num, 0);
1247
1248 /* Action should have been reassigned. */
1249 ASSERT_EQ(action, cube->adt->action);
1250 ASSERT_EQ(action, suzanne->adt->action);
1251}
1252
1253TEST_F(ActionLayersTest, action_move_slot_without_channelbag)
1254{
1255 Action *action_2 = BKE_id_new<Action>(bmain, "Action 2");
1256 EXPECT_TRUE(action->is_empty());
1257
1258 Slot &slot_cube = action->slot_add();
1259 Slot &slot_suzanne = action_2->slot_add();
1260 EXPECT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1261 EXPECT_EQ(assign_action_and_slot(action_2, &slot_suzanne, suzanne->id),
1263
1264 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1265 PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
1266
1267 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1268 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1269
1270 /* Make sure action_2 has a keyframe strip, but without a channelbag. */
1271 action_2->layer_add("Bagless").strip_add(*action_2, Strip::Type::Keyframe);
1272
1273 ASSERT_EQ(action->layer_array_num, 1);
1274 ASSERT_EQ(action_2->layer_array_num, 1);
1275
1276 Layer *layer_1 = action->layer(0);
1277 Layer *layer_2 = action_2->layer(0);
1278
1279 ASSERT_EQ(layer_1->strip_array_num, 1);
1280 ASSERT_EQ(layer_2->strip_array_num, 1);
1281
1282 StripKeyframeData &strip_data_1 = layer_1->strip(0)->data<StripKeyframeData>(*action);
1283 StripKeyframeData &strip_data_2 = layer_2->strip(0)->data<StripKeyframeData>(*action_2);
1284
1285 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1286 ASSERT_EQ(strip_data_2.channelbag_array_num, 0)
1287 << "the keyframe strip of action_2 should NOT have a channelbag in this test";
1288
1289 Channelbag *bag_1 = strip_data_1.channelbag(0);
1290 ASSERT_EQ(bag_1->fcurve_array_num, 2);
1291
1292 move_slot(*bmain, slot_suzanne, *action_2, *action);
1293
1294 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1295 ASSERT_EQ(strip_data_2.channelbag_array_num, 0);
1296
1297 ASSERT_EQ(action->slot_array_num, 2);
1298 ASSERT_EQ(action_2->slot_array_num, 0);
1299
1300 /* Action should have been reassigned. */
1301 ASSERT_EQ(action, cube->adt->action);
1302 ASSERT_EQ(action, suzanne->adt->action);
1303}
1304
1305TEST_F(ActionLayersTest, action_duplicate_slot)
1306{
1307 ASSERT_TRUE(action->is_empty());
1308
1309 Slot &slot_cube = action->slot_add();
1310 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1311
1312 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1313
1314 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1315 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1316
1317 ASSERT_EQ(action->layer_array_num, 1);
1318 Layer *layer = action->layer(0);
1319
1320 ASSERT_EQ(layer->strip_array_num, 1);
1321 StripKeyframeData &strip_data = layer->strip(0)->data<StripKeyframeData>(*action);
1322
1323 ASSERT_EQ(strip_data.channelbag_array_num, 1);
1324 Channelbag *bag = strip_data.channelbag(0);
1325 ASSERT_EQ(bag->fcurve_array_num, 2);
1326
1327 /* Duplicate the slot and check it for uniqueness within the Action. */
1328 Slot &dupli_slot = duplicate_slot(*action, slot_cube);
1329 EXPECT_NE(dupli_slot.identifier, slot_cube.identifier);
1330 EXPECT_NE(dupli_slot.handle, slot_cube.handle);
1331 ASSERT_EQ(action->slot_array_num, 2);
1332 EXPECT_EQ(&dupli_slot, action->slot(1));
1333
1334 /* Check the channelbag has been duplicated correctly. */
1335 ASSERT_EQ(strip_data.channelbag_array_num, 2);
1336 Channelbag *dupli_bag = strip_data.channelbag(1);
1337 EXPECT_EQ(dupli_bag->slot_handle, dupli_slot.handle);
1338 EXPECT_EQ(dupli_bag->fcurve_array_num, 2);
1339
1340 /* Check the original channelbag is untouched. */
1341 EXPECT_EQ(bag->slot_handle, slot_cube.handle);
1342 EXPECT_EQ(bag->fcurve_array_num, 2);
1343
1344 /* The slot should NOT have been reassigned. */
1345 EXPECT_EQ(action, cube->adt->action);
1346 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
1347}
1348
1349TEST_F(ActionLayersTest, action_duplicate_slot_without_channelbag)
1350{
1351 ASSERT_TRUE(action->is_empty());
1352
1353 Slot &slot_cube = action->slot_add();
1354 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1355
1356 /* Create a keyframe strip, but without any channelbags. */
1357 action->layer_keystrip_ensure();
1358
1359 ASSERT_EQ(action->layer_array_num, 1);
1360 Layer *layer = action->layer(0);
1361
1362 ASSERT_EQ(layer->strip_array_num, 1);
1363 StripKeyframeData &strip_data = layer->strip(0)->data<StripKeyframeData>(*action);
1364
1365 ASSERT_EQ(strip_data.channelbag_array_num, 0);
1366
1367 /* Duplicate the slot and check it for uniqueness within the Action. */
1368 Slot &dupli_slot = duplicate_slot(*action, slot_cube);
1369 EXPECT_NE(dupli_slot.identifier, slot_cube.identifier);
1370 EXPECT_NE(dupli_slot.handle, slot_cube.handle);
1371 ASSERT_EQ(action->slot_array_num, 2);
1372 EXPECT_EQ(&dupli_slot, action->slot(1));
1373
1374 /* Check there are still no channelbags. */
1375 EXPECT_EQ(strip_data.channelbag_array_num, 0);
1376
1377 /* The slot should NOT have been reassigned. */
1378 EXPECT_EQ(action, cube->adt->action);
1379 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
1380}
1381
1382/*-----------------------------------------------------------*/
1383
1384/* Allocate fcu->bezt, and also return a unique_ptr to it for easily freeing the memory. */
1385static void allocate_keyframes(FCurve &fcu, const size_t num_keyframes)
1386{
1387 fcu.bezt = MEM_calloc_arrayN<BezTriple>(num_keyframes, __func__);
1388}
1389
1390/* Append keyframe, assumes that fcu->bezt is allocated and has enough space. */
1391static void add_keyframe(FCurve &fcu, float x, float y)
1392{
1393 /* The insert_keyframe functions are in the editors, so we cannot link to those here. */
1394 BezTriple the_keyframe = {};
1395
1396 /* Copied from insert_vert_fcurve() in `keyframing.cc`. */
1397 the_keyframe.vec[0][0] = x - 1.0f;
1398 the_keyframe.vec[0][1] = y;
1399 the_keyframe.vec[1][0] = x;
1400 the_keyframe.vec[1][1] = y;
1401 the_keyframe.vec[2][0] = x + 1.0f;
1402 the_keyframe.vec[2][1] = y;
1403
1404 memcpy(&fcu.bezt[fcu.totvert], &the_keyframe, sizeof(the_keyframe));
1405 fcu.totvert++;
1406}
1407
1408static void add_fcurve_to_action(Action &action, FCurve &fcu)
1409{
1410 Slot &slot = action.slot_array_num > 0 ? *action.slot(0) : action.slot_add();
1411 action.layer_keystrip_ensure();
1412 StripKeyframeData &strip_data = action.layer(0)->strip(0)->data<StripKeyframeData>(action);
1413 Channelbag &cbag = strip_data.channelbag_for_slot_ensure(slot);
1414 cbag.fcurve_append(fcu);
1415}
1416
1417class ActionQueryTest : public testing::Test {
1418 public:
1420
1421 static void SetUpTestSuite()
1422 {
1423 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
1424 CLG_init();
1425
1426 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
1428 }
1429
1430 static void TearDownTestSuite()
1431 {
1432 CLG_exit();
1433 }
1434
1435 void SetUp() override
1436 {
1437 bmain = BKE_main_new();
1438 }
1439
1440 void TearDown() override
1441 {
1443 }
1444
1446 {
1447 return *BKE_id_new<Action>(bmain, "ACÄnimåtië");
1448 }
1449};
1450
1451TEST_F(ActionQueryTest, BKE_action_frame_range_calc)
1452{
1453 /* No FCurves. */
1454 {
1455 const Action &empty = action_new();
1456 EXPECT_EQ((float2{0.0f, 0.0f}), empty.get_frame_range_of_keys(false));
1457 }
1458
1459 /* One curve with one key. */
1460 {
1461 FCurve &fcu = *MEM_callocN<FCurve>(__func__);
1462 allocate_keyframes(fcu, 1);
1463 add_keyframe(fcu, 1.0f, 2.0f);
1464
1465 Action &action = action_new();
1466 add_fcurve_to_action(action, fcu);
1467
1468 const float2 frame_range = action.get_frame_range_of_keys(false);
1469 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1470 EXPECT_FLOAT_EQ(frame_range[1], 1.0f);
1471 }
1472
1473 /* Two curves with one key each on different frames. */
1474 {
1475 FCurve &fcu1 = *MEM_callocN<FCurve>(__func__);
1476 FCurve &fcu2 = *MEM_callocN<FCurve>(__func__);
1477 allocate_keyframes(fcu1, 1);
1478 allocate_keyframes(fcu2, 1);
1479 add_keyframe(fcu1, 1.0f, 2.0f);
1480 add_keyframe(fcu2, 1.5f, 2.0f);
1481
1482 Action &action = action_new();
1483 add_fcurve_to_action(action, fcu1);
1484 add_fcurve_to_action(action, fcu2);
1485
1486 const float2 frame_range = action.get_frame_range_of_keys(false);
1487 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1488 EXPECT_FLOAT_EQ(frame_range[1], 1.5f);
1489 }
1490
1491 /* One curve with two keys. */
1492 {
1493 FCurve &fcu = *MEM_callocN<FCurve>(__func__);
1494 allocate_keyframes(fcu, 2);
1495 add_keyframe(fcu, 1.0f, 2.0f);
1496 add_keyframe(fcu, 1.5f, 2.0f);
1497
1498 Action &action = action_new();
1499 add_fcurve_to_action(action, fcu);
1500
1501 const float2 frame_range = action.get_frame_range_of_keys(false);
1502 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1503 EXPECT_FLOAT_EQ(frame_range[1], 1.5f);
1504 }
1505
1506 /* TODO: action with fcurve modifiers. */
1507}
1508
1509/*-----------------------------------------------------------*/
1510
1511class ChannelbagTest : public testing::Test {
1512 public:
1514
1515 static void SetUpTestSuite() {}
1516
1517 static void TearDownTestSuite() {}
1518
1519 void SetUp() override
1520 {
1521 channelbag = new Channelbag();
1522 }
1523
1524 void TearDown() override
1525 {
1526 delete channelbag;
1527 }
1528};
1529
1530TEST_F(ChannelbagTest, fcurve_create_many)
1531{
1532 FCurve &existing1 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1533 FCurve &existing2 = channelbag->fcurve_ensure(nullptr, {"fcu0", 1, {}, {}, "group0"});
1534 FCurve &existing3 = channelbag->fcurve_ensure(nullptr, {"fcu1", 1, {}, {}, "group1"});
1535 FCurve &existing4 = channelbag->fcurve_ensure(nullptr, {"fcu_", 0});
1536 ASSERT_EQ(2, channelbag->channel_groups().size());
1537 ASSERT_EQ(4, channelbag->fcurves().size());
1538
1539 FCurveDescriptor desc[] = {
1540 /* New group. */
1541 {"fcu2", 0, {}, {}, "group2"},
1542 {"fcu2", 1, {}, {}, "group2"},
1543 {"fcu2", 2, {}, {}, "group2"},
1544 /* Existing groups. */
1545 {"fcu3", 0, {}, {}, "group1"},
1546 {"fcu4", 0, {}, {}, "group0"},
1547 {"fcu5", 0, {}, {}, "group1"},
1548 {"fcu6", 0, {}, {}, "group0"},
1549 {"fcu7", 0, {}, {}, "group2"},
1550 /* No group. */
1551 {"fcu8", 0},
1552 {"fcu8", 1},
1553 /* Empty rna path, should return null. */
1554 {"", 0, {}, {}, "irrelevant"},
1555 /* Should return null since such curves already exist. */
1556 {"fcu0", 1, {}, {}, "irrelevant"},
1557 {"fcu5", 0, {}, {}, "also unused"},
1558 {"fcu2", 0, {}, {}, "group2"},
1559 {"fcu6", 0},
1560 };
1561 Vector<FCurve *> fcurves = channelbag->fcurve_create_many(nullptr, {desc, ARRAY_SIZE(desc)});
1562 ASSERT_EQ(15, fcurves.size());
1563
1564 EXPECT_STREQ("group2", fcurves[0]->grp->name);
1565 EXPECT_STREQ("group2", fcurves[1]->grp->name);
1566 EXPECT_STREQ("group2", fcurves[2]->grp->name);
1567 EXPECT_STREQ("group1", fcurves[3]->grp->name);
1568 EXPECT_STREQ("group0", fcurves[4]->grp->name);
1569 EXPECT_STREQ("group1", fcurves[5]->grp->name);
1570 EXPECT_STREQ("group0", fcurves[6]->grp->name);
1571 EXPECT_STREQ("group2", fcurves[7]->grp->name);
1572 EXPECT_EQ(nullptr, fcurves[8]->grp);
1573 EXPECT_EQ(nullptr, fcurves[9]->grp);
1574 EXPECT_EQ(nullptr, fcurves[10]);
1575 EXPECT_EQ(nullptr, fcurves[11]);
1576 EXPECT_EQ(nullptr, fcurves[12]);
1577 EXPECT_EQ(nullptr, fcurves[13]);
1578 EXPECT_EQ(nullptr, fcurves[14]);
1579
1580 EXPECT_EQ(3, channelbag->channel_groups().size());
1581 EXPECT_EQ(14, channelbag->fcurves().size());
1582
1583 EXPECT_STREQ("group0", existing1.grp->name);
1584 EXPECT_STREQ("group0", existing2.grp->name);
1585 EXPECT_STREQ("group1", existing3.grp->name);
1586 EXPECT_EQ(nullptr, existing4.grp);
1587}
1588
1589TEST_F(ChannelbagTest, fcurve_move_to_index)
1590{
1591 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1592 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1593 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1594 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
1595 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1596
1597 ASSERT_EQ(5, channelbag->fcurves().size());
1598 ASSERT_EQ(2, channelbag->channel_groups().size());
1599
1600 bActionGroup &group0 = *channelbag->channel_group(0);
1601 bActionGroup &group1 = *channelbag->channel_group(1);
1602
1603 /* Moving an fcurve to where it already is should be fine. */
1604 channelbag->fcurve_move_to_index(fcu0, 0);
1605 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1606 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1607 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1608 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1609 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1610 EXPECT_EQ(&group0, fcu0.grp);
1611 EXPECT_EQ(&group0, fcu1.grp);
1612 EXPECT_EQ(&group1, fcu2.grp);
1613 EXPECT_EQ(&group1, fcu3.grp);
1614 EXPECT_EQ(nullptr, fcu4.grp);
1615
1616 /* Move to first. */
1617 channelbag->fcurve_move_to_index(fcu4, 0);
1618 EXPECT_EQ(0, group0.fcurve_range_start);
1619 EXPECT_EQ(2, group0.fcurve_range_length);
1620 EXPECT_EQ(2, group1.fcurve_range_start);
1621 EXPECT_EQ(2, group1.fcurve_range_length);
1622 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
1623 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1624 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
1625 EXPECT_EQ(&fcu2, channelbag->fcurve(3));
1626 EXPECT_EQ(&fcu3, channelbag->fcurve(4));
1627 EXPECT_EQ(&group0, fcu4.grp);
1628 EXPECT_EQ(&group0, fcu0.grp);
1629 EXPECT_EQ(&group1, fcu1.grp);
1630 EXPECT_EQ(&group1, fcu2.grp);
1631 EXPECT_EQ(nullptr, fcu3.grp);
1632
1633 /* Move to last. */
1634 channelbag->fcurve_move_to_index(fcu1, 4);
1635 EXPECT_EQ(0, group0.fcurve_range_start);
1636 EXPECT_EQ(2, group0.fcurve_range_length);
1637 EXPECT_EQ(2, group1.fcurve_range_start);
1638 EXPECT_EQ(2, group1.fcurve_range_length);
1639 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
1640 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1641 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1642 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1643 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
1644 EXPECT_EQ(&group0, fcu4.grp);
1645 EXPECT_EQ(&group0, fcu0.grp);
1646 EXPECT_EQ(&group1, fcu2.grp);
1647 EXPECT_EQ(&group1, fcu3.grp);
1648 EXPECT_EQ(nullptr, fcu1.grp);
1649
1650 /* Move to middle. */
1651 channelbag->fcurve_move_to_index(fcu4, 2);
1652 EXPECT_EQ(0, group0.fcurve_range_start);
1653 EXPECT_EQ(2, group0.fcurve_range_length);
1654 EXPECT_EQ(2, group1.fcurve_range_start);
1655 EXPECT_EQ(2, group1.fcurve_range_length);
1656 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1657 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
1658 EXPECT_EQ(&fcu4, channelbag->fcurve(2));
1659 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1660 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
1661 EXPECT_EQ(&group0, fcu0.grp);
1662 EXPECT_EQ(&group0, fcu2.grp);
1663 EXPECT_EQ(&group1, fcu4.grp);
1664 EXPECT_EQ(&group1, fcu3.grp);
1665 EXPECT_EQ(nullptr, fcu1.grp);
1666}
1667
1668TEST_F(ChannelbagTest, channel_group_create)
1669{
1670 ASSERT_TRUE(channelbag->channel_groups().is_empty());
1671
1672 bActionGroup &group0 = channelbag->channel_group_create("Foo");
1673 ASSERT_EQ(channelbag->channel_groups().size(), 1);
1674 EXPECT_EQ(StringRef{group0.name}, StringRef{"Foo"});
1675 EXPECT_EQ(group0.fcurve_range_start, 0);
1676 EXPECT_EQ(group0.fcurve_range_length, 0);
1677 EXPECT_EQ(&group0, channelbag->channel_group(0));
1678
1679 /* Set for testing purposes. Does not reflect actual fcurves in this test. */
1680 group0.fcurve_range_length = 2;
1681
1682 bActionGroup &group1 = channelbag->channel_group_create("Bar");
1683 ASSERT_EQ(channelbag->channel_groups().size(), 2);
1684 EXPECT_EQ(StringRef{group1.name}, StringRef{"Bar"});
1685 EXPECT_EQ(group1.fcurve_range_start, 2);
1686 EXPECT_EQ(group1.fcurve_range_length, 0);
1687 EXPECT_EQ(&group0, channelbag->channel_group(0));
1688 EXPECT_EQ(&group1, channelbag->channel_group(1));
1689
1690 /* Set for testing purposes. Does not reflect actual fcurves in this test. */
1691 group1.fcurve_range_length = 1;
1692
1693 bActionGroup &group2 = channelbag->channel_group_create("Yar");
1694 ASSERT_EQ(channelbag->channel_groups().size(), 3);
1695 EXPECT_EQ(StringRef{group2.name}, StringRef{"Yar"});
1696 EXPECT_EQ(group2.fcurve_range_start, 3);
1697 EXPECT_EQ(group2.fcurve_range_length, 0);
1698 EXPECT_EQ(&group0, channelbag->channel_group(0));
1699 EXPECT_EQ(&group1, channelbag->channel_group(1));
1700 EXPECT_EQ(&group2, channelbag->channel_group(2));
1701}
1702
1703TEST_F(ChannelbagTest, channel_group_remove)
1704{
1705 bActionGroup &group0 = channelbag->channel_group_create("Group0");
1706 bActionGroup &group1 = channelbag->channel_group_create("Group1");
1707 bActionGroup &group2 = channelbag->channel_group_create("Group2");
1708
1709 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "Group0"});
1710 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "Group0"});
1711 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "Group2"});
1712 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "Group2"});
1713 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1714
1715 ASSERT_EQ(3, channelbag->channel_groups().size());
1716 ASSERT_EQ(5, channelbag->fcurves().size());
1717
1718 /* Attempt to remove a group that's not in the channel bag. Shouldn't do
1719 * anything. */
1720 bActionGroup bogus;
1721 EXPECT_EQ(false, channelbag->channel_group_remove(bogus));
1722 ASSERT_EQ(3, channelbag->channel_groups().size());
1723 ASSERT_EQ(5, channelbag->fcurves().size());
1724 EXPECT_EQ(&group0, channelbag->channel_group(0));
1725 EXPECT_EQ(&group1, channelbag->channel_group(1));
1726 EXPECT_EQ(&group2, channelbag->channel_group(2));
1727 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1728 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1729 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1730 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1731 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1732 EXPECT_EQ(&group0, fcu0.grp);
1733 EXPECT_EQ(&group0, fcu1.grp);
1734 EXPECT_EQ(&group2, fcu2.grp);
1735 EXPECT_EQ(&group2, fcu3.grp);
1736 EXPECT_EQ(nullptr, fcu4.grp);
1737
1738 /* Removing an empty group shouldn't affect the fcurves at all. */
1739 EXPECT_EQ(true, channelbag->channel_group_remove(group1));
1740 ASSERT_EQ(2, channelbag->channel_groups().size());
1741 ASSERT_EQ(5, channelbag->fcurves().size());
1742 EXPECT_EQ(&group0, channelbag->channel_group(0));
1743 EXPECT_EQ(&group2, channelbag->channel_group(1));
1744 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1745 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1746 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1747 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1748 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1749 EXPECT_EQ(&group0, fcu0.grp);
1750 EXPECT_EQ(&group0, fcu1.grp);
1751 EXPECT_EQ(&group2, fcu2.grp);
1752 EXPECT_EQ(&group2, fcu3.grp);
1753 EXPECT_EQ(nullptr, fcu4.grp);
1754
1755 /* Removing a group that's not at the end of the group array should move its
1756 * fcurves to be just after the grouped fcurves. */
1757 EXPECT_EQ(true, channelbag->channel_group_remove(group0));
1758 ASSERT_EQ(1, channelbag->channel_groups().size());
1759 ASSERT_EQ(5, channelbag->fcurves().size());
1760 EXPECT_EQ(&group2, channelbag->channel_group(0));
1761 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
1762 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1763 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1764 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
1765 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1766 EXPECT_EQ(nullptr, fcu0.grp);
1767 EXPECT_EQ(nullptr, fcu1.grp);
1768 EXPECT_EQ(&group2, fcu2.grp);
1769 EXPECT_EQ(&group2, fcu3.grp);
1770 EXPECT_EQ(nullptr, fcu4.grp);
1771
1772 /* Removing a group at the end of the group array shouldn't move its
1773 * fcurves. */
1774 EXPECT_EQ(true, channelbag->channel_group_remove(group2));
1775 ASSERT_EQ(0, channelbag->channel_groups().size());
1776 ASSERT_EQ(5, channelbag->fcurves().size());
1777 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
1778 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1779 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1780 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
1781 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1782 EXPECT_EQ(nullptr, fcu0.grp);
1783 EXPECT_EQ(nullptr, fcu1.grp);
1784 EXPECT_EQ(nullptr, fcu2.grp);
1785 EXPECT_EQ(nullptr, fcu3.grp);
1786 EXPECT_EQ(nullptr, fcu4.grp);
1787}
1788
1789TEST_F(ChannelbagTest, channel_group_find)
1790{
1791 bActionGroup &group0a = channelbag->channel_group_create("Foo");
1792 bActionGroup &group1a = channelbag->channel_group_create("Bar");
1793 bActionGroup &group2a = channelbag->channel_group_create("Yar");
1794
1795 bActionGroup *group0b = channelbag->channel_group_find("Foo");
1796 bActionGroup *group1b = channelbag->channel_group_find("Bar");
1797 bActionGroup *group2b = channelbag->channel_group_find("Yar");
1798
1799 EXPECT_EQ(&group0a, group0b);
1800 EXPECT_EQ(&group1a, group1b);
1801 EXPECT_EQ(&group2a, group2b);
1802
1803 EXPECT_EQ(nullptr, channelbag->channel_group_find("Wat"));
1804}
1805
1806TEST_F(ChannelbagTest, channel_group_ensure)
1807{
1808 bActionGroup &group0 = channelbag->channel_group_create("Foo");
1809 bActionGroup &group1 = channelbag->channel_group_create("Bar");
1810 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1811
1812 EXPECT_EQ(&group0, &channelbag->channel_group_ensure("Foo"));
1813 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1814
1815 EXPECT_EQ(&group1, &channelbag->channel_group_ensure("Bar"));
1816 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1817
1818 bActionGroup &group2 = channelbag->channel_group_ensure("Yar");
1819 ASSERT_EQ(channelbag->channel_groups().size(), 3);
1820 EXPECT_EQ(&group2, channelbag->channel_group(2));
1821}
1822
1823TEST_F(ChannelbagTest, channel_group_fcurve_creation)
1824{
1825 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0});
1826 EXPECT_EQ(1, channelbag->fcurves().size());
1827 EXPECT_TRUE(channelbag->channel_groups().is_empty());
1828
1829 /* If an fcurve already exists, then ensuring it with a channel group in the
1830 * fcurve descriptor should NOT add it that group, nor should the group be
1831 * created if it doesn't already exist. */
1832 channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1833 EXPECT_EQ(1, channelbag->fcurves().size());
1834 EXPECT_EQ(nullptr, fcu0.grp);
1835 EXPECT_TRUE(channelbag->channel_groups().is_empty());
1836
1837 /* Creating a new fcurve with a channel group in the fcurve descriptor should
1838 * create the group and put the fcurve in it. This also implies that the
1839 * fcurve will be added before any non-grouped fcurves in the array. */
1840 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1841 ASSERT_EQ(2, channelbag->fcurves().size());
1842 ASSERT_EQ(1, channelbag->channel_groups().size());
1843 bActionGroup &group0 = *channelbag->channel_group(0);
1844 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1845 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1846 EXPECT_EQ(&group0, fcu1.grp);
1847 EXPECT_EQ(nullptr, fcu0.grp);
1848 EXPECT_EQ(0, group0.fcurve_range_start);
1849 EXPECT_EQ(1, group0.fcurve_range_length);
1850
1851 /* Creating a new fcurve with a second channel group in the fcurve descriptor
1852 * should create the group and put the fcurve in it. This also implies that
1853 * the fcurve will be added before non-grouped fcurves, but after other
1854 * grouped ones. */
1855 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1856 ASSERT_EQ(3, channelbag->fcurves().size());
1857 ASSERT_EQ(2, channelbag->channel_groups().size());
1858 EXPECT_EQ(&group0, channelbag->channel_group(0));
1859 bActionGroup &group1 = *channelbag->channel_group(1);
1860 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1861 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
1862 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1863 EXPECT_EQ(&group0, fcu1.grp);
1864 EXPECT_EQ(&group1, fcu2.grp);
1865 EXPECT_EQ(nullptr, fcu0.grp);
1866 EXPECT_EQ(0, group0.fcurve_range_start);
1867 EXPECT_EQ(1, group0.fcurve_range_length);
1868 EXPECT_EQ(1, group1.fcurve_range_start);
1869 EXPECT_EQ(1, group1.fcurve_range_length);
1870
1871 /* Creating a new fcurve with the first channel group again should put it at
1872 * the end of that group. */
1873 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group0"});
1874 ASSERT_EQ(4, channelbag->fcurves().size());
1875 ASSERT_EQ(2, channelbag->channel_groups().size());
1876 EXPECT_EQ(&group0, channelbag->channel_group(0));
1877 EXPECT_EQ(&group1, channelbag->channel_group(1));
1878 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1879 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1880 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1881 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
1882 EXPECT_EQ(&group0, fcu1.grp);
1883 EXPECT_EQ(&group0, fcu3.grp);
1884 EXPECT_EQ(&group1, fcu2.grp);
1885 EXPECT_EQ(nullptr, fcu0.grp);
1886 EXPECT_EQ(0, group0.fcurve_range_start);
1887 EXPECT_EQ(2, group0.fcurve_range_length);
1888 EXPECT_EQ(2, group1.fcurve_range_start);
1889 EXPECT_EQ(1, group1.fcurve_range_length);
1890
1891 /* Finally, creating a new fcurve with the second channel group again should
1892 * also put it at the end of that group. */
1893 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0, {}, {}, "group1"});
1894 ASSERT_EQ(5, channelbag->fcurves().size());
1895 ASSERT_EQ(2, channelbag->channel_groups().size());
1896 EXPECT_EQ(&group0, channelbag->channel_group(0));
1897 EXPECT_EQ(&group1, channelbag->channel_group(1));
1898 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1899 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1900 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1901 EXPECT_EQ(&fcu4, channelbag->fcurve(3));
1902 EXPECT_EQ(&fcu0, channelbag->fcurve(4));
1903 EXPECT_EQ(&group0, fcu1.grp);
1904 EXPECT_EQ(&group0, fcu3.grp);
1905 EXPECT_EQ(&group1, fcu2.grp);
1906 EXPECT_EQ(&group1, fcu4.grp);
1907 EXPECT_EQ(nullptr, fcu0.grp);
1908 EXPECT_EQ(0, group0.fcurve_range_start);
1909 EXPECT_EQ(2, group0.fcurve_range_length);
1910 EXPECT_EQ(2, group1.fcurve_range_start);
1911 EXPECT_EQ(2, group1.fcurve_range_length);
1912}
1913
1914TEST_F(ChannelbagTest, channel_group_fcurve_removal)
1915{
1916 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1917 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1918 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1919 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
1920 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1921
1922 ASSERT_EQ(5, channelbag->fcurves().size());
1923 ASSERT_EQ(2, channelbag->channel_groups().size());
1924
1925 bActionGroup &group0 = *channelbag->channel_group(0);
1926 bActionGroup &group1 = *channelbag->channel_group(1);
1927
1928 EXPECT_EQ(0, group0.fcurve_range_start);
1929 EXPECT_EQ(2, group0.fcurve_range_length);
1930 EXPECT_EQ(2, group1.fcurve_range_start);
1931 EXPECT_EQ(2, group1.fcurve_range_length);
1932 EXPECT_EQ(&group0, fcu0.grp);
1933 EXPECT_EQ(&group0, fcu1.grp);
1934 EXPECT_EQ(&group1, fcu2.grp);
1935 EXPECT_EQ(&group1, fcu3.grp);
1936 EXPECT_EQ(nullptr, fcu4.grp);
1937
1938 channelbag->fcurve_remove(fcu3);
1939 ASSERT_EQ(4, channelbag->fcurves().size());
1940 ASSERT_EQ(2, channelbag->channel_groups().size());
1941 EXPECT_EQ(&group0, channelbag->channel_group(0));
1942 EXPECT_EQ(&group1, channelbag->channel_group(1));
1943 EXPECT_EQ(0, group0.fcurve_range_start);
1944 EXPECT_EQ(2, group0.fcurve_range_length);
1945 EXPECT_EQ(2, group1.fcurve_range_start);
1946 EXPECT_EQ(1, group1.fcurve_range_length);
1947 EXPECT_EQ(&group0, fcu0.grp);
1948 EXPECT_EQ(&group0, fcu1.grp);
1949 EXPECT_EQ(&group1, fcu2.grp);
1950 EXPECT_EQ(nullptr, fcu4.grp);
1951
1952 channelbag->fcurve_remove(fcu0);
1953 ASSERT_EQ(3, channelbag->fcurves().size());
1954 ASSERT_EQ(2, channelbag->channel_groups().size());
1955 EXPECT_EQ(&group0, channelbag->channel_group(0));
1956 EXPECT_EQ(&group1, channelbag->channel_group(1));
1957 EXPECT_EQ(0, group0.fcurve_range_start);
1958 EXPECT_EQ(1, group0.fcurve_range_length);
1959 EXPECT_EQ(1, group1.fcurve_range_start);
1960 EXPECT_EQ(1, group1.fcurve_range_length);
1961 EXPECT_EQ(&group0, fcu1.grp);
1962 EXPECT_EQ(&group1, fcu2.grp);
1963 EXPECT_EQ(nullptr, fcu4.grp);
1964
1965 channelbag->fcurve_remove(fcu1);
1966 ASSERT_EQ(2, channelbag->fcurves().size());
1967 ASSERT_EQ(1, channelbag->channel_groups().size());
1968 EXPECT_EQ(&group1, channelbag->channel_group(0));
1969 EXPECT_EQ(0, group1.fcurve_range_start);
1970 EXPECT_EQ(1, group1.fcurve_range_length);
1971 EXPECT_EQ(&group1, fcu2.grp);
1972 EXPECT_EQ(nullptr, fcu4.grp);
1973
1974 channelbag->fcurve_remove(fcu4);
1975 ASSERT_EQ(1, channelbag->fcurves().size());
1976 ASSERT_EQ(1, channelbag->channel_groups().size());
1977 EXPECT_EQ(&group1, channelbag->channel_group(0));
1978 EXPECT_EQ(0, group1.fcurve_range_start);
1979 EXPECT_EQ(1, group1.fcurve_range_length);
1980 EXPECT_EQ(&group1, fcu2.grp);
1981
1982 channelbag->fcurve_remove(fcu2);
1983 ASSERT_EQ(0, channelbag->fcurves().size());
1984 ASSERT_EQ(0, channelbag->channel_groups().size());
1985}
1986
1987TEST_F(ChannelbagTest, channel_group_move_to_index)
1988{
1989 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1990 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group1"});
1991 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1992 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group2"});
1993 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1994
1995 ASSERT_EQ(5, channelbag->fcurves().size());
1996 ASSERT_EQ(3, channelbag->channel_groups().size());
1997
1998 bActionGroup &group0 = *channelbag->channel_group(0);
1999 bActionGroup &group1 = *channelbag->channel_group(1);
2000 bActionGroup &group2 = *channelbag->channel_group(2);
2001
2002 channelbag->channel_group_move_to_index(group0, 2);
2003 EXPECT_EQ(&group1, channelbag->channel_group(0));
2004 EXPECT_EQ(&group2, channelbag->channel_group(1));
2005 EXPECT_EQ(&group0, channelbag->channel_group(2));
2006 EXPECT_EQ(0, group1.fcurve_range_start);
2007 EXPECT_EQ(2, group1.fcurve_range_length);
2008 EXPECT_EQ(2, group2.fcurve_range_start);
2009 EXPECT_EQ(1, group2.fcurve_range_length);
2010 EXPECT_EQ(3, group0.fcurve_range_start);
2011 EXPECT_EQ(1, group0.fcurve_range_length);
2012 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2013 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2014 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2015 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2016 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2017 EXPECT_EQ(&group1, fcu1.grp);
2018 EXPECT_EQ(&group1, fcu2.grp);
2019 EXPECT_EQ(&group2, fcu3.grp);
2020 EXPECT_EQ(&group0, fcu0.grp);
2021 EXPECT_EQ(nullptr, fcu4.grp);
2022
2023 channelbag->channel_group_move_to_index(group1, 1);
2024 EXPECT_EQ(&group2, channelbag->channel_group(0));
2025 EXPECT_EQ(&group1, channelbag->channel_group(1));
2026 EXPECT_EQ(&group0, channelbag->channel_group(2));
2027 EXPECT_EQ(0, group2.fcurve_range_start);
2028 EXPECT_EQ(1, group2.fcurve_range_length);
2029 EXPECT_EQ(1, group1.fcurve_range_start);
2030 EXPECT_EQ(2, group1.fcurve_range_length);
2031 EXPECT_EQ(3, group0.fcurve_range_start);
2032 EXPECT_EQ(1, group0.fcurve_range_length);
2033 EXPECT_EQ(&fcu3, channelbag->fcurve(0));
2034 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
2035 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2036 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2037 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2038 EXPECT_EQ(&group2, fcu3.grp);
2039 EXPECT_EQ(&group1, fcu1.grp);
2040 EXPECT_EQ(&group1, fcu2.grp);
2041 EXPECT_EQ(&group0, fcu0.grp);
2042 EXPECT_EQ(nullptr, fcu4.grp);
2043
2044 channelbag->channel_group_move_to_index(group0, 0);
2045 EXPECT_EQ(&group0, channelbag->channel_group(0));
2046 EXPECT_EQ(&group2, channelbag->channel_group(1));
2047 EXPECT_EQ(&group1, channelbag->channel_group(2));
2048 EXPECT_EQ(0, group0.fcurve_range_start);
2049 EXPECT_EQ(1, group0.fcurve_range_length);
2050 EXPECT_EQ(1, group2.fcurve_range_start);
2051 EXPECT_EQ(1, group2.fcurve_range_length);
2052 EXPECT_EQ(2, group1.fcurve_range_start);
2053 EXPECT_EQ(2, group1.fcurve_range_length);
2054 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2055 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
2056 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2057 EXPECT_EQ(&fcu2, channelbag->fcurve(3));
2058 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2059 EXPECT_EQ(&group0, fcu0.grp);
2060 EXPECT_EQ(&group2, fcu3.grp);
2061 EXPECT_EQ(&group1, fcu1.grp);
2062 EXPECT_EQ(&group1, fcu2.grp);
2063 EXPECT_EQ(nullptr, fcu4.grp);
2064}
2065
2066TEST_F(ChannelbagTest, channel_group_move_fcurve_into)
2067{
2068 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0});
2069 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0});
2070 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0});
2071 bActionGroup &group0 = channelbag->channel_group_create("group0");
2072 bActionGroup &group1 = channelbag->channel_group_create("group1");
2073
2074 ASSERT_EQ(3, channelbag->fcurves().size());
2075 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2076 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
2077 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2078 ASSERT_EQ(2, channelbag->channel_groups().size());
2079 EXPECT_EQ(&group0, channelbag->channel_group(0));
2080 EXPECT_EQ(&group1, channelbag->channel_group(1));
2081 EXPECT_EQ(0, group0.fcurve_range_start);
2082 EXPECT_EQ(0, group0.fcurve_range_length);
2083 EXPECT_EQ(0, group1.fcurve_range_start);
2084 EXPECT_EQ(0, group1.fcurve_range_length);
2085
2086 channelbag->fcurve_assign_to_channel_group(fcu2, group1);
2087 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
2088 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2089 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2090 EXPECT_EQ(0, group0.fcurve_range_start);
2091 EXPECT_EQ(0, group0.fcurve_range_length);
2092 EXPECT_EQ(0, group1.fcurve_range_start);
2093 EXPECT_EQ(1, group1.fcurve_range_length);
2094
2095 channelbag->fcurve_assign_to_channel_group(fcu1, group0);
2096 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2097 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2098 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2099 EXPECT_EQ(0, group0.fcurve_range_start);
2100 EXPECT_EQ(1, group0.fcurve_range_length);
2101 EXPECT_EQ(1, group1.fcurve_range_start);
2102 EXPECT_EQ(1, group1.fcurve_range_length);
2103
2104 channelbag->fcurve_assign_to_channel_group(fcu0, group1);
2105 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2106 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2107 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2108 EXPECT_EQ(0, group0.fcurve_range_start);
2109 EXPECT_EQ(1, group0.fcurve_range_length);
2110 EXPECT_EQ(1, group1.fcurve_range_start);
2111 EXPECT_EQ(2, group1.fcurve_range_length);
2112
2113 channelbag->fcurve_assign_to_channel_group(fcu0, group0);
2114 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2115 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2116 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2117 EXPECT_EQ(0, group0.fcurve_range_start);
2118 EXPECT_EQ(2, group0.fcurve_range_length);
2119 EXPECT_EQ(2, group1.fcurve_range_start);
2120 EXPECT_EQ(1, group1.fcurve_range_length);
2121
2122 channelbag->fcurve_assign_to_channel_group(fcu1, group1);
2123 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2124 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2125 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2126 EXPECT_EQ(0, group0.fcurve_range_start);
2127 EXPECT_EQ(1, group0.fcurve_range_length);
2128 EXPECT_EQ(1, group1.fcurve_range_start);
2129 EXPECT_EQ(2, group1.fcurve_range_length);
2130}
2131
2132TEST_F(ChannelbagTest, channel_group_fcurve_ungroup)
2133{
2134 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
2135 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
2136 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
2137 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
2138 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
2139
2140 ASSERT_EQ(5, channelbag->fcurves().size());
2141 ASSERT_EQ(2, channelbag->channel_groups().size());
2142
2143 bActionGroup &group0 = *channelbag->channel_group(0);
2144 bActionGroup &group1 = *channelbag->channel_group(1);
2145
2146 /* Attempting to ungroup an fcurve that's not in the channel bag should fail. */
2147 FCurve bogus = {};
2148 EXPECT_FALSE(channelbag->fcurve_ungroup(bogus));
2149
2150 /* Attempting to ungroup an fcurve that's already ungrouped is fine. */
2151 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu4));
2152
2153 /* Ungroup each fcurve until all are ungrouped. */
2154
2155 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu0));
2156 EXPECT_EQ(0, group0.fcurve_range_start);
2157 EXPECT_EQ(1, group0.fcurve_range_length);
2158 EXPECT_EQ(1, group1.fcurve_range_start);
2159 EXPECT_EQ(2, group1.fcurve_range_length);
2160 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2161 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2162 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2163 EXPECT_EQ(&fcu4, channelbag->fcurve(3));
2164 EXPECT_EQ(&fcu0, channelbag->fcurve(4));
2165 EXPECT_EQ(&group0, fcu1.grp);
2166 EXPECT_EQ(&group1, fcu2.grp);
2167 EXPECT_EQ(&group1, fcu3.grp);
2168 EXPECT_EQ(nullptr, fcu4.grp);
2169 EXPECT_EQ(nullptr, fcu0.grp);
2170
2171 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu3));
2172 EXPECT_EQ(0, group0.fcurve_range_start);
2173 EXPECT_EQ(1, group0.fcurve_range_length);
2174 EXPECT_EQ(1, group1.fcurve_range_start);
2175 EXPECT_EQ(1, group1.fcurve_range_length);
2176 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2177 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2178 EXPECT_EQ(&fcu4, channelbag->fcurve(2));
2179 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2180 EXPECT_EQ(&fcu3, channelbag->fcurve(4));
2181 EXPECT_EQ(&group0, fcu1.grp);
2182 EXPECT_EQ(&group1, fcu2.grp);
2183 EXPECT_EQ(nullptr, fcu4.grp);
2184 EXPECT_EQ(nullptr, fcu0.grp);
2185 EXPECT_EQ(nullptr, fcu3.grp);
2186
2187 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu1));
2188 EXPECT_EQ(1, channelbag->channel_groups().size());
2189 EXPECT_EQ(&group1, channelbag->channel_group(0));
2190 EXPECT_EQ(0, group1.fcurve_range_start);
2191 EXPECT_EQ(1, group1.fcurve_range_length);
2192 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
2193 EXPECT_EQ(&fcu4, channelbag->fcurve(1));
2194 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2195 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
2196 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
2197 EXPECT_EQ(&group1, fcu2.grp);
2198 EXPECT_EQ(nullptr, fcu4.grp);
2199 EXPECT_EQ(nullptr, fcu0.grp);
2200 EXPECT_EQ(nullptr, fcu3.grp);
2201 EXPECT_EQ(nullptr, fcu1.grp);
2202
2203 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu2));
2204 EXPECT_EQ(0, channelbag->channel_groups().size());
2205 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
2206 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2207 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2208 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
2209 EXPECT_EQ(&fcu2, channelbag->fcurve(4));
2210 EXPECT_EQ(nullptr, fcu4.grp);
2211 EXPECT_EQ(nullptr, fcu0.grp);
2212 EXPECT_EQ(nullptr, fcu3.grp);
2213 EXPECT_EQ(nullptr, fcu1.grp);
2214 EXPECT_EQ(nullptr, fcu2.grp);
2215}
2216
2217/*-----------------------------------------------------------*/
2218
2219class ActionFCurveMoveTest : public testing::Test {
2220 public:
2222
2223 static void SetUpTestSuite()
2224 {
2225 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
2226 CLG_init();
2227
2228 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
2230 }
2231
2232 static void TearDownTestSuite()
2233 {
2234 CLG_exit();
2235 }
2236
2237 void SetUp() override
2238 {
2239 bmain = BKE_main_new();
2240 }
2241
2242 void TearDown() override
2243 {
2245 }
2246
2247 static FCurve *fcurve_create(const StringRefNull rna_path, const int array_index)
2248 {
2249 FCurve *fcurve = BKE_fcurve_create();
2250 fcurve->rna_path = BLI_strdupn(rna_path.c_str(), array_index);
2251 return fcurve;
2252 };
2253};
2254
2255TEST_F(ActionFCurveMoveTest, test_fcurve_move_legacy)
2256{
2257 Action &action_src = action_add(*this->bmain, "SourceAction");
2258 Action &action_dst = action_add(*this->bmain, "DestinationAction");
2259
2260 /* Add F-Curves to source Action. */
2261 BLI_addtail(&action_src.curves, fcurve_create("source_prop", 0));
2262 FCurve *fcurve_to_move = fcurve_create("source_prop", 2);
2263 BLI_addtail(&action_src.curves, fcurve_to_move);
2264
2265 /* Add F-Curves to destination Action. */
2266 BLI_addtail(&action_dst.curves, fcurve_create("dest_prop", 0));
2267
2268 ASSERT_TRUE(action_src.is_action_legacy());
2269 ASSERT_TRUE(action_dst.is_action_legacy());
2270
2271 action_fcurve_move(action_dst, Slot::unassigned, action_src, *fcurve_to_move);
2272
2273 EXPECT_TRUE(action_src.is_action_legacy());
2274 EXPECT_TRUE(action_dst.is_action_legacy());
2275
2276 EXPECT_EQ(-1, BLI_findindex(&action_src.curves, fcurve_to_move))
2277 << "F-Curve should no longer exist in source Action";
2278 EXPECT_EQ(1, BLI_findindex(&action_dst.curves, fcurve_to_move))
2279 << "F-Curve should exist in destination Action";
2280
2281 EXPECT_EQ(1, BLI_listbase_count(&action_src.curves))
2282 << "Source Action should still have the other F-Curve";
2283 EXPECT_EQ(2, BLI_listbase_count(&action_dst.curves))
2284 << "Destination Action should have its original and the moved F-Curve";
2285}
2286
2287TEST_F(ActionFCurveMoveTest, test_fcurve_move_layered)
2288{
2289 Action &action_src = action_add(*this->bmain, "SourceAction");
2290 Action &action_dst = action_add(*this->bmain, "DestinationAction");
2291
2292 /* Add F-Curves to source Action. */
2293 Slot &slot_src = action_src.slot_add();
2294 action_src.layer_keystrip_ensure();
2295 StripKeyframeData &strip_data_src = action_src.layer(0)->strip(0)->data<StripKeyframeData>(
2296 action_src);
2297 Channelbag &cbag_src = strip_data_src.channelbag_for_slot_ensure(slot_src);
2298
2299 cbag_src.fcurve_ensure(this->bmain, {"source_prop", 0});
2300 FCurve &fcurve_to_move = cbag_src.fcurve_ensure(this->bmain, {"source_prop", 2});
2301 bActionGroup &group_src = cbag_src.channel_group_create("Gröpje");
2302 cbag_src.fcurve_assign_to_channel_group(fcurve_to_move, group_src);
2303
2304 /* Add F-Curves to destination Action. */
2305 Slot &slot_dst = action_dst.slot_add();
2306 action_dst.layer_keystrip_ensure();
2307 StripKeyframeData &strip_data_dst = action_dst.layer(0)->strip(0)->data<StripKeyframeData>(
2308 action_dst);
2309 Channelbag &cbag_dst = strip_data_dst.channelbag_for_slot_ensure(slot_dst);
2310
2311 cbag_dst.fcurve_ensure(this->bmain, {"dest_prop", 0});
2312
2313 ASSERT_TRUE(action_src.is_action_layered());
2314 ASSERT_TRUE(action_dst.is_action_layered());
2315
2316 action_fcurve_move(action_dst, slot_dst.handle, action_src, fcurve_to_move);
2317
2318 EXPECT_TRUE(action_src.is_action_layered());
2319 EXPECT_TRUE(action_dst.is_action_layered());
2320
2321 EXPECT_EQ(nullptr, cbag_src.fcurve_find({fcurve_to_move.rna_path, fcurve_to_move.array_index}))
2322 << "F-Curve should no longer exist in source Action";
2323 EXPECT_EQ(&fcurve_to_move,
2324 cbag_dst.fcurve_find({fcurve_to_move.rna_path, fcurve_to_move.array_index}))
2325 << "F-Curve should exist in destination Action";
2326
2327 EXPECT_EQ(1, cbag_src.fcurves().size()) << "Source Action should still have the other F-Curve";
2328 EXPECT_EQ(2, cbag_dst.fcurves().size())
2329 << "Destination Action should have its original and the moved F-Curve";
2330
2331 bActionGroup *group_dst = cbag_dst.channel_group_find("Gröpje");
2332 ASSERT_NE(nullptr, group_dst) << "Expected channel group to be created";
2333 ASSERT_EQ(group_dst, fcurve_to_move.grp) << "Expected group membership to move as well";
2334}
2335
2336} // 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
int us
Definition DNA_ID.h:425
char name[66]
Definition DNA_ID.h:415
void * first
ListBase curves
int32_t last_slot_handle
ListBase groups