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