Anim: fps dependent frame display in animation editors

Currently, the frame scrub area shows different frame numbers based on
the zoom level. But they would not show frame 24 until you zoom in. Since 24
is the most used frame rate for animation that's inconvenient because you never
see the spot with the full second.

With this PR the numbers and lines depend on the frame rate, and will favor
full seconds, then divide by either 2, 3 or 5 to break down the number.

This works for most FPS values, however custom FPS may hit a prime number, in which case
this logic is aborted and the next step down when zooming in will be 1. This can
result in quite large gaps where no frame numbers are displayed. But I think the impact
will be small since large prime number frame rates are rare.

This patch also reduces UI jumping because the major lines will always be turned into minor lines before disappearing.
When zooming in, any minor line will become a major line eventually.

To reduce the bunching up of minor lines when dividing by a large prime number,
they are only drawn if the distance between them is large enough.

Note that the line drawing in the VSE is not quite following the zoom level. Since this doesn't use the code path i modified in this PR I left it as is. Info from the Sequencer module is appreciated.

Pull Request: https://projects.blender.org/blender/blender/pulls/143562
This commit is contained in:
Christoph Lendenfeld
2025-09-13 20:09:31 +02:00
committed by Christoph Lendenfeld
parent 924d72e05b
commit ab43c57a2a
11 changed files with 227 additions and 185 deletions

View File

@@ -195,7 +195,8 @@ void ED_time_scrub_draw_current_frame(const ARegion *region,
void ED_time_scrub_draw(const ARegion *region,
const Scene *scene,
bool display_seconds,
bool discrete_frames)
bool discrete_frames,
const int base)
{
const View2D *v2d = &region->v2d;
@@ -211,11 +212,11 @@ void ED_time_scrub_draw(const ARegion *region,
numbers_rect.ymin = get_centered_text_y(&scrub_region_rect) - 4 * UI_SCALE_FAC;
if (discrete_frames) {
UI_view2d_draw_scale_x__discrete_frames_or_seconds(
region, v2d, &numbers_rect, scene, display_seconds, TH_TIME_SCRUB_TEXT);
region, v2d, &numbers_rect, scene, display_seconds, TH_TIME_SCRUB_TEXT, base);
}
else {
UI_view2d_draw_scale_x__frames_or_seconds(
region, v2d, &numbers_rect, scene, display_seconds, TH_TIME_SCRUB_TEXT);
region, v2d, &numbers_rect, scene, display_seconds, TH_TIME_SCRUB_TEXT, base);
}
GPU_matrix_pop_projection();

View File

