Zoom to frame options, requested by the Hwoozeberry (dutch translation)
authorAntony Riakiotakis <kalast@gmail.com>
Wed, 13 May 2015 18:30:53 +0000 (20:30 +0200)
committerAntony Riakiotakis <kalast@gmail.com>
Fri, 15 May 2015 11:39:30 +0000 (13:39 +0200)
team.

There are 3 options here:

1) Keep range (previous behaviour)
2) Seconds - allows a specified offset in seconds around current frame
3) keyframes - zoom to include a number of keyframes around the cursor

Options 2 and 3 have their own properties to tweak the behaviour and all
options can be found in User Preferences->Interface under the 2D
viewports section.

Number 3 will probably need some refinement so commiting here for the
hwoozeberry team to test first.

release/scripts/startup/bl_ui/space_userpref.py
source/blender/editors/animation/anim_draw.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/include/UI_view2d.h
source/blender/editors/interface/view2d_ops.c
source/blender/editors/space_action/action_edit.c
source/blender/editors/space_graph/graph_edit.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesrna/intern/rna_userdef.c

index 3b17e40c29454dded683609fdb25800a119210c7..9b3a2b50121490a2f202187a718d177733908d19 100644 (file)
@@ -200,6 +200,11 @@ class USERPREF_PT_interface(Panel):
         col.label(text="2D Viewports:")
         col.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing")
         col.prop(view, "timecode_style")
+        col.prop(view, "view_frame_type")
+        if (view.view_frame_type == 'SECONDS'):
+            col.prop(view, "view_frame_seconds")
+        elif (view.view_frame_type == 'KEYFRAMES'):
+            col.prop(view, "view_frame_keyframes")
 
         row.separator()
         row.separator()
index 54d371d8f50089f12c456ebd96099b22a8a9e57c..7d669e8c93e95cf23777175c054767e3cdeee842 100644 (file)
 #include "DNA_scene_types.h"
 #include "DNA_space_types.h"
 #include "DNA_userdef_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_object_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_mask_types.h"
 
 #include "BLI_math.h"
 #include "BLI_timecode.h"
 #include "BLI_utildefines.h"
+#include "BLI_rect.h"
+#include "BLI_dlrbTree.h"
 
 #include "BKE_context.h"
 #include "BKE_global.h"
 #include "BKE_nla.h"
+#include "BKE_mask.h"
 
 #include "ED_anim_api.h"
 #include "ED_keyframes_edit.h"
+#include "ED_keyframes_draw.h"
 
 #include "RNA_access.h"
 
@@ -381,4 +389,136 @@ float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag
        return 1.0f;
 }
 
