Blender V4.5
anim_ipo_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* This file contains code for presenting F-Curves and other animation data
10 * in the UI (especially for use in the Animation Editors).
11 *
12 * -- Joshua Leung, Dec 2008
13 */
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_math_color.h"
18#include "BLI_string.h"
19
20#include "BLT_translation.hh"
21
22#include "DNA_anim_types.h"
23#include "DNA_modifier_types.h"
24#include "DNA_node_types.h"
25
26#include "BKE_node.hh"
27#include "BKE_node_runtime.hh"
28
29#include "RNA_access.hh"
30#include "RNA_path.hh"
31#include "RNA_prototypes.hh"
32
33#include "ED_anim_api.hh"
34
35#include "ANIM_action.hh"
36
37#include <fmt/format.h>
38
39#include <cstring>
40
41struct StructRNA;
42
43/* ----------------------- Getter functions ----------------------- */
44
45std::optional<int> getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
46{
47 using namespace blender;
48 /* Could make an argument, it's a documented limit at the moment. */
49 constexpr size_t name_maxncpy = 256;
50
51 /* Handle some nullptr cases. */
52 if (name == nullptr) {
53 /* A 'get name' function should be able to get the name, otherwise it's a bug. */
55 return {};
56 }
57 if (fcu == nullptr) {
58 BLI_strncpy(name, RPT_("<invalid>"), name_maxncpy);
59 return {};
60 }
61 if (fcu->rna_path == nullptr) {
62 BLI_strncpy(name, RPT_("<no path>"), name_maxncpy);
63 return {};
64 }
65 if (id == nullptr) {
66 BLI_snprintf(name, name_maxncpy, "%s[%d]", fcu->rna_path, fcu->array_index);
67 return {};
68 }
69
71
73 PropertyRNA *prop;
74
75 if (!RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
76 /* Could not resolve the path, so just use the path itself as 'name'. */
77 BLI_snprintf(name, name_maxncpy, "\"%s[%d]\"", fcu->rna_path, fcu->array_index);
78
79 /* Tag F-Curve as disabled - as not usable path. */
80 fcu->flag |= FCURVE_DISABLED;
81 return {};
82 }
83
84 const char *structname = nullptr, *propname = nullptr;
85 char arrayindbuf[16];
86 const char *arrayname = nullptr;
87 bool free_structname = false;
88
89 /* For now, name will consist of 3 parts: struct-name, property name, array index
90 * There are several options possible:
91 * 1) <struct-name>.<property-name>.<array-index>
92 * i.e. Bone1.Location.X, or Object.Location.X
93 * 2) <array-index> <property-name> (<struct name>)
94 * i.e. X Location (Bone1), or X Location (Object)
95 *
96 * Currently, option 2 is in use, to try and make it easier to quickly identify F-Curves
97 * (it does have problems with looking rather odd though).
98 * Option 1 is better in terms of revealing a consistent sense of hierarchy though,
99 * which isn't so clear with option 2.
100 */
101
102 /* For struct-name:
103 * - As base, we use a custom name from the structs if one is available
104 * - However, if we're showing sub-data of bones
105 * (probably there will be other exceptions later).
106 * need to include that info too since it gets confusing otherwise.
107 * - If a pointer just refers to the ID-block, then don't repeat this info
108 * since this just introduces clutter.
109 */
110
111 char pchanName[name_maxncpy], constName[name_maxncpy];
112 if (BLI_str_quoted_substr(fcu->rna_path, "bones[", pchanName, sizeof(pchanName)) &&
113 BLI_str_quoted_substr(fcu->rna_path, "constraints[", constName, sizeof(constName)))
114 {
115 structname = BLI_sprintfN("%s : %s", pchanName, constName);
116 free_structname = true;
117 }
118 else if (ptr.data != ptr.owner_id) {
119 PropertyRNA *nameprop = RNA_struct_name_property(ptr.type);
120 if (nameprop) {
121 structname = RNA_property_string_get_alloc(&ptr, nameprop, nullptr, 0, nullptr);
122 free_structname = true;
123 }
124 else {
125 structname = RNA_struct_ui_name(ptr.type);
126 }
127
128 /* For the sequencer, a strip's 'Transform' or 'Crop' is a nested (under Strip)
129 * struct, but displaying the struct name alone is no meaningful information
130 * (and also cannot be filtered well), same for modifiers.
131 * So display strip name alongside as well. */
132 if (GS(ptr.owner_id->name) == ID_SCE) {
133 char stripname[name_maxncpy];
135 fcu->rna_path, "sequence_editor.strips_all[", stripname, sizeof(stripname)))
136 {
137 if (strstr(fcu->rna_path, ".transform.") || strstr(fcu->rna_path, ".crop.") ||
138 strstr(fcu->rna_path, ".modifiers["))
139 {
140 const char *structname_all = BLI_sprintfN("%s : %s", stripname, structname);
141 if (free_structname) {
142 MEM_freeN(structname);
143 }
144 structname = structname_all;
145 free_structname = true;
146 }
147 }
148 }
149
150 if (RNA_struct_is_a(ptr.type, &RNA_NodeSocket)) {
151 /* Display the name/label of a node socket's node to allow distinguishing multiple nodes. */
152 BLI_assert(GS(ptr.owner_id->name) == ID_NT);
153 const bNodeTree *ntree = reinterpret_cast<const bNodeTree *>(ptr.owner_id);
154 const bNodeSocket *socket = static_cast<const bNodeSocket *>(ptr.data);
155 const bNode &node = bke::node_find_node(*ntree, *socket);
156 if (free_structname) {
157 MEM_freeN(structname);
158 }
159 structname = node.label_or_name().c_str();
160 free_structname = false;
161 }
162 else if (RNA_struct_is_a(ptr.type, &RNA_Node)) {
163 /* Display the label of the node if available to distinguish nodes like "Value". */
164 BLI_assert(GS(ptr.owner_id->name) == ID_NT);
165 const bNode *node = static_cast<const bNode *>(ptr.data);
166 if (free_structname) {
167 MEM_freeN(structname);
168 }
169 structname = node->label_or_name().c_str();
170 free_structname = false;
171 }
172 }
173
174 propname = RNA_property_ui_name(prop);
175
176 if (RNA_struct_is_a(ptr.type, &RNA_NodesModifier)) {
177 /* Display geometry node properties with node-tree socket labels. */
178 const NodesModifierData *nmd = static_cast<const NodesModifierData *>(ptr.data);
179 if (const bNodeTree *node_group = nmd->node_group) {
181 *node_group, propname))
182 {
183 propname = input->name;
184 }
185 }
186 }
187 else if (RNA_struct_is_a(ptr.type, &RNA_NodeSocket)) {
188 /* Use the socket's name rather than the "Default Value" name of the socket's RNA property. */
189 const bNodeSocket *socket = static_cast<const bNodeSocket *>(ptr.data);
190 propname = socket->name;
191 }
192
193 /* Array Index - only if applicable */
194 if (RNA_property_array_check(prop)) {
195 char c = RNA_property_array_item_char(prop, fcu->array_index);
196
197 /* we need to write the index to a temp buffer (in py syntax) */
198 if (c) {
199 SNPRINTF(arrayindbuf, "%c ", c);
200 }
201 else {
202 SNPRINTF(arrayindbuf, "[%d]", fcu->array_index);
203 }
204
205 arrayname = &arrayindbuf[0];
206 }
207 else {
208 /* no array index */
209 arrayname = "";
210 }
211
212 /* putting this all together into the buffer */
213 /* XXX we need to check for invalid names...
214 * XXX the name length limit needs to be passed in or as some define */
215 if (structname) {
216 BLI_snprintf(name, name_maxncpy, "%s%s (%s)", arrayname, propname, structname);
217 }
218 else {
219 BLI_snprintf(name, name_maxncpy, "%s%s", arrayname, propname);
220 }
221
222 /* free temp name if nameprop is set */
223 if (free_structname) {
224 MEM_freeN(structname);
225 }
226
227 /* Use the property's owner struct icon. */
228 return RNA_struct_ui_icon(ptr.type);
229}
230
232 const blender::animrig::Slot &slot,
233 FCurve &fcurve)
234{
235 /* TODO: Refactor to avoid this variable. */
236 constexpr size_t name_maxncpy = 256;
237 char name_buffer[name_maxncpy];
238 name_buffer[0] = '\0';
239
240 /* Check the Slot's users to see if we can find an ID* that can resolve the F-Curve. */
241 for (ID *user : slot.users(bmain)) {
242 const std::optional<int> icon = getname_anim_fcurve(name_buffer, user, &fcurve);
243 if (icon.has_value()) {
244 /* Managed to find a name! */
245 return name_buffer;
246 }
247 }
248
249 if (!slot.users(bmain).is_empty()) {
250 /* This slot is assigned to at least one ID, and still the property it animates could not be
251 * found. There is no use in continuing. */
252 fcurve.flag |= FCURVE_DISABLED;
253 return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
254 }
255
256 /* If this part of the code is hit, the slot is not assigned to anything. The remainder of
257 * this function is all a best-effort attempt. Because of that, it will not set the
258 * FCURVE_DISABLED flag on the F-Curve, as having unassigned animation data is not an error (and
259 * that flag indicates an error). */
260
261 /* Fall back to the ID type of the slot for simple properties. */
262 if (!slot.has_idtype()) {
263 /* The Slot has never been assigned to any ID, so we don't even know what type of ID it is
264 * meant for. */
265 return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
266 }
267
269 /* Not a simple property, so bail out. This needs path resolution, which needs an ID*. */
270 return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
271 }
272
273 /* Find the StructRNA for this Slot's ID type. */
275 if (!srna) {
276 return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
277 }
278
279 /* Find the property. */
281 if (!prop) {
282 return fmt::format("\"{}[{}]\"", fcurve.rna_path, fcurve.array_index);
283 }
284
285 /* Property Name is straightforward */
286 const char *propname = RNA_property_ui_name(prop);
287
288 /* Array Index - only if applicable */
289 if (!RNA_property_array_check(prop)) {
290 return propname;
291 }
292
293 std::string arrayname;
294 char c = RNA_property_array_item_char(prop, fcurve.array_index);
295 if (c) {
296 arrayname = std::string(1, c);
297 }
298 else {
299 arrayname = fmt::format("[{}]", fcurve.array_index);
300 }
301 return arrayname + " " + propname;
302}
303
304/* ------------------------------- Color Codes for F-Curve Channels ---------------------------- */
305
306/* step between the major distinguishable color bands of the primary colors */
307#define HSV_BANDWIDTH 0.3f
308
309/* used to determine the color of F-Curves with FCURVE_COLOR_AUTO_RAINBOW set */
310// void fcurve_rainbow(uint cur, uint tot, float *out)
311void getcolor_fcurve_rainbow(int cur, int tot, float out[3])
312{
313 float hsv[3], fac;
314 int grouping;
315
316 /* we try to divide the color into groupings of n colors,
317 * where n is:
318 * 3 - for 'odd' numbers of curves - there should be a majority of triplets of curves
319 * 4 - for 'even' numbers of curves - there should be a majority of quartets of curves
320 * so the base color is simply one of the three primary colors
321 */
322 grouping = (4 - (tot % 2));
323 hsv[0] = HSV_BANDWIDTH * float(cur % grouping);
324
325 /* 'Value' (i.e. darkness) needs to vary so that larger sets of three will be
326 * 'darker' (i.e. smaller value), so that they don't look that similar to previous ones.
327 * However, only a range of 0.3 to 1.0 is really usable to avoid clashing
328 * with some other stuff
329 */
330 fac = (float(cur) / float(tot)) * 0.7f;
331
332 /* the base color can get offset a bit so that the colors aren't so identical */
333 hsv[0] += fac * HSV_BANDWIDTH;
334 if (hsv[0] > 1.0f) {
335 hsv[0] = fmod(hsv[0], 1.0f);
336 }
337
338 /* saturation adjustments for more visible range */
339 hsv[1] = ((hsv[0] > 0.5f) && (hsv[0] < 0.8f)) ? 0.5f : 0.6f;
340
341 /* value is fixed at 1.0f, otherwise we cannot clearly see the curves... */
342 hsv[2] = 1.0f;
343
344 /* finally, convert this to RGB colors */
345 hsv_to_rgb_v(hsv, out);
346}
Functions and classes to work with Actions.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.cc:531
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define RPT_(msgid)
@ ID_NT
@ ID_SCE
@ FCURVE_DISABLED
Read Guarded memory(de)allocation.
StructRNA * ID_code_to_RNA_type(short idcode)
std::string getname_anim_fcurve_for_slot(Main &bmain, const blender::animrig::Slot &slot, FCurve &fcurve)
std::optional< int > getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
void getcolor_fcurve_rainbow(int cur, int tot, float out[3])
#define HSV_BANDWIDTH
constexpr bool is_empty() const
Definition BLI_span.hh:260
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
Span< ID * > users(Main &bmain) const
#define input
#define out
#define GS(a)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fmod(const float2 a, const float b)
const bNodeTreeInterfaceSocket * node_find_interface_input_by_identifier(const bNodeTree &ntree, StringRef identifier)
Definition node.cc:3638
bNode & node_find_node(bNodeTree &ntree, bNodeSocket &socket)
Definition node.cc:3613
PropertyRNA * RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
bool RNA_property_array_check(PropertyRNA *prop)
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
char * RNA_property_string_get_alloc(PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len)
char RNA_property_array_item_char(PropertyRNA *prop, int index)
int RNA_struct_ui_icon(const StructRNA *type)
const char * RNA_struct_ui_name(const StructRNA *type)
const char * RNA_property_ui_name(const PropertyRNA *prop)
PropertyRNA * RNA_struct_name_property(const StructRNA *type)
PointerRNA RNA_id_pointer_create(ID *id)
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:560
char * rna_path
int array_index
Definition DNA_ID.h:404
struct bNodeTree * node_group
const char * name
PointerRNA * ptr
Definition wm_files.cc:4227