@@ -21,11 +21,17 @@ void ED_time_scrub_draw_current_frame(const ARegion *region,
const Scene *scene,
bool display_seconds,
bool display_stalk = true);
/**
* Draw the scrub area with numbers inside.
* \param display_seconds defines if the display is in seconds or in frames.
* \param base defines the base number from which the number distance is calculated. The distance
* is always a simple fraction or a multiple of that number.
*/
void ED_time_scrub_draw(const ARegion *region,
const Scene *scene,
bool display_seconds,
bool discrete_frames);
bool discrete_frames,
int base);
/**
* Scroll-bars shouldn't overlap the time scrub UI. So this returns a mask adjusted to exclude it,
* which can be passed to #UI_view2d_scrollers_draw().

View File

@@ -218,12 +218,16 @@ void UI_view2d_dot_grid_draw(const View2D *v2d,
float min_step,
int grid_subdivisions);
void UI_view2d_draw_lines_y__values(const View2D *v2d);
void UI_view2d_draw_lines_x__values(const View2D *v2d);
void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d, bool display_minor_lines);
void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d,
const Scene *scene,
bool display_minor_lines);
/**
* Draw horizontal lines. The \param base defines in what step the lines are drawn. Depending on
* the zoom level of the `v2d` the step is a full fraction of the given base.
*/
void UI_view2d_draw_lines_y__values(const View2D *v2d, int base);
void UI_view2d_draw_lines_x__values(const View2D *v2d, int base);
void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d,
int base,
bool display_minor_lines);
void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, int base, bool display_minor_lines);
void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const View2D *v2d,
const Scene *scene,
bool display_seconds,
@@ -232,30 +236,36 @@ void UI_view2d_draw_lines_x__frames_or_seconds(const View2D *v2d,
const Scene *scene,
bool display_seconds);
float UI_view2d_grid_resolution_x__frames_or_seconds(const View2D *v2d,
const Scene *scene,
bool display_seconds);
float UI_view2d_grid_resolution_y__values(const View2D *v2d);
float UI_view2d_grid_resolution_x__frames_or_seconds(const View2D *v2d, const Scene *scene);
float UI_view2d_grid_resolution_y__values(const View2D *v2d, int base);
/**
* Scale indicator text drawing.
*/
void UI_view2d_draw_scale_y__values(const ARegion *region,
const View2D *v2d,
const rcti *rect,
int colorid);
void UI_view2d_draw_scale_y__values(
const ARegion *region, const View2D *v2d, const rcti *rect, int colorid, int base);
/**
* Draw a text scale in either frames or seconds. The minimum step distance is 1, meaning no
* subframe indicators will be drawn.
*/
void UI_view2d_draw_scale_x__discrete_frames_or_seconds(const ARegion *region,
const View2D *v2d,
const rcti *rect,
const Scene *scene,
bool display_seconds,
int colorid);
int colorid,
int base);
/**
* Draw a text scale in either frames or seconds.
* This can draw indicators on subframes, e.g. "1.5".
*/
void UI_view2d_draw_scale_x__frames_or_seconds(const ARegion *region,
const View2D *v2d,
const rcti *rect,
const Scene *scene,
bool display_seconds,
int colorid);
int colorid,
int base);
/** \} */

View File

