Blender V4.5
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(
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
405
406 /* draw the two handles first (if they're shown, the curve doesn't
407 * have just a single keyframe, and the curve is being edited) */
408 if (do_handles) {
409 draw_fcurve_handle_vertices(fcu, v2d, sel_handle_only, pos);
410 }
411
412 /* draw keyframes over the handles */
414
417}
418
419/* Handles ---------------- */
420
421static bool draw_fcurve_handles_check(const SpaceGraph *sipo, const FCurve *fcu)
422{
423 /* don't draw handle lines if handles are not to be shown */
424 if (/* handles shouldn't be shown anywhere */
425 (sipo->flag & SIPO_NOHANDLES) ||
426 /* keyframes aren't editable */
427 (fcu->flag & FCURVE_PROTECTED) ||
428#if 0
429 /* handles can still be selected and handle types set, better draw - campbell */
430 /* editing the handles here will cause weird/incorrect interpolation issues */
431 (fcu->flag & FCURVE_INT_VALUES) ||
432#endif
433 /* group that curve belongs to is not editable */
434 ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)))
435 {
436 return false;
437 }
438 return true;
439}
440
441/* draw lines for F-Curve handles only (this is only done in EditMode)
442 * NOTE: draw_fcurve_handles_check must be checked before running this. */
443static void draw_fcurve_handles(SpaceGraph *sipo, ARegion *region, const FCurve *fcu)
444{
445 using namespace blender;
446
451 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
452 GPU_line_smooth(true);
453 }
455
457
458 const IndexRange index_range = get_bounding_bezt_index_range(
459 fcu, region->v2d.cur.xmin, region->v2d.cur.xmax);
460
461 /* slightly hacky, but we want to draw unselected points before selected ones
462 * so that selected points are clearly visible
463 */
464 for (int sel = 0; sel < 2; sel++) {
465 int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE;
466 float col[4];
467
468 BezTriple *prevbezt = nullptr;
469 for (const int i : index_range) {
470 BezTriple *bezt = &fcu->bezt[i];
471 /* if only selected keyframes can get their handles shown,
472 * check that keyframe is selected
473 */
474 if (sipo->flag & SIPO_SELVHANDLESONLY) {
475 if (BEZT_ISSEL_ANY(bezt) == 0) {
476 prevbezt = bezt;
477 continue;
478 }
479 }
480
481 /* draw handle with appropriate set of colors if selection is ok */
482 if ((bezt->f2 & SELECT) == sel) {
483 /* only draw first handle if previous segment had handles */
484 if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
485 (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))
486 {
487 UI_GetThemeColor3fv(basecol + bezt->h1, col);
488 col[3] = fcurve_display_alpha(fcu);
490 immVertex2fv(pos, bezt->vec[0]);
492 immVertex2fv(pos, bezt->vec[1]);
493 }
494
495 /* only draw second handle if this segment is bezier */
496 if (bezt->ipo == BEZT_IPO_BEZ) {
497 UI_GetThemeColor3fv(basecol + bezt->h2, col);
498 col[3] = fcurve_display_alpha(fcu);
500 immVertex2fv(pos, bezt->vec[1]);
502 immVertex2fv(pos, bezt->vec[2]);
503 }
504 }
505 else {
506 /* only draw first handle if previous segment was had handles, and selection is ok */
507 if (((bezt->f1 & SELECT) == sel) && ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
508 (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))))
509 {
510 UI_GetThemeColor3fv(basecol + bezt->h1, col);
511 col[3] = fcurve_display_alpha(fcu);
513 immVertex2fv(pos, bezt->vec[0]);
515 immVertex2fv(pos, bezt->vec[1]);
516 }
517
518 /* only draw second handle if this segment is bezier, and selection is ok */
519 if (((bezt->f3 & SELECT) == sel) && (bezt->ipo == BEZT_IPO_BEZ)) {
520 UI_GetThemeColor3fv(basecol + bezt->h2, col);
521 col[3] = fcurve_display_alpha(fcu);
523 immVertex2fv(pos, bezt->vec[1]);
525 immVertex2fv(pos, bezt->vec[2]);
526 }
527 }
528 prevbezt = bezt;
529 }
530 }
531
532 immEnd();
535 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
536 GPU_line_smooth(false);
537 }
538}
539
540/* Samples ---------------- */
541
542/* helper func - draw keyframe vertices only for an F-Curve */
543static void draw_fcurve_samples(ARegion *region, const FCurve *fcu, const float unit_scale)
544{
545 FPoint *first, *last;
546 float scale[2];
547
548 /* get view settings */
549 const float hsize = UI_GetThemeValuef(TH_VERTEX_SIZE);
550 UI_view2d_scale_get(&region->v2d, &scale[0], &scale[1]);
551
552 scale[0] /= hsize;
553 scale[1] /= hsize / unit_scale;
554
555 /* get verts */
556 first = fcu->fpt;
557 last = (first) ? (first + (fcu->totvert - 1)) : (nullptr);
558
559 /* draw */
560 if (first && last) {
561 /* anti-aliased lines for more consistent appearance */
562 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
563 GPU_line_smooth(true);
564 }
566
569
571
572 draw_cross(first->vec, scale, pos);
573 draw_cross(last->vec, scale, pos);
574
576
578 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
579 GPU_line_smooth(false);
580 }
581 }
582}
583
584/* Curve ---------------- */
585
586/* Helper func - just draw the F-Curve by sampling the visible region
587 * (for drawing curves with modifiers). */
589 ID *id,
590 const FCurve *fcu_,
591 View2D *v2d,
592 uint pos,
593 const bool use_nla_remap,
594 const bool draw_extrapolation)
595{
596 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
597
598 /* when opening a blend file on a different sized screen or while dragging the toolbar this can
599 * happen best just bail out in this case. */
600 if (UI_view2d_scale_get_x(v2d) <= 0.0f) {
601 return;
602 }
603
604 /* disable any drivers */
605 FCurve fcurve_for_draw = *fcu_;
606 fcurve_for_draw.driver = nullptr;
607
608 /* compute unit correction factor */
609 float offset;
610 float unitFac = ANIM_unit_mapping_get_factor(
611 ac->scene, id, &fcurve_for_draw, mapping_flag, &offset);
612
613 /* Note about sampling frequency:
614 * Ideally, this is chosen such that we have 1-2 pixels = 1 segment
615 * which means that our curves can be as smooth as possible. However,
616 * this does mean that curves may not be fully accurate (i.e. if they have
617 * sudden spikes which happen at the sampling point, we may have problems).
618 * Also, this may introduce lower performance on less densely detailed curves,
619 * though it is impossible to predict this from the modifiers!
620 *
621 * If the automatically determined sampling frequency is likely to cause an infinite
622 * loop (i.e. too close to 0), then clamp it to a determined "safe" value. The value
623 * chosen here is just the coarsest value which still looks reasonable.
624 */
625
626 /* TODO: perhaps we should have 1.0 frames
627 * as upper limit so that curves don't get too distorted? */
628 float pixels_per_sample = 1.5f;
629 float samplefreq = pixels_per_sample / UI_view2d_scale_get_x(v2d);
630
631 if (!(U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING)) {
632 /* Low Precision = coarse lower-bound clamping
633 *
634 * Although the "Beauty Draw" flag was originally for AA'd
635 * line drawing, the sampling rate here has a much greater
636 * impact on performance (e.g. for #40372)!
637 *
638 * This one still amounts to 10 sample-frames for each 1-frame interval
639 * which should be quite a decent approximation in many situations.
640 */
641 samplefreq = std::max(samplefreq, 0.1f);
642 }
643 else {
644 /* "Higher Precision" but slower - especially on larger windows (e.g. #40372) */
645 samplefreq = std::max(samplefreq, 0.00001f);
646 }
647
648 /* the start/end times are simply the horizontal extents of the 'cur' rect */
649 float stime = v2d->cur.xmin;
650 float etime = v2d->cur.xmax;
651
652 AnimData *adt = use_nla_remap ? BKE_animdata_from_id(id) : nullptr;
653
654 /* If not drawing extrapolation, then change fcurve drawing bounds to its keyframe bounds clamped
655 * by graph editor bounds. */
656 if (!draw_extrapolation) {
657 float fcu_start = 0;
658 float fcu_end = 0;
659 BKE_fcurve_calc_range(fcu_, &fcu_start, &fcu_end, false);
660
661 fcu_start = BKE_nla_tweakedit_remap(adt, fcu_start, NLATIME_CONVERT_MAP);
662 fcu_end = BKE_nla_tweakedit_remap(adt, fcu_end, NLATIME_CONVERT_MAP);
663
664 /* Account for reversed NLA strip effect. */
665 if (fcu_end < fcu_start) {
666 std::swap(fcu_start, fcu_end);
667 }
668
669 /* Clamp to graph editor rendering bounds. */
670 stime = max_ff(stime, fcu_start);
671 etime = min_ff(etime, fcu_end);
672 }
673
674 const int total_samples = roundf((etime - stime) / samplefreq);
675 if (total_samples <= 0) {
676 return;
677 }
678
679 /* NLA remapping is linear so we don't have to remap per iteration. */
680 const float eval_start = BKE_nla_tweakedit_remap(adt, stime, NLATIME_CONVERT_UNMAP);
681 const float eval_freq = BKE_nla_tweakedit_remap(adt, stime + samplefreq, NLATIME_CONVERT_UNMAP) -
682 eval_start;
683 const float eval_end = BKE_nla_tweakedit_remap(adt, etime, NLATIME_CONVERT_UNMAP);
684
685 immBegin(GPU_PRIM_LINE_STRIP, (total_samples + 1));
686
687 /* At each sampling interval, add a new vertex.
688 *
689 * Apply the unit correction factor to the calculated values so that the displayed values appear
690 * correctly in the viewport.
691 */
692 for (int i = 0; i < total_samples; i++) {
693 const float ctime = stime + i * samplefreq;
694 float eval_time = eval_start + i * eval_freq;
695
696 /* Prevent drawing past bounds, due to floating point problems.
697 * User-wise, prevent visual flickering.
698 *
699 * This is to cover the case where:
700 * eval_start + total_samples * eval_freq > eval_end
701 * due to floating point problems.
702 */
703 eval_time = std::min(eval_time, eval_end);
704
705 immVertex2f(pos, ctime, (evaluate_fcurve(&fcurve_for_draw, eval_time) + offset) * unitFac);
706 }
707
708 /* Ensure we include end boundary point.
709 * User-wise, prevent visual flickering.
710 *
711 * This is to cover the case where:
712 * eval_start + total_samples * eval_freq < eval_end
713 * due to floating point problems.
714 */
715 immVertex2f(pos, etime, (evaluate_fcurve(&fcurve_for_draw, eval_end) + offset) * unitFac);
716
717 immEnd();
718}
719
720/* helper func - draw a samples-based F-Curve */
722 ID *id,
723 FCurve *fcu,
724 View2D *v2d,
725 const uint shdr_pos,
726 const bool draw_extrapolation)
727{
728 if (!draw_extrapolation && fcu->totvert == 1) {
729 return;
730 }
731
732 FPoint *prevfpt = fcu->fpt;
733 FPoint *fpt = prevfpt + 1;
734 float fac, v[2];
735 int b = fcu->totvert;
736 float unit_scale, offset;
737 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
738 int count = fcu->totvert;
739
740 const bool extrap_left = draw_extrapolation && prevfpt->vec[0] > v2d->cur.xmin;
741 if (extrap_left) {
742 count++;
743 }
744
745 const bool extrap_right = draw_extrapolation && (prevfpt + b - 1)->vec[0] < v2d->cur.xmax;
746 if (extrap_right) {
747 count++;
748 }
749
750 /* apply unit mapping */
752 unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
753 GPU_matrix_scale_2f(1.0f, unit_scale);
754 GPU_matrix_translate_2f(0.0f, offset);
755
757
758 /* extrapolate to left? - left-side of view comes before first keyframe? */
759 if (extrap_left) {
760 v[0] = v2d->cur.xmin;
761
762 /* y-value depends on the interpolation */
763 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
764 (fcu->totvert == 1))
765 {
766 /* just extend across the first keyframe's value */
767 v[1] = prevfpt->vec[1];
768 }
769 else {
770 /* extrapolate linear doesn't use the handle, use the next points center instead */
771 fac = (prevfpt->vec[0] - fpt->vec[0]) / (prevfpt->vec[0] - v[0]);
772 if (fac) {
773 fac = 1.0f / fac;
774 }
775 v[1] = prevfpt->vec[1] - fac * (prevfpt->vec[1] - fpt->vec[1]);
776 }
777
778 immVertex2fv(shdr_pos, v);
779 }
780
781 /* loop over samples, drawing segments */
782 /* draw curve between first and last keyframe (if there are enough to do so) */
783 while (b--) {
784 /* Linear interpolation: just add one point (which should add a new line segment) */
785 immVertex2fv(shdr_pos, prevfpt->vec);
786
787 /* get next pointers */
788 if (b > 0) {
789 prevfpt++;
790 }
791 }
792
793 /* extrapolate to right? (see code for left-extrapolation above too) */
794 if (extrap_right) {
795 v[0] = v2d->cur.xmax;
796
797 /* y-value depends on the interpolation */
798 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
799 (fcu->totvert == 1))
800 {
801 /* based on last keyframe's value */
802 v[1] = prevfpt->vec[1];
803 }
804 else {
805 /* extrapolate linear doesn't use the handle, use the previous points center instead */
806 fpt = prevfpt - 1;
807 fac = (prevfpt->vec[0] - fpt->vec[0]) / (prevfpt->vec[0] - v[0]);
808 if (fac) {
809 fac = 1.0f / fac;
810 }
811 v[1] = prevfpt->vec[1] - fac * (prevfpt->vec[1] - fpt->vec[1]);
812 }
813
814 immVertex2fv(shdr_pos, v);
815 }
816
817 immEnd();
818
820}
821
823 BezTriple *prevbezt,
824 const blender::float2 pixels_per_unit)
825{
826 const float points_per_pixel = 0.25f;
827 const int resolution_x = int(((bezt->vec[1][0] - prevbezt->vec[1][0]) * pixels_per_unit[0]) *
828 points_per_pixel);
829 /* Include the handles in the resolution calculation to cover the case where keys have the same
830 * y-value, but their handles are offset to create an arc. */
831 const float min_y = min_ffff(
832 bezt->vec[1][1], bezt->vec[2][1], prevbezt->vec[1][1], prevbezt->vec[0][1]);
833 const float max_y = max_ffff(
834 bezt->vec[1][1], bezt->vec[2][1], prevbezt->vec[1][1], prevbezt->vec[0][1]);
835 const int resolution_y = int(((max_y - min_y) * pixels_per_unit[1]) * points_per_pixel);
836
837 /* Using a simple sum instead of calculating the diagonal. This gives a slightly higher
838 * resolution but it does compensate for the fact that bezier curves can create long arcs between
839 * keys. */
840 return resolution_x + resolution_y;
841}
842
847static void add_bezt_vertices(BezTriple *bezt,
848 BezTriple *prevbezt,
849 int resolution,
850 blender::Vector<blender::float2> &curve_vertices)
851{
852 if (resolution < 2) {
853 curve_vertices.append({prevbezt->vec[1][0], prevbezt->vec[1][1]});
854 return;
855 }
856
857 /* If the resolution goes too high the line will not end exactly at the keyframe. Probably due to
858 * accumulating floating point issues in BKE_curve_forward_diff_bezier. */
859 resolution = min_ii(64, resolution);
860
861 float prev_key[2], prev_handle[2], bez_handle[2], bez_key[2];
862 /* Allocation needs +1 on resolution because BKE_curve_forward_diff_bezier uses it to iterate
863 * inclusively. */
864 float *bezier_diff_points = MEM_malloc_arrayN<float>(((resolution + 1) * 2), "Draw bezt data");
865
866 prev_key[0] = prevbezt->vec[1][0];
867 prev_key[1] = prevbezt->vec[1][1];
868 prev_handle[0] = prevbezt->vec[2][0];
869 prev_handle[1] = prevbezt->vec[2][1];
870
871 bez_handle[0] = bezt->vec[0][0];
872 bez_handle[1] = bezt->vec[0][1];
873 bez_key[0] = bezt->vec[1][0];
874 bez_key[1] = bezt->vec[1][1];
875
876 BKE_fcurve_correct_bezpart(prev_key, prev_handle, bez_handle, bez_key);
877
879 prev_handle[0],
880 bez_handle[0],
881 bez_key[0],
882 bezier_diff_points,
883 resolution,
884 sizeof(float[2]));
886 prev_handle[1],
887 bez_handle[1],
888 bez_key[1],
889 bezier_diff_points + 1,
890 resolution,
891 sizeof(float[2]));
892
893 for (float *fp = bezier_diff_points; resolution; resolution--, fp += 2) {
894 const float x = *fp;
895 const float y = *(fp + 1);
896 curve_vertices.append({x, y});
897 }
898 MEM_freeN(bezier_diff_points);
899}
900
902 const float v2d_xmin,
903 blender::Vector<blender::float2> &curve_vertices)
904{
905 /* left-side of view comes before first keyframe, so need to extend as not cyclic */
906 float vertex_position[2];
907 vertex_position[0] = v2d_xmin;
908 BezTriple *bezt = &fcu->bezt[0];
909
910 /* y-value depends on the interpolation */
911 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (bezt->ipo == BEZT_IPO_CONST) ||
912 (bezt->ipo == BEZT_IPO_LIN && fcu->totvert == 1))
913 {
914 /* just extend across the first keyframe's value */
915 vertex_position[1] = bezt->vec[1][1];
916 }
917 else if (bezt->ipo == BEZT_IPO_LIN) {
918 BezTriple *next_bezt = bezt + 1;
919 /* extrapolate linear doesn't use the handle, use the next points center instead */
920 float fac = (bezt->vec[1][0] - next_bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
921 if (fac) {
922 fac = 1.0f / fac;
923 }
924 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[1][1] - next_bezt->vec[1][1]);
925 }
926 else {
927 /* based on angle of handle 1 (relative to keyframe) */
928 float fac = (bezt->vec[0][0] - bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
929 if (fac) {
930 fac = 1.0f / fac;
931 }
932 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[0][1] - bezt->vec[1][1]);
933 }
934
935 curve_vertices.append(vertex_position);
936}
937
939 const float v2d_xmax,
940 blender::Vector<blender::float2> &curve_vertices)
941{
942 float vertex_position[2];
943 vertex_position[0] = v2d_xmax;
944 BezTriple *bezt = &fcu->bezt[fcu->totvert - 1];
945
946 /* y-value depends on the interpolation. */
947 if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
948 (bezt->ipo == BEZT_IPO_CONST) || (bezt->ipo == BEZT_IPO_LIN && fcu->totvert == 1))
949 {
950 /* based on last keyframe's value */
951 vertex_position[1] = bezt->vec[1][1];
952 }
953 else if (bezt->ipo == BEZT_IPO_LIN) {
954 /* Extrapolate linear doesn't use the handle, use the previous points center instead. */
955 BezTriple *prev_bezt = bezt - 1;
956 float fac = (bezt->vec[1][0] - prev_bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
957 if (fac) {
958 fac = 1.0f / fac;
959 }
960 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[1][1] - prev_bezt->vec[1][1]);
961 }
962 else {
963 /* Based on angle of handle 1 (relative to keyframe). */
964 float fac = (bezt->vec[2][0] - bezt->vec[1][0]) / (bezt->vec[1][0] - vertex_position[0]);
965 if (fac) {
966 fac = 1.0f / fac;
967 }
968 vertex_position[1] = bezt->vec[1][1] - fac * (bezt->vec[2][1] - bezt->vec[1][1]);
969 }
970
971 curve_vertices.append(vertex_position);
972}
973
974static blender::float2 calculate_pixels_per_unit(View2D *v2d, const float unit_scale)
975{
976 const int window_width = BLI_rcti_size_x(&v2d->mask);
977 const int window_height = BLI_rcti_size_y(&v2d->mask);
978
979 const float v2d_frame_range = BLI_rctf_size_x(&v2d->cur);
980 const float v2d_value_range = BLI_rctf_size_y(&v2d->cur);
981 const blender::float2 pixels_per_unit = {window_width / v2d_frame_range,
982 (window_height / v2d_value_range) * unit_scale};
983 return pixels_per_unit;
984}
985
986static float calculate_pixel_distance(const rctf &bounds, const blender::float2 pixels_per_unit)
987{
988 return BLI_rctf_size_x(&bounds) * pixels_per_unit[0] +
989 BLI_rctf_size_y(&bounds) * pixels_per_unit[1];
990}
991
992static void expand_key_bounds(const BezTriple *left_key, const BezTriple *right_key, rctf &bounds)
993{
994 bounds.xmax = right_key->vec[1][0];
995 if (left_key->ipo == BEZT_IPO_BEZ) {
996 /* Respect handles of bezier keys. */
997 bounds.ymin = min_ffff(
998 bounds.ymin, right_key->vec[1][1], right_key->vec[0][1], left_key->vec[2][1]);
999 bounds.ymax = max_ffff(
1000 bounds.ymax, right_key->vec[1][1], right_key->vec[0][1], left_key->vec[2][1]);
1001 }
1002 else {
1003 bounds.ymax = max_ff(bounds.ymax, right_key->vec[1][1]);
1004 bounds.ymin = min_ff(bounds.ymin, right_key->vec[1][1]);
1005 }
1006}
1007
1008/* Helper function - draw one repeat of an F-Curve (using Bezier curve approximations). */
1010 bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, uint pos, const bool draw_extrapolation)
1011{
1012 using namespace blender;
1013 if (!draw_extrapolation && fcu->totvert == 1) {
1014 return;
1015 }
1016
1017 /* Apply unit mapping. */
1019 float offset;
1020 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
1021 const float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
1022 GPU_matrix_scale_2f(1.0f, unit_scale);
1023 GPU_matrix_translate_2f(0.0f, offset);
1024
1025 Vector<float2> curve_vertices;
1026
1027 /* Extrapolate to the left? */
1028 if (draw_extrapolation && fcu->bezt[0].vec[1][0] > v2d->cur.xmin) {
1029 add_extrapolation_point_left(fcu, v2d->cur.xmin, curve_vertices);
1030 }
1031
1032 const IndexRange index_range = get_bounding_bezt_index_range(fcu, v2d->cur.xmin, v2d->cur.xmax);
1033
1034 /* Always add the first point so the extrapolation line doesn't jump. */
1035 curve_vertices.append(
1036 {fcu->bezt[index_range.first()].vec[1][0], fcu->bezt[index_range.first()].vec[1][1]});
1037
1038 const float2 pixels_per_unit = calculate_pixels_per_unit(v2d, unit_scale);
1039 const int window_width = BLI_rcti_size_x(&v2d->mask);
1040 const float v2d_frame_range = BLI_rctf_size_x(&v2d->cur);
1041 const float pixel_width = v2d_frame_range / window_width;
1042 const float samples_per_pixel = 0.66f;
1043 const float evaluation_step = pixel_width / samples_per_pixel;
1044
1045 BezTriple *first_key = &fcu->bezt[index_range.first()];
1046 rctf key_bounds = {
1047 first_key->vec[1][0], first_key->vec[1][0], first_key->vec[1][1], first_key->vec[1][1]};
1048 /* Used when skipping keys. */
1049 bool has_skipped_keys = false;
1050 const float min_pixel_distance = 3.0f;
1051
1052 /* Draw curve between first and last keyframe (if there are enough to do so). */
1053 for (const int i : index_range.drop_front(1)) {
1054 BezTriple *prevbezt = &fcu->bezt[i - 1];
1055 BezTriple *bezt = &fcu->bezt[i];
1056 expand_key_bounds(prevbezt, bezt, key_bounds);
1057 float pixel_distance = calculate_pixel_distance(key_bounds, pixels_per_unit);
1058
1059 if (pixel_distance >= min_pixel_distance && has_skipped_keys) {
1060 /* When the pixel distance is greater than the threshold, and we've skipped at least one, add
1061 * a point. The point position is the average of all keys from INCLUDING prevbezt to
1062 * EXCLUDING bezt. prevbezt then gets reset to the key before bezt because the distance
1063 * between those is potentially below the threshold. */
1064 curve_vertices.append({BLI_rctf_cent_x(&key_bounds), BLI_rctf_cent_y(&key_bounds)});
1065 has_skipped_keys = false;
1066 key_bounds = {
1067 prevbezt->vec[1][0], prevbezt->vec[1][0], prevbezt->vec[1][1], prevbezt->vec[1][1]};
1068 expand_key_bounds(prevbezt, bezt, key_bounds);
1069 /* Calculate again based on the new prevbezt. */
1070 pixel_distance = calculate_pixel_distance(key_bounds, pixels_per_unit);
1071 }
1072
1073 if (pixel_distance < min_pixel_distance) {
1074 /* Skip any keys that are too close to each other in screen space. */
1075 has_skipped_keys = true;
1076 continue;
1077 }
1078
1079 switch (prevbezt->ipo) {
1080
1081 case BEZT_IPO_CONST:
1082 /* Constant-Interpolation: draw segment between previous keyframe and next,
1083 * but holding same value */
1084 curve_vertices.append({prevbezt->vec[1][0], prevbezt->vec[1][1]});
1085 curve_vertices.append({bezt->vec[1][0], prevbezt->vec[1][1]});
1086 break;
1087
1088 case BEZT_IPO_LIN:
1089 /* Linear interpolation: just add one point (which should add a new line segment) */
1090 curve_vertices.append({prevbezt->vec[1][0], prevbezt->vec[1][1]});
1091 break;
1092
1093 case BEZT_IPO_BEZ: {
1094 const int resolution = calculate_bezt_draw_resolution(bezt, prevbezt, pixels_per_unit);
1095 add_bezt_vertices(bezt, prevbezt, resolution, curve_vertices);
1096 break;
1097 }
1098
1099 default: {
1100 /* In case there is no other way to get curve points, evaluate the FCurve. */
1101 curve_vertices.append(prevbezt->vec[1]);
1102 float current_frame = prevbezt->vec[1][0] + evaluation_step;
1103 while (current_frame < bezt->vec[1][0]) {
1104 curve_vertices.append({current_frame, evaluate_fcurve(fcu, current_frame)});
1105 current_frame += evaluation_step;
1106 }
1107 break;
1108 }
1109 }
1110
1111 prevbezt = bezt;
1112 }
1113
1114 /* Always add the last point so the extrapolation line doesn't jump. */
1115 curve_vertices.append(
1116 {fcu->bezt[index_range.last()].vec[1][0], fcu->bezt[index_range.last()].vec[1][1]});
1117
1118 /* Extrapolate to the right? (see code for left-extrapolation above too) */
1119 if (draw_extrapolation && fcu->bezt[fcu->totvert - 1].vec[1][0] < v2d->cur.xmax) {
1120 add_extrapolation_point_right(fcu, v2d->cur.xmax, curve_vertices);
1121 }
1122
1123 if (curve_vertices.size() < 2) {
1125 return;
1126 }
1127
1128 immBegin(GPU_PRIM_LINE_STRIP, curve_vertices.size());
1129 for (const float2 vertex : curve_vertices) {
1130 immVertex2fv(pos, vertex);
1131 }
1132 immEnd();
1133
1135}
1136
1137static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAnimListElem *ale)
1138{
1139 FCurve *fcu = (FCurve *)ale->key_data;
1141
1142 /* map keyframes for drawing if scaled F-Curve */
1143 ANIM_nla_mapping_apply_if_needed_fcurve(ale, static_cast<FCurve *>(ale->key_data), false, false);
1144
1145 /* draw curve:
1146 * - curve line may be result of one or more destructive modifiers or just the raw data,
1147 * so we need to check which method should be used
1148 * - controls from active modifier take precedence over keyframes
1149 * (XXX! editing tools need to take this into account!)
1150 */
1151
1152 /* 1) draw curve line */
1153 if (((fcu->modifiers.first) || (fcu->flag & FCURVE_INT_VALUES)) ||
1154 (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)))
1155 {
1156 /* set color/drawing style for curve itself */
1157 /* draw active F-Curve thicker than the rest to make it stand out */
1158 if (fcu->flag & FCURVE_ACTIVE && !BKE_fcurve_is_protected(fcu)) {
1159 GPU_line_width(2.5);
1160 }
1161 else {
1162 GPU_line_width(1.0);
1163 }
1164
1165 /* anti-aliased lines for less jagged appearance */
1166 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1167 GPU_line_smooth(true);
1168 }
1170
1171 const uint shdr_pos = GPU_vertformat_attr_add(
1173
1174 float viewport_size[4];
1175 GPU_viewport_size_get_f(viewport_size);
1176
1177 if (BKE_fcurve_is_protected(fcu)) {
1178 /* Protected curves (non editable) are drawn with dotted lines. */
1181 "viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
1182 immUniform1i("colors_len", 0); /* Simple dashes. */
1183 immUniform1f("dash_width", 16.0f * U.scale_factor);
1184 immUniform1f("udash_factor", 0.35f * U.scale_factor);
1185 }
1186 else {
1188 immUniform2fv("viewportSize", &viewport_size[2]);
1189 immUniform1f("lineWidth", GPU_line_width_get());
1190 }
1191
1192 if (((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) || (fcu->flag & FCURVE_MUTED)) {
1193 /* muted curves are drawn in a grayish hue */
1194 /* XXX should we have some variations? */
1196 }
1197 else {
1198 /* set whatever color the curve has set
1199 * - unselected curves draw less opaque to help distinguish the selected ones
1200 */
1202 }
1203
1204 const bool draw_extrapolation = (sipo->flag & SIPO_NO_DRAW_EXTRAPOLATION) == 0;
1205 /* draw F-Curve */
1206 if ((fcu->modifiers.first) || (fcu->flag & FCURVE_INT_VALUES)) {
1207 /* draw a curve affected by modifiers or only allowed to have integer values
1208 * by sampling it at various small-intervals over the visible region
1209 */
1210 /* We have to do this mapping dance since the keyframes were remapped but the F-modifier
1211 * evaluations are not.
1212 *
1213 * So we undo the keyframe remapping and instead remap the evaluation time when drawing
1214 * the curve itself. Afterward, we go back and redo the keyframe remapping so the controls
1215 * are drawn properly. */
1217 ale, static_cast<FCurve *>(ale->key_data), true, false);
1219 ale->id,
1220 fcu,
1221 &region->v2d,
1222 shdr_pos,
1224 draw_extrapolation);
1226 ale, static_cast<FCurve *>(ale->key_data), false, false);
1227 }
1228 else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
1229 /* just draw curve based on defined data (i.e. no modifiers) */
1230 if (fcu->bezt) {
1231 draw_fcurve_curve_keys(ac, ale->id, fcu, &region->v2d, shdr_pos, draw_extrapolation);
1232 }
1233 else if (fcu->fpt) {
1234 draw_fcurve_curve_samples(ac, ale->id, fcu, &region->v2d, shdr_pos, draw_extrapolation);
1235 }
1236 }
1237
1239
1240 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1241 GPU_line_smooth(false);
1242 }
1244 }
1245
1246 /* 2) draw handles and vertices as appropriate based on active
1247 * - If the option to only show controls if the F-Curve is selected is enabled,
1248 * we must obey this.
1249 */
1250 if (!(U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) ||
1251 (fcu->flag & FCURVE_SELECTED))
1252 {
1253 if (!BKE_fcurve_are_keyframes_usable(fcu) && !(fcu->fpt && fcu->totvert)) {
1254 /* only draw controls if this is the active modifier */
1255 if ((fcu->flag & FCURVE_ACTIVE) && (fcm)) {
1256 switch (fcm->type) {
1257 case FMODIFIER_TYPE_ENVELOPE: /* envelope */
1258 draw_fcurve_modifier_controls_envelope(fcm, &region->v2d, ale);
1259 break;
1260 }
1261 }
1262 }
1263 else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
1264 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
1265 float offset;
1266 const float unit_scale = ANIM_unit_mapping_get_factor(
1267 ac->scene, ale->id, fcu, mapping_flag, &offset);
1268
1269 /* apply unit-scaling to all values via OpenGL */
1271 GPU_matrix_scale_2f(1.0f, unit_scale);
1272 GPU_matrix_translate_2f(0.0f, offset);
1273
1274 /* Set this once and for all -
1275 * all handles and handle-verts should use the same thickness. */
1276 GPU_line_width(1.0);
1277
1278 if (fcu->bezt) {
1279 bool do_handles = draw_fcurve_handles_check(sipo, fcu);
1280
1281 if (do_handles) {
1282 /* only draw handles/vertices on keyframes */
1283 draw_fcurve_handles(sipo, region, fcu);
1284 }
1285
1286 draw_fcurve_vertices(region, fcu, do_handles, (sipo->flag & SIPO_SELVHANDLESONLY));
1287 }
1288 else {
1289 /* samples: only draw two indicators at either end as indicators */
1290 draw_fcurve_samples(region, fcu, unit_scale);
1291 }
1292
1294 }
1295 }
1296
1297 /* 3) draw driver debugging stuff */
1298 if ((ac->datatype == ANIMCONT_DRIVERS) && (fcu->flag & FCURVE_ACTIVE)) {
1299 graph_draw_driver_debug(ac, ale->id, fcu);
1300 }
1301
1302 /* undo mapping of keyframes for drawing if scaled F-Curve */
1303 ANIM_nla_mapping_apply_if_needed_fcurve(ale, static_cast<FCurve *>(ale->key_data), true, false);
1304}
1305
1306/* Debugging -------------------------------- */
1307
1308/* Draw indicators which show the value calculated from the driver,
1309 * and how this is mapped to the value that comes out of it. This
1310 * is handy for helping users better understand how to interpret
1311 * the graphs, and also facilitates debugging.
1312 */
1314{
1315 ChannelDriver *driver = fcu->driver;
1316 View2D *v2d = &ac->region->v2d;
1317 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
1318 float offset;
1319 float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
1320
1321 const uint shdr_pos = GPU_vertformat_attr_add(
1324
1325 float viewport_size[4];
1326 GPU_viewport_size_get_f(viewport_size);
1327 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
1328
1329 immUniform1i("colors_len", 0); /* Simple dashes. */
1330
1331 /* No curve to modify/visualize the result?
1332 * => We still want to show the 1-1 default...
1333 */
1334 if ((fcu->totvert == 0) && BLI_listbase_is_empty(&fcu->modifiers)) {
1335 float t;
1336
1337 /* draw with thin dotted lines in style of what curve would have been */
1339
1340 immUniform1f("dash_width", 40.0f);
1341 immUniform1f("udash_factor", 0.5f);
1342 GPU_line_width(2.0f);
1343
1344 /* draw 1-1 line, stretching just past the screen limits
1345 * NOTE: we need to scale the y-values to be valid for the units
1346 */
1348
1349 t = v2d->cur.xmin;
1350 immVertex2f(shdr_pos, t, (t + offset) * unitfac);
1351
1352 t = v2d->cur.xmax;
1353 immVertex2f(shdr_pos, t, (t + offset) * unitfac);
1354
1355 immEnd();
1356 }
1357
1358 /* draw driver only if actually functional */
1359 if ((driver->flag & DRIVER_FLAG_INVALID) == 0) {
1360 /* grab "coordinates" for driver outputs */
1361 float x = driver->curval;
1362 float y = fcu->curval * unitfac;
1363
1364 /* Only draw indicators if the point is in range. */
1365 if (x >= v2d->cur.xmin) {
1366 float co[2];
1367
1368 /* draw dotted lines leading towards this point from both axes ....... */
1369 immUniformColor3f(0.9f, 0.9f, 0.9f);
1370 immUniform1f("dash_width", 10.0f);
1371 immUniform1f("udash_factor", 0.5f);
1372 GPU_line_width(1.0f);
1373
1374 immBegin(GPU_PRIM_LINES, (y <= v2d->cur.ymax) ? 4 : 2);
1375
1376 /* x-axis lookup */
1377 co[0] = x;
1378
1379 if (y <= v2d->cur.ymax) {
1380 co[1] = v2d->cur.ymax + 1.0f;
1381 immVertex2fv(shdr_pos, co);
1382
1383 co[1] = y;
1384 immVertex2fv(shdr_pos, co);
1385 }
1386
1387 /* y-axis lookup */
1388 co[1] = y;
1389
1390 co[0] = v2d->cur.xmin - 1.0f;
1391 immVertex2fv(shdr_pos, co);
1392
1393 co[0] = x;
1394 immVertex2fv(shdr_pos, co);
1395
1396 immEnd();
1397
1399
1400 /* GPU_PRIM_POINTS do not survive dashed line geometry shader... */
1402
1403 /* x marks the spot .................................................... */
1404 /* -> outer frame */
1405 immUniformColor3f(0.9f, 0.9f, 0.9f);
1406 GPU_point_size(7.0);
1407
1409 immVertex2f(shdr_pos, x, y);
1410 immEnd();
1411
1412 /* inner frame */
1413 immUniformColor3f(0.9f, 0.0f, 0.0f);
1414 GPU_point_size(3.0);
1415
1417 immVertex2f(shdr_pos, x, y);
1418 immEnd();
1419 }
1420 }
1421
1423}
1424
1425/* Public Curve-Drawing API ---------------- */
1426
1428{
1429 /* draw with thick dotted lines */
1430 GPU_line_width(3.0f);
1431
1432 /* anti-aliased lines for less jagged appearance */
1433 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1434 GPU_line_smooth(true);
1435 }
1437
1438 const uint shdr_pos = GPU_vertformat_attr_add(
1440
1442
1443 float viewport_size[4];
1444 GPU_viewport_size_get_f(viewport_size);
1445 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
1446
1447 immUniform1i("colors_len", 0); /* Simple dashes. */
1448 immUniform1f("dash_width", 20.0f);
1449 immUniform1f("udash_factor", 0.5f);
1450
1451 /* Don't draw extrapolation on sampled ghost curves because it doesn't
1452 * match the curves they're ghosting anyway.
1453 * See issue #109920 for details. */
1454 const bool draw_extrapolation = false;
1455 /* the ghost curves are simply sampled F-Curves stored in sipo->runtime.ghost_curves */
1456 LISTBASE_FOREACH (FCurve *, fcu, &sipo->runtime.ghost_curves) {
1457 /* set whatever color the curve has set
1458 * - this is set by the function which creates these
1459 * - draw with a fixed opacity of 2
1460 */
1461 immUniformColor3fvAlpha(fcu->color, 0.5f);
1462
1463 /* simply draw the stored samples */
1464 draw_fcurve_curve_samples(ac, nullptr, fcu, &region->v2d, shdr_pos, draw_extrapolation);
1465 }
1466
1468
1469 if (U.animation_flag & USER_ANIM_HIGH_QUALITY_DRAWING) {
1470 GPU_line_smooth(false);
1471 }
1473}
1474
1475void graph_draw_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, short sel)
1476{
1477 ListBase anim_data = {nullptr, nullptr};
1478 int filter;
1479
1480 /* build list of curves to draw */
1482 filter |= ((sel) ? (ANIMFILTER_SEL) : (ANIMFILTER_UNSEL));
1484 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1485
1486 /* for each curve:
1487 * draw curve, then handle-lines, and finally vertices in this order so that
1488 * the data will be layered correctly
1489 */
1490 bAnimListElem *ale_active_fcurve = nullptr;
1491 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1492 const FCurve *fcu = (FCurve *)ale->key_data;
1493 if ((fcu->flag & FCURVE_ACTIVE) && !ale_active_fcurve) {
1494 ale_active_fcurve = ale;
1495 continue;
1496 }
1497 draw_fcurve(ac, sipo, region, ale);
1498 }
1499
1500 /* Draw the active FCurve last so that it (especially the active keyframe)
1501 * shows on top of the other curves. */
1502 if (ale_active_fcurve != nullptr) {
1503 draw_fcurve(ac, sipo, region, ale_active_fcurve);
1504 }
1505
1506 /* free list of curves */
1507 ANIM_animdata_freelist(&anim_data);
1508}
1509
1511
1512/* -------------------------------------------------------------------- */
1515
1517 bAnimContext *ac,
1518 ARegion *region,
1519 const ListBase /*bAnimListElem*/ &anim_data)
1520{
1521 bAnimListElem *ale;
1522
1523 View2D *v2d = &region->v2d;
1524
1525 const float channel_step = ANIM_UI_get_channel_step();
1526
1527 /* Loop through channels, and set up drawing depending on their type. */
1528 { /* first pass: just the standard GL-drawing for backdrop + text */
1529 size_t channel_index = 0;
1530 float ymax = ANIM_UI_get_first_channel_top(v2d);
1531
1532 for (ale = static_cast<bAnimListElem *>(anim_data.first); ale;
1533 ale = ale->next, ymax -= channel_step, channel_index++)
1534 {
1535 const float ymin = ymax - ANIM_UI_get_channel_height();
1536
1537 /* check if visible */
1538 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
1539 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1540 {
1541 /* draw all channels using standard channel-drawing API */
1542 ANIM_channel_draw(ac, ale, ymin, ymax, channel_index);
1543 }
1544 }
1545 }
1546 { /* second pass: widgets */
1547 uiBlock *block = UI_block_begin(C, region, __func__, blender::ui::EmbossType::Emboss);
1548 size_t channel_index = 0;
1549 float ymax = ANIM_UI_get_first_channel_top(v2d);
1550
1551 /* set blending again, as may not be set in previous step */
1553
1554 for (ale = static_cast<bAnimListElem *>(anim_data.first); ale;
1555 ale = ale->next, ymax -= channel_step, channel_index++)
1556 {
1557 const float ymin = ymax - ANIM_UI_get_channel_height();
1558
1559 /* check if visible */
1560 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
1561 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1562 {
1563 /* draw all channels using standard channel-drawing API */
1564 rctf channel_rect;
1565 BLI_rctf_init(&channel_rect, 0, v2d->cur.xmax - V2D_SCROLL_WIDTH, ymin, ymax);
1566 ANIM_channel_draw_widgets(C, ac, ale, block, &channel_rect, channel_index);
1567 }
1568 }
1569
1570 UI_block_end(C, block);
1571 UI_block_draw(C, block);
1572
1574 }
1575}
1576
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:82
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1673
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:543
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:540
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)
float[3] Vector
@ 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 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 immBindBuiltinProgram(eGPUBuiltinShader shader_id)
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
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
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
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
#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:1911
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1920
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:353
void ANIM_nla_mapping_apply_if_needed_fcurve(bAnimListElem *ale, FCurve *fcu, const bool restore, const bool only_keys)
Definition anim_draw.cc:339
bool ANIM_nla_mapping_allowed(const bAnimListElem *ale)
Definition anim_draw.cc:212
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
Definition anim_draw.cc:562
float ANIM_nla_tweakedit_remap(bAnimListElem *ale, const float cframe, const eNlaTime_ConvertModes mode)
Definition anim_draw.cc:262
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
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint pos
struct @064345207361167251075330302113175271221317160336::@201157344026354305110036153026103256267276205234 attr_id
uint col
#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:404
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