+static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prevfra) {
+       Scene *scene = CTX_data_scene(C);
+       Object *ob = CTX_data_active_object(C);
+       bGPdata *gpd = CTX_data_gpencil_data(C);
+       Mask *mask = CTX_data_edit_mask(C);
+       bDopeSheet ads = {NULL};
+       DLRBT_Tree keys;
+       ActKeyColumn *aknext, *akprev;
+       float cfranext, cfraprev;
+       bool donenext = false, doneprev = false;
+       int nextcount = 0, prevcount = 0;
+
+       cfranext = cfraprev = (float)(CFRA);
+
+       /* init binarytree-list for getting keyframes */
+       BLI_dlrbTree_init(&keys);
+
+       /* seed up dummy dopesheet context with flags to perform necessary filtering */
+       if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
+               /* only selected channels are included */
+               ads.filterflag |= ADS_FILTER_ONLYSEL;
+       }
+
+       /* populate tree with keyframe nodes */
+       scene_to_keylist(&ads, scene, &keys, NULL);
+
+       if (ob)
+               ob_to_keylist(&ads, ob, &keys, NULL);
+
+       gpencil_to_keylist(&ads, gpd, &keys);
+
+       if (mask) {
+               MaskLayer *masklay = BKE_mask_layer_active(mask);
+               mask_to_keylist(&ads, masklay, &keys);
+       }
+
+       /* build linked-list for searching */
+       BLI_dlrbTree_linkedlist_sync(&keys);
+
+       /* find matching keyframe in the right direction */
+       do {
+               aknext = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfranext);
+
+               if (aknext) {
+                       if (CFRA == (int)aknext->cfra) {
+                               /* make this the new starting point for the search and ignore */
+                               cfranext = aknext->cfra;
+                       }
+                       else {
+                               /* this changes the frame, so set the frame and we're done */
+                               if (++nextcount == U.view_frame_keyframes)
+                                       donenext = true;
+                       }
+                       cfranext = aknext->cfra;
+               }
+       } while ((aknext != NULL) && (donenext == false));
+
+       do {
+               akprev = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfraprev);
+
+               if (akprev) {
+                       if (CFRA == (int)akprev->cfra) {
+                               /* make this the new starting point for the search */
+                       }
+                       else {
+                               /* this changes the frame, so set the frame and we're done */
+                               if (++prevcount == U.view_frame_keyframes)
+                                       doneprev = true;
+                       }
+                       cfraprev = akprev->cfra;
+               }
+       } while ((akprev != NULL) && (doneprev == false));
+
+       /* free temp stuff */
+       BLI_dlrbTree_free(&keys);
+
+       /* any success? */
+       if (doneprev || donenext) {
+               if (doneprev)
+                       *prevfra = cfraprev;
+               else
+                       *prevfra = CFRA - (cfranext - CFRA);
+
+               if (donenext)
+                       *nextfra = cfranext;
+               else
+                       *nextfra = CFRA + (CFRA - cfraprev);
+
+               return true;
+       }
+
+       return false;
+}
+
+void ANIM_center_frame(struct bContext *C, int smooth_viewtx)
+{
+       ARegion *ar = CTX_wm_region(C);
+       Scene *scene = CTX_data_scene(C);
+       float w = BLI_rctf_size_x(&ar->v2d.cur);
+       rctf newrct;
+       int nextfra, prevfra;
+
+       switch (U.view_frame_type) {
+               case ZOOM_FRAME_MODE_SECONDS:
+                       newrct.xmax = scene->r.cfra + U.view_frame_seconds * FPS + 1;
+                       newrct.xmin = scene->r.cfra - U.view_frame_seconds * FPS - 1;
+                       newrct.ymax = ar->v2d.cur.ymax;
+                       newrct.ymin = ar->v2d.cur.ymin;
+                       break;
+
+               /* hardest case of all, look for all keyframes around frame and display those */
+               case ZOOM_FRAME_MODE_KEYFRAMES:
+                       if (find_prev_next_keyframes(C, &nextfra, &prevfra)) {
+                               newrct.xmax = nextfra;
+                               newrct.xmin = prevfra;
+                               newrct.ymax = ar->v2d.cur.ymax;
+                               newrct.ymin = ar->v2d.cur.ymin;
+                               break;
+                       }
+                       /* else drop through, keep range instead */
+
+               case ZOOM_FRAME_MODE_KEEP_RANGE:
+               default:
+                       newrct.xmax = scene->r.cfra + (w / 2);
+                       newrct.xmin = scene->r.cfra - (w / 2);
+                       newrct.ymax = ar->v2d.cur.ymax;
+                       newrct.ymin = ar->v2d.cur.ymin;
+                       break;
+       }
+
+       UI_view2d_smooth_view(C, ar, &newrct, smooth_viewtx);
+}
 /* *************************************************** */
index 73f28986375bc09ee501db08bcafb9646a6a4979..0f70bf3c74529ed8641c7126416a3ad3d22e0aea 100644 (file)
@@ -659,6 +659,7 @@ void ANIM_list_elem_update(struct Scene *scene, bAnimListElem *ale);
 /* data -> channels syncing */
 void ANIM_sync_animchannels_to_data(const struct bContext *C);
 
+void ANIM_center_frame(struct bContext *C, int smooth_viewtx);
 /* ************************************************* */
 /* OPERATORS */
        
index 430093b641b3166d3924598f75675b1900d148c9..2c8f5f6590a40e0f081b5083666887f5ec76e24e 100644 (file)
@@ -233,7 +233,6 @@ void ED_keymap_view2d(struct wmKeyConfig *keyconf);
 
 void UI_view2d_smooth_view(struct bContext *C, struct ARegion *ar,
                            const struct rctf *cur, const int smooth_viewtx);
-void UI_view2d_center_frame(struct bContext *C, int smooth_viewtx);
 #define UI_MARKER_MARGIN_Y (42 * UI_DPI_FAC)
 
 #endif /* __UI_VIEW2D_H__ */
index 9fed02ee0967c3fc468acf98a27cfd0fb27683bb..eafc5c1d24c4b59fee1393d442c6e7c87a0d2a54 100644 (file)
@@ -1468,21 +1468,6 @@ void UI_view2d_smooth_view(
        }
 }
 
