diff options
author | Jacques Lucke <mail@jlucke.com> | 2019-06-04 12:35:57 +0300 |
---|---|---|
committer | Jacques Lucke <mail@jlucke.com> | 2019-06-04 12:35:57 +0300 |
commit | 04bac387315ce09822c36bc20e0fe4e7d533fec8 (patch) | |
tree | d9f2254162f672935f4594f310043cccc07025bb /source/blender/editors/space_graph | |
parent | c3f00d78796216ce15b6032f980539af6f1b0687 (diff) |
Graph Editor: drag to box select keyframes
There is a keymap conflict with ctrl+tweak.
Therefore, I did not include this yet.
Reviewers: brecht
Differential Revision: https://developer.blender.org/D4999
Diffstat (limited to 'source/blender/editors/space_graph')
-rw-r--r-- | source/blender/editors/space_graph/graph_select.c | 582 |
1 files changed, 303 insertions, 279 deletions
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 5ea07c56286..4bd738fca95 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -59,6 +59,282 @@ /* ************************************************************************** */ /* KEYFRAMES STUFF */ +/* temp info for caching handle vertices close */ +typedef struct tNearestVertInfo { + struct tNearestVertInfo *next, *prev; + + FCurve *fcu; /* F-Curve that keyframe comes from */ + + BezTriple *bezt; /* keyframe to consider */ + FPoint *fpt; /* sample point to consider */ + + short hpoint; /* the handle index that we hit (eHandleIndex) */ + short sel; /* whether the handle is selected or not */ + int dist; /* distance from mouse to vert */ + + eAnim_ChannelType ctype; /* type of animation channel this FCurve comes from */ + + float frame; /* frame that point was on when it matched (global time) */ +} tNearestVertInfo; + +/* Tags for the type of graph vert that we have */ +typedef enum eGraphVertIndex { + NEAREST_HANDLE_LEFT = -1, + NEAREST_HANDLE_KEY, + NEAREST_HANDLE_RIGHT, +} eGraphVertIndex; + +/* Tolerance for absolute radius (in pixels) of the vert from the cursor to use */ +// TODO: perhaps this should depend a bit on the size that the user set the vertices to be? +#define GVERTSEL_TOL (10 * U.pixelsize) + +/* ....... */ + +/* check if its ok to select a handle */ +// XXX also need to check for int-values only? +static bool fcurve_handle_sel_check(SpaceGraph *sipo, BezTriple *bezt) +{ + if (sipo->flag & SIPO_NOHANDLES) { + return 0; + } + if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZT_ISSEL_ANY(bezt) == 0) { + return 0; + } + return 1; +} + +/* check if the given vertex is within bounds or not */ +// TODO: should we return if we hit something? +static void nearest_fcurve_vert_store(ListBase *matches, + View2D *v2d, + FCurve *fcu, + eAnim_ChannelType ctype, + BezTriple *bezt, + FPoint *fpt, + short hpoint, + const int mval[2], + float unit_scale, + float offset) +{ + /* Keyframes or Samples? */ + if (bezt) { + int screen_co[2], dist; + + /* convert from data-space to screen coordinates + * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle, + * needed to access the relevant vertex coordinates in the 3x3 + * 'vec' matrix + */ + if (UI_view2d_view_to_region_clip(v2d, + bezt->vec[hpoint + 1][0], + (bezt->vec[hpoint + 1][1] + offset) * unit_scale, + &screen_co[0], + &screen_co[1]) && + /* check if distance from mouse cursor to vert in screen space is within tolerance */ + ((dist = len_v2v2_int(mval, screen_co)) <= GVERTSEL_TOL)) { + tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last; + bool replace = false; + + /* If there is already a point for the F-Curve, + * check if this point is closer than that was. */ + if ((nvi) && (nvi->fcu == fcu)) { + /* replace if we are closer, or if equal and that one wasn't selected but we are... */ + if ((nvi->dist > dist) || ((nvi->sel == 0) && BEZT_ISSEL_ANY(bezt))) { + replace = 1; + } + } + /* add new if not replacing... */ + if (replace == 0) { + nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt"); + } + + /* store values */ + nvi->fcu = fcu; + nvi->ctype = ctype; + + nvi->bezt = bezt; + nvi->hpoint = hpoint; + nvi->dist = dist; + + nvi->frame = bezt->vec[1][0]; /* currently in global time... */ + + nvi->sel = BEZT_ISSEL_ANY(bezt); // XXX... should this use the individual verts instead? + + /* add to list of matches if appropriate... */ + if (replace == 0) { + BLI_addtail(matches, nvi); + } + } + } + else if (fpt) { + /* TODO... */ + } +} + +/* helper for find_nearest_fcurve_vert() - build the list of nearest matches */ +static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], ListBase *matches) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + SpaceGraph *sipo = (SpaceGraph *)ac->sl; + View2D *v2d = &ac->ar->v2d; + short mapping_flag = 0; + + /* get curves to search through + * - if the option to only show keyframes that belong to selected F-Curves is enabled, + * include the 'only selected' flag... + */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); + if (sipo->flag & + SIPO_SELCUVERTSONLY) { // FIXME: this should really be check for by the filtering code... + filter |= ANIMFILTER_SEL; + } + mapping_flag |= ANIM_get_normalization_flags(ac); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->key_data; + AnimData *adt = ANIM_nla_mapping_get(ac, ale); + float offset; + float unit_scale = ANIM_unit_mapping_get_factor( + ac->scene, ale->id, fcu, mapping_flag, &offset); + + /* apply NLA mapping to all the keyframes */ + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); + } + + if (fcu->bezt) { + BezTriple *bezt1 = fcu->bezt, *prevbezt = NULL; + int i; + + for (i = 0; i < fcu->totvert; i++, prevbezt = bezt1, bezt1++) { + /* keyframe */ + nearest_fcurve_vert_store(matches, + v2d, + fcu, + ale->type, + bezt1, + NULL, + NEAREST_HANDLE_KEY, + mval, + unit_scale, + offset); + + /* handles - only do them if they're visible */ + if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) { + /* first handle only visible if previous segment had handles */ + if ((!prevbezt && (bezt1->ipo == BEZT_IPO_BEZ)) || + (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { + nearest_fcurve_vert_store(matches, + v2d, + fcu, + ale->type, + bezt1, + NULL, + NEAREST_HANDLE_LEFT, + mval, + unit_scale, + offset); + } + + /* second handle only visible if this segment is bezier */ + if (bezt1->ipo == BEZT_IPO_BEZ) { + nearest_fcurve_vert_store(matches, + v2d, + fcu, + ale->type, + bezt1, + NULL, + NEAREST_HANDLE_RIGHT, + mval, + unit_scale, + offset); + } + } + } + } + else if (fcu->fpt) { + // TODO; do this for samples too + } + + /* un-apply NLA mapping from all the keyframes */ + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); + } + } + + /* free channels */ + ANIM_animdata_freelist(&anim_data); +} + +/* helper for find_nearest_fcurve_vert() - get the best match to use */ +static tNearestVertInfo *get_best_nearest_fcurve_vert(ListBase *matches) +{ + tNearestVertInfo *nvi = NULL; + short found = 0; + + /* abort if list is empty */ + if (BLI_listbase_is_empty(matches)) { + return NULL; + } + + /* if list only has 1 item, remove it from the list and return */ + if (BLI_listbase_is_single(matches)) { + /* need to remove from the list, otherwise it gets freed and then we can't return it */ + return BLI_pophead(matches); + } + + /* try to find the first selected F-Curve vert, then take the one after it */ + for (nvi = matches->first; nvi; nvi = nvi->next) { + /* which mode of search are we in: find first selected, or find vert? */ + if (found) { + /* Just take this vert now that we've found the selected one + * - We'll need to remove this from the list + * so that it can be returned to the original caller. + */ + BLI_remlink(matches, nvi); + return nvi; + } + else { + /* if vert is selected, we've got what we want... */ + if (nvi->sel) { + found = 1; + } + } + } + + /* if we're still here, this means that we failed to find anything appropriate in the first pass, + * so just take the first item now... + */ + return BLI_pophead(matches); +} + +/** + * Find the nearest vertices (either a handle or the keyframe) + * that are nearest to the mouse cursor (in area coordinates) + * + * \note the match info found must still be freed. + */ +static tNearestVertInfo *find_nearest_fcurve_vert(bAnimContext *ac, const int mval[2]) +{ + ListBase matches = {NULL, NULL}; + tNearestVertInfo *nvi; + + /* step 1: get the nearest verts */ + get_nearest_fcurve_verts_list(ac, mval, &matches); + + /* step 2: find the best vert */ + nvi = get_best_nearest_fcurve_vert(&matches); + + BLI_freelistN(&matches); + + /* return the best vert found */ + return nvi; +} + /* ******************** Deselect All Operator ***************************** */ /* This operator works in one of three ways: * 1) (de)select all (AKEY) - test if select all or deselect all @@ -347,6 +623,28 @@ static void box_select_graphkeys(bAnimContext *ac, /* ------------------- */ +static int graphkeys_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + bAnimContext ac; + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + if (RNA_boolean_get(op->ptr, "tweak")) { + tNearestVertInfo *under_mouse = find_nearest_fcurve_vert(&ac, event->mval); + bool mouse_is_over_element = under_mouse != NULL; + if (under_mouse) { + MEM_freeN(under_mouse); + } + + if (mouse_is_over_element) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + } + + return WM_gesture_box_invoke(C, op, event); +} + static int graphkeys_box_select_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -410,7 +708,7 @@ void GRAPH_OT_select_box(wmOperatorType *ot) ot->description = "Select all keyframes within the specified region"; /* api callbacks */ - ot->invoke = WM_gesture_box_invoke; + ot->invoke = graphkeys_box_select_invoke; ot->exec = graphkeys_box_select_exec; ot->modal = WM_gesture_box_modal; ot->cancel = WM_gesture_box_cancel; @@ -428,6 +726,10 @@ void GRAPH_OT_select_box(wmOperatorType *ot) "Include Handles", "Are handles tested individually against the selection criteria"); + PropertyRNA *prop = RNA_def_boolean( + ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_gesture_box(ot); WM_operator_properties_select_operation_simple(ot); } @@ -1114,284 +1416,6 @@ void GRAPH_OT_select_leftright(wmOperatorType *ot) * selection mode between replacing the selection (without) and inverting the selection (with). */ -/* temp info for caching handle vertices close */ -typedef struct tNearestVertInfo { - struct tNearestVertInfo *next, *prev; - - FCurve *fcu; /* F-Curve that keyframe comes from */ - - BezTriple *bezt; /* keyframe to consider */ - FPoint *fpt; /* sample point to consider */ - - short hpoint; /* the handle index that we hit (eHandleIndex) */ - short sel; /* whether the handle is selected or not */ - int dist; /* distance from mouse to vert */ - - eAnim_ChannelType ctype; /* type of animation channel this FCurve comes from */ - - float frame; /* frame that point was on when it matched (global time) */ -} tNearestVertInfo; - -/* Tags for the type of graph vert that we have */ -typedef enum eGraphVertIndex { - NEAREST_HANDLE_LEFT = -1, - NEAREST_HANDLE_KEY, - NEAREST_HANDLE_RIGHT, -} eGraphVertIndex; - -/* Tolerance for absolute radius (in pixels) of the vert from the cursor to use */ -// TODO: perhaps this should depend a bit on the size that the user set the vertices to be? -#define GVERTSEL_TOL (10 * U.pixelsize) - -/* ....... */ - -/* check if its ok to select a handle */ -// XXX also need to check for int-values only? -static bool fcurve_handle_sel_check(SpaceGraph *sipo, BezTriple *bezt) -{ - if (sipo->flag & SIPO_NOHANDLES) { - return 0; - } - if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZT_ISSEL_ANY(bezt) == 0) { - return 0; - } - return 1; -} - -/* check if the given vertex is within bounds or not */ -// TODO: should we return if we hit something? -static void nearest_fcurve_vert_store(ListBase *matches, - View2D *v2d, - FCurve *fcu, - eAnim_ChannelType ctype, - BezTriple *bezt, - FPoint *fpt, - short hpoint, - const int mval[2], - float unit_scale, - float offset) -{ - /* Keyframes or Samples? */ - if (bezt) { - int screen_co[2], dist; - - /* convert from data-space to screen coordinates - * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle, - * needed to access the relevant vertex coordinates in the 3x3 - * 'vec' matrix - */ - if (UI_view2d_view_to_region_clip(v2d, - bezt->vec[hpoint + 1][0], - (bezt->vec[hpoint + 1][1] + offset) * unit_scale, - &screen_co[0], - &screen_co[1]) && - /* check if distance from mouse cursor to vert in screen space is within tolerance */ - ((dist = len_v2v2_int(mval, screen_co)) <= GVERTSEL_TOL)) { - tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last; - bool replace = false; - - /* If there is already a point for the F-Curve, - * check if this point is closer than that was. */ - if ((nvi) && (nvi->fcu == fcu)) { - /* replace if we are closer, or if equal and that one wasn't selected but we are... */ - if ((nvi->dist > dist) || ((nvi->sel == 0) && BEZT_ISSEL_ANY(bezt))) { - replace = 1; - } - } - /* add new if not replacing... */ - if (replace == 0) { - nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt"); - } - - /* store values */ - nvi->fcu = fcu; - nvi->ctype = ctype; - - nvi->bezt = bezt; - nvi->hpoint = hpoint; - nvi->dist = dist; - - nvi->frame = bezt->vec[1][0]; /* currently in global time... */ - - nvi->sel = BEZT_ISSEL_ANY(bezt); // XXX... should this use the individual verts instead? - - /* add to list of matches if appropriate... */ - if (replace == 0) { - BLI_addtail(matches, nvi); - } - } - } - else if (fpt) { - /* TODO... */ - } -} - -/* helper for find_nearest_fcurve_vert() - build the list of nearest matches */ -static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], ListBase *matches) -{ - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - SpaceGraph *sipo = (SpaceGraph *)ac->sl; - View2D *v2d = &ac->ar->v2d; - short mapping_flag = 0; - - /* get curves to search through - * - if the option to only show keyframes that belong to selected F-Curves is enabled, - * include the 'only selected' flag... - */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); - if (sipo->flag & - SIPO_SELCUVERTSONLY) { // FIXME: this should really be check for by the filtering code... - filter |= ANIMFILTER_SEL; - } - mapping_flag |= ANIM_get_normalization_flags(ac); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - for (ale = anim_data.first; ale; ale = ale->next) { - FCurve *fcu = (FCurve *)ale->key_data; - AnimData *adt = ANIM_nla_mapping_get(ac, ale); - float offset; - float unit_scale = ANIM_unit_mapping_get_factor( - ac->scene, ale->id, fcu, mapping_flag, &offset); - - /* apply NLA mapping to all the keyframes */ - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - } - - if (fcu->bezt) { - BezTriple *bezt1 = fcu->bezt, *prevbezt = NULL; - int i; - - for (i = 0; i < fcu->totvert; i++, prevbezt = bezt1, bezt1++) { - /* keyframe */ - nearest_fcurve_vert_store(matches, - v2d, - fcu, - ale->type, - bezt1, - NULL, - NEAREST_HANDLE_KEY, - mval, - unit_scale, - offset); - - /* handles - only do them if they're visible */ - if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) { - /* first handle only visible if previous segment had handles */ - if ((!prevbezt && (bezt1->ipo == BEZT_IPO_BEZ)) || - (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { - nearest_fcurve_vert_store(matches, - v2d, - fcu, - ale->type, - bezt1, - NULL, - NEAREST_HANDLE_LEFT, - mval, - unit_scale, - offset); - } - - /* second handle only visible if this segment is bezier */ - if (bezt1->ipo == BEZT_IPO_BEZ) { - nearest_fcurve_vert_store(matches, - v2d, - fcu, - ale->type, - bezt1, - NULL, - NEAREST_HANDLE_RIGHT, - mval, - unit_scale, - offset); - } - } - } - } - else if (fcu->fpt) { - // TODO; do this for samples too - } - - /* un-apply NLA mapping from all the keyframes */ - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); - } - } - - /* free channels */ - ANIM_animdata_freelist(&anim_data); -} - -/* helper for find_nearest_fcurve_vert() - get the best match to use */ -static tNearestVertInfo *get_best_nearest_fcurve_vert(ListBase *matches) -{ - tNearestVertInfo *nvi = NULL; - short found = 0; - - /* abort if list is empty */ - if (BLI_listbase_is_empty(matches)) { - return NULL; - } - - /* if list only has 1 item, remove it from the list and return */ - if (BLI_listbase_is_single(matches)) { - /* need to remove from the list, otherwise it gets freed and then we can't return it */ - return BLI_pophead(matches); - } - - /* try to find the first selected F-Curve vert, then take the one after it */ - for (nvi = matches->first; nvi; nvi = nvi->next) { - /* which mode of search are we in: find first selected, or find vert? */ - if (found) { - /* Just take this vert now that we've found the selected one - * - We'll need to remove this from the list - * so that it can be returned to the original caller. - */ - BLI_remlink(matches, nvi); - return nvi; - } - else { - /* if vert is selected, we've got what we want... */ - if (nvi->sel) { - found = 1; - } - } - } - - /* if we're still here, this means that we failed to find anything appropriate in the first pass, - * so just take the first item now... - */ - return BLI_pophead(matches); -} - -/** - * Find the nearest vertices (either a handle or the keyframe) - * that are nearest to the mouse cursor (in area coordinates) - * - * \note the match info found must still be freed. - */ -static tNearestVertInfo *find_nearest_fcurve_vert(bAnimContext *ac, const int mval[2]) -{ - ListBase matches = {NULL, NULL}; - tNearestVertInfo *nvi; - - /* step 1: get the nearest verts */ - get_nearest_fcurve_verts_list(ac, mval, &matches); - - /* step 2: find the best vert */ - nvi = get_best_nearest_fcurve_vert(&matches); - - BLI_freelistN(&matches); - - /* return the best vert found */ - return nvi; -} - -/* ------------------- */ - /* option 1) select keyframe directly under mouse */ static void mouse_graph_keys(bAnimContext *ac, const int mval[2], |