Blender V4.3
interface_ops_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
5#include <string.h>
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_anim_types.h"
16
17#include "RNA_access.hh"
18#include "RNA_prototypes.hh"
19
20#include "BLI_listbase.h"
21#include "BLI_string.h"
22
23#include "ED_keyframing.hh"
24
25#include "ANIM_action.hh"
26#include "ANIM_animdata.hh"
27#include "ANIM_fcurve.hh"
28
29#include "interface_intern.hh"
30
31#include "CLG_log.h"
32#include "testing/testing.h"
33
35
36class CopyDriversToSelected : public testing::Test {
37 public:
39
42
46
50
51 static void SetUpTestSuite()
52 {
53 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
54 CLG_init();
55
56 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
58 }
59
60 static void TearDownTestSuite()
61 {
62 CLG_exit();
63 }
64
65 void SetUp() override
66 {
68
71
72 cube_ptr = RNA_pointer_create(&cube->id, &RNA_Object, &cube->id);
75
79
81 AnimData *adt_suzanne = BKE_animdata_ensure_id(&suzanne->id);
82
83 ReportList tmp_report_list;
84
85 /* Set up cube drivers. */
86 ANIM_add_driver(&tmp_report_list, &cube->id, "rotation_quaternion", 0, 0, DRIVER_TYPE_PYTHON);
87 ANIM_add_driver(&tmp_report_list, &cube->id, "rotation_quaternion", 1, 0, DRIVER_TYPE_PYTHON);
88 FCurve *cube_quat_0_driver = static_cast<FCurve *>(BLI_findlink(&adt_cube->drivers, 0));
89 FCurve *cube_quat_1_driver = static_cast<FCurve *>(BLI_findlink(&adt_cube->drivers, 1));
90 STRNCPY(cube_quat_0_driver->driver->expression, "0.0");
91 STRNCPY(cube_quat_1_driver->driver->expression, "1.0");
92
93 /* Set up suzanne drivers. */
95 &tmp_report_list, &suzanne->id, "rotation_quaternion", 0, 0, DRIVER_TYPE_PYTHON);
97 &tmp_report_list, &suzanne->id, "rotation_quaternion", 2, 0, DRIVER_TYPE_PYTHON);
99 &tmp_report_list, &suzanne->id, "rotation_quaternion", 3, 0, DRIVER_TYPE_PYTHON);
100 ANIM_add_driver(&tmp_report_list, &suzanne->id, "rotation_mode", 0, 0, DRIVER_TYPE_PYTHON);
101 FCurve *suzanne_quat_0_driver = static_cast<FCurve *>(BLI_findlink(&adt_suzanne->drivers, 0));
102 FCurve *suzanne_quat_2_driver = static_cast<FCurve *>(BLI_findlink(&adt_suzanne->drivers, 1));
103 FCurve *suzanne_quat_3_driver = static_cast<FCurve *>(BLI_findlink(&adt_suzanne->drivers, 2));
104 FCurve *suzanne_rotation_mode_driver = static_cast<FCurve *>(
105 BLI_findlink(&adt_suzanne->drivers, 3));
106 STRNCPY(suzanne_quat_0_driver->driver->expression, "0.5");
107 STRNCPY(suzanne_quat_2_driver->driver->expression, "2.5");
108 STRNCPY(suzanne_quat_3_driver->driver->expression, "3.5");
109 STRNCPY(suzanne_rotation_mode_driver->driver->expression, "4");
110
111 /* Add animation to cube's fourth quaternion element. */
112 PointerRNA cube_ptr = RNA_pointer_create(&cube->id, &RNA_Object, &cube->id);
115 bmain, act, "Object Transforms", &cube_ptr, {"rotation_quaternion", 3});
117 insert_vert_fcurve(fcu, {1.0, 1.0}, keyframe_settings, INSERTKEY_NOFLAGS);
118 }
119
120 void TearDown() override
121 {
123 }
124};
125
126TEST_F(CopyDriversToSelected, get_property_drivers)
127{
128 /* Cube quaternion: get all drivers. */
129 {
130 bool is_array_prop;
132 &cube_ptr, cube_quaternion_prop, true, -1, &is_array_prop);
133
134 EXPECT_EQ(is_array_prop, true);
135 EXPECT_EQ(drivers.size(), 4);
136
137 EXPECT_NE(drivers[0], nullptr);
138 EXPECT_NE(drivers[1], nullptr);
139 EXPECT_EQ(drivers[2], nullptr);
140 EXPECT_EQ(drivers[3], nullptr);
141
142 EXPECT_STREQ(drivers[0]->driver->expression, "0.0");
143 EXPECT_STREQ(drivers[1]->driver->expression, "1.0");
144 }
145
146 /* Cube quaternion: get first element driver. */
147 {
148 bool is_array_prop;
150 &cube_ptr, cube_quaternion_prop, false, 0, &is_array_prop);
151
152 EXPECT_EQ(is_array_prop, true);
153 EXPECT_EQ(drivers.size(), 4);
154
155 EXPECT_NE(drivers[0], nullptr);
156 EXPECT_EQ(drivers[1], nullptr);
157 EXPECT_EQ(drivers[2], nullptr);
158 EXPECT_EQ(drivers[3], nullptr);
159
160 EXPECT_STREQ(drivers[0]->driver->expression, "0.0");
161 }
162
163 /* Cube quaternion: try to get fourth element driver. Since there is none, we
164 * should get back an empty vector, indicating that no drivers were found. */
165 {
166 bool is_array_prop;
168 &cube_ptr, cube_quaternion_prop, false, 3, &is_array_prop);
169
170 EXPECT_EQ(drivers.size(), 0);
171 }
172
173 /* Cube rotation mode: get driver. Since there is none, we should get back an
174 * empty vector, indicating that no drivers were found. */
175 {
176 bool is_array_prop;
178 &cube_ptr, cube_rotation_mode_prop, false, 0, &is_array_prop);
179
180 EXPECT_EQ(drivers.size(), 0);
181 }
182
183 /* Suzanne quaternion: get all drivers. */
184 {
185 bool is_array_prop;
187 &suzanne_ptr, suzanne_quaternion_prop, true, -1, &is_array_prop);
188
189 EXPECT_EQ(is_array_prop, true);
190 EXPECT_EQ(drivers.size(), 4);
191
192 EXPECT_NE(drivers[0], nullptr);
193 EXPECT_EQ(drivers[1], nullptr);
194 EXPECT_NE(drivers[2], nullptr);
195 EXPECT_NE(drivers[3], nullptr);
196
197 EXPECT_STREQ(drivers[0]->driver->expression, "0.5");
198 EXPECT_STREQ(drivers[2]->driver->expression, "2.5");
199 EXPECT_STREQ(drivers[3]->driver->expression, "3.5");
200 }
201
202 /* Suzanne quaternion: get first element driver. */
203 {
204 bool is_array_prop;
206 &suzanne_ptr, suzanne_quaternion_prop, false, 0, &is_array_prop);
207
208 EXPECT_EQ(is_array_prop, true);
209 EXPECT_EQ(drivers.size(), 4);
210
211 EXPECT_NE(drivers[0], nullptr);
212 EXPECT_EQ(drivers[1], nullptr);
213 EXPECT_EQ(drivers[2], nullptr);
214 EXPECT_EQ(drivers[3], nullptr);
215
216 EXPECT_STREQ(drivers[0]->driver->expression, "0.5");
217 }
218
219 /* Suzanne quaternion: get second element driver. Since there is none, we
220 * should get back an empty vector, indicating that no drivers were found. */
221 {
222 bool is_array_prop;
224 &suzanne_ptr, suzanne_quaternion_prop, false, 1, &is_array_prop);
225
226 EXPECT_EQ(drivers.size(), 0);
227 }
228
229 /* Suzanne rotation mode: get driver. */
230 {
231 bool is_array_prop;
233 &suzanne_ptr, suzanne_rotation_mode_prop, false, 0, &is_array_prop);
234
235 EXPECT_EQ(is_array_prop, false);
236 EXPECT_EQ(drivers.size(), 1);
237 EXPECT_STREQ(drivers[0]->driver->expression, "4");
238 }
239}
240
241TEST_F(CopyDriversToSelected, paste_property_drivers)
242{
243 /* Copy all quaternion channel drivers from Suzanne to Cube. The result on
244 * Cube should be the following:
245 *
246 * - [0]: overwritten by the driver from Suzanne.
247 * - [1]: Cube's driver remains, since there was no driver here on Suzanne.
248 * - [2]: Suzanne's driver is pasted. There was no driver on Cube before.
249 * - [3]: remains without a driver. Cube has animation on this channel,
250 * preventing driver pasting.
251 */
252 {
253 bool is_array_prop;
254 Vector<FCurve *> suzanne_location_drivers = internal::get_property_drivers(
255 &suzanne_ptr, suzanne_quaternion_prop, true, -1, &is_array_prop);
256
258 suzanne_location_drivers.as_span(), is_array_prop, &cube_ptr, cube_quaternion_prop);
259
260 Vector<FCurve *> cube_location_drivers = internal::get_property_drivers(
261 &cube_ptr, cube_quaternion_prop, true, -1, &is_array_prop);
262
263 EXPECT_NE(cube_location_drivers[0], nullptr);
264 EXPECT_NE(cube_location_drivers[1], nullptr);
265 EXPECT_NE(cube_location_drivers[2], nullptr);
266 EXPECT_EQ(cube_location_drivers[3], nullptr);
267
268 EXPECT_STREQ(cube_location_drivers[0]->driver->expression, "0.5");
269 EXPECT_STREQ(cube_location_drivers[1]->driver->expression, "1.0");
270 EXPECT_STREQ(cube_location_drivers[2]->driver->expression, "2.5");
271 }
272
273 /* Copy the rotation_mode driver from Suzanne to Cube. */
274 {
275 bool is_array_prop;
276 Vector<FCurve *> suzanne_rotation_mode_driver = internal::get_property_drivers(
277 &suzanne_ptr, suzanne_rotation_mode_prop, false, 0, &is_array_prop);
278
280 suzanne_rotation_mode_driver.as_span(), is_array_prop, &cube_ptr, cube_rotation_mode_prop);
281
282 Vector<FCurve *> cube_rotation_mode_drivers = internal::get_property_drivers(
283 &cube_ptr, cube_rotation_mode_prop, false, 0, &is_array_prop);
284
285 EXPECT_NE(cube_rotation_mode_drivers[0], nullptr);
286 EXPECT_STREQ(cube_rotation_mode_drivers[0]->driver->expression, "4");
287 }
288}
289
290} // namespace blender::interface::tests
Functions and classes to work with Actions.
Functions to work with AnimData.
Functions to modify FCurves.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:103
void BKE_idtype_init()
Definition idtype.cc:127
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.
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)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
void CLG_exit(void)
Definition clog.c:706
void CLG_init(void)
Definition clog.c:699
@ DRIVER_TYPE_PYTHON
@ INSERTKEY_NOFLAGS
@ HD_AUTO
@ BEZT_IPO_BEZ
@ BEZT_KEYTYPE_KEYFRAME
@ OB_EMPTY
int64_t size() const
Span< T > as_span() const
int ANIM_add_driver(ReportList *reports, ID *id, const char rna_path[], int array_index, short flag, int type)
Main Driver Management API calls.
Definition drivers.cc:399
bAction * id_action_ensure(Main *bmain, ID *id)
Definition animdata.cc:195
FCurve * action_fcurve_ensure(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, FCurveDescriptor fcurve_descriptor)
blender::Vector< FCurve * > get_property_drivers(PointerRNA *ptr, PropertyRNA *prop, bool get_all, int index, bool *r_is_array_prop)
int paste_property_drivers(blender::Span< FCurve * > src_drivers, bool is_array_prop, PointerRNA *dst_ptr, PropertyRNA *dst_prop)
TEST_F(CopyDriversToSelected, get_property_drivers)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
ListBase drivers
char expression[256]
ChannelDriver * driver