-void UI_view2d_center_frame(struct bContext *C, int smooth_viewtx)
-{
-       ARegion *ar = CTX_wm_region(C);
-       Scene *scene = CTX_data_scene(C);
-       float w = BLI_rctf_size_x(&ar->v2d.cur);
-       rctf newrct;
-
-       newrct.xmax = scene->r.cfra + (w / 2);
-       newrct.xmin = scene->r.cfra - (w / 2);
-       newrct.ymax = ar->v2d.cur.ymax;
-       newrct.ymin = ar->v2d.cur.ymin;
-
-       UI_view2d_smooth_view(C, ar, &newrct, smooth_viewtx);
-}
-
 /* only meant for timer usage */
 static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
 {
index debe3cb9730a27d735ac577fc2c537be663d2aa1..90c384261642a5c7169a46f3d2a25c95c857743a 100644 (file)
@@ -429,7 +429,7 @@ static int actkeys_viewsel_exec(bContext *C, wmOperator *UNUSED(op))
 static int actkeys_view_frame_exec(bContext *C, wmOperator *op)
 {
        const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-       UI_view2d_center_frame(C, smooth_viewtx);
+       ANIM_center_frame(C, smooth_viewtx);
 
        return OPERATOR_FINISHED;
 }
index d450335d16a4e4de0cd60aaf3b03c3ce4a41b300..a8cb4ce56ea06b0973754fd600c321203b79ea80 100644 (file)
@@ -261,7 +261,7 @@ static int graphkeys_view_selected_exec(bContext *C, wmOperator *op)
 static int graphkeys_view_frame_exec(bContext *C, wmOperator *op)
 {
        const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
-       UI_view2d_center_frame(C, smooth_viewtx);
+       ANIM_center_frame(C, smooth_viewtx);
        return OPERATOR_FINISHED;
 }
 
index e0b51a1b25b0e2350a197c50792930a2f46a79fb..af0c3bfbf0e668fb3f263680a662eadbdf82fad1 100644 (file)
@@ -503,7 +503,10 @@ typedef struct UserDef {
        char  ipo_new;                  /* interpolation mode for newly added F-Curves */
        char  keyhandles_new;   /* handle types for newly added keyframes */
        char  gpu_select_method;
-       char  pad1;
+       char  view_frame_type;
+
+       int view_frame_keyframes; /* number of keyframes to zoom around current frame */
+       float view_frame_seconds; /* seconds to zoom around current frame */
 
        short scrcastfps;               /* frame rate for screencast to be played back */
        short scrcastwait;              /* milliseconds between screencast snapshots */
@@ -686,6 +689,13 @@ typedef enum eAutokey_Mode {
        AUTOKEY_MODE_EDITKEYS  = 5
 } eAutokey_Mode;
 
+/* Zoom to frame mode */
+typedef enum eZoomFrame_Mode {
+       ZOOM_FRAME_MODE_KEEP_RANGE = 0,
+       ZOOM_FRAME_MODE_SECONDS = 1,
+       ZOOM_FRAME_MODE_KEYFRAMES = 2
+} eZoomFrame_Mode;
+
 /* Auto-Keying flag
  * U.autokey_flag (not strictly used when autokeying only - is also used when keyframing these days)
  * note: AUTOKEY_FLAG_* is used with a macro, search for lines like IS_AUTOKEY_FLAG(INSERTAVAIL)
index a441f2f57dc47b237d72204b9dfcc989440c49e1..82afcfdbf2954d262452a3e9630a3620d6950676 100644 (file)
@@ -3263,7 +3263,14 @@ static void rna_def_userdef_view(BlenderRNA *brna)
                                             "Direct conversion of frame numbers to seconds"},
                {0, NULL, 0, NULL, NULL}
        };
-       
+
+       static EnumPropertyItem zoom_frame_modes[] = {
+               {ZOOM_FRAME_MODE_KEEP_RANGE, "KEEP_RANGE", 0, "Keep Range", ""},
+               {ZOOM_FRAME_MODE_SECONDS, "SECONDS", 0, "Seconds", ""},
+           {ZOOM_FRAME_MODE_KEYFRAMES, "KEYFRAMES", 0, "Keyframes", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+
        PropertyRNA *prop;
        StructRNA *srna;
        
@@ -3505,6 +3512,22 @@ static void rna_def_userdef_view(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "TimeCode Style",
                                 "Format of Time Codes displayed when not displaying timing in terms of frames");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop = RNA_def_property(srna, "view_frame_type", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_items(prop, zoom_frame_modes);
+       RNA_def_property_enum_sdna(prop, NULL, "view_frame_type");
+       RNA_def_property_ui_text(prop, "Zoom To Frame Type", "How zooming to frame focuses around current frame");
+
+       prop = RNA_def_property(srna, "view_frame_keyframes", PROP_INT, PROP_NONE);
+       RNA_def_property_range(prop, 1, 500);
+       RNA_def_property_ui_text(prop, "Zoom Keyframes",
+                                "Keyframes around cursor that we zoom around");
+
+       prop = RNA_def_property(srna, "view_frame_seconds", PROP_FLOAT, PROP_TIME);
+       RNA_def_property_range(prop, 0.0, 10000.0);
+       RNA_def_property_ui_text(prop, "Zoom Seconds",
+                                "Seconds around cursor that we zoom around");
+
 }
 
 static void rna_def_userdef_edit(BlenderRNA *brna)
@@ -3625,7 +3648,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "New Interpolation Type",
                                 "Interpolation mode used for first keyframe on newly added F-Curves "
                                 "(subsequent keyframes take interpolation from preceding keyframe)");
-       
+
        prop = RNA_def_property(srna, "keyframe_new_handle_type", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_items(prop, keyframe_handle_type_items);
        RNA_def_property_enum_sdna(prop, NULL, "keyhandles_new");