Blender V5.0
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 "RNA_access.hh"
16#include "RNA_prototypes.hh"
17
18#include "CLG_log.h"
19#include "testing/testing.h"
20
22class ActionIteratorsTest : public testing::Test {
23 public:
26
27 static void SetUpTestSuite()
28 {
29 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
30 CLG_init();
31
32 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
34 }
35
36 static void TearDownTestSuite()
37 {
38 CLG_exit();
39 }
40
41 void SetUp() override
42 {
44 action = BKE_id_new<Action>(bmain, "ACLayeredAction");
45 }
46
47 void TearDown() override
48 {
50 }
51};
52
53TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
54{
55 Slot &cube_slot = action->slot_add();
56 Slot &monkey_slot = action->slot_add();
57 EXPECT_TRUE(action->is_action_layered());
58
59 /* Try iterating an empty action. */
62 *action, cube_slot.handle, [&](FCurve &fcurve) { no_fcurves.append(&fcurve); });
63
64 ASSERT_TRUE(no_fcurves.is_empty());
65
66 Layer &layer = action->layer_add("Layer One");
67 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
68 StripKeyframeData &strip_data = strip.data<StripKeyframeData>(*action);
69 const KeyframeSettings settings = get_keyframe_settings(false);
70
71 /* Insert 3 FCurves for each slot. */
72 for (int i = 0; i < 3; i++) {
73 SingleKeyingResult result_cube = strip_data.keyframe_insert(
74 bmain, cube_slot, {"location", i}, {1.0f, 0.0f}, settings);
75 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_cube)
76 << "Expected keyframe insertion to be successful";
77
78 SingleKeyingResult result_monkey = strip_data.keyframe_insert(
79 bmain, monkey_slot, {"rotation", i}, {1.0f, 0.0f}, settings);
80 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_monkey)
81 << "Expected keyframe insertion to be successful";
82 }
83
84 /* Get all FCurves. */
85 blender::Vector<FCurve *> cube_fcurves;
87 *action, cube_slot.handle, [&](FCurve &fcurve) { cube_fcurves.append(&fcurve); });
88
89 ASSERT_EQ(cube_fcurves.size(), 3);
90 for (FCurve *fcurve : cube_fcurves) {
91 ASSERT_STREQ(fcurve->rna_path, "location");
92 }
93
94 /* Get only FCurves with index 0 which should be 1. */
95 blender::Vector<FCurve *> monkey_fcurves;
96 foreach_fcurve_in_action_slot(*action, monkey_slot.handle, [&](FCurve &fcurve) {
97 if (fcurve.array_index == 0) {
98 monkey_fcurves.append(&fcurve);
99 }
100 });
101
102 ASSERT_EQ(monkey_fcurves.size(), 1);
103 ASSERT_STREQ(monkey_fcurves[0]->rna_path, "rotation");
104
105 /* Slots handles are just numbers. Passing in a slot handle that doesn't exist should return
106 * nothing. */
107 blender::Vector<FCurve *> invalid_slot_fcurves;
109 monkey_slot.handle + cube_slot.handle,
110 [&](FCurve &fcurve) { invalid_slot_fcurves.append(&fcurve); });
111 ASSERT_TRUE(invalid_slot_fcurves.is_empty());
112}
113
115{
116 /* Create a cube and assign the Action + a slot. */
117 Object *cube = BKE_id_new<Object>(bmain, "OBCube");
118 Slot *slot_cube = assign_action_ensure_slot_for_keying(*action, cube->id);
119 ASSERT_NE(slot_cube, nullptr);
120
121 /* Create another Action with slot to assign. */
122 Action &other_action = BKE_id_new<bAction>(bmain, "ACAnotherAction")->wrap();
123 Slot &another_slot = other_action.slot_add();
124
125 std::optional<ActionSlotAssignmentResult> slot_assignment_result;
126
127 bool all_assigns_ok = true;
128 const auto assign_other_action = [&](ID & /* animated_id */,
129 bAction *&action_ptr_ref,
130 slot_handle_t &slot_handle_ref,
131 char *last_slot_identifier) -> bool {
132 /* Assign the other Action. */
133 all_assigns_ok &= generic_assign_action(
134 cube->id, &other_action, action_ptr_ref, slot_handle_ref, last_slot_identifier);
135
136 /* Assign the slot of the other Action. */
137 slot_assignment_result = generic_assign_action_slot(
138 &another_slot, cube->id, action_ptr_ref, slot_handle_ref, last_slot_identifier);
139
140 return true;
141 };
142
143 foreach_action_slot_use_with_references(cube->id, assign_other_action);
144 ASSERT_TRUE(all_assigns_ok);
145
146 /* Check the result, the slot assignment should have been changed. */
147 ASSERT_TRUE(slot_assignment_result.has_value());
148 EXPECT_EQ(ActionSlotAssignmentResult::OK, slot_assignment_result.value());
149
150 std::optional<std::pair<Action *, Slot *>> action_and_slot = get_action_slot_pair(cube->id);
151
152 ASSERT_TRUE(action_and_slot.has_value());
153 EXPECT_EQ(&other_action, action_and_slot->first)
154 << "Expected Action " << other_action.id.name << " but found "
155 << action_and_slot->first->id.name;
156 EXPECT_EQ(&another_slot, action_and_slot->second)
157 << "Expected Slot " << another_slot.identifier << " but found "
158 << action_and_slot->second->identifier;
159}
160
162{
163 /* Create a cube and assign the Action + a slot. */
164 Object *cube = BKE_id_new<Object>(bmain, "OBCube");
165 Slot *slot_cube = assign_action_ensure_slot_for_keying(*action, cube->id);
166 ASSERT_NE(slot_cube, nullptr);
167 Slot &another_slot = action->slot_add();
168
169 const auto assign_other_slot = [&](ID & /* animated_id */,
170 bAction *action,
171 PointerRNA &action_slot_owner_ptr,
172 PropertyRNA &action_slot_prop,
173 char * /*last_slot_identifier*/) -> bool {
174 PointerRNA rna_slot = RNA_pointer_create_discrete(&action->id, &RNA_ActionSlot, &another_slot);
175 RNA_property_pointer_set(&action_slot_owner_ptr, &action_slot_prop, rna_slot, nullptr);
176 return true;
177 };
178
179 foreach_action_slot_use_with_rna(cube->id, assign_other_slot);
180
181 /* Check the result, the slot assignment should have been changed. */
182 std::optional<std::pair<Action *, Slot *>> action_and_slot = get_action_slot_pair(cube->id);
183
184 ASSERT_TRUE(action_and_slot.has_value());
185 EXPECT_EQ(action, action_and_slot->first)
186 << "Expected Action " << action->id.name << " but found " << action_and_slot->first->id.name;
187 EXPECT_EQ(&another_slot, action_and_slot->second)
188 << "Expected Slot " << another_slot.identifier << " but found "
189 << action_and_slot->second->identifier;
190}
191
192} // 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:121
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1514
Main * BKE_main_new()
Definition main.cc:89
void BKE_main_free(Main *bmain)
Definition main.cc:192
General operations, lookup, etc. for blender objects.
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void CLG_exit()
Definition clog.cc:880
void CLG_init()
Definition clog.cc:873
Object is a sort of wrapper for general info.
TEST_F(BrushTest, deep_copy)
Definition brush_test.cc:54
int64_t size() const
bool is_empty() const
Strip & strip_add(Action &owning_action, Strip::Type strip_type)
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)
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)
KeyframeSettings get_keyframe_settings(bool from_userprefs)
Slot * assign_action_ensure_slot_for_keying(Action &action, ID &animated_id)
bool generic_assign_action(ID &animated_id, bAction *action_to_assign, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_identifier)
ActionSlotAssignmentResult generic_assign_action_slot(Slot *slot_to_assign, ID &animated_id, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *slot_identifier)
decltype(::ActionSlot::handle) slot_handle_t
bool foreach_action_slot_use_with_rna(ID &animated_id, FunctionRef< bool(ID &animated_id, bAction *action, PointerRNA &action_slot_owner_ptr, PropertyRNA &action_slot_prop, char *last_slot_identifier)> callback)
std::optional< std::pair< Action *, Slot * > > get_action_slot_pair(ID &animated_id)
bool foreach_action_slot_use_with_references(ID &animated_id, FunctionRef< bool(ID &animated_id, bAction *&action_ptr_ref, slot_handle_t &slot_handle_ref, char *last_slot_identifier)> callback)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
char identifier[258]
char * rna_path
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
i
Definition text_draw.cc:230