Animation: Equalize Handle Operator
The Equalize Handles operator allows users to make selected handle lengths uniform: either respecting their original angle from the key control point or by flattening their angle (removing the overshoot sometimes produced by certain handle types). Design: T94172 Reviewed by: sybren Differential Revision: https://developer.blender.org/D13702
This commit is contained in:
parent
a000de7c2a
commit
17b0c06946
|
@ -335,6 +335,7 @@ class GRAPH_MT_key_snap(Menu):
|
|||
layout.operator("graph.snap", text="Selection to Nearest Second").type = 'NEAREST_SECOND'
|
||||
layout.operator("graph.snap", text="Selection to Nearest Marker").type = 'NEAREST_MARKER'
|
||||
layout.operator("graph.snap", text="Flatten Handles").type = 'HORIZONTAL'
|
||||
layout.operator("graph.equalize_handles", text="Equalize Handles").side = 'BOTH'
|
||||
layout.separator()
|
||||
layout.operator("graph.frame_jump", text="Cursor to Selection")
|
||||
layout.operator("graph.snap_cursor_value", text="Cursor Value to Selection")
|
||||
|
|
|
@ -1283,6 +1283,61 @@ static short set_bezt_sine(KeyframeEditData *UNUSED(ked), BezTriple *bezt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
|
||||
{
|
||||
BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index");
|
||||
|
||||
add_v2_v2v2(vec[idx], vec[1], direction);
|
||||
}
|
||||
|
||||
static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
|
||||
{
|
||||
BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index");
|
||||
|
||||
float handle_direction[2];
|
||||
sub_v2_v2v2(handle_direction, vec[idx], vec[1]);
|
||||
normalize_v2_length(handle_direction, handle_length);
|
||||
add_v2_v2v2(vec[idx], vec[1], handle_direction);
|
||||
}
|
||||
|
||||
void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu,
|
||||
const eEditKeyframes_Equalize mode,
|
||||
const float handle_length,
|
||||
const bool flatten)
|
||||
{
|
||||
uint i;
|
||||
BezTriple *bezt;
|
||||
const float flat_direction_left[2] = {-handle_length, 0.f};
|
||||
const float flat_direction_right[2] = {handle_length, 0.f};
|
||||
|
||||
/* Loop through an F-Curves keyframes. */
|
||||
for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
|
||||
if ((bezt->f2 & SELECT) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Perform handle equalization if mode is 'Both' or 'Left'. */
|
||||
if (mode & EQUALIZE_HANDLES_LEFT) {
|
||||
if (flatten) {
|
||||
handle_flatten(bezt->vec, 0, flat_direction_left);
|
||||
}
|
||||
else {
|
||||
handle_set_length(bezt->vec, 0, handle_length);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform handle equalization if mode is 'Both' or 'Right'. */
|
||||
if (mode & EQUALIZE_HANDLES_RIGHT) {
|
||||
if (flatten) {
|
||||
handle_flatten(bezt->vec, 2, flat_direction_right);
|
||||
}
|
||||
else {
|
||||
handle_set_length(bezt->vec, 2, handle_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyframeEditFunc ANIM_editkeyframes_ipo(short mode)
|
||||
{
|
||||
switch (mode) {
|
||||
|
|
|
@ -93,6 +93,13 @@ typedef enum eEditKeyframes_Snap {
|
|||
SNAP_KEYS_TIME,
|
||||
} eEditKeyframes_Snap;
|
||||
|
||||
/* equalizing tools */
|
||||
typedef enum eEditKeyframes_Equalize {
|
||||
EQUALIZE_HANDLES_LEFT = (1 << 0),
|
||||
EQUALIZE_HANDLES_RIGHT = (1 << 1),
|
||||
EQUALIZE_HANDLES_BOTH = (EQUALIZE_HANDLES_LEFT | EQUALIZE_HANDLES_RIGHT),
|
||||
} eEditKeyframes_Equalize;
|
||||
|
||||
/* mirroring tools */
|
||||
typedef enum eEditKeyframes_Mirror {
|
||||
MIRROR_KEYS_CURFRAME = 1,
|
||||
|
@ -258,6 +265,18 @@ short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked,
|
|||
KeyframeEditFunc key_ok,
|
||||
KeyframeEditFunc key_cb,
|
||||
FcuEditFunc fcu_cb);
|
||||
/**
|
||||
* Sets selected keyframes' bezier handles to an equal length and optionally makes
|
||||
* the keyframes' handles horizontal.
|
||||
* \param handle_length: Desired handle length, must be positive.
|
||||
* \param flatten: Makes the keyframes' handles the same value as the keyframe,
|
||||
* flattening the curve at that point.
|
||||
*/
|
||||
void ANIM_fcurve_equalize_keyframes_loop(struct FCurve *fcu,
|
||||
eEditKeyframes_Equalize mode,
|
||||
float handle_length,
|
||||
bool flatten);
|
||||
|
||||
/**
|
||||
* Function for working with any type (i.e. one of the known types) of animation channel.
|
||||
*/
|
||||
|
|
|
@ -2352,6 +2352,103 @@ void GRAPH_OT_snap(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Equalize Handles Operator
|
||||
* \{ */
|
||||
|
||||
/* Defines for equalize handles tool. */
|
||||
static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[] = {
|
||||
{GRAPHKEYS_EQUALIZE_LEFT, "LEFT", 0, "Left", "Equalize selected keyframes' left handles"},
|
||||
{GRAPHKEYS_EQUALIZE_RIGHT, "RIGHT", 0, "Right", "Equalize selected keyframes' right handles"},
|
||||
{GRAPHKEYS_EQUALIZE_BOTH, "BOTH", 0, "Both", "Equalize both of a keyframe's handles"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
/* Equalize selected keyframes' bezier handles. */
|
||||
static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten)
|
||||
{
|
||||
/* Filter data. */
|
||||
const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
|
||||
ANIMFILTER_NODUPLIS);
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
|
||||
|
||||
/* Equalize keyframes. */
|
||||
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
||||
ANIM_fcurve_equalize_keyframes_loop(ale->key_data, mode, handle_length, flatten);
|
||||
ale->update |= ANIM_UPDATE_DEFAULT;
|
||||
}
|
||||
|
||||
ANIM_animdata_update(ac, &anim_data);
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
static int graphkeys_equalize_handles_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bAnimContext ac;
|
||||
|
||||
/* Get editor data. */
|
||||
if (ANIM_animdata_get_context(C, &ac) == 0) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Get equalize mode. */
|
||||
int mode = RNA_enum_get(op->ptr, "side");
|
||||
float handle_length = RNA_float_get(op->ptr, "handle_length");
|
||||
bool flatten = RNA_boolean_get(op->ptr, "flatten");
|
||||
|
||||
/* Equalize graph keyframes. */
|
||||
equalize_graph_keys(&ac, mode, handle_length, flatten);
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GRAPH_OT_equalize_handles(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers */
|
||||
ot->name = "Equalize Handles";
|
||||
ot->idname = "GRAPH_OT_equalize_handles";
|
||||
ot->description =
|
||||
"Ensure selected keyframes' handles have equal length, optionally making them horizontal";
|
||||
|
||||
/* API callbacks */
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->exec = graphkeys_equalize_handles_exec;
|
||||
ot->poll = graphop_editable_keyframes_poll;
|
||||
|
||||
/* Flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* Properties */
|
||||
ot->prop = RNA_def_enum(ot->srna,
|
||||
"side",
|
||||
prop_graphkeys_equalize_handles_sides,
|
||||
0,
|
||||
"Side",
|
||||
"Side of the keyframes' bezier handles to affect");
|
||||
RNA_def_float(ot->srna,
|
||||
"handle_length",
|
||||
5.0f,
|
||||
0.1f,
|
||||
FLT_MAX,
|
||||
"Handle Length",
|
||||
"Length to make selected keyframes' bezier handles",
|
||||
1.0f,
|
||||
50.0f);
|
||||
RNA_def_boolean(
|
||||
ot->srna,
|
||||
"flatten",
|
||||
false,
|
||||
"Flatten",
|
||||
"Make the values of the selected keyframes' handles the same as their respective keyframes");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mirror Keyframes Operator
|
||||
* \{ */
|
||||
|
|
|
@ -144,6 +144,7 @@ void GRAPH_OT_easing_type(struct wmOperatorType *ot);
|
|||
void GRAPH_OT_frame_jump(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_snap_cursor_value(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_snap(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_equalize_handles(struct wmOperatorType *ot);
|
||||
void GRAPH_OT_mirror(struct wmOperatorType *ot);
|
||||
|
||||
/* defines for snap keyframes
|
||||
|
@ -158,6 +159,15 @@ enum eGraphKeys_Snap_Mode {
|
|||
GRAPHKEYS_SNAP_VALUE,
|
||||
};
|
||||
|
||||
/* Defines for equalize keyframe handles.
|
||||
* NOTE: Keep in sync with eEditKeyframes_Equalize (in ED_keyframes_edit.h).
|
||||
*/
|
||||
enum eGraphKeys_Equalize_Mode {
|
||||
GRAPHKEYS_EQUALIZE_LEFT = 1,
|
||||
GRAPHKEYS_EQUALIZE_RIGHT,
|
||||
GRAPHKEYS_EQUALIZE_BOTH,
|
||||
};
|
||||
|
||||
/* defines for mirror keyframes
|
||||
* NOTE: keep in sync with eEditKeyframes_Mirror (in ED_keyframes_edit.h)
|
||||
*/
|
||||
|
|
|
@ -456,6 +456,7 @@ void graphedit_operatortypes(void)
|
|||
|
||||
/* editing */
|
||||
WM_operatortype_append(GRAPH_OT_snap);
|
||||
WM_operatortype_append(GRAPH_OT_equalize_handles);
|
||||
WM_operatortype_append(GRAPH_OT_mirror);
|
||||
WM_operatortype_append(GRAPH_OT_frame_jump);
|
||||
WM_operatortype_append(GRAPH_OT_snap_cursor_value);
|
||||
|
|
Loading…
Reference in New Issue