@@ -37,88 +37,93 @@
#define MIN_MAJOR_LINE_DISTANCE (U.v2d_min_gridsize * UI_SCALE_FAC)
static float select_major_distance(const float *possible_distances,
uint amount,
float pixel_width,
float view_width)
{
BLI_assert(amount >= 1);
/* This number defines the smalles scale unit that will be displayed. For example 100 will give
* 1/100 -> 0.01 as the smallest step. This is only relevant for editors that do display subframe
* information, for example the Graph Editor. */
constexpr int subframe_range = 100;
if (IS_EQF(view_width, 0.0f)) {
return possible_distances[0];
/* This esentially performs a special prime factor decomposition where it can only use 2, 3 and 5
* as prime factors. Divisions that result in 2 are preferred. */
static int get_divisor(const int distance)
{
const int divisors[3] = {2, 3, 5};
constexpr uint8_t num_divisors = ARRAY_SIZE(divisors);
bool divides_no_remainder[num_divisors];
for (int i = 0; i < num_divisors; i++) {
const int divisor = divisors[i];
const int result = distance / divisor;
/* If the division was without loss due to integer cast and the result is 2, return
* that. Animating on 2s is a very useful thing for animators so the lines should be shown with
* that distance. */
const bool has_no_remainder = result * divisor == distance;
if (has_no_remainder && result == 2) {
return divisor;
}
divides_no_remainder[i] = has_no_remainder;
}
const float pixels_per_view_unit = pixel_width / view_width;
for (uint i = 0; i < amount; i++) {
const float distance = possible_distances[i];
if (pixels_per_view_unit * distance >= MIN_MAJOR_LINE_DISTANCE) {
return distance;
/* If no division results in a 2, take the first to divide cleanly. */
for (int i = 0; i < num_divisors; i++) {
if (divides_no_remainder[i]) {
return divisors[i];
}
}
return possible_distances[amount - 1];
}
static const float discrete_value_scales[] = {
1, 2, 4, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000};
static const float continuous_value_scales[] = {0.01, 0.02, 0.04, 0.1, 0.2, 0.4, 1, 2,
4, 10, 20, 50, 100, 200, 500, 1000,
2000, 5000, 10000, 20000, 50000, 100000};
static uint view2d_major_step_x__discrete(const View2D *v2d)
{
return select_major_distance(discrete_value_scales,
ARRAY_SIZE(discrete_value_scales),
BLI_rcti_size_x(&v2d->mask),
BLI_rctf_size_x(&v2d->cur));
}
static float view2d_major_step_x__continuous(const View2D *v2d)
{
return select_major_distance(continuous_value_scales,
ARRAY_SIZE(continuous_value_scales),
BLI_rcti_size_x(&v2d->mask),
BLI_rctf_size_x(&v2d->cur));
}
static float view2d_major_step_y__continuous(const View2D *v2d)
{
return select_major_distance(continuous_value_scales,
ARRAY_SIZE(continuous_value_scales),
BLI_rcti_size_y(&v2d->mask),
BLI_rctf_size_y(&v2d->cur));
}
static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene)
{
/* If we don't have a scene available, pick an arbitrary framerate to show *something*. */
const double fps = scene ? scene->frames_per_second() : 25;
blender::Vector<float, 32> possible_distances;
for (int step = 1; step < fps; step *= 2) {
possible_distances.append(step);
}
for (int i = 0; i <= 5; i++) {
uint fac = pow(60, i);
possible_distances.append(fac * fps);
possible_distances.append(fac * 2 * fps);
possible_distances.append(fac * 5 * fps);
possible_distances.append(fac * 10 * fps);
possible_distances.append(fac * 30 * fps);
possible_distances.append(fac * 60 * fps);
}
float distance = select_major_distance(possible_distances.data(),
possible_distances.size(),
BLI_rcti_size_x(&v2d->mask),
BLI_rctf_size_x(&v2d->cur));
/* In case none of the above if is true, the divisor will be the full distance meaning the next
* step down from that number is 1. */
return distance;
}
/**
* Calculates the distance in frames between major lines. The lowest value it can return is 1.
* The \param base defines how the step is calculated. The returned step is either a full fraction
* or a multiple of that number.
*/
static int calculate_grid_step(const int base, const float pixel_width, const float view_width)
{
if (IS_EQF(view_width, 0.0f) || base == 0) {
return 1;
}
const float pixels_per_view_unit = pixel_width / view_width;
int distance = base;
if (pixels_per_view_unit * distance > MIN_MAJOR_LINE_DISTANCE) {
/* Shrink the distance. */
while (distance > 1) {
const int divisor = get_divisor(distance);
const int result = (distance / divisor);
if (pixels_per_view_unit * result < MIN_MAJOR_LINE_DISTANCE) {
/* If the distance would fall below the threshold, stop dividing. */
break;
}
distance = result;
}
}
else {
/* Grow the distance, doubling every time. */
while (pixels_per_view_unit * distance < MIN_MAJOR_LINE_DISTANCE) {
distance *= 2;
}
}
BLI_assert(distance != 0);
return distance;
}
/* Mostly the same as `calculate_grid_step, except in can divide into the 0-1 range. */
static float calculate_grid_step_subframes(const int base,
const float pixel_width,
const float view_width)
{
float distance = calculate_grid_step(base, pixel_width, view_width);
if (distance > 1) {
return distance;
}
/* Using `calculate_grid_step` to break down subframe_range simulating a larger view. */
distance = calculate_grid_step(subframe_range, pixel_width, view_width * subframe_range);
return distance / subframe_range;
}
/* Draw parallel lines
************************************/
@@ -236,10 +241,36 @@ static void view2d_draw_lines_internal(const View2D *v2d,
}
static void view2d_draw_lines(const View2D *v2d,
float major_distance,
bool display_minor_lines,
char direction)
const float major_distance,
const bool display_minor_lines,
const char direction)
{
if (display_minor_lines) {
uchar minor_color[3];
UI_GetThemeColorShade3ubv(TH_GRID, 16, minor_color);
ParallelLinesSet minor_lines;
int distance_int;
if (major_distance > 1) {
distance_int = round_fl_to_int(major_distance);
}
else {
/* By multiplying by the subframe range, the smallest distance in which minor lines are drawn
* is the same as the smallest distance between major lines. We can just do this
* multiplication because from the result, the next divisor is found and applied to the
* major distance. The returned divisor may be 1. */
distance_int = round_fl_to_int(major_distance * subframe_range);
}
const int divisor = get_divisor(distance_int);
minor_lines.distance = major_distance / divisor;
minor_lines.offset = 0;
const int pixel_width = BLI_rcti_size_x(&v2d->mask);
const float view_width = BLI_rctf_size_x(&v2d->cur);
if ((pixel_width / view_width) * (major_distance / divisor) > MIN_MAJOR_LINE_DISTANCE / 5) {
view2d_draw_lines_internal(v2d, &minor_lines, minor_color, direction);
}
}
{
uchar major_color[3];
UI_GetThemeColor3ubv(TH_GRID, major_color);
@@ -248,17 +279,6 @@ static void view2d_draw_lines(const View2D *v2d,
major_lines.offset = 0;
view2d_draw_lines_internal(v2d, &major_lines, major_color, direction);
}
if (display_minor_lines) {
uchar minor_color[3];
UI_GetThemeColorShade3ubv(TH_GRID, 16, minor_color);
ParallelLinesSet minor_lines;
/* Draw minor between major lines. In order for the lines to be on full frames values in
* `discrete_value_scales` have to be even numbers. */
minor_lines.distance = major_distance;
minor_lines.offset = major_distance / 2.0f;
view2d_draw_lines_internal(v2d, &minor_lines, minor_color, direction);
}
}
/* Scale indicator text drawing
@@ -439,7 +459,7 @@ static void view_to_string__value(
if (v2d_step >= 1.0f) {
BLI_snprintf_utf8(r_str, str_maxncpy, "%d", int(v2d_pos));
}
else if (v2d_step >= 0.1f) {
else if (v2d_step >= 0.5f) {
BLI_snprintf_utf8(r_str, str_maxncpy, "%.1f", v2d_pos);
}
else if (v2d_step >= 0.01f) {
@@ -453,48 +473,52 @@ static void view_to_string__value(
/* Grid Resolution API
**************************************************/
float UI_view2d_grid_resolution_x__frames_or_seconds(const View2D *v2d,
const Scene *scene,
bool display_seconds)
float UI_view2d_grid_resolution_x__frames_or_seconds(const View2D *v2d, const Scene *scene)
{
if (display_seconds) {
return view2d_major_step_x__time(v2d, scene);
}
return view2d_major_step_x__continuous(v2d);
const int fps = round_db_to_int(scene->frames_per_second());
return calculate_grid_step_subframes(
fps, BLI_rcti_size_x(&v2d->mask), BLI_rctf_size_x(&v2d->cur));
}
float UI_view2d_grid_resolution_y__values(const View2D *v2d)
float UI_view2d_grid_resolution_y__values(const View2D *v2d, const int base)
{
return view2d_major_step_y__continuous(v2d);
return calculate_grid_step_subframes(
base, BLI_rcti_size_y(&v2d->mask), BLI_rctf_size_y(&v2d->cur));
}
/* Line Drawing API
**************************************************/
void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d, bool display_minor_lines)
void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d,
const int base,
bool display_minor_lines)
{
const uint major_line_distance = view2d_major_step_x__discrete(v2d);
const float major_line_distance = calculate_grid_step(
base, BLI_rcti_size_x(&v2d->mask), BLI_rctf_size_x(&v2d->cur));
view2d_draw_lines(
v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v');
}
void UI_view2d_draw_lines_x__values(const View2D *v2d)
void UI_view2d_draw_lines_x__values(const View2D *v2d, const int base)
{
const float major_line_distance = view2d_major_step_x__continuous(v2d);
const float major_line_distance = calculate_grid_step_subframes(
base, BLI_rcti_size_x(&v2d->mask), BLI_rctf_size_x(&v2d->cur));
view2d_draw_lines(v2d, major_line_distance, true, 'v');
}
void UI_view2d_draw_lines_y__values(const View2D *v2d)
void UI_view2d_draw_lines_y__values(const View2D *v2d, const int base)
{
const float major_line_distance = view2d_major_step_y__continuous(v2d);
const float major_line_distance = calculate_grid_step_subframes(
base, BLI_rcti_size_y(&v2d->mask), BLI_rctf_size_y(&v2d->cur));
view2d_draw_lines(v2d, major_line_distance, true, 'h');
}
void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d,
const Scene *scene,
const int base,
bool display_minor_lines)
{
const float major_line_distance = view2d_major_step_x__time(v2d, scene);
const float major_line_distance = calculate_grid_step(
base, BLI_rcti_size_x(&v2d->mask), BLI_rctf_size_x(&v2d->cur));
view2d_draw_lines(
v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v');
}
@@ -504,11 +528,13 @@ void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const View2D *v2d,
bool display_seconds,
bool display_minor_lines)
{
/* Rounding fractional framerates for drawing. */
const int fps = round_db_to_int(scene->frames_per_second());
if (display_seconds) {
UI_view2d_draw_lines_x__discrete_time(v2d, scene, display_minor_lines);
UI_view2d_draw_lines_x__discrete_time(v2d, fps, display_minor_lines);
}
else {
UI_view2d_draw_lines_x__discrete_values(v2d, display_minor_lines);
UI_view2d_draw_lines_x__discrete_values(v2d, fps, display_minor_lines);
}
}
@@ -516,51 +542,23 @@ void UI_view2d_draw_lines_x__frames_or_seconds(const View2D *v2d,
const Scene *scene,
bool display_seconds)
{
const int fps = round_db_to_int(scene->frames_per_second());
if (display_seconds) {
UI_view2d_draw_lines_x__discrete_time(v2d, scene, true);
UI_view2d_draw_lines_x__discrete_time(v2d, fps, true);
}
else {
UI_view2d_draw_lines_x__values(v2d);
UI_view2d_draw_lines_x__values(v2d, fps);
}
}
/* Scale indicator text drawing API
**************************************************/
static void draw_scale_x_discrete_values(const ARegion *region,
const View2D *v2d,
const rcti *rect,
int colorid)
void UI_view2d_draw_scale_y__values(
const ARegion *region, const View2D *v2d, const rcti *rect, int colorid, const int base)
{
const float number_step = view2d_major_step_x__discrete(v2d);
draw_horizontal_scale_indicators(
region, v2d, number_step, rect, view_to_string__frame_number, nullptr, colorid);
}
static void draw_scale_x_discrete_time(
const ARegion *region, const View2D *v2d, const rcti *rect, const Scene *scene, int colorid)
{
const float step = view2d_major_step_x__time(v2d, scene);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__time, (void *)scene, colorid);
}
static void draw_scale_x_values(const ARegion *region,
const View2D *v2d,
const rcti *rect,
int colorid)
{
const float step = view2d_major_step_x__continuous(v2d);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__value, nullptr, colorid);
}
void UI_view2d_draw_scale_y__values(const ARegion *region,
const View2D *v2d,
const rcti *rect,
int colorid)
{
const float step = view2d_major_step_y__continuous(v2d);
const float step = calculate_grid_step_subframes(
base, BLI_rcti_size_y(&v2d->mask), BLI_rctf_size_y(&v2d->cur));
draw_vertical_scale_indicators(
region, v2d, step, 0.0f, rect, view_to_string__value, nullptr, colorid);
}
@@ -570,13 +568,18 @@ void UI_view2d_draw_scale_x__discrete_frames_or_seconds(const ARegion *region,
const rcti *rect,
const Scene *scene,
bool display_seconds,
int colorid)
int colorid,
const int base)
{
const float step = calculate_grid_step(
base, BLI_rcti_size_x(&v2d->mask), BLI_rctf_size_x(&v2d->cur));
if (display_seconds) {
draw_scale_x_discrete_time(region, v2d, rect, scene, colorid);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__time, (void *)scene, colorid);
}
else {
draw_scale_x_discrete_values(region, v2d, rect, colorid);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__frame_number, nullptr, colorid);
}
}
@@ -585,12 +588,17 @@ void UI_view2d_draw_scale_x__frames_or_seconds(const ARegion *region,
const rcti *rect,
const Scene *scene,
bool display_seconds,
int colorid)
int colorid,
const int base)
{
const float step = calculate_grid_step_subframes(
base, BLI_rcti_size_x(&v2d->mask), BLI_rctf_size_x(&v2d->cur));
if (display_seconds) {
draw_scale_x_discrete_time(region, v2d, rect, scene, colorid);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__time, (void *)scene, colorid);
}
else {
draw_scale_x_values(region, v2d, rect, colorid);
draw_horizontal_scale_indicators(
region, v2d, step, rect, view_to_string__value, nullptr, colorid);
}
}

