diff options
Diffstat (limited to 'source/blender/editors/space_graph/graph_edit.c')
-rw-r--r-- | source/blender/editors/space_graph/graph_edit.c | 195 |
1 files changed, 158 insertions, 37 deletions
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 5756c99862d..962cadba1f3 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -27,6 +27,11 @@ * ***** END GPL LICENSE BLOCK ***** */ +/** \file blender/editors/space_graph/graph_edit.c + * \ingroup spgraph + */ + + #include <math.h> #include <stdlib.h> #include <string.h> @@ -54,6 +59,7 @@ #include "BKE_report.h" #include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "ED_anim_api.h" @@ -75,7 +81,7 @@ /* Get the min/max keyframes*/ /* note: it should return total boundbox, filter for selection only can be argument... */ -void get_graph_keyframe_extents (bAnimContext *ac, float *xmin, float *xmax, float *ymin, float *ymax) +void get_graph_keyframe_extents (bAnimContext *ac, float *xmin, float *xmax, float *ymin, float *ymax, const short selOnly) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -101,7 +107,7 @@ void get_graph_keyframe_extents (bAnimContext *ac, float *xmin, float *xmax, flo float unitFac; /* get range */ - calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax); + calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax, selOnly); /* apply NLA scaling */ if (adt) { @@ -122,8 +128,8 @@ void get_graph_keyframe_extents (bAnimContext *ac, float *xmin, float *xmax, flo } /* ensure that the extents are not too extreme that view implodes...*/ - if ((xmin && xmax) && (fabs(*xmax - *xmin) < 0.1)) *xmax += 0.1; - if ((ymin && ymax) && (fabs(*ymax - *ymin) < 0.1)) *ymax += 0.1; + if ((xmin && xmax) && (fabsf(*xmax - *xmin) < 0.1f)) *xmax += 0.1f; + if ((ymin && ymax) && (fabsf(*ymax - *ymin) < 0.1f)) *ymax += 0.1f; /* free memory */ BLI_freelistN(&anim_data); @@ -161,7 +167,7 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) scene= ac.scene; /* set the range directly */ - get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL); + get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, FALSE); scene->r.flag |= SCER_PRV_RANGE; scene->r.psfra= (int)floor(min + 0.5f); scene->r.pefra= (int)floor(max + 0.5f); @@ -190,37 +196,51 @@ void GRAPH_OT_previewrange_set (wmOperatorType *ot) /* ****************** View-All Operator ****************** */ -static int graphkeys_viewall_exec(bContext *C, wmOperator *UNUSED(op)) +static int graphkeys_viewall(bContext *C, const short selOnly) { bAnimContext ac; View2D *v2d; float extra; - + /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; v2d= &ac.ar->v2d; - + /* set the horizontal range, with an extra offset so that the extreme keys will be in view */ - get_graph_keyframe_extents(&ac, &v2d->cur.xmin, &v2d->cur.xmax, &v2d->cur.ymin, &v2d->cur.ymax); - + get_graph_keyframe_extents(&ac, &v2d->cur.xmin, &v2d->cur.xmax, &v2d->cur.ymin, &v2d->cur.ymax, selOnly); + extra= 0.1f * (v2d->cur.xmax - v2d->cur.xmin); v2d->cur.xmin -= extra; v2d->cur.xmax += extra; - + extra= 0.1f * (v2d->cur.ymax - v2d->cur.ymin); v2d->cur.ymin -= extra; v2d->cur.ymax += extra; - + /* do View2D syncing */ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); - + /* set notifier that things have changed */ ED_area_tag_redraw(CTX_wm_area(C)); - + return OPERATOR_FINISHED; } + +/* ......... */ + +static int graphkeys_viewall_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /* whole range */ + return graphkeys_viewall(C, FALSE); +} +static int graphkeys_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /* only selected */ + return graphkeys_viewall(C, TRUE); +} + void GRAPH_OT_view_all (wmOperatorType *ot) { /* identifiers */ @@ -236,6 +256,21 @@ void GRAPH_OT_view_all (wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } +void GRAPH_OT_view_selected (wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "View Selected"; + ot->idname= "GRAPH_OT_view_selected"; + ot->description= "Reset viewable area to show selected keyframe range"; + + /* api callbacks */ + ot->exec= graphkeys_view_selected_exec; + ot->poll= ED_operator_graphedit_active; // XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + /* ******************** Create Ghost-Curves Operator *********************** */ /* This operator samples the data of the selected F-Curves to F-Points, storing them * as 'ghost curves' in the active Graph Editor @@ -775,9 +810,6 @@ static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) static int graphkeys_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { graphkeys_duplicate_exec(C, op); - - RNA_int_set(op->ptr, "mode", TFM_TRANSLATION); - WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr); return OPERATOR_FINISHED; } @@ -798,7 +830,7 @@ void GRAPH_OT_duplicate (wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* to give to transform */ - RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX); + RNA_def_enum(ot->srna, "mode", transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /* ******************** Delete Keyframes Operator ************************* */ @@ -1469,12 +1501,13 @@ static int graphkeys_handletype_exec(bContext *C, wmOperator *op) * of values to -180 degrees to 180 degrees. */ -#if 0 // XXX this is not ready for the primetime yet - /* set of three euler-rotation F-Curves */ typedef struct tEulerFilter { + struct tEulerFilter *next, *prev; + ID *id; /* ID-block which owns the channels */ - FCurve (*fcurves)[3]; /* 3 Pointers to F-Curves */ + FCurve *(fcurves[3]); /* 3 Pointers to F-Curves */ + char *rna_path; /* Pointer to one of the RNA Path's used by one of the F-Curves */ } tEulerFilter; static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op) @@ -1486,7 +1519,8 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op) int filter; ListBase eulers = {NULL, NULL}; - tEulerFilter *euf= NULL; + tEulerFilter *euf= NULL; + int groups=0, failed=0; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -1496,7 +1530,7 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op) * 1) Sets of three related rotation curves are identified from the selected channels, * and are stored as a single 'operation unit' for the next step * 2) Each set of three F-Curves is processed for each keyframe, with the values being - * processed according to one of several ways. + * processed as necessary */ /* step 1: extract only the rotation f-curves */ @@ -1510,45 +1544,134 @@ static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op) * - only rotation curves * - for pchan curves, make sure we're only using the euler curves */ - if (strstr(fcu->rna_path, "rotation_euler") == 0) + if (strstr(fcu->rna_path, "rotation_euler") == NULL) continue; + else if (ELEM3(fcu->array_index, 0, 1, 2) == 0) { + BKE_reportf(op->reports, RPT_WARNING, + "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)", + (ale->id)? ale->id->name:"<No ID>", fcu->rna_path, fcu->array_index); + continue; + } - /* check if current set of 3-curves is suitable to add this curve to - * - things like whether the current set of curves is 'full' should be checked later only - * - first check if id-blocks are compatible + /* optimisation: assume that xyz curves will always be stored consecutively, + * so if the paths or the ID's don't match up, then a curve needs to be added + * to a new group */ - if ((euf) && (ale->id != euf->id)) { - /* if the paths match, add this curve to the set of curves */ - // NOTE: simple string compare for now... could be a bit more fancy... - + if ((euf) && (euf->id == ale->id) && (strcmp(euf->rna_path, fcu->rna_path)==0)) { + /* this should be fine to add to the existing group then */ + euf->fcurves[fcu->array_index]= fcu; } else { /* just add to a new block */ euf= MEM_callocN(sizeof(tEulerFilter), "tEulerFilter"); BLI_addtail(&eulers, euf); + groups++; euf->id= ale->id; + euf->rna_path = fcu->rna_path; /* this should be safe, since we're only using it for a short time */ euf->fcurves[fcu->array_index]= fcu; } } BLI_freelistN(&anim_data); + if (groups == 0) { + BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up"); + return OPERATOR_CANCELLED; + } + /* step 2: go through each set of curves, processing the values at each keyframe * - it is assumed that there must be a full set of keyframes at each keyframe position */ for (euf= eulers.first; euf; euf= euf->next) { + int f; + + /* sanity check: ensure that there are enough F-Curves to work on in this group */ + // TODO: also enforce assumption that there be a full set of keyframes at each position by ensuring that totvert counts are same? + if (ELEM3(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) { + /* report which components are missing */ + BKE_reportf(op->reports, RPT_WARNING, + "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'", + (euf->fcurves[0]==NULL)? "X":"", + (euf->fcurves[1]==NULL)? "Y":"", + (euf->fcurves[2]==NULL)? "Z":"", + euf->id->name, euf->rna_path); + + /* keep track of number of failed sets, and carry on to next group */ + failed++; + continue; + } + /* simple method: just treat any difference between keys of greater than 180 degrees as being a flip */ + // FIXME: there are more complicated methods that will be needed to fix more cases than just some + for (f = 0; f < 3; f++) { + FCurve *fcu = euf->fcurves[f]; + BezTriple *bezt, *prev=NULL; + unsigned int i; + + /* skip if not enough vets to do a decent analysis of... */ + if (fcu->totvert <= 2) + continue; + + /* prev follows bezt, bezt = "current" point to be fixed */ + for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, prev=bezt, bezt++) { + /* our method depends on determining a "difference" from the previous vert */ + if (prev == NULL) + continue; + + /* > 180 degree flip? */ + if (fabs(prev->vec[1][1] - bezt->vec[1][1]) >= M_PI) { + /* 360 degrees to add/subtract frame value until difference is acceptably small that there's no more flip */ + const float fac = 2.0f * (float)M_PI; + + if (prev->vec[1][1] > bezt->vec[1][1]) { + while (fabsf(bezt->vec[1][1] - prev->vec[1][1]) >= (float)M_PI) { + bezt->vec[0][1] += fac; + bezt->vec[1][1] += fac; + bezt->vec[2][1] += fac; + } + } + else /* if (prev->vec[1][1] < bezt->vec[1][1]) */ { + while (fabsf(bezt->vec[1][1] - prev->vec[1][1]) >= (float)M_PI) { + bezt->vec[0][1] -= fac; + bezt->vec[1][1] -= fac; + bezt->vec[2][1] -= fac; + } + } + } + } + } } BLI_freelistN(&eulers); - return OPERATOR_FINISHED; + /* updates + finishing warnings */ + if (failed == groups) { + BKE_report(op->reports, RPT_ERROR, + "No Euler Rotations could be corrected. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected."); + return OPERATOR_CANCELLED; + } + else { + if (failed) { + BKE_report(op->reports, RPT_ERROR, + "Some Euler Rotations couldn't be corrected due to missing/unselected/out-of-order F-Curves. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected."); + } + + /* validate keyframes after editing */ + ANIM_editkeyframes_refresh(&ac); + + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL); + + /* done at last */ + return OPERATOR_FINISHED; + } } void GRAPH_OT_euler_filter (wmOperatorType *ot) { /* identifiers */ - ot->name= "Euler Filter"; + ot->name= "Euler Discontinuity Filter"; ot->idname= "GRAPH_OT_euler_filter"; + ot->description= "Fixes the most common causes of gimbal lock in the selected Euler Rotation F-Curves"; /* api callbacks */ ot->exec= graphkeys_euler_filter_exec; @@ -1558,8 +1681,6 @@ void GRAPH_OT_euler_filter (wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } -#endif // XXX this is not ready for the primetime yet - /* ***************** Jump to Selected Frames Operator *********************** */ /* snap current-frame indicator to 'average time' of selected keyframe */ @@ -1923,7 +2044,7 @@ static int graph_fmodifier_add_invoke (bContext *C, wmOperator *op, wmEvent *UNU uiLayout *layout; int i; - pup= uiPupMenuBegin(C, "Add F-Curve Modifier", ICON_NULL); + pup= uiPupMenuBegin(C, "Add F-Curve Modifier", ICON_NONE); layout= uiPupMenuLayout(pup); /* start from 1 to skip the 'Invalid' modifier type */ @@ -1936,7 +2057,7 @@ static int graph_fmodifier_add_invoke (bContext *C, wmOperator *op, wmEvent *UNU continue; /* create operator menu item with relevant properties filled in */ - props_ptr= uiItemFullO(layout, "GRAPH_OT_fmodifier_add", fmi->name, ICON_NULL, NULL, WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS); + props_ptr= uiItemFullO(layout, "GRAPH_OT_fmodifier_add", fmi->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS); /* the only thing that gets set from the menu is the type of F-Modifier to add */ RNA_enum_set(&props_ptr, "type", i); /* the following properties are just repeats of existing ones... */ |