Grease Pencil animation channels are now also shown in the Dopesheet mode of the Dopesheet editor and in the Timeline. Grease pencil related events are now listened not only by container `SACTCONT_GPENCIL` (Grease Pencil Dopesheet), but also `SACTCONT_DOPESHEET` (main Dopesheet), and `SACTCONT_TIMELINE` (timeline). A new Animation Filter flag was added: `ANIMFILTER_FCURVESONLY`. For now this only filters out Grease Pencil Layer channels. **Implemented:** - Preview range set: now only considers selected Grease Pencil keyframes when `onlySel` parameter is true. Not only this allows the operator to work with grease pencil keyframes in main dopesheet, but it also fixes the operator in the Grease Pencil dopesheet. - Translation: allocation (and freeing) of specific memory for translation of Grease Pencil keyframes. - Copy/Paste: call to both Fcurve and GPencil operators, to allow for mixed selection. Errors are only reported when both the FCurve and GPencil functions fail to paste anything. - Keyframe Type change and Insert Keyframe: removed some code here to unify Grease Pencil dopesheet and main dopesheet code. - Jump, Snap, Mirror, Select all/box/lasso/circle, Select left/right, Clickselect: account for Grease Pencil channels within the channels loop, no need for `ANIMFILTER_FCURVESONLY` there. **Not Implemented:** - Graph-related operators. The filter `ANIMFILTER_FCURVESONLY` is naively added to all graph-related operators, meaning more-or-less all operators that used `ANIMFILTER_CURVE_VISIBLE`. - Select linked: is for F-curves channel only - Select more/less: not yet implemented for grease pencil layers. - Clean Keys, Sample, Extrapolation, Interpolation, Easing, and Handle type change: work on Fcurve-channels only, so the `ANIMFILTER_FCURVESONLY` filter is activated Graying out these operators (when no fcurve keyframe is selected) can be done with custom poll functions BUT may affect performance. This is NOT done in this patch. **Dopesheet Summary Selection:** The main summary of the dopesheet now also takes into account Grease Pencil keyframes, using some nasty copy/pasting of code, as explained [on devtalk](https://devtalk.blender.org/t/gpencil-layers-integration-in-main-dopesheet-selection-issue/24527). It works, but may be improved, providing some deeper changes. Reviewed By: mendio, pepeland, sybren Maniphest Tasks: T97477 Differential Revision: https://developer.blender.org/D15003
933 lines
25 KiB
C
933 lines
25 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2020 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup spgraph
|
|
*
|
|
* Graph Slider Operators
|
|
*
|
|
* This file contains a collection of operators to modify keyframes in the graph editor.
|
|
* All operators are modal and use a slider that allows the user to define a percentage
|
|
* to modify the operator.
|
|
*/
|
|
|
|
#include <float.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_string.h"
|
|
|
|
#include "DNA_anim_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "UI_interface.h"
|
|
|
|
#include "ED_anim_api.h"
|
|
#include "ED_keyframes_edit.h"
|
|
#include "ED_numinput.h"
|
|
#include "ED_screen.h"
|
|
#include "ED_util.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "graph_intern.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Internal Struct & Defines
|
|
* \{ */
|
|
|
|
/* Used to obtain a list of animation channels for the operators to work on. */
|
|
#define OPERATOR_DATA_FILTER \
|
|
(ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | \
|
|
ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS)
|
|
|
|
/* This data type is only used for modal operation. */
|
|
typedef struct tGraphSliderOp {
|
|
bAnimContext ac;
|
|
Scene *scene;
|
|
ScrArea *area;
|
|
ARegion *region;
|
|
|
|
/** A 0-1 value for determining how much we should decimate. */
|
|
PropertyRNA *factor_prop;
|
|
|
|
/** The original bezt curve data (used for restoring fcurves). */
|
|
ListBase bezt_arr_list;
|
|
|
|
struct tSlider *slider;
|
|
|
|
/* Each operator has a specific update function. */
|
|
void (*modal_update)(struct bContext *, struct wmOperator *);
|
|
|
|
NumInput num;
|
|
} tGraphSliderOp;
|
|
|
|
typedef struct tBeztCopyData {
|
|
int tot_vert;
|
|
BezTriple *bezt;
|
|
} tBeztCopyData;
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Utility Functions
|
|
* \{ */
|
|
|
|
/**
|
|
* Construct a list with the original bezt arrays so we can restore them during modal operation.
|
|
* The data is stored on the struct that is passed.
|
|
*/
|
|
static void store_original_bezt_arrays(tGraphSliderOp *gso)
|
|
{
|
|
ListBase anim_data = {NULL, NULL};
|
|
bAnimContext *ac = &gso->ac;
|
|
bAnimListElem *ale;
|
|
|
|
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
|
|
|
/* Loop through filtered data and copy the curves. */
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
|
|
if (fcu->bezt == NULL) {
|
|
/* This curve is baked, skip it. */
|
|
continue;
|
|
}
|
|
|
|
const int arr_size = sizeof(BezTriple) * fcu->totvert;
|
|
|
|
tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy");
|
|
BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array");
|
|
|
|
copy->tot_vert = fcu->totvert;
|
|
memcpy(bezts_copy, fcu->bezt, arr_size);
|
|
|
|
copy->bezt = bezts_copy;
|
|
|
|
LinkData *link = NULL;
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "Bezt Link");
|
|
link->data = copy;
|
|
|
|
BLI_addtail(&gso->bezt_arr_list, link);
|
|
}
|
|
|
|
ANIM_animdata_freelist(&anim_data);
|
|
}
|
|
|
|
/* Overwrite the current bezts arrays with the original data. */
|
|
static void reset_bezts(tGraphSliderOp *gso)
|
|
{
|
|
ListBase anim_data = {NULL, NULL};
|
|
LinkData *link_bezt;
|
|
bAnimListElem *ale;
|
|
|
|
bAnimContext *ac = &gso->ac;
|
|
|
|
/* Filter data. */
|
|
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
|
|
|
/* Loop through filtered data and reset bezts. */
|
|
for (ale = anim_data.first, link_bezt = gso->bezt_arr_list.first; ale; ale = ale->next) {
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
|
|
if (fcu->bezt == NULL) {
|
|
/* This curve is baked, skip it. */
|
|
continue;
|
|
}
|
|
|
|
tBeztCopyData *data = link_bezt->data;
|
|
|
|
const int arr_size = sizeof(BezTriple) * data->tot_vert;
|
|
|
|
MEM_freeN(fcu->bezt);
|
|
|
|
fcu->bezt = MEM_mallocN(arr_size, __func__);
|
|
fcu->totvert = data->tot_vert;
|
|
|
|
memcpy(fcu->bezt, data->bezt, arr_size);
|
|
|
|
link_bezt = link_bezt->next;
|
|
}
|
|
|
|
ANIM_animdata_freelist(&anim_data);
|
|
}
|
|
|
|
/**
|
|
* Get factor value and store it in RNA property.
|
|
* Custom data of #wmOperator needs to contain #tGraphSliderOp.
|
|
*/
|
|
static float slider_factor_get_and_remember(wmOperator *op)
|
|
{
|
|
tGraphSliderOp *gso = op->customdata;
|
|
const float factor = ED_slider_factor_get(gso->slider);
|
|
RNA_property_float_set(op->ptr, gso->factor_prop, factor);
|
|
return factor;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Common Modal Functions
|
|
* \{ */
|
|
|
|
static void graph_slider_exit(bContext *C, wmOperator *op)
|
|
{
|
|
tGraphSliderOp *gso = op->customdata;
|
|
wmWindow *win = CTX_wm_window(C);
|
|
|
|
/* If data exists, clear its data and exit. */
|
|
if (gso == NULL) {
|
|
return;
|
|
}
|
|
|
|
ScrArea *area = gso->area;
|
|
LinkData *link;
|
|
|
|
ED_slider_destroy(C, gso->slider);
|
|
|
|
for (link = gso->bezt_arr_list.first; link != NULL; link = link->next) {
|
|
tBeztCopyData *copy = link->data;
|
|
MEM_freeN(copy->bezt);
|
|
MEM_freeN(link->data);
|
|
}
|
|
|
|
BLI_freelistN(&gso->bezt_arr_list);
|
|
MEM_freeN(gso);
|
|
|
|
/* Return to normal cursor and header status. */
|
|
WM_cursor_modal_restore(win);
|
|
ED_area_status_text(area, NULL);
|
|
|
|
/* cleanup */
|
|
op->customdata = NULL;
|
|
}
|
|
|
|
static int graph_slider_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
tGraphSliderOp *gso = op->customdata;
|
|
|
|
const bool has_numinput = hasNumInput(&gso->num);
|
|
|
|
ED_slider_modal(gso->slider, event);
|
|
|
|
switch (event->type) {
|
|
/* Confirm */
|
|
case LEFTMOUSE:
|
|
case EVT_RETKEY:
|
|
case EVT_PADENTER: {
|
|
if (event->val == KM_PRESS) {
|
|
graph_slider_exit(C, op);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Cancel */
|
|
case EVT_ESCKEY:
|
|
case RIGHTMOUSE: {
|
|
if (event->val == KM_PRESS) {
|
|
reset_bezts(gso);
|
|
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
|
|
graph_slider_exit(C, op);
|
|
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* When the mouse is moved, the percentage and the keyframes update. */
|
|
case MOUSEMOVE: {
|
|
if (has_numinput == false) {
|
|
/* Do the update as specified by the operator. */
|
|
gso->modal_update(C, op);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
if ((event->val == KM_PRESS) && handleNumInput(C, &gso->num, event)) {
|
|
float value;
|
|
float percentage = RNA_property_float_get(op->ptr, gso->factor_prop);
|
|
|
|
/* Grab percentage from numeric input, and store this new value for redo
|
|
* NOTE: users see ints, while internally we use a 0-1 float.
|
|
*/
|
|
value = percentage * 100.0f;
|
|
applyNumInput(&gso->num, &value);
|
|
|
|
percentage = value / 100.0f;
|
|
ED_slider_factor_set(gso->slider, percentage);
|
|
RNA_property_float_set(op->ptr, gso->factor_prop, percentage);
|
|
|
|
gso->modal_update(C, op);
|
|
break;
|
|
}
|
|
|
|
/* Unhandled event - maybe it was some view manipulation? */
|
|
/* Allow to pass through. */
|
|
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
|
|
}
|
|
}
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
/* Allocate tGraphSliderOp and assign to op->customdata. */
|
|
static int graph_slider_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
tGraphSliderOp *gso;
|
|
|
|
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EW_SCROLL);
|
|
|
|
/* Init slide-op data. */
|
|
gso = op->customdata = MEM_callocN(sizeof(tGraphSliderOp), "tGraphSliderOp");
|
|
|
|
/* Get editor data. */
|
|
if (ANIM_animdata_get_context(C, &gso->ac) == 0) {
|
|
graph_slider_exit(C, op);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
gso->scene = CTX_data_scene(C);
|
|
gso->area = CTX_wm_area(C);
|
|
gso->region = CTX_wm_region(C);
|
|
|
|
store_original_bezt_arrays(gso);
|
|
|
|
gso->slider = ED_slider_create(C);
|
|
ED_slider_init(gso->slider, event);
|
|
|
|
if (gso->bezt_arr_list.first == NULL) {
|
|
WM_report(RPT_ERROR, "Cannot find keys to operate on.");
|
|
graph_slider_exit(C, op);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
WM_event_add_modal_handler(C, op);
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Decimate Keyframes Operator
|
|
* \{ */
|
|
|
|
typedef enum tDecimModes {
|
|
DECIM_RATIO = 1,
|
|
DECIM_ERROR,
|
|
} tDecimModes;
|
|
|
|
static void decimate_graph_keys(bAnimContext *ac, float factor, float error_sq_max)
|
|
{
|
|
ListBase anim_data = {NULL, NULL};
|
|
bAnimListElem *ale;
|
|
|
|
/* Filter data. */
|
|
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
|
|
|
/* Loop through filtered data and clean curves. */
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
if (!decimate_fcurve(ale, factor, error_sq_max)) {
|
|
/* The selection contains unsupported keyframe types! */
|
|
WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!");
|
|
}
|
|
|
|
ale->update |= ANIM_UPDATE_DEFAULT;
|
|
}
|
|
|
|
ANIM_animdata_update(ac, &anim_data);
|
|
ANIM_animdata_freelist(&anim_data);
|
|
}
|
|
|
|
/* Draw a percentage indicator in workspace footer. */
|
|
static void decimate_draw_status(bContext *C, tGraphSliderOp *gso)
|
|
{
|
|
char status_str[UI_MAX_DRAW_STR];
|
|
char mode_str[32];
|
|
char slider_string[UI_MAX_DRAW_STR];
|
|
|
|
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
|
|
|
|
strcpy(mode_str, TIP_("Decimate Keyframes"));
|
|
|
|
if (hasNumInput(&gso->num)) {
|
|
char str_ofs[NUM_STR_REP_LEN];
|
|
|
|
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
|
|
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
|
|
}
|
|
else {
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
|
|
}
|
|
|
|
ED_workspace_status_text(C, status_str);
|
|
}
|
|
|
|
static void decimate_modal_update(bContext *C, wmOperator *op)
|
|
{
|
|
/* Perform decimate updates - in response to some user action
|
|
* (e.g. pressing a key or moving the mouse). */
|
|
tGraphSliderOp *gso = op->customdata;
|
|
|
|
decimate_draw_status(C, gso);
|
|
|
|
/* Reset keyframe data (so we get back to the original state). */
|
|
reset_bezts(gso);
|
|
|
|
/* Apply... */
|
|
const float factor = slider_factor_get_and_remember(op);
|
|
/* We don't want to limit the decimation to a certain error margin. */
|
|
const float error_sq_max = FLT_MAX;
|
|
decimate_graph_keys(&gso->ac, factor, error_sq_max);
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
}
|
|
|
|
static int decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
const int invoke_result = graph_slider_invoke(C, op, event);
|
|
|
|
if (invoke_result == OPERATOR_CANCELLED) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
tGraphSliderOp *gso = op->customdata;
|
|
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
|
|
gso->modal_update = decimate_modal_update;
|
|
ED_slider_allow_overshoot_set(gso->slider, false);
|
|
|
|
return invoke_result;
|
|
}
|
|
|
|
static int decimate_exec(bContext *C, wmOperator *op)
|
|
{
|
|
bAnimContext ac;
|
|
|
|
/* Get editor data. */
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
tDecimModes mode = RNA_enum_get(op->ptr, "mode");
|
|
/* We want to be able to work on all available keyframes. */
|
|
float factor = 1.0f;
|
|
/* We don't want to limit the decimation to a certain error margin. */
|
|
float error_sq_max = FLT_MAX;
|
|
|
|
switch (mode) {
|
|
case DECIM_RATIO:
|
|
factor = RNA_float_get(op->ptr, "factor");
|
|
break;
|
|
case DECIM_ERROR:
|
|
error_sq_max = RNA_float_get(op->ptr, "remove_error_margin");
|
|
/* The decimate algorithm expects the error to be squared. */
|
|
error_sq_max *= error_sq_max;
|
|
|
|
break;
|
|
}
|
|
|
|
if (factor == 0.0f || error_sq_max == 0.0f) {
|
|
/* Nothing to remove. */
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
decimate_graph_keys(&ac, factor, error_sq_max);
|
|
|
|
/* Set notifier that keyframes have changed. */
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static bool decimate_poll_property(const bContext *UNUSED(C),
|
|
wmOperator *op,
|
|
const PropertyRNA *prop)
|
|
{
|
|
const char *prop_id = RNA_property_identifier(prop);
|
|
|
|
if (STRPREFIX(prop_id, "remove")) {
|
|
int mode = RNA_enum_get(op->ptr, "mode");
|
|
|
|
if (STREQ(prop_id, "factor") && mode != DECIM_RATIO) {
|
|
return false;
|
|
}
|
|
if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static char *decimate_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), PointerRNA *ptr)
|
|
{
|
|
|
|
if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) {
|
|
return BLI_strdup(
|
|
TIP_("Decimate F-Curves by specifying how much they can deviate from the original curve"));
|
|
}
|
|
|
|
/* Use default description. */
|
|
return NULL;
|
|
}
|
|
|
|
static const EnumPropertyItem decimate_mode_items[] = {
|
|
{DECIM_RATIO,
|
|
"RATIO",
|
|
0,
|
|
"Ratio",
|
|
"Use a percentage to specify how many keyframes you want to remove"},
|
|
{DECIM_ERROR,
|
|
"ERROR",
|
|
0,
|
|
"Error Margin",
|
|
"Use an error margin to specify how much the curve is allowed to deviate from the original "
|
|
"path"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
void GRAPH_OT_decimate(wmOperatorType *ot)
|
|
{
|
|
/* Identifiers */
|
|
ot->name = "Decimate Keyframes";
|
|
ot->idname = "GRAPH_OT_decimate";
|
|
ot->description =
|
|
"Decimate F-Curves by removing keyframes that influence the curve shape the least";
|
|
|
|
/* API callbacks */
|
|
ot->poll_property = decimate_poll_property;
|
|
ot->get_description = decimate_desc;
|
|
ot->invoke = decimate_invoke;
|
|
ot->modal = graph_slider_modal;
|
|
ot->exec = decimate_exec;
|
|
ot->poll = graphop_editable_keyframes_poll;
|
|
|
|
/* Flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* Properties */
|
|
RNA_def_enum(ot->srna,
|
|
"mode",
|
|
decimate_mode_items,
|
|
DECIM_RATIO,
|
|
"Mode",
|
|
"Which mode to use for decimation");
|
|
|
|
RNA_def_float_factor(ot->srna,
|
|
"factor",
|
|
1.0f / 3.0f,
|
|
0.0f,
|
|
1.0f,
|
|
"Remove",
|
|
"The ratio of remaining keyframes after the operation",
|
|
0.0f,
|
|
1.0f);
|
|
RNA_def_float(ot->srna,
|
|
"remove_error_margin",
|
|
0.0f,
|
|
0.0f,
|
|
FLT_MAX,
|
|
"Max Error Margin",
|
|
"How much the new decimated curve is allowed to deviate from the original",
|
|
0.0f,
|
|
10.0f);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Blend to Neighbor Operator
|
|
* \{ */
|
|
|
|
static void blend_to_neighbor_graph_keys(bAnimContext *ac, float factor)
|
|
{
|
|
ListBase anim_data = {NULL, NULL};
|
|
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
|
|
|
bAnimListElem *ale;
|
|
|
|
/* Loop through filtered data and blend keys. */
|
|
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
ListBase segments = find_fcurve_segments(fcu);
|
|
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
|
|
blend_to_neighbor_fcurve_segment(fcu, segment, factor);
|
|
}
|
|
BLI_freelistN(&segments);
|
|
ale->update |= ANIM_UPDATE_DEFAULT;
|
|
}
|
|
|
|
ANIM_animdata_update(ac, &anim_data);
|
|
ANIM_animdata_freelist(&anim_data);
|
|
}
|
|
|
|
static void blend_to_neighbor_draw_status_header(bContext *C, tGraphSliderOp *gso)
|
|
{
|
|
char status_str[UI_MAX_DRAW_STR];
|
|
char mode_str[32];
|
|
char slider_string[UI_MAX_DRAW_STR];
|
|
|
|
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
|
|
|
|
strcpy(mode_str, TIP_("Blend to Neighbor"));
|
|
|
|
if (hasNumInput(&gso->num)) {
|
|
char str_ofs[NUM_STR_REP_LEN];
|
|
|
|
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
|
|
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
|
|
}
|
|
else {
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
|
|
}
|
|
|
|
ED_workspace_status_text(C, status_str);
|
|
}
|
|
|
|
static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op)
|
|
{
|
|
tGraphSliderOp *gso = op->customdata;
|
|
|
|
blend_to_neighbor_draw_status_header(C, gso);
|
|
|
|
/* Reset keyframe data to the state at invoke. */
|
|
reset_bezts(gso);
|
|
|
|
const float factor = slider_factor_get_and_remember(op);
|
|
blend_to_neighbor_graph_keys(&gso->ac, factor);
|
|
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
}
|
|
|
|
static int blend_to_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
const int invoke_result = graph_slider_invoke(C, op, event);
|
|
|
|
if (invoke_result == OPERATOR_CANCELLED) {
|
|
return invoke_result;
|
|
}
|
|
|
|
tGraphSliderOp *gso = op->customdata;
|
|
gso->modal_update = blend_to_neighbor_modal_update;
|
|
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
|
|
blend_to_neighbor_draw_status_header(C, gso);
|
|
|
|
return invoke_result;
|
|
}
|
|
|
|
static int blend_to_neighbor_exec(bContext *C, wmOperator *op)
|
|
{
|
|
bAnimContext ac;
|
|
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
const float factor = RNA_float_get(op->ptr, "factor");
|
|
|
|
blend_to_neighbor_graph_keys(&ac, factor);
|
|
|
|
/* Set notifier that keyframes have changed. */
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void GRAPH_OT_blend_to_neighbor(wmOperatorType *ot)
|
|
{
|
|
/* Identifiers. */
|
|
ot->name = "Blend to Neighbor";
|
|
ot->idname = "GRAPH_OT_blend_to_neighbor";
|
|
ot->description = "Blend selected keyframes to their left or right neighbor";
|
|
|
|
/* API callbacks. */
|
|
ot->invoke = blend_to_neighbor_invoke;
|
|
ot->modal = graph_slider_modal;
|
|
ot->exec = blend_to_neighbor_exec;
|
|
ot->poll = graphop_editable_keyframes_poll;
|
|
|
|
/* Flags. */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
RNA_def_float_factor(ot->srna,
|
|
"factor",
|
|
1.0f / 3.0f,
|
|
-FLT_MAX,
|
|
FLT_MAX,
|
|
"Blend",
|
|
"The blend factor with 0.5 being the current frame",
|
|
0.0f,
|
|
1.0f);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Breakdown Operator
|
|
* \{ */
|
|
|
|
static void breakdown_graph_keys(bAnimContext *ac, float factor)
|
|
{
|
|
ListBase anim_data = {NULL, NULL};
|
|
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
|
|
|
bAnimListElem *ale;
|
|
|
|
for (ale = anim_data.first; ale; ale = ale->next) {
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
ListBase segments = find_fcurve_segments(fcu);
|
|
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
|
|
breakdown_fcurve_segment(fcu, segment, factor);
|
|
}
|
|
BLI_freelistN(&segments);
|
|
ale->update |= ANIM_UPDATE_DEFAULT;
|
|
}
|
|
|
|
ANIM_animdata_update(ac, &anim_data);
|
|
ANIM_animdata_freelist(&anim_data);
|
|
}
|
|
|
|
static void breakdown_draw_status_header(bContext *C, tGraphSliderOp *gso)
|
|
{
|
|
char status_str[UI_MAX_DRAW_STR];
|
|
char mode_str[32];
|
|
char slider_string[UI_MAX_DRAW_STR];
|
|
|
|
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
|
|
|
|
strcpy(mode_str, TIP_("Breakdown"));
|
|
|
|
if (hasNumInput(&gso->num)) {
|
|
char str_ofs[NUM_STR_REP_LEN];
|
|
|
|
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
|
|
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
|
|
}
|
|
else {
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
|
|
}
|
|
|
|
ED_workspace_status_text(C, status_str);
|
|
}
|
|
|
|
static void breakdown_modal_update(bContext *C, wmOperator *op)
|
|
{
|
|
tGraphSliderOp *gso = op->customdata;
|
|
|
|
breakdown_draw_status_header(C, gso);
|
|
|
|
/* Reset keyframe data to the state at invoke. */
|
|
reset_bezts(gso);
|
|
const float factor = slider_factor_get_and_remember(op);
|
|
breakdown_graph_keys(&gso->ac, factor);
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
}
|
|
|
|
static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
const int invoke_result = graph_slider_invoke(C, op, event);
|
|
|
|
if (invoke_result == OPERATOR_CANCELLED) {
|
|
return invoke_result;
|
|
}
|
|
|
|
tGraphSliderOp *gso = op->customdata;
|
|
gso->modal_update = breakdown_modal_update;
|
|
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
|
|
breakdown_draw_status_header(C, gso);
|
|
|
|
return invoke_result;
|
|
}
|
|
|
|
static int breakdown_exec(bContext *C, wmOperator *op)
|
|
{
|
|
bAnimContext ac;
|
|
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
const float factor = RNA_float_get(op->ptr, "factor");
|
|
|
|
breakdown_graph_keys(&ac, factor);
|
|
|
|
/* Set notifier that keyframes have changed. */
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void GRAPH_OT_breakdown(wmOperatorType *ot)
|
|
{
|
|
/* Identifiers. */
|
|
ot->name = "Breakdown";
|
|
ot->idname = "GRAPH_OT_breakdown";
|
|
ot->description = "Move selected keyframes to an inbetween position relative to adjacent keys";
|
|
|
|
/* API callbacks. */
|
|
ot->invoke = breakdown_invoke;
|
|
ot->modal = graph_slider_modal;
|
|
ot->exec = breakdown_exec;
|
|
ot->poll = graphop_editable_keyframes_poll;
|
|
|
|
/* Flags. */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
RNA_def_float_factor(ot->srna,
|
|
"factor",
|
|
1.0f / 3.0f,
|
|
-FLT_MAX,
|
|
FLT_MAX,
|
|
"Factor",
|
|
"Favor either the left or the right key",
|
|
0.0f,
|
|
1.0f);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Blend to Default Value Operator
|
|
* \{ */
|
|
|
|
static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
|
|
{
|
|
ListBase anim_data = {NULL, NULL};
|
|
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
|
|
|
|
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
|
FCurve *fcu = (FCurve *)ale->key_data;
|
|
|
|
/* Check if the curves actually have any points. */
|
|
if (fcu == NULL || fcu->bezt == NULL || fcu->totvert == 0) {
|
|
continue;
|
|
}
|
|
|
|
PointerRNA id_ptr;
|
|
RNA_id_pointer_create(ale->id, &id_ptr);
|
|
|
|
blend_to_default_fcurve(&id_ptr, fcu, factor);
|
|
ale->update |= ANIM_UPDATE_DEFAULT;
|
|
}
|
|
|
|
ANIM_animdata_update(ac, &anim_data);
|
|
ANIM_animdata_freelist(&anim_data);
|
|
}
|
|
|
|
static void blend_to_default_draw_status_header(bContext *C, tGraphSliderOp *gso)
|
|
{
|
|
char status_str[UI_MAX_DRAW_STR];
|
|
char mode_str[32];
|
|
char slider_string[UI_MAX_DRAW_STR];
|
|
|
|
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
|
|
|
|
strcpy(mode_str, TIP_("Blend to Default Value"));
|
|
|
|
if (hasNumInput(&gso->num)) {
|
|
char str_ofs[NUM_STR_REP_LEN];
|
|
|
|
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
|
|
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
|
|
}
|
|
else {
|
|
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
|
|
}
|
|
|
|
ED_workspace_status_text(C, status_str);
|
|
}
|
|
|
|
static void blend_to_default_modal_update(bContext *C, wmOperator *op)
|
|
{
|
|
tGraphSliderOp *gso = op->customdata;
|
|
|
|
blend_to_default_draw_status_header(C, gso);
|
|
|
|
/* Set notifier that keyframes have changed. */
|
|
reset_bezts(gso);
|
|
const float factor = ED_slider_factor_get(gso->slider);
|
|
RNA_property_float_set(op->ptr, gso->factor_prop, factor);
|
|
blend_to_default_graph_keys(&gso->ac, factor);
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
}
|
|
|
|
static int blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
const int invoke_result = graph_slider_invoke(C, op, event);
|
|
|
|
if (invoke_result == OPERATOR_CANCELLED) {
|
|
return invoke_result;
|
|
}
|
|
|
|
tGraphSliderOp *gso = op->customdata;
|
|
gso->modal_update = blend_to_default_modal_update;
|
|
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
|
|
blend_to_default_draw_status_header(C, gso);
|
|
|
|
return invoke_result;
|
|
}
|
|
|
|
static int blend_to_default_exec(bContext *C, wmOperator *op)
|
|
{
|
|
bAnimContext ac;
|
|
|
|
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
const float factor = RNA_float_get(op->ptr, "factor");
|
|
|
|
blend_to_default_graph_keys(&ac, factor);
|
|
|
|
/* Set notifier that keyframes have changed. */
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void GRAPH_OT_blend_to_default(wmOperatorType *ot)
|
|
{
|
|
/* Identifiers. */
|
|
ot->name = "Blend to Default Value";
|
|
ot->idname = "GRAPH_OT_blend_to_default";
|
|
ot->description = "Blend selected keys to their default value from their current position";
|
|
|
|
/* API callbacks. */
|
|
ot->invoke = blend_to_default_invoke;
|
|
ot->modal = graph_slider_modal;
|
|
ot->exec = blend_to_default_exec;
|
|
ot->poll = graphop_editable_keyframes_poll;
|
|
|
|
/* Flags. */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
RNA_def_float_factor(ot->srna,
|
|
"factor",
|
|
1.0f / 3.0f,
|
|
-FLT_MAX,
|
|
FLT_MAX,
|
|
"Factor",
|
|
"How much to blend to the default value",
|
|
0.0f,
|
|
1.0f);
|
|
}
|
|
/** \} */
|