View File

@@ -274,7 +274,8 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
WM_gizmomap_draw(region->runtime->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D);
/* scrubbing region */
ED_time_scrub_draw(region, scene, saction->flag & SACTION_DRAWTIME, true);
const int fps = round_db_to_int(scene->frames_per_second());
ED_time_scrub_draw(region, scene, saction->flag & SACTION_DRAWTIME, true, fps);
}
static void action_main_region_draw_overlay(const bContext *C, ARegion *region)

View File

@@ -265,8 +265,8 @@ void clip_draw_graph(SpaceClip *sc, ARegion *region, Scene *scene)
View2D *v2d = &region->v2d;
/* grid */
UI_view2d_draw_lines_x__values(v2d);
UI_view2d_draw_lines_y__values(v2d);
UI_view2d_draw_lines_x__values(v2d, 10);
UI_view2d_draw_lines_y__values(v2d, 10);
if (clip) {
uint pos = GPU_vertformat_attr_add(

View File

@@ -19,6 +19,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_path_utils.hh"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
@@ -875,7 +876,8 @@ static void graph_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* time-scrubbing */
ED_time_scrub_draw(region, scene, sc->flag & SC_SHOW_SECONDS, true);
const int fps = round_db_to_int(scene->frames_per_second());
ED_time_scrub_draw(region, scene, sc->flag & SC_SHOW_SECONDS, true, fps);
/* current frame indicator */
ED_time_scrub_draw_current_frame(region, scene, sc->flag & SC_SHOW_SECONDS, !minimized);
@@ -895,7 +897,7 @@ static void graph_region_draw(const bContext *C, ARegion *region)
rcti rect;
BLI_rcti_init(
&rect, 0, 15 * UI_SCALE_FAC, 15 * UI_SCALE_FAC, region->winy - UI_TIME_SCRUB_MARGIN_Y);
UI_view2d_draw_scale_y__values(region, v2d, &rect, TH_TEXT);
UI_view2d_draw_scale_y__values(region, v2d, &rect, TH_TEXT, 10);
}
}
@@ -936,7 +938,8 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* time-scrubbing */
ED_time_scrub_draw(region, scene, sc->flag & SC_SHOW_SECONDS, true);
const int fps = round_db_to_int(scene->frames_per_second());
ED_time_scrub_draw(region, scene, sc->flag & SC_SHOW_SECONDS, true, fps);
/* current frame indicator */
ED_time_scrub_draw_current_frame(region, scene, sc->flag & SC_SHOW_SECONDS, !minimized);

