Blender V4.5
view2d_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cfloat>
11#include <cmath>
12#include <cstring>
13
14#include "DNA_scene_types.h"
15#include "DNA_userdef_types.h"
16
17#include "BLI_math_base.h"
18#include "BLI_rect.h"
19#include "BLI_string.h"
20#include "BLI_timecode.h"
21#include "BLI_utildefines.h"
22#include "BLI_vector.hh"
23
24#include "GPU_immediate.hh"
25#include "GPU_matrix.hh"
26#include "GPU_state.hh"
27
28#include "WM_api.hh"
29
30#include "BLF_api.hh"
31
32#include "UI_resources.hh"
33#include "UI_view2d.hh"
34
35/* Compute display grid resolution
36 ********************************************************/
37
38#define MIN_MAJOR_LINE_DISTANCE (U.v2d_min_gridsize * UI_SCALE_FAC)
39
40static float select_major_distance(const float *possible_distances,
41 uint amount,
42 float pixel_width,
43 float view_width)
44{
45 BLI_assert(amount >= 1);
46
47 if (IS_EQF(view_width, 0.0f)) {
48 return possible_distances[0];
49 }
50
51 const float pixels_per_view_unit = pixel_width / view_width;
52
53 for (uint i = 0; i < amount; i++) {
54 const float distance = possible_distances[i];
55 if (pixels_per_view_unit * distance >= MIN_MAJOR_LINE_DISTANCE) {
56 return distance;
57 }
58 }
59 return possible_distances[amount - 1];
60}
61
62static const float discrete_value_scales[] = {
63 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000};
64
65static const float continuous_value_scales[] = {0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2,
66 5, 10, 20, 50, 100, 200, 500, 1000,
67 2000, 5000, 10000, 20000, 50000, 100000};
68
76
84
92
93static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene)
94{
95 const double fps = FPS;
96
97 blender::Vector<float, 32> possible_distances;
98
99 for (int step = 1; step < fps; step *= 2) {
100 possible_distances.append(step);
101 }
102
103 for (int i = 0; i <= 5; i++) {
104 uint fac = pow(60, i);
105 possible_distances.append(fac * fps);
106 possible_distances.append(fac * 2 * fps);
107 possible_distances.append(fac * 5 * fps);
108 possible_distances.append(fac * 10 * fps);
109 possible_distances.append(fac * 30 * fps);
110 possible_distances.append(fac * 60 * fps);
111 }
112
113 float distance = select_major_distance(possible_distances.data(),
114 possible_distances.size(),
115 BLI_rcti_size_x(&v2d->mask),
116 BLI_rctf_size_x(&v2d->cur));
117
118 return distance;
119}
120
121/* Draw parallel lines
122 ************************************/
123
125 float offset;
126 float distance;
127};
128
130 float region_start,
131 float region_end,
132 float *r_first,
133 uint *r_steps)
134{
135 if (region_start >= region_end) {
136 *r_first = 0;
137 *r_steps = 0;
138 return;
139 }
140
141 BLI_assert(lines->distance > 0);
142 BLI_assert(region_start <= region_end);
143
144 *r_first = ceilf((region_start - lines->offset) / lines->distance) * lines->distance +
145 lines->offset;
146
147 if (region_start <= *r_first && region_end >= *r_first) {
148 *r_steps = std::max(0.0f, floorf((region_end - *r_first) / lines->distance)) + 1;
149 }
150 else {
151 *r_steps = 0;
152 }
153}
154
158static void draw_parallel_lines(const ParallelLinesSet *lines,
159 const rctf *rect,
160 const rcti *rect_mask,
161 const uchar color[3],
162 char direction)
163{
164 float first;
165 uint steps, steps_max;
166
167 if (direction == 'v') {
168 get_parallel_lines_draw_steps(lines, rect->xmin, rect->xmax, &first, &steps);
169 steps_max = BLI_rcti_size_x(rect_mask);
170 }
171 else {
172 BLI_assert(direction == 'h');
173 get_parallel_lines_draw_steps(lines, rect->ymin, rect->ymax, &first, &steps);
174 steps_max = BLI_rcti_size_y(rect_mask);
175 }
176
177 if (steps == 0) {
178 return;
179 }
180
181 if (UNLIKELY(steps >= steps_max)) {
182 /* Note that we could draw a solid color,
183 * however this flickers because of numeric instability when zoomed out. */
184 return;
185 }
186
189
190 if (U.pixelsize > 1.0f) {
191 float viewport[4];
192 GPU_viewport_size_get_f(viewport);
193
195 immUniform2fv("viewportSize", &viewport[2]);
196 /* -1.0f offset here is because the line is too fat due to the builtin anti-aliasing.
197 * TODO: make a variant or a uniform to toggle it off. */
198 immUniform1f("lineWidth", U.pixelsize - 1.0f);
199 }
200 else {
202 }
203 immUniformColor3ubv(color);
205
206 if (direction == 'v') {
207 for (uint i = 0; i < steps; i++) {
208 const float xpos = first + i * lines->distance;
209 immVertex2f(pos, xpos, rect->ymin);
210 immVertex2f(pos, xpos, rect->ymax);
211 }
212 }
213 else {
214 for (uint i = 0; i < steps; i++) {
215 const float ypos = first + i * lines->distance;
216 immVertex2f(pos, rect->xmin, ypos);
217 immVertex2f(pos, rect->xmax, ypos);
218 }
219 }
220
221 immEnd();
223}
224
225static void view2d_draw_lines_internal(const View2D *v2d,
226 const ParallelLinesSet *lines,
227 const uchar color[3],
228 char direction)
229{
232 draw_parallel_lines(lines, &v2d->cur, &v2d->mask, color, direction);
234}
235
236static void view2d_draw_lines(const View2D *v2d,
237 float major_distance,
238 bool display_minor_lines,
239 char direction)
240{
241 {
242 uchar major_color[3];
243 UI_GetThemeColor3ubv(TH_GRID, major_color);
244 ParallelLinesSet major_lines;
245 major_lines.distance = major_distance;
246 major_lines.offset = 0;
247 view2d_draw_lines_internal(v2d, &major_lines, major_color, direction);
248 }
249
250 if (display_minor_lines) {
251 uchar minor_color[3];
252 UI_GetThemeColorShade3ubv(TH_GRID, 16, minor_color);
253 ParallelLinesSet minor_lines;
254 minor_lines.distance = major_distance;
255 minor_lines.offset = major_distance / 2.0f;
256 view2d_draw_lines_internal(v2d, &minor_lines, minor_color, direction);
257 }
258}
259
260/* Scale indicator text drawing
261 **************************************************/
262
264 void (*)(void *user_data, float v2d_pos, float v2d_step, char *r_str, uint str_maxncpy);
265
267 const View2D *v2d,
268 float distance,
269 const rcti *rect,
271 void *to_string_data,
272 int colorid)
273{
274 if (UI_view2d_scale_get_x(v2d) <= 0.0f) {
275 return;
276 }
277
278 float start;
279 uint steps;
280 {
281 ParallelLinesSet lines;
282 lines.distance = distance;
283 lines.offset = 0;
287 &start,
288 &steps);
289 const uint steps_max = BLI_rcti_size_x(&v2d->mask);
290 if (UNLIKELY(steps >= steps_max)) {
291 return;
292 }
293 }
294
297
298 const int font_id = BLF_set_default();
299 UI_FontThemeColor(font_id, colorid);
300
302
303 const float ypos = rect->ymin + 4 * UI_SCALE_FAC;
304 const float xmin = rect->xmin;
305 const float xmax = rect->xmax;
306
307 char text[32];
308
309 /* Calculate max_label_count and draw_frequency based on largest visible label. */
310 int draw_frequency;
311 {
312 to_string(to_string_data, start, 0, text, sizeof(text));
313 const float left_text_width = BLF_width(font_id, text, strlen(text));
314 to_string(to_string_data, start + steps * distance, 0, text, sizeof(text));
315 const float right_text_width = BLF_width(font_id, text, strlen(text));
316 const float max_text_width = max_ff(left_text_width, right_text_width);
317 const float max_label_count = BLI_rcti_size_x(&v2d->mask) / (max_text_width + 10.0f);
318 draw_frequency = ceil(float(steps) / max_label_count);
319 }
320
321 if (draw_frequency != 0) {
322 const int start_index = abs(int(start / distance)) % draw_frequency;
323 for (uint i = start_index; i < steps; i += draw_frequency) {
324 const float xpos_view = start + i * distance;
325 const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view);
326 to_string(to_string_data, xpos_view, distance, text, sizeof(text));
327 const float text_width = BLF_width(font_id, text, strlen(text));
328
329 if (xpos_region - text_width / 2.0f >= xmin && xpos_region + text_width / 2.0f <= xmax) {
331 xpos_region - std::trunc(text_width / 2.0f), ypos, 0.0f, text, sizeof(text));
332 }
333 }
334 }
335
338}
339
340static void draw_vertical_scale_indicators(const ARegion *region,
341 const View2D *v2d,
342 float distance,
343 float display_offset,
344 const rcti *rect,
346 void *to_string_data,
347 int colorid)
348{
349 if (UI_view2d_scale_get_y(v2d) <= 0.0f) {
350 return;
351 }
352
353 float start;
354 uint steps;
355 {
356 ParallelLinesSet lines;
357 lines.distance = distance;
358 lines.offset = 0;
362 &start,
363 &steps);
364 const uint steps_max = BLI_rcti_size_y(&v2d->mask);
365 if (UNLIKELY(steps >= steps_max)) {
366 return;
367 }
368 }
369
372
373 const int font_id = BLF_set_default();
374 UI_FontThemeColor(font_id, colorid);
375
377
378 BLF_enable(font_id, BLF_SHADOW);
379 float shadow_color[4];
380 UI_GetThemeColor4fv(TH_BACK, shadow_color);
381 BLF_shadow_offset(font_id, 0, 0);
382 BLF_shadow(font_id, FontShadowType::Outline, shadow_color);
383
384 const float x_offset = 8.0f;
385 const float xpos = (rect->xmin + x_offset) * UI_SCALE_FAC;
386 const float ymin = rect->ymin;
387 const float ymax = rect->ymax;
388 const float y_offset = (BLF_height(font_id, "0", 1) / 2.0f) - U.pixelsize;
389
390 for (uint i = 0; i < steps; i++) {
391 const float ypos_view = start + i * distance;
392 const float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset);
393 char text[32];
394 to_string(to_string_data, ypos_view, distance, text, sizeof(text));
395
396 if (ypos_region - y_offset >= ymin && ypos_region + y_offset <= ymax) {
397 BLF_draw_default(xpos, ypos_region - y_offset, 0.0f, text, sizeof(text));
398 }
399 }
400
401 BLF_disable(font_id, BLF_SHADOW);
402
404
406}
407
409 void * /*user_data*/, float v2d_pos, float /*v2d_step*/, char *r_str, uint str_maxncpy)
410{
411 BLI_snprintf(r_str, str_maxncpy, "%d", int(v2d_pos));
412}
413
415 void *user_data, float v2d_pos, float v2d_step, char *r_str, uint str_maxncpy)
416{
417 const Scene *scene = (const Scene *)user_data;
418
419 int brevity_level = -1;
420 if (U.timecode_style == USER_TIMECODE_MINIMAL && v2d_step >= FPS) {
421 brevity_level = 1;
422 }
423
425 r_str, str_maxncpy, brevity_level, v2d_pos / float(FPS), FPS, U.timecode_style);
426}
427
429 void * /*user_data*/, float v2d_pos, float v2d_step, char *r_str, uint str_maxncpy)
430{
431 if (v2d_step >= 1.0f) {
432 BLI_snprintf(r_str, str_maxncpy, "%d", int(v2d_pos));
433 }
434 else if (v2d_step >= 0.1f) {
435 BLI_snprintf(r_str, str_maxncpy, "%.1f", v2d_pos);
436 }
437 else if (v2d_step >= 0.01f) {
438 BLI_snprintf(r_str, str_maxncpy, "%.2f", v2d_pos);
439 }
440 else {
441 BLI_snprintf(r_str, str_maxncpy, "%.3f", v2d_pos);
442 }
443}
444
445/* Grid Resolution API
446 **************************************************/
447
449 const Scene *scene,
450 bool display_seconds)
451{
452 if (display_seconds) {
453 return view2d_major_step_x__time(v2d, scene);
454 }
456}
457
462
463/* Line Drawing API
464 **************************************************/
465
466void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d, bool display_minor_lines)
467{
468 const uint major_line_distance = view2d_major_step_x__discrete(v2d);
470 v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v');
471}
472
474{
475 const float major_line_distance = view2d_major_step_x__continuous(v2d);
476 view2d_draw_lines(v2d, major_line_distance, true, 'v');
477}
478
480{
481 const float major_line_distance = view2d_major_step_y__continuous(v2d);
482 view2d_draw_lines(v2d, major_line_distance, true, 'h');
483}
484
486 const Scene *scene,
487 bool display_minor_lines)
488{
489 const float major_line_distance = view2d_major_step_x__time(v2d, scene);
491 v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v');
492}
493
495 const Scene *scene,
496 bool display_seconds,
497 bool display_minor_lines)
498{
499 if (display_seconds) {
500 UI_view2d_draw_lines_x__discrete_time(v2d, scene, display_minor_lines);
501 }
502 else {
503 UI_view2d_draw_lines_x__discrete_values(v2d, display_minor_lines);
504 }
505}
506
508 const Scene *scene,
509 bool display_seconds)
510{
511 if (display_seconds) {
513 }
514 else {
516 }
517}
518
519/* Scale indicator text drawing API
520 **************************************************/
521
523 const View2D *v2d,
524 const rcti *rect,
525 int colorid)
526{
527 const float number_step = view2d_major_step_x__discrete(v2d);
529 region, v2d, number_step, rect, view_to_string__frame_number, nullptr, colorid);
530}
531
533 const ARegion *region, const View2D *v2d, const rcti *rect, const Scene *scene, int colorid)
534{
535 const float step = view2d_major_step_x__time(v2d, scene);
537 region, v2d, step, rect, view_to_string__time, (void *)scene, colorid);
538}
539
540static void UI_view2d_draw_scale_x__values(const ARegion *region,
541 const View2D *v2d,
542 const rcti *rect,
543 int colorid)
544{
545 const float step = view2d_major_step_x__continuous(v2d);
547 region, v2d, step, rect, view_to_string__value, nullptr, colorid);
548}
549
551 const View2D *v2d,
552 const rcti *rect,
553 int colorid)
554{
555 const float step = view2d_major_step_y__continuous(v2d);
557 region, v2d, step, 0.0f, rect, view_to_string__value, nullptr, colorid);
558}
559
561 const View2D *v2d,
562 const rcti *rect,
563 int colorid)
564{
566 region, v2d, 1.0f, 0.5f, rect, view_to_string__value, nullptr, colorid);
567}
568
570 const View2D *v2d,
571 const rcti *rect,
572 const Scene *scene,
573 bool display_seconds,
574 int colorid)
575{
576 if (display_seconds) {
577 UI_view2d_draw_scale_x__discrete_time(region, v2d, rect, scene, colorid);
578 }
579 else {
580 UI_view2d_draw_scale_x__discrete_values(region, v2d, rect, colorid);
581 }
582}
583
585 const View2D *v2d,
586 const rcti *rect,
587 const Scene *scene,
588 bool display_seconds,
589 int colorid)
590{
591 if (display_seconds) {
592 UI_view2d_draw_scale_x__discrete_time(region, v2d, rect, scene, colorid);
593 }
594 else {
595 UI_view2d_draw_scale_x__values(region, v2d, rect, colorid);
596 }
597}
int BLF_set_default()
void BLF_shadow(int fontid, FontShadowType type, const float rgba[4]=nullptr)
Definition blf.cc:928
float BLF_height(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:840
@ BLF_SHADOW
Definition BLF_api.hh:435
void BLF_shadow_offset(int fontid, int x, int y)
Definition blf.cc:940
void BLF_batch_draw_begin()
Definition blf.cc:531
void BLF_disable(int fontid, int option)
Definition blf.cc:329
void BLF_batch_draw_end()
Definition blf.cc:544
void BLF_enable(int fontid, int option)
Definition blf.cc:320
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:805
void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL()
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float max_ff(float a, float b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned char uchar
unsigned int uint
size_t BLI_timecode_string_from_time(char *str, size_t maxncpy, int brevity_level, float time_seconds, double fps, short timecode_style) ATTR_NONNULL()
Definition timecode.cc:22
#define ARRAY_SIZE(arr)
#define UNLIKELY(x)
#define IS_EQF(a, b)
#define FPS
#define UI_SCALE_FAC
@ USER_TIMECODE_MINIMAL
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniformColor3ubv(const unsigned char rgb[3])
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immBegin(GPUPrimType, uint vertex_len)
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ TH_GRID
@ TH_BACK
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_FontThemeColor(int fontid, int colorid)
void UI_GetThemeColorShade3ubv(int colorid, int offset, unsigned char col[3])
float UI_view2d_region_to_view_y(const View2D *v2d, float y)
Definition view2d.cc:1661
float UI_view2d_view_to_region_y(const View2D *v2d, float y)
Definition view2d.cc:1695
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1095
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1924
float UI_view2d_region_to_view_x(const View2D *v2d, float x)
Definition view2d.cc:1656
float UI_view2d_view_to_region_x(const View2D *v2d, float x)
Definition view2d.cc:1690
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1920
#define U
int64_t size() const
void append(const T &value)
#define ceilf(x)
#define floorf(x)
static const char * to_string(const Interpolation &interp)
Definition gl_shader.cc:108
uint pos
#define pow
#define abs
#define ceil
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
format
static const int steps
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
i
Definition text_draw.cc:230
static void draw_vertical_scale_indicators(const ARegion *region, const View2D *v2d, float distance, float display_offset, const rcti *rect, PositionToString to_string, void *to_string_data, int colorid)
static void draw_horizontal_scale_indicators(const ARegion *region, const View2D *v2d, float distance, const rcti *rect, PositionToString to_string, void *to_string_data, int colorid)
float UI_view2d_grid_resolution_x__frames_or_seconds(const View2D *v2d, const Scene *scene, bool display_seconds)
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)
void UI_view2d_draw_lines_x__values(const View2D *v2d)
static float select_major_distance(const float *possible_distances, uint amount, float pixel_width, float view_width)
void(*)(void *user_data, float v2d_pos, float v2d_step, char *r_str, uint str_maxncpy) PositionToString
static void UI_view2d_draw_scale_x__values(const ARegion *region, const View2D *v2d, const rcti *rect, int colorid)
#define MIN_MAJOR_LINE_DISTANCE
static void view_to_string__frame_number(void *, float v2d_pos, float, char *r_str, uint str_maxncpy)
void UI_view2d_draw_lines_x__frames_or_seconds(const View2D *v2d, const Scene *scene, bool display_seconds)
static float view2d_major_step_x__continuous(const View2D *v2d)
static void view2d_draw_lines(const View2D *v2d, float major_distance, bool display_minor_lines, char direction)
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)
static void view_to_string__time(void *user_data, float v2d_pos, float v2d_step, char *r_str, uint str_maxncpy)
void UI_view2d_draw_lines_y__values(const View2D *v2d)
static const float discrete_value_scales[]
static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene)
void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const View2D *v2d, const Scene *scene, bool display_seconds, bool display_minor_lines)
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)
float UI_view2d_grid_resolution_y__values(const View2D *v2d)
static void UI_view2d_draw_scale_x__discrete_time(const ARegion *region, const View2D *v2d, const rcti *rect, const Scene *scene, int colorid)
void UI_view2d_draw_scale_y__values(const ARegion *region, const View2D *v2d, const rcti *rect, int colorid)
static void view2d_draw_lines_internal(const View2D *v2d, const ParallelLinesSet *lines, const uchar color[3], char direction)
void UI_view2d_draw_scale_y__block(const ARegion *region, const View2D *v2d, const rcti *rect, int colorid)
static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region, const View2D *v2d, const rcti *rect, int colorid)
static float view2d_major_step_y__continuous(const View2D *v2d)
static uint view2d_major_step_x__discrete(const View2D *v2d)
static void view_to_string__value(void *, float v2d_pos, float v2d_step, char *r_str, uint str_maxncpy)
static void draw_parallel_lines(const ParallelLinesSet *lines, const rctf *rect, const rcti *rect_mask, const uchar color[3], char direction)
static const float continuous_value_scales[]
static void get_parallel_lines_draw_steps(const ParallelLinesSet *lines, float region_start, float region_end, float *r_first, uint *r_steps)
void wmOrtho2_region_pixelspace(const ARegion *region)