Animation: Shear operator for Graph Editor #111735

Merged
Christoph Lendenfeld merged 17 commits from ChrisLend/blender:shear_slider into main 2023-08-31 17:09:08 +02:00
6 changed files with 217 additions and 0 deletions

View File

@ -302,6 +302,7 @@ class GRAPH_MT_key_blending(Menu):
layout.operator("graph.blend_offset", text="Blend Offset")
layout.operator("graph.blend_to_ease", text="Blend to Ease")
layout.operator("graph.match_slope", text="Match Slope")
layout.operator("graph.shear", text="Shear Keys")
class GRAPH_MT_key_smoothing(Menu):

View File

@ -826,6 +826,42 @@ bool match_slope_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float
/* ---------------- */
void shear_fcurve_segment(FCurve *fcu,
FCurveSegment *segment,
const float factor,
tShearDirection direction)
{
const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
const float key_x_range = right_key->vec[1][0] - left_key->vec[1][0];
const float key_y_range = right_key->vec[1][1] - left_key->vec[1][1];
/* Happens if there is only 1 key on the FCurve. Needs to be skipped because it
* would be a divide by 0. */
if (IS_EQF(key_x_range, 0.0f)) {
return;
}
for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
/* For easy calculation of the curve, the values are normalized. */
float normalized_x;
if (direction == SHEAR_FROM_LEFT) {
normalized_x = (fcu->bezt[i].vec[1][0] - left_key->vec[1][0]) / key_x_range;
}
else {
normalized_x = (right_key->vec[1][0] - fcu->bezt[i].vec[1][0]) / key_x_range;
}
const float y_delta = key_y_range * normalized_x;

Is lineal a typo? Or just a term I'm not familiar with?

Is `lineal` a typo? Or just a term I'm not familiar with?

I just kept what ares had. In german this means ruler, but I am not sure that was the original intent

changed to y_delta

I just kept what ares had. In german this means ruler, but I am not sure that was the original intent changed to `y_delta`
const float key_y_value = fcu->bezt[i].vec[1][1] + y_delta * factor;
BKE_fcurve_keyframe_move_value_with_handles(&fcu->bezt[i], key_y_value);
}
}
/* ---------------- */
void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
{
const BezTriple *left_bezt = fcurve_segment_start_get(fcu, segment->start_index);

View File

@ -458,6 +458,14 @@ void smooth_fcurve_segment(FCurve *fcu,
int kernel_size,
double *kernel);
void ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float factor);
enum tShearDirection {
SHEAR_FROM_LEFT = 1,
SHEAR_FROM_RIGHT,
};
void shear_fcurve_segment(struct FCurve *fcu,
struct FCurveSegment *segment,
float factor,
tShearDirection direction);
/**
* Shift the FCurve segment up/down so that it aligns with the key before/after
* the segment.

View File

@ -122,6 +122,7 @@ void GRAPH_OT_ease(struct wmOperatorType *ot);
void GRAPH_OT_blend_offset(struct wmOperatorType *ot);
void GRAPH_OT_blend_to_ease(struct wmOperatorType *ot);
void GRAPH_OT_match_slope(struct wmOperatorType *ot);
void GRAPH_OT_shear(struct wmOperatorType *ot);
void GRAPH_OT_decimate(struct wmOperatorType *ot);
void GRAPH_OT_blend_to_default(struct wmOperatorType *ot);
void GRAPH_OT_butterworth_smooth(struct wmOperatorType *ot);

View File

@ -469,6 +469,7 @@ void graphedit_operatortypes()
WM_operatortype_append(GRAPH_OT_blend_to_neighbor);
WM_operatortype_append(GRAPH_OT_breakdown);
WM_operatortype_append(GRAPH_OT_ease);
WM_operatortype_append(GRAPH_OT_shear);
WM_operatortype_append(GRAPH_OT_blend_offset);
WM_operatortype_append(GRAPH_OT_blend_to_ease);
WM_operatortype_append(GRAPH_OT_match_slope);

View File

@ -1290,6 +1290,176 @@ void GRAPH_OT_match_slope(wmOperatorType *ot)
1.0f);
}
/* -------------------------------------------------------------------- */
/** \name Shear Operator
* \{ */
static const EnumPropertyItem shear_direction_items[] = {
{SHEAR_FROM_LEFT, "FROM_LEFT", 0, "From Left", "foo"},
{SHEAR_FROM_RIGHT, "FROM_RIGHT", 0, "From Right", "foo"},
{0, nullptr, 0, nullptr, nullptr},
};
static void shear_graph_keys(bAnimContext *ac, const float factor, tShearDirection direction)
{
ListBase anim_data = {nullptr, nullptr};
ANIM_animdata_filter(
ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
FCurve *fcu = (FCurve *)ale->key_data;
ListBase segments = find_fcurve_segments(fcu);
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
shear_fcurve_segment(fcu, segment, factor, direction);
}
ale->update |= ANIM_UPDATE_DEFAULT;
BLI_freelistN(&segments);
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
static void shear_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);
STRNCPY(mode_str, TIP_("Shear Keys"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
SNPRINTF(status_str, "%s: %s", mode_str, str_ofs);
}
else {
const char *operator_string = "D - Toggle Direction";
SNPRINTF(status_str, "%s: %s | %s", mode_str, slider_string, operator_string);
}
ED_workspace_status_text(C, status_str);
}
static void shear_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
shear_draw_status_header(C, gso);
/* Reset keyframes to the state at invoke. */
reset_bezts(gso);
const float factor = slider_factor_get_and_remember(op);
const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
shear_graph_keys(&gso->ac, factor, direction);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
static int shear_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
if (event->val != KM_PRESS) {
return graph_slider_modal(C, op, event);
}
switch (event->type) {
{
case EVT_DKEY: {
tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
RNA_enum_set(op->ptr,
"direction",
(direction == SHEAR_FROM_LEFT) ? SHEAR_FROM_RIGHT : SHEAR_FROM_LEFT);
shear_modal_update(C, op);
break;
}
default:
return graph_slider_modal(C, op, event);
break;
}
}
return OPERATOR_RUNNING_MODAL;
}
static int shear_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 = static_cast<tGraphSliderOp *>(op->customdata);
gso->modal_update = shear_modal_update;
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
shear_draw_status_header(C, gso);
ED_slider_factor_bounds_set(gso->slider, -1, 1);
ED_slider_factor_set(gso->slider, 0.0f);
return invoke_result;
}
static int shear_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
/* Get editor data. */
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
const float factor = RNA_float_get(op->ptr, "factor");
const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
shear_graph_keys(&ac, factor, direction);
/* Set notifier that keyframes have changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void GRAPH_OT_shear(wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Shear Keyframes";
ot->idname = "GRAPH_OT_shear";
ot->description =
"Affects the value of the keys linearly keeping the same \n\
relationship between them using either the left or the right key as reference";
/* API callbacks. */
ot->invoke = shear_invoke;
ot->modal = shear_modal;
ot->exec = shear_exec;
ot->poll = graphop_editable_keyframes_poll;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_float_factor(ot->srna,
"factor",
0.0f,
-FLT_MAX,
FLT_MAX,
"Shear Factor",
"The amount of shear to apply",

Shear is a linear operation, so talking about curves and bending feels misleading to me. When I first read this I honestly thought, "Wait, is there a third parameter that adds curvature on top of the shear?"

Maybe just call this parameter "Shear", and document it as "The amount of shear". I realize that's kind of stupid and redundant, and doesn't really explain things further to people who don't know what shear is. But I don't know if there's a succinct way to explain shear with just words. And it's relatively easy for people to look up if they don't know what it is, which will then provide pictures that do explain it succinctly.

Shear is a linear operation, so talking about curves and bending feels misleading to me. When I first read this I honestly thought, "Wait, is there a third parameter that adds curvature on top of the shear?" Maybe just call this parameter "Shear", and document it as "The amount of shear". I realize that's kind of stupid and redundant, and doesn't really explain things further to people who don't know what shear is. But I don't know if there's a succinct way to explain shear with just words. And it's relatively easy for people to look up if they don't know what it is, which will then provide pictures that do explain it succinctly.

good catch. This slipped by me and seems like a copy paste issue. The blend to ease operator has the same description

good catch. This slipped by me and seems like a copy paste issue. The blend to ease operator has the same description
-1.0f,
1.0f);
RNA_def_enum(ot->srna,
"direction",
shear_direction_items,
SHEAR_FROM_LEFT,
"Direction",
"Which end of the segment to use as a reference to shear from");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Gauss Smooth Operator