View File

@@ -233,11 +233,19 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
/* In driver mode, both X and Y axes are in the same units as the driven property, and so the
* grid size should be independent of the scene's frame rate. */
constexpr int driver_step = 10;
/* grid */
bool display_seconds = (sipo->mode == SIPO_MODE_ANIMATION) && (sipo->flag & SIPO_DRAWTIME);
if (region->winy > min_height) {
UI_view2d_draw_lines_x__frames_or_seconds(v2d, scene, display_seconds);
UI_view2d_draw_lines_y__values(v2d);
if (sipo->mode == SIPO_MODE_DRIVERS) {
UI_view2d_draw_lines_x__values(v2d, driver_step);
}
else {
UI_view2d_draw_lines_x__frames_or_seconds(v2d, scene, display_seconds);
}
UI_view2d_draw_lines_y__values(v2d, 10);
}
ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW);
@@ -336,7 +344,11 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
/* time-scrubbing */
ED_time_scrub_draw(region, scene, display_seconds, false);
int base = round_db_to_int(scene->frames_per_second());
if (sipo->mode == SIPO_MODE_DRIVERS) {
base = driver_step;
}
ED_time_scrub_draw(region, scene, display_seconds, false, base);
}
static void graph_main_region_draw_overlay(const bContext *C, ARegion *region)
@@ -366,7 +378,7 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region)
rcti rect;
BLI_rcti_init(
&rect, 0, 15 * UI_SCALE_FAC, 15 * UI_SCALE_FAC, region->winy - UI_TIME_SCRUB_MARGIN_Y);
UI_view2d_draw_scale_y__values(region, v2d, &rect, TH_SCROLL_TEXT);
UI_view2d_draw_scale_y__values(region, v2d, &rect, TH_SCROLL_TEXT, 10);
}
}
else {

View File

@@ -16,6 +16,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
@@ -303,7 +304,8 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
/* reset view matrix */
UI_view2d_view_restore(C);
ED_time_scrub_draw(region, scene, snla->flag & SNLA_DRAWTIME, true);
const int fps = round_db_to_int(scene->frames_per_second());
ED_time_scrub_draw(region, scene, snla->flag & SNLA_DRAWTIME, true, fps);
}
static void nla_main_region_draw_overlay(const bContext *C, ARegion *region)

