Blender V4.3
action_iterators_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4#include "ANIM_action.hh"
6
7#include "BKE_idtype.hh"
8#include "BKE_lib_id.hh"
9#include "BKE_main.hh"
10#include "BKE_object.hh"
11
12#include "DNA_anim_types.h"
13#include "DNA_object_types.h"
14
15#include "CLG_log.h"
16#include "testing/testing.h"
17
19class ActionIteratorsTest : public testing::Test {
20 public:
23
24 static void SetUpTestSuite()
25 {
26 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
27 CLG_init();
28
29 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
31 }
32
33 static void TearDownTestSuite()
34 {
35 CLG_exit();
36 }
37
38 void SetUp() override
39 {
41 action = static_cast<Action *>(BKE_id_new(bmain, ID_AC, "ACLayeredAction"));
42 }
43
44 void TearDown() override
45 {
47 }
48};
49
50TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
51{
52 Slot &cube_slot = action->slot_add();
53 Slot &monkey_slot = action->slot_add();
54 EXPECT_TRUE(action->is_action_layered());
55
56 /* Try iterating an empty action. */
59 *action, cube_slot.handle, [&](FCurve &fcurve) { no_fcurves.append(&fcurve); });
60
61 ASSERT_TRUE(no_fcurves.is_empty());
62
63 Layer &layer = action->layer_add("Layer One");
64 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
65 StripKeyframeData &strip_data = strip.data<StripKeyframeData>(*action);
66 const KeyframeSettings settings = get_keyframe_settings(false);
67
68 /* Insert 3 FCurves for each slot. */
69 for (int i = 0; i < 3; i++) {
70 SingleKeyingResult result_cube = strip_data.keyframe_insert(
71 bmain, cube_slot, {"location", i}, {1.0f, 0.0f}, settings);
72 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_cube)
73 << "Expected keyframe insertion to be successful";
74
75 SingleKeyingResult result_monkey = strip_data.keyframe_insert(
76 bmain, monkey_slot, {"rotation", i}, {1.0f, 0.0f}, settings);
77 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_monkey)
78 << "Expected keyframe insertion to be successful";
79 }
80
81 /* Get all FCurves. */
82 blender::Vector<FCurve *> cube_fcurves;
84 *action, cube_slot.handle, [&](FCurve &fcurve) { cube_fcurves.append(&fcurve); });
85
86 ASSERT_EQ(cube_fcurves.size(), 3);
87 for (FCurve *fcurve : cube_fcurves) {
88 ASSERT_STREQ(fcurve->rna_path, "location");
89 }
90
91 /* Get only FCurves with index 0 which should be 1. */
92 blender::Vector<FCurve *> monkey_fcurves;
93 foreach_fcurve_in_action_slot(*action, monkey_slot.handle, [&](FCurve &fcurve) {
94 if (fcurve.array_index == 0) {
95 monkey_fcurves.append(&fcurve);
96 }
97 });
98
99 ASSERT_EQ(monkey_fcurves.size(), 1);
100 ASSERT_STREQ(monkey_fcurves[0]->rna_path, "rotation");
101
102 /* Slots handles are just numbers. Passing in a slot handle that doesn't exist should return
103 * nothing. */
104 blender::Vector<FCurve *> invalid_slot_fcurves;
106 monkey_slot.handle + cube_slot.handle,
107 [&](FCurve &fcurve) { invalid_slot_fcurves.append(&fcurve); });
108 ASSERT_TRUE(invalid_slot_fcurves.is_empty());
109}
110
111TEST_F(ActionIteratorsTest, foreach_action_slot_use_with_references)
112{
113 /* Create a cube and assign the Action + a slot. */
114 Object *cube = static_cast<Object *>(BKE_id_new(bmain, ID_OB, "OBCube"));
115 Slot *slot_cube = assign_action_ensure_slot_for_keying(*action, cube->id);
116 ASSERT_NE(slot_cube, nullptr);
117
118 /* Create another Action with slot to assign. */
119 Action &other_action =
120 static_cast<bAction *>(BKE_id_new(bmain, ID_AC, "ACAnotherAction"))->wrap();
121 Slot &another_slot = other_action.slot_add();
122
123 std::optional<ActionSlotAssignmentResult> slot_assignment_result;
124
125 bool all_assigns_ok = true;
126 const auto assign_other_action =
127 [&](bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name) -> bool {
128 /* Assign the other Action. */
129 all_assigns_ok &= generic_assign_action(
130 cube->id, &other_action, action_ptr_ref, slot_handle_ref, slot_name);
131
132 /* Assign the slot of the other Action. */
133 slot_assignment_result = generic_assign_action_slot(
134 &another_slot, cube->id, action_ptr_ref, slot_handle_ref, slot_name);
135
136 return true;
137 };
138
139 foreach_action_slot_use_with_references(cube->id, assign_other_action);
140 ASSERT_TRUE(all_assigns_ok);
141
142 /* Check the result, the slot assignment should have been changed. */
143 ASSERT_TRUE(slot_assignment_result.has_value());
144 EXPECT_EQ(ActionSlotAssignmentResult::OK, slot_assignment_result.value());
145
146 std::optional<std::pair<Action *, Slot *>> action_and_slot = get_action_slot_pair(cube->id);
147
148 ASSERT_TRUE(action_and_slot.has_value());
149 EXPECT_EQ(&other_action, action_and_slot->first)
150 << "Expected Action " << other_action.id.name << " but found "
151 << action_and_slot->first->id.name;
152 EXPECT_EQ(&another_slot, action_and_slot->second)
153 << "Expected Slot " << another_slot.name << " but found " << action_and_slot->second->name;
154}
155
156} // namespace blender::animrig::tests
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
void BKE_idtype_init()
Definition idtype.cc:127
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1482
Main * BKE_main_new(void)
Definition main.cc:45
void BKE_main_free(Main *bmain)
Definition main.cc:175
General operations, lookup, etc. for blender objects.
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
TEST_F(StringFindSplitWords, Simple)
void CLG_exit(void)
Definition clog.c:706
void CLG_init(void)
Definition clog.c:699
@ ID_AC
@ ID_OB
Object is a sort of wrapper for general info.
int64_t size() const
bool is_empty() const
SingleKeyingResult keyframe_insert(Main *bmain, const Slot &slot, FCurveDescriptor fcurve_descriptor, float2 time_value, const KeyframeSettings &settings, eInsertKeyFlags insert_key_flags=INSERTKEY_NOFLAGS)
const T & data(const Action &owning_action) const
TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
bool foreach_action_slot_use_with_references(ID &animated_id, FunctionRef< bool(bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name)> callback)
KeyframeSettings get_keyframe_settings(bool from_userprefs)
Slot * assign_action_ensure_slot_for_keying(Action &action, ID &animated_id)
ActionSlotAssignmentResult generic_assign_action_slot(Slot *slot_to_assign, ID &animated_id, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name) ATTR_WARN_UNUSED_RESULT
bool generic_assign_action(ID &animated_id, bAction *action_to_assign, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_name)
decltype(::ActionSlot::handle) slot_handle_t
std::optional< std::pair< Action *, Slot * > > get_action_slot_pair(ID &animated_id)
float wrap(float value, float max, float min)
Definition node_math.h:71
char * rna_path
char name[66]
Definition DNA_ID.h:425