Animation: Shear operator for Graph Editor #111735
@ -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):
|
||||
|
@ -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;
|
||||
|
||||
|
||||
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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
Nathan Vegdahl
commented
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.
|
||||
-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
|
||||
|
Loading…
Reference in New Issue
Block a user
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