Blender V5.0
graph_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 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 <cstdio>
13#include <cstring>
14
15#include "BLI_listbase.h"
17#include "BLI_utildefines.h"
18#include "BLI_vector.hh"
19
20#include "DNA_anim_types.h"
21#include "DNA_screen_types.h"
22#include "DNA_space_types.h"
23#include "DNA_userdef_types.h"
24
25#include "BKE_anim_data.hh"
26#include "BKE_curve.hh"
27#include "BKE_fcurve.hh"
28#include "BKE_nla.hh"
29
30#include "GPU_immediate.hh"
31#include "GPU_matrix.hh"
32#include "GPU_state.hh"
33
34#include "ED_anim_api.hh"
35
36#include "graph_intern.hh"
37
38#include "UI_interface.hh"
39#include "UI_resources.hh"
40#include "UI_view2d.hh"
41
42static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu);
43
44/* -------------------------------------------------------------------- */
47
48/* determine the alpha value that should be used when
49 * drawing components for some F-Curve (fcu)
50 * - selected F-Curves should be more visible than partially visible ones
51 */
52static float fcurve_display_alpha(const FCurve *fcu)
53{
54 return (fcu->flag & FCURVE_SELECTED) ? 1.0f : U.fcu_inactive_alpha;
55}
56
59 const float min,
60 const float max)
61{
62 bool replace;
63 int first, last;
64 first = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, min, fcu->totvert, &replace);
65 first = clamp_i(first - 1, 0, fcu->totvert - 1);
66
67 last = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, max, fcu->totvert, &replace);
68 last = replace ? last + 1 : last;
69 last = clamp_i(last, 0, fcu->totvert - 1);
70 /* Iterating over index range is exclusive of the last index.
71 * But we need `last` to be visited. */
72 return blender::IndexRange(first, (last - first) + 1);
73}
74
76
77/* -------------------------------------------------------------------- */
80
81/* Envelope -------------- */
82
83/* TODO: draw a shaded poly showing the region of influence too!!! */
89 View2D *v2d,
90 bAnimListElem *ale_nla_remap)
91{
92 FMod_Envelope *env = (FMod_Envelope *)fcm->data;
94 const float fac = 0.05f * BLI_rctf_size_x(&v2d->cur);
95 int i;
96
97 const uint shdr_pos = GPU_vertformat_attr_add(
98 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
99
100 GPU_line_width(1.0f);
101
103
104 float viewport_size[4];
105 GPU_viewport_size_get_f(viewport_size);
106 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
107
108 immUniform1i("colors_len", 0); /* Simple dashes. */
109 immUniformColor3f(0.0f, 0.0f, 0.0f);
110 immUniform1f("dash_width", 10.0f);
111 immUniform1f("udash_factor", 0.5f);
112
113 /* draw two black lines showing the standard reference levels */
114
116 immVertex2f(shdr_pos, v2d->cur.xmin, env->midval + env->min);
117 immVertex2f(shdr_pos, v2d->cur.xmax, env->midval + env->min);
118
119 immVertex2f(shdr_pos, v2d->cur.xmin, env->midval + env->max);
120 immVertex2f(shdr_pos, v2d->cur.xmax, env->midval + env->max);
121 immEnd();
122
124
125 if (env->totvert > 0) {
126 /* set size of vertices (non-adjustable for now) */
127 GPU_point_size(2.0f);
128
130
131 /* for now, point color is fixed, and is white */
132 immUniformColor3f(1.0f, 1.0f, 1.0f);
133
135
136 for (i = 0, fed = env->data; i < env->totvert; i++, fed++) {
137 const float env_scene_time = ANIM_nla_tweakedit_remap(
138 ale_nla_remap, fed->time, NLATIME_CONVERT_MAP);
139
140 /* only draw if visible
141 * - min/max here are fixed, not relative
142 */
143 if (IN_RANGE(env_scene_time, (v2d->cur.xmin - fac), (v2d->cur.xmax + fac))) {
144 immVertex2f(shdr_pos, env_scene_time, fed->min);
145 immVertex2f(shdr_pos, env_scene_time, fed->max);
146 }
147 }
148
149 immEnd();
150
152 }
153}
154
156
157/* -------------------------------------------------------------------- */
160
161/* Points ---------------- */
162
163/* helper func - set color to draw F-Curve data with */
164static void set_fcurve_vertex_color(FCurve *fcu, bool sel)
165{
166 float color[4];
167 float diff;
168
169 /* Set color of curve vertex based on state of curve (i.e. 'Edit' Mode) */
170 if ((fcu->flag & FCURVE_PROTECTED) == 0) {
171 /* Curve's points ARE BEING edited */
173 }
174 else {
175 /* Curve's points CANNOT BE edited */
177 }
178
179 /* Fade the 'intensity' of the vertices based on the selection of the curves too
180 * - Only fade by 50% the amount the curves were faded by, so that the points
181 * still stand out for easier selection
182 */
183 diff = 1.0f - fcurve_display_alpha(fcu);
184 color[3] = 1.0f - (diff * 0.5f);
185 CLAMP(color[3], 0.2f, 1.0f);
186
187 immUniformColor4fv(color);
188}
189
190/* Draw a cross at the given position. Shader must already be bound.
191 * NOTE: the caller MUST HAVE GL_LINE_SMOOTH & GL_BLEND ENABLED, otherwise the controls don't
192 * have a consistent appearance (due to off-pixel alignments).
193 */
194static void draw_cross(float position[2], const float scale[2], uint attr_id)
195{
197 GPU_matrix_translate_2fv(position);
198 GPU_matrix_scale_2f(1.0f / scale[0], 1.0f / scale[1]);
199
200 /* Draw X shape. */
201 const float line_length = 0.7f;
203 immVertex2f(attr_id, -line_length, -line_length);
204 immVertex2f(attr_id, +line_length, +line_length);
205
206 immVertex2f(attr_id, -line_length, +line_length);
207 immVertex2f(attr_id, +line_length, -line_length);
208 immEnd();
209
211}
212
214 bool sel,
215 uint pos,
216 const blender::IndexRange index_range)
217{
218 set_fcurve_vertex_color(fcu, sel);
219
221
222 for (const int i : index_range) {
223 BezTriple *bezt = &fcu->bezt[i];
224 /* 'Keyframe' vertex only, as handle lines and handles have already been drawn
225 * - only draw those with correct selection state for the current drawing color
226 * -
227 */
228 if ((bezt->f2 & SELECT) == sel) {
229 immVertex2fv(pos, bezt->vec[1]);
230 }
231 }
232
233 immEnd();
234}
235
239static void draw_fcurve_active_vertex(const FCurve *fcu, const View2D *v2d, const uint pos)
240{
241 const int active_keyframe_index = BKE_fcurve_active_keyframe_index(fcu);
242 if (!(fcu->flag & FCURVE_ACTIVE) || active_keyframe_index == FCURVE_ACTIVE_KEYFRAME_NONE) {
243 return;
244 }
245
246 const float fac = 0.05f * BLI_rctf_size_x(&v2d->cur);
247 const BezTriple *bezt = &fcu->bezt[active_keyframe_index];
248
249 if (!IN_RANGE(bezt->vec[1][0], (v2d->cur.xmin - fac), (v2d->cur.xmax + fac))) {
250 return;
251 }
252 if (!(bezt->f2 & SELECT)) {
253 return;
254 }
255
258 immVertex2fv(pos, bezt->vec[1]);
259 immEnd();
260}
261
262/* helper func - draw keyframe vertices only for an F-Curve */
263static void draw_fcurve_keyframe_vertices(FCurve *fcu, View2D *v2d, const uint pos)
264{
266
267 if ((fcu->flag & FCURVE_PROTECTED) == 0) {
269 }
270 else {
271 /* Draw keyframes on locked curves slightly smaller to give them less visual weight. */
273 }
274
276 fcu, v2d->cur.xmin, v2d->cur.xmax);
277 draw_fcurve_selected_keyframe_vertices(fcu, false, pos, index_range);
278 draw_fcurve_selected_keyframe_vertices(fcu, true, pos, index_range);
280
282}
283
284/* helper func - draw handle vertices only for an F-Curve (if it is not protected) */
286 FCurve *fcu, View2D *v2d, bool sel, bool sel_handle_only, uint pos)
287{
289 fcu, v2d->cur.xmin, v2d->cur.xmax);
290
291 /* set handle color */
292 float hcolor[3];
294 immUniform4f("outlineColor", hcolor[0], hcolor[1], hcolor[2], 1.0f);
295 immUniformColor3fvAlpha(hcolor, 0.01f); /* almost invisible - only keep for smoothness */
296
298
299 BezTriple *prevbezt = nullptr;
300 for (const int i : index_range) {
301 BezTriple *bezt = &fcu->bezt[i];
302 /* Draw the editmode handles for a bezier curve (others don't have handles)
303 * if their selection status matches the selection status we're drawing for
304 * - first handle only if previous beztriple was bezier-mode
305 * - second handle only if current beztriple is bezier-mode
306 *
307 * Also, need to take into account whether the keyframe was selected
308 * if a Graph Editor option to only show handles of selected keys is on.
309 */
310 if (!sel_handle_only || BEZT_ISSEL_ANY(bezt)) {
311 if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
312 (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))
313 {
314 if ((bezt->f1 & SELECT) == sel
315 /* && v2d->cur.xmin < bezt->vec[0][0] < v2d->cur.xmax) */)
316 {
317 immVertex2fv(pos, bezt->vec[0]);
318 }
319 }
320
321 if (bezt->ipo == BEZT_IPO_BEZ) {
322 if ((bezt->f3 & SELECT) == sel
323 /* && v2d->cur.xmin < bezt->vec[2][0] < v2d->cur.xmax) */)
324 {
325 immVertex2fv(pos, bezt->vec[2]);
326 }
327 }
328 }
329 prevbezt = bezt;
330 }
331
332 immEnd();
333}
334
339 const bool sel_handle_only,
340 const uint pos)
341{
342 const int active_keyframe_index = BKE_fcurve_active_keyframe_index(fcu);
343 if (!(fcu->flag & FCURVE_ACTIVE) || active_keyframe_index == FCURVE_ACTIVE_KEYFRAME_NONE) {
344 return;
345 }
346
347 const BezTriple *bezt = &fcu->bezt[active_keyframe_index];
348
349 if (sel_handle_only && !BEZT_ISSEL_ANY(bezt)) {
350 return;
351 }
352
353 float active_col[4];
355 immUniform4fv("outlineColor", active_col);
356 immUniformColor3fvAlpha(active_col, 0.01f); /* Almost invisible - only keep for smoothness. */
358
359 const BezTriple *left_bezt = active_keyframe_index > 0 ? &fcu->bezt[active_keyframe_index - 1] :
360 bezt;
361 if (left_bezt->ipo == BEZT_IPO_BEZ && (bezt->f1 & SELECT)) {
362 immVertex2fv(pos, bezt->vec[0]);
363 }
364 if (bezt->ipo == BEZT_IPO_BEZ && (bezt->f3 & SELECT)) {
365 immVertex2fv(pos, bezt->vec[2]);
366 }
367 immEnd();
368}
369
370/* helper func - draw handle vertices only for an F-Curve (if it is not protected) */
371static void draw_fcurve_handle_vertices(FCurve *fcu, View2D *v2d, bool sel_handle_only, uint pos)
372{
373 /* smooth outlines for more consistent appearance */
375
376 /* set handle size */
378 immUniform1f("outlineWidth", 1.5f * UI_SCALE_FAC);
379
380 draw_fcurve_selected_handle_vertices(fcu, v2d, false, sel_handle_only, pos);
381 draw_fcurve_selected_handle_vertices(fcu, v2d, true, sel_handle_only, pos);
382 draw_fcurve_active_handle_vertices(fcu, sel_handle_only, pos);
383
385}
386
387static void draw_fcurve_vertices(ARegion *region,
388 FCurve *fcu,
389 bool do_handles,
390 bool sel_handle_only)
391{
392 View2D *v2d = &region->v2d;
393
394 /* only draw points if curve is visible
395 * - Draw unselected points before selected points as separate passes
396 * to make sure in the case of overlapping points that the selected is always visible
397 * - Draw handles before keyframes, so that keyframes will overlap handles
398 * (keyframes are more important for users).
399 */
400
402 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
403
406
407 /* draw the two handles first (if they're shown, the curve doesn't
408 * have just a single keyframe, and the curve is being edited) */
409 if (do_handles) {
410 draw_fcurve_handle_vertices(fcu, v2d, sel_handle_only, pos);
411 }
412
413 /* draw keyframes over the handles */
415
418}
419
420/* Handles ---------------- */
421
422static bool draw_fcurve_handles_check(const SpaceGraph *sipo, const FCurve *fcu)
423{
424 /* don't draw handle lines if handles are not to be shown */
425 if (/* handles shouldn't be shown anywhere */
426 (sipo->flag & SIPO_NOHANDLES) ||
427 /* keyframes aren't editable */
428 (fcu->flag & FCURVE_PROTECTED) ||
429#if 0
430 /* handles can still be selected and handle types set, better draw - campbell */
431 /* editing the handles here will cause weird/incorrect interpolation issues */
432 (fcu->flag & FCURVE_INT_VALUES) ||
433#endif
434 /* group that curve belongs to is not editable */
435 ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)))
436 {
437 return false;
438 }
439 return true;
440}
441
442/* draw lines for F-Curve handles only (this is only done in EditMode)
443 * NOTE: draw_fcurve_handles_check must be checked before running this. */
444static void draw_fcurve_handles(SpaceGraph *sipo, ARegion *region, const FCurve *fcu)
445{
446 using namespace blender;
447
449 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
451 format, "color", blender::gpu::VertAttrType::SFLOAT_32_32_32_32);
453 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
454 GPU_line_smooth(true);
455 }
457
459
460 const IndexRange index_range = get_bounding_bezt_index_range(
461 fcu, region->v2d.cur.xmin, region->v2d.cur.xmax);
462
463 /* slightly hacky, but we want to draw unselected points before selected ones
464 * so that selected points are clearly visible
465 */
466 for (int sel = 0; sel < 2; sel++) {
467 int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE;
468 float col[4];
469
470 BezTriple *prevbezt = nullptr;
471 for (const int i : index_range) {
472 BezTriple *bezt = &fcu->bezt[i];
473 /* if only selected keyframes can get their handles shown,
474 * check that keyframe is selected
475 */
476 if (sipo->flag & SIPO_SELVHANDLESONLY) {
477 if (BEZT_ISSEL_ANY(bezt) == 0) {
478 prevbezt = bezt;
479 continue;
480 }
481 }
482
483 /* draw handle with appropriate set of colors if selection is ok */
484 if ((bezt->f2 & SELECT) == sel) {
485 /* only draw first handle if previous segment had handles */
486 if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
487 (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))
488 {
489 UI_GetThemeColor3fv(basecol + bezt->h1, col);
490 col[3] = fcurve_display_alpha(fcu);
492 immVertex2fv(pos, bezt->vec[0]);
494 immVertex2fv(pos, bezt->vec[1]);
495 }
496
497 /* only draw second handle if this segment is bezier */
498 if (bezt->ipo == BEZT_IPO_BEZ) {
499 UI_GetThemeColor3fv(basecol + bezt->h2, col);
500 col[3] = fcurve_display_alpha(fcu);
502 immVertex2fv(pos, bezt->vec[1]);
504 immVertex2fv(pos, bezt->vec[2]);
505 }
506 }
507 else {
508 /* only draw first handle if previous segment was had handles, and selection is ok */
509 if (((bezt->f1 & SELECT) == sel) && ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
510 (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))))
511 {
512 UI_GetThemeColor3fv(basecol + bezt->h1, col);
513 col[3] = fcurve_display_alpha(fcu);
515 immVertex2fv(pos, bezt->vec[0]);
517 immVertex2fv(pos, bezt->vec[1]);
518 }
519
520 /* only draw second handle if this segment is bezier, and selection is ok */
521 if (((bezt->f3 & SELECT) == sel) && (bezt->ipo == BEZT_IPO_BEZ)) {
522 UI_GetThemeColor3fv(basecol + bezt->h2, col);
523 col[3] = fcurve_display_alpha(fcu);
525 immVertex2fv(pos, bezt->vec[1]);
527 immVertex2fv(pos, bezt->vec[2]);
528 }
529 }
530 prevbezt = bezt;
531 }
532 }
533
534 immEnd();
537 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
538 GPU_line_smooth(false);
539 }
540}
541
542/* Samples ---------------- */
543
544/* helper func - draw keyframe vertices only for an F-Curve */
545static void draw_fcurve_samples(ARegion *region, const FCurve *fcu, const float unit_scale)
546{
547 FPoint *first, *last;
548 float scale[2];
549
550 /* get view settings */
551 const float hsize = UI_GetThemeValuef(TH_VERTEX_SIZE);
552 UI_view2d_scale_get(&region->v2d, &scale[0], &scale[1]);
553
554 scale[0] /= hsize;
555 scale[1] /= hsize / unit_scale;
556
557 /* get verts */
558 first = fcu->fpt;
559 last = (first) ? (first + (fcu->totvert - 1)) : (nullptr);
560
561 /* draw */
562 if (first && last) {
563 /* anti-aliased lines for more consistent appearance */
564 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
565 GPU_line_smooth(true);
566 }
568
570 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
572
574
575 draw_cross(first->vec, scale, pos);
576 draw_cross(last->vec, scale, pos);
577
579
581 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
582 GPU_line_smooth(false);
583 }
584 }
585}
586
587/* Curve ---------------- */
588
589/* Helper func - just draw the F-Curve by sampling the visible region
590 * (for drawing curves with modifiers). */
592 ID *id,
593 const FCurve *fcu_,
594 View2D *v2d,
595 uint pos,
596 const bool use_nla_remap,
597 const bool draw_extrapolation)
598{
599 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
600
601 /* when opening a blend file on a different sized screen or while dragging the toolbar this can
602 * happen best just bail out in this case. */
603 if (UI_view2d_scale_get_x(v2d) <= 0.0f) {
604 return;
605 }
606
607 /* disable any drivers */
608 FCurve fcurve_for_draw = *fcu_;
609 fcurve_for_draw.driver = nullptr;
610
611 /* compute unit correction factor */
612 float offset;
613 float unitFac = ANIM_unit_mapping_get_factor(
614 ac->scene, id, &fcurve_for_draw, mapping_flag, &offset);
615
616 /* Note about sampling frequency:
617 * Ideally, this is chosen such that we have 1-2 pixels = 1 segment
618 * which means that our curves can be as smooth as possible. However,
619 * this does mean that curves may not be fully accurate (i.e. if they have
620 * sudden spikes which happen at the sampling point, we may have problems).
621 * Also, this may introduce lower performance on less densely detailed curves,
622 * though it is impossible to predict this from the modifiers!
623 *
624 * If the automatically determined sampling frequency is likely to cause an infinite
625 * loop (i.e. too close to 0), then clamp it to a determined "safe" value. The value
626 * chosen here is just the coarsest value which still looks reasonable.
627 */
628
629 /* TODO: perhaps we should have 1.0 frames
630 * as upper limit so that curves don't get too distorted? */
631 float pixels_per_sample = 1.5f;
632 float samplefreq = pixels_per_sample / UI_view2d_scale_get_x(v2d);
633
634 if (!(U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING)) {
635 /* Low Precision = coarse lower-bound clamping
636 *
637 * Although the "Beauty Draw" flag was originally for AA'd
638 * line drawing, the sampling rate here has a much greater
639 * impact on performance (e.g. for #40372)!
640 *
641 * This one still amounts to 10 sample-frames for each 1-frame interval
642 * which should be quite a decent approximation in many situations.
643 */
644 samplefreq = std::max(samplefreq, 0.1f);
645 }
646 else {
647 /* "Higher Precision" but slower - especially on larger windows (e.g. #40372) */
648 samplefreq = std::max(samplefreq, 0.00001f);
649 }
650
651 /* the start/end times are simply the horizontal extents of the 'cur' rect */
652 float stime = v2d->cur.xmin;
653 float etime = v2d->cur.xmax;
654
655 AnimData *adt = use_nla_remap ? BKE_animdata_from_id(id) : nullptr;
656
657 /* If not drawing extrapolation, then change fcurve drawing bounds to its keyframe bounds clamped
658 * by graph editor bounds. */
659 if (!draw_extrapolation) {
660 float fcu_start = 0;
661 float fcu_end = 0;
662 BKE_fcurve_calc_range(fcu_, &fcu_start, &fcu_end, false);
663
664 fcu_start = BKE_nla_tweakedit_remap(adt, fcu_start, NLATIME_CONVERT_MAP);
665 fcu_end = BKE_nla_tweakedit_remap(adt, fcu_end, NLATIME_CONVERT_MAP);
666
667 /* Account for reversed NLA strip effect. */
668 if (fcu_end < fcu_start) {
669 std::swap(fcu_start, fcu_end);
670 }
671
672 /* Clamp to graph editor rendering bounds. */
673 stime = max_ff(stime, fcu_start);
674 etime = min_ff(etime, fcu_end);
675 }
676
677 const int total_samples = roundf((etime - stime) / samplefreq);
678 if (total_samples <= 0) {
679 return;
680 }
681
682 /* NLA remapping is linear so we don't have to remap per iteration. */
683 const float eval_start = BKE_nla_tweakedit_remap(adt, stime, NLATIME_CONVERT_UNMAP);
684 const float eval_freq = BKE_nla_tweakedit_remap(adt, stime + samplefreq, NLATIME_CONVERT_UNMAP) -
685 eval_start;
686 const float eval_end = BKE_nla_tweakedit_remap(adt, etime, NLATIME_CONVERT_UNMAP);
687
688 immBegin(GPU_PRIM_LINE_STRIP, (total_samples + 1));
689
690 /* At each sampling interval, add a new vertex.
691 *
692 * Apply the unit correction factor to the calculated values so that the displayed values appear
693 * correctly in the viewport.
694 */
695 for (int i = 0; i < total_samples; i++) {
696 const float ctime = stime + i * samplefreq;
697 float eval_time = eval_start + i * eval_freq;
698
699 /* Prevent drawing past bounds, due to floating point problems.
700 * User-wise, prevent visual flickering.
701 *
702 * This is to cover the case where:
703 * eval_start + total_samples * eval_freq > eval_end
704 * due to floating point problems.
705 */
706 eval_time = std::min(eval_time, eval_end);
707
708 immVertex2f(pos, ctime, (evaluate_fcurve(&fcurve_for_draw, eval_time) + offset) * unitFac);
709 }
710
711 /* Ensure we include end boundary point.
712 * User-wise, prevent visual flickering.
713 *
714 * This is to cover the case where:
715 * eval_start + total_samples * eval_freq < eval_end
716 * due to floating point problems.
717 */
718 immVertex2f(pos, etime, (evaluate_fcurve(&fcurve_for_draw, eval_end) + offset) * unitFac);
719
720 immEnd();
721}
722
723/* helper func - draw a samples-based F-Curve */
725 ID *id,
726 FCurve *fcu,
727 View2D *v2d,
728 const uint shdr_pos,
729 const bool draw_extrapolation)
730{
731 if (!draw_extrapolation && fcu->totvert == 1) {
732 return;
733 }
734
735 FPoint *prevfpt = fcu->fpt;
736 FPoint *fpt = prevfpt + 1;
737 float fac, v[2];
738 int b = fcu->totvert;
739 float unit_scale, offset;
740 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
741 int count = fcu->totvert;
742
743 const bool extrap_left = draw_extrapolation && prevfpt->vec[0] > v2d->cur.xmin;
744 if (extrap_left) {
745 count++;
746 }
747
748 const bool extrap_right = draw_extrapolation && (prevfpt + b - 1)->vec[0] < v2d->cur.xmax;
749 if (extrap_right) {
750 count++;
751 }
752
753 /* apply unit mapping */
755 unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
756 GPU_matrix_scale_2f(1.0f, unit_scale);
757 GPU_matrix_translate_2f(0.0f, offset);
758
760
761 /* extrapolate to left? - left-side of view comes before first keyframe? */
762 if (extrap_left) {
763 v[0] = v2d->cur.xmin;
764
765 /* y-value depends on the interpolation */
766 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
767 (fcu->totvert == 1))
768 {
769 /* just extend across the first keyframe's value */
770 v[1] = prevfpt->vec[1];
771 }
772 else {
773 /* extrapolate linear doesn't use the handle, use the next points center instead */
774 fac = (prevfpt->vec[0] - fpt->vec[0]) / (prevfpt->vec[0] - v[0]);
775 if (fac) {
776 fac = 1.0f / fac;
777 }
778 v[1] = prevfpt->vec[1] - fac * (prevfpt->vec[1] - fpt->vec[1]);
779 }
780
781 immVertex2fv(shdr_pos, v);
782 }
783
784 /* loop over samples, drawing segments */
785 /* draw curve between first and last keyframe (if there are enough to do so) */
786 while (b--) {
787 /* Linear interpolation: just add one point (which should add a new line segment) */
788 immVertex2fv(shdr_pos, prevfpt->vec);
789
790 /* get next pointers */
791 if (b > 0) {
792 prevfpt++;
793 }
794 }
795
796 /* extrapolate to right? (see code for left-extrapolation above too) */
797 if (extrap_right) {
798 v[0] = v2d->cur.xmax;
799
800 /* y-value depends on the interpolation */
801 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
802 (fcu->totvert == 1))
803 {
804 /* based on last keyframe's value */
805 v[1] = prevfpt->vec[1];
806 }
807 else {
808 /* extrapolate linear doesn't use the handle, use the previous points center instead */
809 fpt = prevfpt - 1;
810 fac = (prevfpt->vec[0] - fpt->vec[0]) / (prevfpt->vec[0] - v[0]);
811 if (fac) {
812 fac = 1.0f / fac;
813 }
814 v[1] = prevfpt->vec[1] - fac * (prevfpt->vec[1] - fpt->vec[1]);
815 }
816
817 immVertex2fv(shdr_pos, v);
818 }
819
820 immEnd();
821
823}
824
826 BezTriple *prevbezt,
827 const blender::float2 pixels_per_unit)
828{
829 const float points_per_pixel = 0.25f;
830 const int resolution_x = int(((bezt->vec[1][0] - prevbezt->vec[1][0]) * pixels_per_unit[0]) *
831 points_per_pixel);
832 /* Include the handles in the resolution calculation to cover the case where keys have the same
833 * y-value, but their handles are offset to create an arc. */
834 const float min_y = min_ffff(
835 bezt->vec[1][1], bezt->vec[2][1], prevbezt->vec[1][1], prevbezt->vec[0][1]);
836 const float max_y = max_ffff(
837 bezt->vec[1][1], bezt->vec[2][1], prevbezt->vec[1][1], prevbezt->vec[0][1]);
838 const int resolution_y = int(((max_y - min_y) * pixels_per_unit[1]) * points_per_pixel);
839
840 /* Using a simple sum instead of calculating the diagonal. This gives a slightly higher
841 * resolution but it does compensate for the fact that bezier curves can create long arcs between
842 * keys. */
843 return resolution_x + resolution_y;
844}
845
850static void add_bezt_vertices(BezTriple *bezt,
851 BezTriple *prevbezt,
852 int resolution,
853 blender::Vector<blender::float2> &curve_vertices)
854{
855 if (resolution < 2) {
856 curve_vertices.append({prevbezt->vec[1][0], prevbezt->vec[1][1]});
857 return;
858 }
859
860 /* If the resolution goes too high the line will not end exactly at the keyframe. Probably due to
861 * accumulating floating point issues in BKE_curve_forward_diff_bezier. */
862 resolution = min_ii(64, resolution);
863
864 float prev_key[2], prev_handle[2], bez_handle[2], bez_key[2];
865 /* Allocation needs +1 on resolution because BKE_curve_forward_diff_bezier uses it to iterate
866 * inclusively. */
867 float *bezier_diff_points = MEM_malloc_arrayN<float>(((resolution + 1) * 2), "Draw bezt data");
868
869 prev_key[0] = prevbezt->vec[1][0];
870 prev_key[1] = prevbezt->vec[1][1];
871 prev_handle[0] = prevbezt->vec[2][0];
872 prev_handle[1] = prevbezt->vec[2][1];
873
874 bez_handle[0] = bezt->vec[0][0];
875 bez_handle[1] = bezt->vec[0][1];
876 bez_key[0] = bezt->vec[1][0];
877 bez_key[1] = bezt->vec[1][1];
878
879 BKE_fcurve_correct_bezpart(prev_key, prev_handle, bez_handle, bez_key);
880
882 prev_handle[0],
883 bez_handle[0],
884 bez_key[0],
885 bezier_diff_points,
886 resolution,
887 sizeof(float[2]));
889 prev_handle[1],
890 bez_handle[1],
891 bez_key[1],
892 bezier_diff_points + 1,
893 resolution,
894 sizeof(float[2]));
895
896 for (float *fp = bezier_diff_points; resolution; resolution--, fp += 2) {
897 const float x = *fp;
898 const float y = *(fp + 1);
899 curve_vertices.append({x, y});
900 }
901 MEM_freeN(bezier_diff_points);
902}
903
905 const float v2d_xmin,
906 blender::Vector<blender::float2> &curve_vertices)
907{
908 /* left-side of view comes before first keyframe, so need to extend as not cyclic */
909 float vertex_position[2];
910 vertex_position[0] = v2d_xmin;
911 BezTriple *bezt = &fcu->bezt[0];
912
913 /* y-value depends on the interpolation */
914 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (bezt->ipo == BEZT_IPO_CONST) ||
915 (bezt->ipo == BEZT_IPO_LIN && fcu->totvert == 1))
916 {
917 /* just extend across the first keyframe's value */
918 vertex_position[1] = bezt->vec[1][1];
919 }
920 else if (bezt->ipo == BEZT_IPO_LIN) {
921 BezTriple *next_bezt = bezt + 1;
922 /* extrapolate linear doesn't use the handle, use the next points center instead */
923 float fac = (bezt->vec[1][0] - next_bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
924 if (fac) {
925 fac = 1.0f / fac;
926 }
927 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[1][1] - next_bezt->vec[1][1]);
928 }
929 else {
930 /* based on angle of handle 1 (relative to keyframe) */
931 float fac = (bezt->vec[0][0] - bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
932 if (fac) {
933 fac = 1.0f / fac;
934 }
935 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[0][1] - bezt->vec[1][1]);
936 }
937
938 curve_vertices.append(vertex_position);
939}
940
942 const float v2d_xmax,
943 blender::Vector<blender::float2> &curve_vertices)
944{
945 float vertex_position[2];
946 vertex_position[0] = v2d_xmax;
947 BezTriple *bezt = &fcu->bezt[fcu->totvert - 1];
948
949 /* y-value depends on the interpolation. */
950 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
951 (bezt->ipo == BEZT_IPO_CONST) || (bezt->ipo == BEZT_IPO_LIN && fcu->totvert == 1))
952 {
953 /* based on last keyframe's value */
954 vertex_position[1] = bezt->vec[1][1];
955 }
956 else if (bezt->ipo == BEZT_IPO_LIN) {
957 /* Extrapolate linear doesn't use the handle, use the previous points center instead. */
958 BezTriple *prev_bezt = bezt - 1;
959 float fac = (bezt->vec[1][0] - prev_bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
960 if (fac) {
961 fac = 1.0f / fac;
962 }
963 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[1][1] - prev_bezt->vec[1][1]);
964 }
965 else {
966 /* Based on angle of handle 1 (relative to keyframe). */
967 float fac = (bezt->vec[2][0] - bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
968 if (fac) {
969 fac = 1.0f / fac;
970 }
971 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[2][1] - bezt->vec[1][1]);
972 }
973
974 curve_vertices.append(vertex_position);
975}
976
977static blender::float2 calculate_pixels_per_unit(View2D *v2d, const float unit_scale)
978{
979 const int window_width = BLI_rcti_size_x(&v2d->mask);
980 const int window_height = BLI_rcti_size_y(&v2d->mask);
981
982 const float v2d_frame_range = BLI_rctf_size_x(&v2d->cur);
983 const float v2d_value_range = BLI_rctf_size_y(&v2d->cur);
984 const blender::float2 pixels_per_unit = {window_width / v2d_frame_range,
985 (window_height / v2d_value_range) * unit_scale};
986 return pixels_per_unit;
987}
988
989static float calculate_pixel_distance(const rctf &bounds, const blender::float2 pixels_per_unit)
990{
991 return BLI_rctf_size_x(&bounds) * pixels_per_unit[0] +
992 BLI_rctf_size_y(&bounds) * pixels_per_unit[1];
993}
994
995static void expand_key_bounds(const BezTriple *left_key, const BezTriple *right_key, rctf &bounds)
996{
997 bounds.xmax = right_key->vec[1][0];
998 if (left_key->ipo == BEZT_IPO_BEZ) {
999 /* Respect handles of bezier keys. */
1000 bounds.ymin = min_ffff(
1001 bounds.ymin, right_key->vec[1][1], right_key->vec[0][1], left_key->vec[2][1]);
1002 bounds.ymax = max_ffff(
1003 bounds.ymax, right_key->vec[1][1], right_key->vec[0][1], left_key->vec[2][1]);
1004 }
1005 else {
1006 bounds.ymax = max_ff(bounds.ymax, right_key->vec[1][1]);
1007 bounds.ymin = min_ff(bounds.ymin, right_key->vec[1][1]);
1008 }
1009}
1010
1011/* Helper function - draw one repeat of an F-Curve (using Bezier curve approximations). */
1013 bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, uint pos, const bool draw_extrapolation)
1014{
1015 using namespace blender;
1016 if (!draw_extrapolation && fcu->totvert == 1) {
1017 return;
1018 }
1019
1020 /* Apply unit mapping. */
1022 float offset;
1023 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
1024 const float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
1025 GPU_matrix_scale_2f(1.0f, unit_scale);
1026 GPU_matrix_translate_2f(0.0f, offset);
1027
1028 Vector<float2> curve_vertices;
1029
1030 /* Extrapolate to the left? */
1031 if (draw_extrapolation && fcu->bezt[0].vec[1][0] > v2d->cur.xmin) {
1032 add_extrapolation_point_left(fcu, v2d->cur.xmin, curve_vertices);
1033 }
1034
1035 const IndexRange index_range = get_bounding_bezt_index_range(fcu, v2d->cur.xmin, v2d->cur.xmax);
1036
1037 /* Always add the first point so the extrapolation line doesn't jump. */
1038 curve_vertices.append(
1039 {fcu->bezt[index_range.first()].vec[1][0], fcu->bezt[index_range.first()].vec[1][1]});
1040
1041 const float2 pixels_per_unit = calculate_pixels_per_unit(v2d, unit_scale);
1042 const int window_width = BLI_rcti_size_x(&v2d->mask);
1043 const float v2d_frame_range = BLI_rctf_size_x(&v2d->cur);
1044 const float pixel_width = v2d_frame_range / window_width;
1045 const float samples_per_pixel = 0.66f;
1046 const float evaluation_step = pixel_width / samples_per_pixel;
1047
1048 BezTriple *first_key = &fcu->bezt[index_range.first()];
1049 rctf key_bounds = {
1050 first_key->vec[1][0], first_key->vec[1][0], first_key->vec[1][1], first_key->vec[1][1]};
1051 /* Used when skipping keys. */
1052 bool has_skipped_keys = false;
1053 const float min_pixel_distance = 3.0f;
1054
1055 /* Draw curve between first and last keyframe (if there are enough to do so). */
1056 for (const int i : index_range.drop_front(1)) {
1057 BezTriple *prevbezt = &fcu->bezt[i - 1];
1058 BezTriple *bezt = &fcu->bezt[i];
1059 expand_key_bounds(prevbezt, bezt, key_bounds);
1060 float pixel_distance = calculate_pixel_distance(key_bounds, pixels_per_unit);
1061
1062 if (pixel_distance >= min_pixel_distance && has_skipped_keys) {
1063 /* When the pixel distance is greater than the threshold, and we've skipped at least one, add
1064 * a point. The point position is the average of all keys from INCLUDING prevbezt to
1065 * EXCLUDING bezt. prevbezt then gets reset to the key before bezt because the distance
1066 * between those is potentially below the threshold. */
1067 curve_vertices.append({BLI_rctf_cent_x(&key_bounds), BLI_rctf_cent_y(&key_bounds)});
1068 has_skipped_keys = false;
1069 key_bounds = {
1070 prevbezt->vec[1][0], prevbezt->vec[1][0], prevbezt->vec[1][1], prevbezt->vec[1][1]};
1071 expand_key_bounds(prevbezt, bezt, key_bounds);
1072 /* Calculate again based on the new prevbezt. */
1073 pixel_distance = calculate_pixel_distance(key_bounds, pixels_per_unit);
1074 }
1075
1076 if (pixel_distance < min_pixel_distance) {
1077 /* Skip any keys that are too close to each other in screen space. */
1078 has_skipped_keys = true;
1079 continue;
1080 }
1081
1082 switch (prevbezt->ipo) {
1083
1084 case BEZT_IPO_CONST:
1085 /* Constant-Interpolation: draw segment between previous keyframe and next,
1086 * but holding same value */
1087 curve_vertices.append({prevbezt->vec[1][0], prevbezt->vec[1][1]});
1088 curve_vertices.append({bezt->vec[1][0], prevbezt->vec[1][1]});
1089 break;
1090
1091 case BEZT_IPO_LIN:
1092 /* Linear interpolation: just add one point (which should add a new line segment) */
1093 curve_vertices.append({prevbezt->vec[1][0], prevbezt->vec[1][1]});
1094 break;
1095
1096 case BEZT_IPO_BEZ: {
1097 const int resolution = calculate_bezt_draw_resolution(bezt, prevbezt, pixels_per_unit);
1098 add_bezt_vertices(bezt, prevbezt, resolution, curve_vertices);
1099 break;
1100 }
1101
1102 default: {
1103 /* In case there is no other way to get curve points, evaluate the FCurve. */
1104 curve_vertices.append(prevbezt->vec[1]);
1105 float current_frame = prevbezt->vec[1][0] + evaluation_step;
1106 while (current_frame < bezt->vec[1][0]) {
1107 curve_vertices.append({current_frame, evaluate_fcurve(fcu, current_frame)});
1108 current_frame += evaluation_step;
1109 }
1110 break;
1111 }
1112 }
1113
1114 prevbezt = bezt;
1115 }
1116
1117 /* Always add the last point so the extrapolation line doesn't jump. */
1118 curve_vertices.append(
1119 {fcu->bezt[index_range.last()].vec[1][0], fcu->bezt[index_range.last()].vec[1][1]});
1120
1121 /* Extrapolate to the right? (see code for left-extrapolation above too) */
1122 if (draw_extrapolation && fcu->bezt[fcu->totvert - 1].vec[1][0] < v2d->cur.xmax) {
1123 add_extrapolation_point_right(fcu, v2d->cur.xmax, curve_vertices);
1124 }
1125
1126 if (curve_vertices.size() < 2) {
1128 return;
1129 }
1130
1131 immBegin(GPU_PRIM_LINE_STRIP, curve_vertices.size());
1132 for (const float2 vertex : curve_vertices) {
1133 immVertex2fv(pos, vertex);
1134 }
1135 immEnd();
1136
1138}
1139
1140static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAnimListElem *ale)
1141{
1142 FCurve *fcu = (FCurve *)ale->key_data;
1144
1145 /* map keyframes for drawing if scaled F-Curve */
1146 ANIM_nla_mapping_apply_if_needed_fcurve(ale, static_cast<FCurve *>(ale->key_data), false, false);
1147
1148 /* draw curve:
1149 * - curve line may be result of one or more destructive modifiers or just the raw data,
1150 * so we need to check which method should be used
1151 * - controls from active modifier take precedence over keyframes
1152 * (XXX! editing tools need to take this into account!)
1153 */
1154
1155 /* 1) draw curve line */
1156 if (((fcu->modifiers.first) || (fcu->flag & FCURVE_INT_VALUES)) ||
1157 (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)))
1158 {
1159 /* set color/drawing style for curve itself */
1160 /* draw active F-Curve thicker than the rest to make it stand out */
1161 if (fcu->flag & FCURVE_ACTIVE && !BKE_fcurve_is_protected(fcu)) {
1162 GPU_line_width(2.5);
1163 }
1164 else {
1165 GPU_line_width(1.0);
1166 }
1167
1168 /* anti-aliased lines for less jagged appearance */
1169 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1170 GPU_line_smooth(true);
1171 }
1173
1174 const uint shdr_pos = GPU_vertformat_attr_add(
1175 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1176
1177 float viewport_size[4];
1178 GPU_viewport_size_get_f(viewport_size);
1179
1180 if (BKE_fcurve_is_protected(fcu)) {
1181 /* Protected curves (non editable) are drawn with dotted lines. */
1184 "viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
1185 immUniform1i("colors_len", 0); /* Simple dashes. */
1186 immUniform1f("dash_width", 16.0f * U.scale_factor);
1187 immUniform1f("udash_factor", 0.35f * U.scale_factor);
1188 }
1189 else {
1191 immUniform2fv("viewportSize", &viewport_size[2]);
1192 immUniform1f("lineWidth", GPU_line_width_get());
1193 }
1194
1195 if (((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) || (fcu->flag & FCURVE_MUTED)) {
1196 /* muted curves are drawn in a grayish hue */
1197 /* XXX should we have some variations? */
1199 }
1200 else {
1201 /* set whatever color the curve has set
1202 * - unselected curves draw less opaque to help distinguish the selected ones
1203 */
1205 }
1206
1207 const bool draw_extrapolation = (sipo->flag & SIPO_NO_DRAW_EXTRAPOLATION) == 0;
1208 /* draw F-Curve */
1209 if ((fcu->modifiers.first) || (fcu->flag & FCURVE_INT_VALUES)) {
1210 /* draw a curve affected by modifiers or only allowed to have integer values
1211 * by sampling it at various small-intervals over the visible region
1212 */
1213 /* We have to do this mapping dance since the keyframes were remapped but the F-modifier
1214 * evaluations are not.
1215 *
1216 * So we undo the keyframe remapping and instead remap the evaluation time when drawing
1217 * the curve itself. Afterward, we go back and redo the keyframe remapping so the controls
1218 * are drawn properly. */
1220 ale, static_cast<FCurve *>(ale->key_data), true, false);
1222 ale->id,
1223 fcu,
1224 &region->v2d,
1225 shdr_pos,
1227 draw_extrapolation);
1229 ale, static_cast<FCurve *>(ale->key_data), false, false);
1230 }
1231 else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
1232 /* just draw curve based on defined data (i.e. no modifiers) */
1233 if (fcu->bezt) {
1234 draw_fcurve_curve_keys(ac, ale->id, fcu, &region->v2d, shdr_pos, draw_extrapolation);
1235 }
1236 else if (fcu->fpt) {
1237 draw_fcurve_curve_samples(ac, ale->id, fcu, &region->v2d, shdr_pos, draw_extrapolation);
1238 }
1239 }
1240
1242
1243 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1244 GPU_line_smooth(false);
1245 }
1247 }
1248
1249 /* 2) draw handles and vertices as appropriate based on active
1250 * - If the option to only show controls if the F-Curve is selected is enabled,
1251 * we must obey this.
1252 */
1253 if (!(U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) ||
1254 (fcu->flag & FCURVE_SELECTED))
1255 {
1256 if (!BKE_fcurve_are_keyframes_usable(fcu) && !(fcu->fpt && fcu->totvert)) {
1257 /* only draw controls if this is the active modifier */
1258 if ((fcu->flag & FCURVE_ACTIVE) && (fcm)) {
1259 switch (fcm->type) {
1260 case FMODIFIER_TYPE_ENVELOPE: /* envelope */
1261 draw_fcurve_modifier_controls_envelope(fcm, &region->v2d, ale);
1262 break;
1263 }
1264 }
1265 }
1266 else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
1267 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
1268 float offset;
1269 const float unit_scale = ANIM_unit_mapping_get_factor(
1270 ac->scene, ale->id, fcu, mapping_flag, &offset);
1271
1272 /* apply unit-scaling to all values via OpenGL */
1274 GPU_matrix_scale_2f(1.0f, unit_scale);
1275 GPU_matrix_translate_2f(0.0f, offset);
1276
1277 /* Set this once and for all -
1278 * all handles and handle-verts should use the same thickness. */
1279 GPU_line_width(1.0);
1280
1281 if (fcu->bezt) {
1282 bool do_handles = draw_fcurve_handles_check(sipo, fcu);
1283
1284 if (do_handles) {
1285 /* only draw handles/vertices on keyframes */
1286 draw_fcurve_handles(sipo, region, fcu);
1287 }
1288
1289 draw_fcurve_vertices(region, fcu, do_handles, (sipo->flag & SIPO_SELVHANDLESONLY));
1290 }
1291 else {
1292 /* samples: only draw two indicators at either end as indicators */
1293 draw_fcurve_samples(region, fcu, unit_scale);
1294 }
1295
1297 }
1298 }
1299
1300 /* 3) draw driver debugging stuff */
1301 if ((ac->datatype == ANIMCONT_DRIVERS) && (fcu->flag & FCURVE_ACTIVE)) {
1302 graph_draw_driver_debug(ac, ale->id, fcu);
1303 }
1304
1305 /* undo mapping of keyframes for drawing if scaled F-Curve */
1306 ANIM_nla_mapping_apply_if_needed_fcurve(ale, static_cast<FCurve *>(ale->key_data), true, false);
1307}
1308
1309/* Debugging -------------------------------- */
1310
1311/* Draw indicators which show the value calculated from the driver,
1312 * and how this is mapped to the value that comes out of it. This
1313 * is handy for helping users better understand how to interpret
1314 * the graphs, and also facilitates debugging.
1315 */
1317{
1318 ChannelDriver *driver = fcu->driver;
1319 View2D *v2d = &ac->region->v2d;
1320 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
1321 float offset;
1322 float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
1323
1324 const uint shdr_pos = GPU_vertformat_attr_add(
1325 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1327
1328 float viewport_size[4];
1329 GPU_viewport_size_get_f(viewport_size);
1330 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
1331
1332 immUniform1i("colors_len", 0); /* Simple dashes. */
1333
1334 /* No curve to modify/visualize the result?
1335 * => We still want to show the 1-1 default...
1336 */
1337 if ((fcu->totvert == 0) && BLI_listbase_is_empty(&fcu->modifiers)) {
1338 float t;
1339
1340 /* draw with thin dotted lines in style of what curve would have been */
1342
1343 immUniform1f("dash_width", 40.0f);
1344 immUniform1f("udash_factor", 0.5f);
1345 GPU_line_width(2.0f);
1346
1347 /* draw 1-1 line, stretching just past the screen limits
1348 * NOTE: we need to scale the y-values to be valid for the units
1349 */
1351
1352 t = v2d->cur.xmin;
1353 immVertex2f(shdr_pos, t, (t + offset) * unitfac);
1354
1355 t = v2d->cur.xmax;
1356 immVertex2f(shdr_pos, t, (t + offset) * unitfac);
1357
1358 immEnd();
1359 }
1360
1361 /* draw driver only if actually functional */
1362 if ((driver->flag & DRIVER_FLAG_INVALID) == 0) {
1363 /* grab "coordinates" for driver outputs */
1364 float x = driver->curval;
1365 float y = fcu->curval * unitfac;
1366
1367 /* Only draw indicators if the point is in range. */
1368 if (x >= v2d->cur.xmin) {
1369 float co[2];
1370
1371 /* draw dotted lines leading towards this point from both axes ....... */
1372 immUniformColor3f(0.9f, 0.9f, 0.9f);
1373 immUniform1f("dash_width", 10.0f);
1374 immUniform1f("udash_factor", 0.5f);
1375 GPU_line_width(1.0f);
1376
1377 immBegin(GPU_PRIM_LINES, (y <= v2d->cur.ymax) ? 4 : 2);
1378
1379 /* x-axis lookup */
1380 co[0] = x;
1381
1382 if (y <= v2d->cur.ymax) {
1383 co[1] = v2d->cur.ymax + 1.0f;
1384 immVertex2fv(shdr_pos, co);
1385
1386 co[1] = y;
1387 immVertex2fv(shdr_pos, co);
1388 }
1389
1390 /* y-axis lookup */
1391 co[1] = y;
1392
1393 co[0] = v2d->cur.xmin - 1.0f;
1394 immVertex2fv(shdr_pos, co);
1395
1396 co[0] = x;
1397 immVertex2fv(shdr_pos, co);
1398
1399 immEnd();
1400
1402
1403 /* GPU_PRIM_POINTS do not survive dashed line geometry shader... */
1405
1406 /* x marks the spot .................................................... */
1407 /* -> outer frame */
1408 immUniformColor3f(0.9f, 0.9f, 0.9f);
1409 GPU_point_size(7.0);
1410
1412 immVertex2f(shdr_pos, x, y);
1413 immEnd();
1414
1415 /* inner frame */
1416 immUniformColor3f(0.9f, 0.0f, 0.0f);
1417 GPU_point_size(3.0);
1418
1420 immVertex2f(shdr_pos, x, y);
1421 immEnd();
1422 }
1423 }
1424
1426}
1427
1428/* Public Curve-Drawing API ---------------- */
1429
1431{
1432 /* draw with thick dotted lines */
1433 GPU_line_width(3.0f);
1434
1435 /* anti-aliased lines for less jagged appearance */
1436 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1437 GPU_line_smooth(true);
1438 }
1440
1441 const uint shdr_pos = GPU_vertformat_attr_add(
1442 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1443
1445
1446 float viewport_size[4];
1447 GPU_viewport_size_get_f(viewport_size);
1448 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
1449
1450 immUniform1i("colors_len", 0); /* Simple dashes. */
1451 immUniform1f("dash_width", 20.0f);
1452 immUniform1f("udash_factor", 0.5f);
1453
1454 /* Don't draw extrapolation on sampled ghost curves because it doesn't
1455 * match the curves they're ghosting anyway.
1456 * See issue #109920 for details. */
1457 const bool draw_extrapolation = false;
1458 /* the ghost curves are simply sampled F-Curves stored in sipo->runtime.ghost_curves */
1459 LISTBASE_FOREACH (FCurve *, fcu, &sipo->runtime.ghost_curves) {
1460 /* set whatever color the curve has set
1461 * - this is set by the function which creates these
1462 * - draw with a fixed opacity of 2
1463 */
1464 immUniformColor3fvAlpha(fcu->color, 0.5f);
1465
1466 /* simply draw the stored samples */
1467 draw_fcurve_curve_samples(ac, nullptr, fcu, &region->v2d, shdr_pos, draw_extrapolation);
1468 }
1469
1471
1472 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1473 GPU_line_smooth(false);
1474 }
1476}
1477
1478void graph_draw_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, short sel)
1479{
1480 ListBase anim_data = {nullptr, nullptr};
1481 int filter;
1482
1483 /* build list of curves to draw */
1485 filter |= ((sel) ? (ANIMFILTER_SEL) : (ANIMFILTER_UNSEL));
1487 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1488
1489 /* for each curve:
1490 * draw curve, then handle-lines, and finally vertices in this order so that
1491 * the data will be layered correctly
1492 */
1493 bAnimListElem *ale_active_fcurve = nullptr;
1494 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1495 const FCurve *fcu = (FCurve *)ale->key_data;
1496 if ((fcu->flag & FCURVE_ACTIVE) && !ale_active_fcurve) {
1497 ale_active_fcurve = ale;
1498 continue;
1499 }
1500 draw_fcurve(ac, sipo, region, ale);
1501 }
1502
1503 /* Draw the active FCurve last so that it (especially the active keyframe)
1504 * shows on top of the other curves. */
1505 if (ale_active_fcurve != nullptr) {
1506 draw_fcurve(ac, sipo, region, ale_active_fcurve);
1507 }
1508
1509 /* free list of curves */
1510 ANIM_animdata_freelist(&anim_data);
1511}
1512
1514
1515/* -------------------------------------------------------------------- */
1518
1520 bAnimContext *ac,
1521 ARegion *region,
1522 const ListBase /*bAnimListElem*/ &anim_data)
1523{
1524 bAnimListElem *ale;
1525
1526 View2D *v2d = &region->v2d;
1527
1528 const float channel_step = ANIM_UI_get_channel_step();
1529
1530 /* Loop through channels, and set up drawing depending on their type. */
1531 { /* first pass: just the standard GL-drawing for backdrop + text */
1532 size_t channel_index = 0;
1533 float ymax = ANIM_UI_get_first_channel_top(v2d);
1534
1535 for (ale = static_cast<bAnimListElem *>(anim_data.first); ale;
1536 ale = ale->next, ymax -= channel_step, channel_index++)
1537 {
1538 const float ymin = ymax - ANIM_UI_get_channel_height();
1539
1540 /* check if visible */
1541 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
1542 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1543 {
1544 /* draw all channels using standard channel-drawing API */
1545 ANIM_channel_draw(ac, ale, ymin, ymax, channel_index);
1546 }
1547 }
1548 }
1549 { /* second pass: widgets */
1550 uiBlock *block = UI_block_begin(C, region, __func__, blender::ui::EmbossType::Emboss);
1551 size_t channel_index = 0;
1552 float ymax = ANIM_UI_get_first_channel_top(v2d);
1553
1554 /* set blending again, as may not be set in previous step */
1556
1557 for (ale = static_cast<bAnimListElem *>(anim_data.first); ale;
1558 ale = ale->next, ymax -= channel_step, channel_index++)
1559 {
1560 const float ymin = ymax - ANIM_UI_get_channel_height();
1561
1562 /* check if visible */
1563 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
1564 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1565 {
1566 /* draw all channels using standard channel-drawing API */
1567 rctf channel_rect;
1568 BLI_rctf_init(&channel_rect, 0, v2d->cur.xmax - V2D_SCROLL_WIDTH, ymin, ymax);
1569 ANIM_channel_draw_widgets(C, ac, ale, block, &channel_rect, channel_index);
1570 }
1571 }
1572
1573 UI_block_end(C, block);
1574 UI_block_draw(C, block);
1575
1577 }
1578}
1579
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1669
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], float frame, int arraylen, bool *r_replace)
int BKE_fcurve_active_keyframe_index(const FCurve *fcu)
bool BKE_fcurve_is_protected(const FCurve *fcu)
bool BKE_fcurve_are_keyframes_usable(const FCurve *fcu)
FModifier * find_active_fmodifier(ListBase *modifiers)
float evaluate_fcurve(const FCurve *fcu, float evaltime)
bool BKE_fcurve_calc_range(const FCurve *fcu, float *r_min, float *r_max, bool selected_keys_only)
void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2])
@ NLATIME_CONVERT_MAP
Definition BKE_nla.hh:552
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
MINLINE float max_ffff(float a, float b, float c, float d)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE float min_ffff(float a, float b, float c, float d)
MINLINE float min_ff(float a, float b)
MINLINE int clamp_i(int value, int min, int max)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.cc:404
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
unsigned int uint
#define CLAMP(a, b, c)
#define IN_RANGE(a, b, c)
@ AGRP_PROTECTED
@ AGRP_MUTED
@ FMODIFIER_TYPE_ENVELOPE
@ DRIVER_FLAG_INVALID
#define FCURVE_ACTIVE_KEYFRAME_NONE
@ FCURVE_MUTED
@ FCURVE_INT_VALUES
@ FCURVE_ACTIVE
@ FCURVE_SELECTED
@ FCURVE_PROTECTED
@ FCURVE_EXTRAPOLATE_CONSTANT
@ BEZT_IPO_CONST
@ BEZT_IPO_BEZ
@ BEZT_IPO_LIN
#define BEZT_ISSEL_ANY(bezt)
@ SIPO_SELVHANDLESONLY
@ SIPO_NO_DRAW_EXTRAPOLATION
@ SIPO_NOHANDLES
#define UI_SCALE_FAC
@ USER_ANIM_HIGH_QUALITY_DRAWING
@ USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS
eAnimCont_Types
@ ANIMCONT_DRIVERS
eAnimFilter_Flags
@ ANIMFILTER_UNSEL
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_FCURVESONLY
@ ANIMFILTER_SEL
void immUniform4f(const char *name, float x, float y, float z, float w)
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immAttr4fv(uint attr_id, const float data[4])
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniform2f(const char *name, float x, float y)
void immVertex2f(uint attr_id, float x, float y)
void immUniformThemeColor(int color_id)
void immUniformThemeColorShade(int color_id, int offset)
void immVertex2fv(uint attr_id, const float data[2])
void immUniform1i(const char *name, int x)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immUniformColor3f(float r, float g, float b)
void immUniform4fv(const char *name, const float data[4])
void immBegin(GPUPrimType, uint vertex_len)
void immUniformColor3fvAlpha(const float rgb[3], float a)
void immUniformColor3fv(const float rgb[3])
void GPU_matrix_translate_2fv(const float vec[2])
void GPU_matrix_scale_2f(float x, float y)
void GPU_matrix_push()
void GPU_matrix_pop()
void GPU_matrix_translate_2f(float x, float y)
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_FLAT_COLOR
@ GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA
@ GPU_SHADER_3D_POINT_UNIFORM_COLOR
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:180
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
float GPU_line_width_get()
Definition gpu_state.cc:256
void GPU_point_size(float size)
Definition gpu_state.cc:172
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
#define C
Definition RandGen.cpp:29
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_block_draw(const bContext *C, uiBlock *block)
void UI_block_end(const bContext *C, uiBlock *block)
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_HEADER
@ TH_VERTEX_ACTIVE
@ TH_HANDLE_VERTEX_SIZE
@ TH_VERTEX
@ TH_VERTEX_SIZE
@ TH_HANDLE_SEL_FREE
@ TH_HANDLE_VERTEX_SELECT
@ TH_HANDLE_VERTEX
@ TH_VERTEX_SELECT
@ TH_HANDLE_FREE
@ TH_TEXT
@ TH_TEXT_HI
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
float UI_GetThemeValuef(int colorid)
#define V2D_SCROLL_WIDTH
Definition UI_view2d.hh:54
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1912
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1921
void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListElem *ale, uiBlock *block, const rctf *rect, size_t channel_index)
void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc, size_t channel_index)
float ANIM_UI_get_channel_step()
float ANIM_UI_get_first_channel_top(View2D *v2d)
float ANIM_UI_get_channel_height()
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
short ANIM_get_normalization_flags(SpaceLink *space_link)
Definition anim_draw.cc:415
void ANIM_nla_mapping_apply_if_needed_fcurve(bAnimListElem *ale, FCurve *fcu, const bool restore, const bool only_keys)
Definition anim_draw.cc:401
bool ANIM_nla_mapping_allowed(const bAnimListElem *ale)
Definition anim_draw.cc:274
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
Definition anim_draw.cc:624
float ANIM_nla_tweakedit_remap(bAnimListElem *ale, const float cframe, const eNlaTime_ConvertModes mode)
Definition anim_draw.cc:324
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr IndexRange drop_front(int64_t n) const
int64_t size() const
void append(const T &value)
void append(const T &value)
#define SELECT
#define roundf(x)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint pos
uint col
struct @021025263243242147216143265077100330027142264337::@240232116316110053135047106323056371161236243121 attr_id
#define filter
static float fcurve_display_alpha(const FCurve *fcu)
Definition graph_draw.cc:52
static void draw_fcurve_keyframe_vertices(FCurve *fcu, View2D *v2d, const uint pos)
static void draw_fcurve_samples(ARegion *region, const FCurve *fcu, const float unit_scale)
static void draw_fcurve_handles(SpaceGraph *sipo, ARegion *region, const FCurve *fcu)
static int calculate_bezt_draw_resolution(BezTriple *bezt, BezTriple *prevbezt, const blender::float2 pixels_per_unit)
static float calculate_pixel_distance(const rctf &bounds, const blender::float2 pixels_per_unit)
static void draw_cross(float position[2], const float scale[2], uint attr_id)
static void add_extrapolation_point_left(const FCurve *fcu, const float v2d_xmin, blender::Vector< blender::float2 > &curve_vertices)
static void draw_fcurve_selected_keyframe_vertices(FCurve *fcu, bool sel, uint pos, const blender::IndexRange index_range)
static void add_bezt_vertices(BezTriple *bezt, BezTriple *prevbezt, int resolution, blender::Vector< blender::float2 > &curve_vertices)
static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
static void draw_fcurve_active_handle_vertices(const FCurve *fcu, const bool sel_handle_only, const uint pos)
static void draw_fcurve_selected_handle_vertices(FCurve *fcu, View2D *v2d, bool sel, bool sel_handle_only, uint pos)
static void set_fcurve_vertex_color(FCurve *fcu, bool sel)
void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region)
static void expand_key_bounds(const BezTriple *left_key, const BezTriple *right_key, rctf &bounds)
static void draw_fcurve_handle_vertices(FCurve *fcu, View2D *v2d, bool sel_handle_only, uint pos)
static void draw_fcurve_vertices(ARegion *region, FCurve *fcu, bool do_handles, bool sel_handle_only)
static void draw_fcurve_curve_keys(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, uint pos, const bool draw_extrapolation)
static void draw_fcurve_curve(bAnimContext *ac, ID *id, const FCurve *fcu_, View2D *v2d, uint pos, const bool use_nla_remap, const bool draw_extrapolation)
static void draw_fcurve_curve_samples(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, const uint shdr_pos, const bool draw_extrapolation)
static void draw_fcurve_modifier_controls_envelope(FModifier *fcm, View2D *v2d, bAnimListElem *ale_nla_remap)
Definition graph_draw.cc:88
static void draw_fcurve_active_vertex(const FCurve *fcu, const View2D *v2d, const uint pos)
void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region, const ListBase &anim_data)
static bool draw_fcurve_handles_check(const SpaceGraph *sipo, const FCurve *fcu)
static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAnimListElem *ale)
static blender::float2 calculate_pixels_per_unit(View2D *v2d, const float unit_scale)
void graph_draw_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, short sel)
static blender::IndexRange get_bounding_bezt_index_range(const FCurve *fcu, const float min, const float max)
Definition graph_draw.cc:58
static void add_extrapolation_point_right(const FCurve *fcu, const float v2d_xmax, blender::Vector< blender::float2 > &curve_vertices)
int count
format
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
VecBase< float, 2 > float2
#define min(a, b)
Definition sort.cc:36
float vec[3][3]
bActionGroup * grp
float curval
FPoint * fpt
ChannelDriver * driver
BezTriple * bezt
float color[3]
short extend
unsigned int totvert
ListBase modifiers
FCM_EnvelopeData * data
float vec[2]
Definition DNA_ID.h:414
void * first
SpaceGraph_Runtime runtime
SpaceLink * sl
eAnimCont_Types datatype
ARegion * region
bAnimListElem * next
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251