Blender  V2.93
graph_slider_ops.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 #include <float.h>
21 #include <string.h>
22 
23 #include "MEM_guardedalloc.h"
24 
25 #include "BLI_listbase.h"
26 #include "BLI_string.h"
27 
28 #include "DNA_anim_types.h"
29 #include "DNA_scene_types.h"
30 
31 #include "RNA_access.h"
32 #include "RNA_define.h"
33 
34 #include "BLT_translation.h"
35 
36 #include "BKE_context.h"
37 
38 #include "UI_interface.h"
39 
40 #include "ED_anim_api.h"
41 #include "ED_keyframes_edit.h"
42 #include "ED_numinput.h"
43 #include "ED_screen.h"
44 
45 #include "WM_api.h"
46 #include "WM_types.h"
47 
48 #include "graph_intern.h"
49 
50 /* ******************** GRAPH SLIDER OPERATORS ************************* */
51 /* This file contains a collection of operators to modify keyframes in the graph editor. All
52  * operators are modal and use a slider that allows the user to define a percentage to modify the
53  * operator.*/
54 
55 /* ******************** Decimate Keyframes Operator ************************* */
56 
57 static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max)
58 {
59  ListBase anim_data = {NULL, NULL};
60  bAnimListElem *ale;
61  int filter;
62 
63  /* Filter data. */
66  ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
67 
68  /* Loop through filtered data and clean curves. */
69  for (ale = anim_data.first; ale; ale = ale->next) {
70  if (!decimate_fcurve(ale, remove_ratio, error_sq_max)) {
71  /* The selection contains unsupported keyframe types! */
72  WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!");
73  }
74 
76  }
77 
78  ANIM_animdata_update(ac, &anim_data);
79  ANIM_animdata_freelist(&anim_data);
80 }
81 
82 /* ------------------- */
83 
84 /* This data type is only used for modal operation. */
85 typedef struct tDecimateGraphOp {
90 
93 
96 
99 
100 typedef struct tBeztCopyData {
101  int tot_vert;
104 
105 typedef enum tDecimModes {
109 
110 /* Overwrite the current bezts arrays with the original data. */
112 {
113  ListBase anim_data = {NULL, NULL};
114  LinkData *link_bezt;
115  bAnimListElem *ale;
116  int filter;
117 
118  bAnimContext *ac = &dgo->ac;
119 
120  /* Filter data. */
123  ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
124 
125  /* Loop through filtered data and reset bezts. */
126  for (ale = anim_data.first, link_bezt = dgo->bezt_arr_list.first; ale; ale = ale->next) {
127  FCurve *fcu = (FCurve *)ale->key_data;
128 
129  if (fcu->bezt == NULL) {
130  /* This curve is baked, skip it. */
131  continue;
132  }
133 
134  tBeztCopyData *data = link_bezt->data;
135 
136  const int arr_size = sizeof(BezTriple) * data->tot_vert;
137 
138  MEM_freeN(fcu->bezt);
139 
140  fcu->bezt = MEM_mallocN(arr_size, __func__);
141  fcu->totvert = data->tot_vert;
142 
143  memcpy(fcu->bezt, data->bezt, arr_size);
144 
145  link_bezt = link_bezt->next;
146  }
147 
148  ANIM_animdata_freelist(&anim_data);
149 }
150 
151 static void decimate_exit(bContext *C, wmOperator *op)
152 {
153  tDecimateGraphOp *dgo = op->customdata;
154  wmWindow *win = CTX_wm_window(C);
155 
156  /* If data exists, clear its data and exit. */
157  if (dgo == NULL) {
158  return;
159  }
160 
161  ScrArea *area = dgo->area;
162  LinkData *link;
163 
164  for (link = dgo->bezt_arr_list.first; link != NULL; link = link->next) {
165  tBeztCopyData *copy = link->data;
166  MEM_freeN(copy->bezt);
167  MEM_freeN(link->data);
168  }
169 
171  MEM_freeN(dgo);
172 
173  /* Return to normal cursor and header status. */
176 
177  /* Cleanup. */
178  op->customdata = NULL;
179 }
180 
181 /* Draw a percentage indicator in header. */
183 {
184  char status_str[UI_MAX_DRAW_STR];
185  char mode_str[32];
186 
187  strcpy(mode_str, TIP_("Decimate Keyframes"));
188 
189  if (hasNumInput(&dgo->num)) {
190  char str_ofs[NUM_STR_REP_LEN];
191 
192  outputNumInput(&dgo->num, str_ofs, &dgo->scene->unit);
193 
194  BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
195  }
196  else {
197  float percentage = RNA_property_float_get(op->ptr, dgo->percentage_prop);
198  BLI_snprintf(
199  status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(percentage * 100.0f));
200  }
201 
202  ED_area_status_text(dgo->area, status_str);
203 }
204 
205 /* Calculate percentage based on position of mouse (we only use x-axis for now.
206  * Since this is more convenient for users to do), and store new percentage value.
207  */
209  wmOperator *op,
210  const wmEvent *event)
211 {
212  float percentage = (event->x - dgo->region->winrct.xmin) / ((float)dgo->region->winx);
213  RNA_property_float_set(op->ptr, dgo->percentage_prop, percentage);
214 }
215 
216 static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
217 {
218  tDecimateGraphOp *dgo;
219 
221 
222  /* Init slide-op data. */
223  dgo = op->customdata = MEM_callocN(sizeof(tDecimateGraphOp), "tDecimateGraphOp");
224 
225  /* Get editor data. */
226  if (ANIM_animdata_get_context(C, &dgo->ac) == 0) {
227  decimate_exit(C, op);
228  return OPERATOR_CANCELLED;
229  }
230 
231  dgo->percentage_prop = RNA_struct_find_property(op->ptr, "remove_ratio");
232 
233  dgo->scene = CTX_data_scene(C);
234  dgo->area = CTX_wm_area(C);
235  dgo->region = CTX_wm_region(C);
236 
237  /* Initialize percentage so that it will have the correct value before the first mouse move. */
238  decimate_mouse_update_percentage(dgo, op, event);
239 
241 
242  /* Construct a list with the original bezt arrays so we can restore them during modal operation.
243  */
244  {
245  ListBase anim_data = {NULL, NULL};
246  bAnimContext *ac = &dgo->ac;
247  bAnimListElem *ale;
248 
249  int filter;
250 
251  /* Filter data. */
254  ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
255 
256  /* Loop through filtered data and copy the curves. */
257  for (ale = anim_data.first; ale; ale = ale->next) {
258  FCurve *fcu = (FCurve *)ale->key_data;
259 
260  if (fcu->bezt == NULL) {
261  /* This curve is baked, skip it. */
262  continue;
263  }
264 
265  const int arr_size = sizeof(BezTriple) * fcu->totvert;
266 
267  tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy");
268  BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array");
269 
270  copy->tot_vert = fcu->totvert;
271  memcpy(bezts_copy, fcu->bezt, arr_size);
272 
273  copy->bezt = bezts_copy;
274 
275  LinkData *link = NULL;
276 
277  link = MEM_callocN(sizeof(LinkData), "Bezt Link");
278  link->data = copy;
279 
280  BLI_addtail(&dgo->bezt_arr_list, link);
281  }
282 
283  ANIM_animdata_freelist(&anim_data);
284  }
285 
286  if (dgo->bezt_arr_list.first == NULL) {
288  "Fcurve Decimate: Can't decimate baked channels. Unbake them and try again.");
289  decimate_exit(C, op);
290  return OPERATOR_CANCELLED;
291  }
292 
294  return OPERATOR_RUNNING_MODAL;
295 }
296 
298 {
299  /* Perform decimate updates - in response to some user action
300  * (e.g. pressing a key or moving the mouse). */
301  tDecimateGraphOp *dgo = op->customdata;
302 
304 
305  /* Reset keyframe data (so we get back to the original state). */
307 
308  /* Apply... */
309  float remove_ratio = RNA_property_float_get(op->ptr, dgo->percentage_prop);
310  /* We don't want to limit the decimation to a certain error margin. */
311  const float error_sq_max = FLT_MAX;
312  decimate_graph_keys(&dgo->ac, remove_ratio, error_sq_max);
314 }
315 
316 static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent *event)
317 {
318  /* This assumes that we are in "DECIM_RATIO" mode. This is because the error margin is very hard
319  * and finicky to control with this modal mouse grab method. Therefore, it is expected that the
320  * error margin mode is not adjusted by the modal operator but instead tweaked via the redo
321  * panel.*/
322  tDecimateGraphOp *dgo = op->customdata;
323 
324  const bool has_numinput = hasNumInput(&dgo->num);
325 
326  switch (event->type) {
327  case LEFTMOUSE: /* Confirm */
328  case EVT_RETKEY:
329  case EVT_PADENTER: {
330  if (event->val == KM_PRESS) {
331  decimate_exit(C, op);
332 
333  return OPERATOR_FINISHED;
334  }
335  break;
336  }
337 
338  case EVT_ESCKEY: /* Cancel */
339  case RIGHTMOUSE: {
340  if (event->val == KM_PRESS) {
342 
344 
345  decimate_exit(C, op);
346 
347  return OPERATOR_CANCELLED;
348  }
349  break;
350  }
351 
352  /* Percentage Change... */
353  case MOUSEMOVE: /* Calculate new position. */
354  {
355  if (has_numinput == false) {
356  /* Update percentage based on position of mouse. */
357  decimate_mouse_update_percentage(dgo, op, event);
358 
359  /* Update pose to reflect the new values. */
361  }
362  break;
363  }
364  default: {
365  if ((event->val == KM_PRESS) && handleNumInput(C, &dgo->num, event)) {
366  float value;
367  float percentage = RNA_property_float_get(op->ptr, dgo->percentage_prop);
368 
369  /* Grab percentage from numeric input, and store this new value for redo
370  * NOTE: users see ints, while internally we use a 0-1 float.
371  */
372  value = percentage * 100.0f;
373  applyNumInput(&dgo->num, &value);
374 
375  percentage = value / 100.0f;
376  RNA_property_float_set(op->ptr, dgo->percentage_prop, percentage);
377 
378  /* Update decimate output to reflect the new values. */
380  break;
381  }
382 
383  /* Unhandled event - maybe it was some view manipulation? */
384  /* Allow to pass through. */
386  }
387  }
388 
389  return OPERATOR_RUNNING_MODAL;
390 }
391 
393 {
394  bAnimContext ac;
395 
396  /* Get editor data. */
397  if (ANIM_animdata_get_context(C, &ac) == 0) {
398  return OPERATOR_CANCELLED;
399  }
400 
401  tDecimModes mode = RNA_enum_get(op->ptr, "mode");
402  /* We want to be able to work on all available keyframes. */
403  float remove_ratio = 1.0f;
404  /* We don't want to limit the decimation to a certain error margin. */
405  float error_sq_max = FLT_MAX;
406 
407  switch (mode) {
408  case DECIM_RATIO:
409  remove_ratio = RNA_float_get(op->ptr, "remove_ratio");
410  break;
411  case DECIM_ERROR:
412  error_sq_max = RNA_float_get(op->ptr, "remove_error_margin");
413  /* The decimate algorithm expects the error to be squared. */
414  error_sq_max *= error_sq_max;
415 
416  break;
417  }
418 
419  if (remove_ratio == 0.0f || error_sq_max == 0.0f) {
420  /* Nothing to remove. */
421  return OPERATOR_FINISHED;
422  }
423 
424  decimate_graph_keys(&ac, remove_ratio, error_sq_max);
425 
426  /* Set notifier that keyframes have changed. */
428 
429  return OPERATOR_FINISHED;
430 }
431 
433  wmOperator *op,
434  const PropertyRNA *prop)
435 {
436  const char *prop_id = RNA_property_identifier(prop);
437 
438  if (STRPREFIX(prop_id, "remove")) {
439  int mode = RNA_enum_get(op->ptr, "mode");
440 
441  if (STREQ(prop_id, "remove_ratio") && mode != DECIM_RATIO) {
442  return false;
443  }
444  if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
445  return false;
446  }
447  }
448 
449  return true;
450 }
451 
453  wmOperatorType *UNUSED(op),
454  PointerRNA *ptr)
455 {
456 
457  if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) {
458  return BLI_strdup(
459  "Decimate F-Curves by specifying how much it can deviate from the original curve");
460  }
461 
462  /* Use default description. */
463  return NULL;
464 }
465 
467  {DECIM_RATIO,
468  "RATIO",
469  0,
470  "Ratio",
471  "Use a percentage to specify how many keyframes you want to remove"},
472  {DECIM_ERROR,
473  "ERROR",
474  0,
475  "Error Margin",
476  "Use an error margin to specify how much the curve is allowed to deviate from the original "
477  "path"},
478  {0, NULL, 0, NULL, NULL},
479 };
480 
482 {
483  /* Identifiers */
484  ot->name = "Decimate Keyframes";
485  ot->idname = "GRAPH_OT_decimate";
486  ot->description =
487  "Decimate F-Curves by removing keyframes that influence the curve shape the least";
488 
489  /* API callbacks */
496 
497  /* Flags */
499 
500  /* Properties */
502  "mode",
504  DECIM_RATIO,
505  "Mode",
506  "Which mode to use for decimation");
507 
509  "remove_ratio",
510  1.0f / 3.0f,
511  0.0f,
512  1.0f,
513  "Remove",
514  "The ratio of remaining keyframes after the operation",
515  0.0f,
516  1.0f);
518  "remove_error_margin",
519  0.0f,
520  0.0f,
521  FLT_MAX,
522  "Max Error Margin",
523  "How much the new decimated curve is allowed to deviate from the original",
524  0.0f,
525  10.0f);
526 }
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:547
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:70
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define STRPREFIX(a, b)
#define UNUSED(x)
#define STREQ(a, b)
#define TIP_(msgid)
struct BezTriple BezTriple
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define ANIM_UPDATE_DEFAULT
Definition: ED_anim_api.h:281
@ ANIMFILTER_FOREDIT
Definition: ED_anim_api.h:315
@ ANIMFILTER_DATA_VISIBLE
Definition: ED_anim_api.h:295
@ ANIMFILTER_CURVE_VISIBLE
Definition: ED_anim_api.h:300
@ ANIMFILTER_NODUPLIS
Definition: ED_anim_api.h:328
@ ANIMFILTER_SEL
Definition: ED_anim_api.h:311
void outputNumInput(NumInput *n, char *str, struct UnitSettings *unit_settings)
Definition: numinput.c:102
#define NUM_STR_REP_LEN
Definition: ED_numinput.h:27
bool applyNumInput(NumInput *n, float *vec)
Definition: numinput.c:207
bool hasNumInput(const NumInput *n)
Definition: numinput.c:185
bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event)
void ED_area_status_text(ScrArea *area, const char *str)
Definition: area.c:815
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:39
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:90
@ OPTYPE_UNDO
Definition: WM_types.h:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define NC_ANIMATION
Definition: WM_types.h:289
#define NA_EDITED
Definition: WM_types.h:462
#define KM_PRESS
Definition: WM_types.h:242
#define ND_KEYFRAME
Definition: WM_types.h:394
void ANIM_animdata_freelist(ListBase *anim_data)
Definition: anim_deps.c:425
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition: anim_deps.c:330
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
Definition: anim_filter.c:405
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode, void *data, eAnimCont_Types datatype)
Definition: anim_filter.c:3442
bool graphop_editable_keyframes_poll(struct bContext *C)
Definition: graph_utils.c:184
static void decimate_exit(bContext *C, wmOperator *op)
static void decimate_draw_status_header(wmOperator *op, tDecimateGraphOp *dgo)
static void decimate_mouse_update_percentage(tDecimateGraphOp *dgo, wmOperator *op, const wmEvent *event)
static void graphkeys_decimate_modal_update(bContext *C, wmOperator *op)
static const EnumPropertyItem decimate_mode_items[]
tDecimModes
@ DECIM_RATIO
@ DECIM_ERROR
static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void decimate_reset_bezts(tDecimateGraphOp *dgo)
static bool graphkeys_decimate_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop)
struct tDecimateGraphOp tDecimateGraphOp
static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_decimate(wmOperatorType *ot)
static int graphkeys_decimate_exec(bContext *C, wmOperator *op)
static char * graphkeys_decimate_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), PointerRNA *ptr)
static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max)
struct tBeztCopyData tBeztCopyData
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
bool decimate_fcurve(bAnimListElem *ale, float remove_ratio, float error_sq_max)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static void area(int d1, int d2, int e1, int e2, float weights[2])
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2941
const char * RNA_property_identifier(const PropertyRNA *prop)
Definition: rna_access.c:1145
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:866
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6355
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
Definition: rna_access.c:2964
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:3825
PropertyRNA * RNA_def_float_factor(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:4133
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3771
BezTriple * bezt
unsigned int totvert
void * data
Definition: DNA_listBase.h:42
struct LinkData * next
Definition: DNA_listBase.h:41
void * first
Definition: DNA_listBase.h:47
struct UnitSettings unit
short datatype
Definition: ED_anim_api.h:75
void * data
Definition: ED_anim_api.h:73
struct bAnimListElem * next
Definition: ED_anim_api.h:135
void * key_data
Definition: ED_anim_api.h:154
int xmin
Definition: DNA_vec_types.h:79
BezTriple * bezt
PropertyRNA * percentage_prop
short val
Definition: WM_types.h:579
short type
Definition: WM_types.h:577
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:752
const char * name
Definition: WM_types.h:721
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:768
const char * idname
Definition: WM_types.h:723
char *(* get_description)(struct bContext *C, struct wmOperatorType *, struct PointerRNA *)
Definition: WM_types.h:799
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
bool(* poll_property)(const struct bContext *C, struct wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:782
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
struct PointerRNA * ptr
void WM_cursor_modal_set(wmWindow *win, int val)
Definition: wm_cursors.c:207
void WM_cursor_modal_restore(wmWindow *win)
Definition: wm_cursors.c:216
@ WM_CURSOR_EW_SCROLL
Definition: wm_cursors.h:69
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_report(ReportType type, const char *message)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ EVT_PADENTER
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition: wm_files.c:3157
wmOperatorType * ot
Definition: wm_files.c:3156