View File

@@ -1893,7 +1893,8 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
draw_timeline_gizmos(&ctx);
draw_timeline_post_view_callbacks(&ctx);
if (ctx.scene) {
ED_time_scrub_draw(region, ctx.scene, !(ctx.sseq->flag & SEQ_DRAWFRAMES), true);
const int fps = round_db_to_int(ctx.scene->frames_per_second());
ED_time_scrub_draw(region, ctx.scene, !(ctx.sseq->flag & SEQ_DRAWFRAMES), true, fps);
}
if (ctx.scene) {

View File

@@ -600,10 +600,8 @@ static void initTranslation(TransInfo *t, wmOperator * /*op*/)
if (t->spacetype == SPACE_GRAPH) {
View2D *v2d = &t->region->v2d;
Scene *scene = t->scene;
SpaceGraph *sipo = static_cast<SpaceGraph *>(t->area->spacedata.first);
aspect[0] = UI_view2d_grid_resolution_x__frames_or_seconds(
v2d, scene, sipo->flag & SIPO_DRAWTIME);
aspect[1] = UI_view2d_grid_resolution_y__values(v2d);
aspect[0] = UI_view2d_grid_resolution_x__frames_or_seconds(v2d, scene);
aspect[1] = UI_view2d_grid_resolution_y__values(v2d, 10);
}
t->increment = t->snap_spatial * aspect;