Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / space_graph / graph_draw.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) Blender Foundation
17  */
18
19 /** \file
20  * \ingroup spgraph
21  */
22
23 #include <stdio.h>
24 #include <math.h>
25 #include <string.h>
26 #include <float.h>
27
28 #include "BLI_blenlib.h"
29 #include "BLI_math.h"
30 #include "BLI_utildefines.h"
31
32 #include "DNA_anim_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_space_types.h"
35 #include "DNA_windowmanager_types.h"
36 #include "DNA_userdef_types.h"
37
38 #include "BKE_context.h"
39 #include "BKE_curve.h"
40 #include "BKE_fcurve.h"
41
42 #include "GPU_draw.h"
43 #include "GPU_immediate.h"
44 #include "GPU_matrix.h"
45 #include "GPU_state.h"
46
47 #include "ED_anim_api.h"
48
49 #include "graph_intern.h"
50
51 #include "UI_interface.h"
52 #include "UI_resources.h"
53 #include "UI_view2d.h"
54
55 /* *************************** */
56 /* Utility Drawing Defines */
57
58 /* determine the alpha value that should be used when
59  * drawing components for some F-Curve (fcu)
60  * - selected F-Curves should be more visible than partially visible ones
61  */
62 static float fcurve_display_alpha(FCurve *fcu)
63 {
64   return (fcu->flag & FCURVE_SELECTED) ? 1.0f : U.fcu_inactive_alpha;
65 }
66
67 /* *************************** */
68 /* F-Curve Modifier Drawing */
69
70 /* Envelope -------------- */
71
72 /* TODO: draw a shaded poly showing the region of influence too!!! */
73 static void draw_fcurve_modifier_controls_envelope(FModifier *fcm, View2D *v2d)
74 {
75   FMod_Envelope *env = (FMod_Envelope *)fcm->data;
76   FCM_EnvelopeData *fed;
77   const float fac = 0.05f * BLI_rctf_size_x(&v2d->cur);
78   int i;
79
80   const uint shdr_pos = GPU_vertformat_attr_add(
81       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
82
83   GPU_line_width(1.0f);
84
85   immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
86
87   float viewport_size[4];
88   GPU_viewport_size_get_f(viewport_size);
89   immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
90
91   immUniform1i("colors_len", 0); /* Simple dashes. */
92   immUniformColor3f(0.0f, 0.0f, 0.0f);
93   immUniform1f("dash_width", 10.0f);
94   immUniform1f("dash_factor", 0.5f);
95
96   /* draw two black lines showing the standard reference levels */
97
98   immBegin(GPU_PRIM_LINES, 4);
99   immVertex2f(shdr_pos, v2d->cur.xmin, env->midval + env->min);
100   immVertex2f(shdr_pos, v2d->cur.xmax, env->midval + env->min);
101
102   immVertex2f(shdr_pos, v2d->cur.xmin, env->midval + env->max);
103   immVertex2f(shdr_pos, v2d->cur.xmax, env->midval + env->max);
104   immEnd();
105
106   immUnbindProgram();
107
108   if (env->totvert > 0) {
109     /* set size of vertices (non-adjustable for now) */
110     GPU_point_size(2.0f);
111
112     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
113
114     /* for now, point color is fixed, and is white */
115     immUniformColor3f(1.0f, 1.0f, 1.0f);
116
117     immBeginAtMost(GPU_PRIM_POINTS, env->totvert * 2);
118
119     for (i = 0, fed = env->data; i < env->totvert; i++, fed++) {
120       /* only draw if visible
121        * - min/max here are fixed, not relative
122        */
123       if (IN_RANGE(fed->time, (v2d->cur.xmin - fac), (v2d->cur.xmax + fac))) {
124         immVertex2f(shdr_pos, fed->time, fed->min);
125         immVertex2f(shdr_pos, fed->time, fed->max);
126       }
127     }
128
129     immEnd();
130
131     immUnbindProgram();
132   }
133 }
134
135 /* *************************** */
136 /* F-Curve Drawing */
137
138 /* Points ---------------- */
139
140 /* helper func - set color to draw F-Curve data with */
141 static void set_fcurve_vertex_color(FCurve *fcu, bool sel)
142 {
143   float color[4];
144   float diff;
145
146   /* Set color of curve vertex based on state of curve (i.e. 'Edit' Mode) */
147   if ((fcu->flag & FCURVE_PROTECTED) == 0) {
148     /* Curve's points ARE BEING edited */
149     UI_GetThemeColor3fv(sel ? TH_VERTEX_SELECT : TH_VERTEX, color);
150   }
151   else {
152     /* Curve's points CANNOT BE edited */
153     UI_GetThemeColor3fv(sel ? TH_TEXT_HI : TH_TEXT, color);
154   }
155
156   /* Fade the 'intensity' of the vertices based on the selection of the curves too
157    * - Only fade by 50% the amount the curves were faded by, so that the points
158    *   still stand out for easier selection
159    */
160   diff = 1.0f - fcurve_display_alpha(fcu);
161   color[3] = 1.0f - (diff * 0.5f);
162   CLAMP(color[3], 0.2f, 1.0f);
163
164   immUniformColor4fv(color);
165 }
166
167 static void draw_fcurve_selected_keyframe_vertices(
168     FCurve *fcu, View2D *v2d, bool edit, bool sel, unsigned pos)
169 {
170   const float fac = 0.05f * BLI_rctf_size_x(&v2d->cur);
171
172   set_fcurve_vertex_color(fcu, sel);
173
174   immBeginAtMost(GPU_PRIM_POINTS, fcu->totvert);
175
176   BezTriple *bezt = fcu->bezt;
177   for (int i = 0; i < fcu->totvert; i++, bezt++) {
178     /* As an optimization step, only draw those in view
179      * - We apply a correction factor to ensure that points
180      *   don't pop in/out due to slight twitches of view size.
181      */
182     if (IN_RANGE(bezt->vec[1][0], (v2d->cur.xmin - fac), (v2d->cur.xmax + fac))) {
183       if (edit) {
184         /* 'Keyframe' vertex only, as handle lines and handles have already been drawn
185          * - only draw those with correct selection state for the current drawing color
186          * -
187          */
188         if ((bezt->f2 & SELECT) == sel) {
189           immVertex2fv(pos, bezt->vec[1]);
190         }
191       }
192       else {
193         /* no check for selection here, as curve is not editable... */
194         /* XXX perhaps we don't want to even draw points?   maybe add an option for that later */
195         immVertex2fv(pos, bezt->vec[1]);
196       }
197     }
198   }
199
200   immEnd();
201 }
202
203 /* helper func - draw keyframe vertices only for an F-Curve */
204 static void draw_fcurve_keyframe_vertices(FCurve *fcu, View2D *v2d, bool edit, unsigned pos)
205 {
206   immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
207
208   immUniform1f("size", UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize);
209
210   draw_fcurve_selected_keyframe_vertices(fcu, v2d, edit, false, pos);
211   draw_fcurve_selected_keyframe_vertices(fcu, v2d, edit, true, pos);
212
213   immUnbindProgram();
214 }
215
216 /* helper func - draw handle vertices only for an F-Curve (if it is not protected) */
217 static void draw_fcurve_selected_handle_vertices(
218     FCurve *fcu, View2D *v2d, bool sel, bool sel_handle_only, unsigned pos)
219 {
220   (void)v2d; /* TODO: use this to draw only points in view */
221
222   /* set handle color */
223   float hcolor[3];
224   UI_GetThemeColor3fv(sel ? TH_HANDLE_VERTEX_SELECT : TH_HANDLE_VERTEX, hcolor);
225   immUniform4f("outlineColor", hcolor[0], hcolor[1], hcolor[2], 1.0f);
226   immUniformColor3fvAlpha(hcolor, 0.01f); /* almost invisible - only keep for smoothness */
227
228   immBeginAtMost(GPU_PRIM_POINTS, fcu->totvert * 2);
229
230   BezTriple *bezt = fcu->bezt;
231   BezTriple *prevbezt = NULL;
232   for (int i = 0; i < fcu->totvert; i++, prevbezt = bezt, bezt++) {
233     /* Draw the editmode handles for a bezier curve (others don't have handles)
234      * if their selection status matches the selection status we're drawing for
235      * - first handle only if previous beztriple was bezier-mode
236      * - second handle only if current beztriple is bezier-mode
237      *
238      * Also, need to take into account whether the keyframe was selected
239      * if a Graph Editor option to only show handles of selected keys is on.
240      */
241     if (!sel_handle_only || BEZT_ISSEL_ANY(bezt)) {
242       if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
243           (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) {
244         if ((bezt->f1 & SELECT) == sel
245             /* && v2d->cur.xmin < bezt->vec[0][0] < v2d->cur.xmax) */) {
246           immVertex2fv(pos, bezt->vec[0]);
247         }
248       }
249
250       if (bezt->ipo == BEZT_IPO_BEZ) {
251         if ((bezt->f3 & SELECT) == sel
252             /* && v2d->cur.xmin < bezt->vec[2][0] < v2d->cur.xmax) */) {
253           immVertex2fv(pos, bezt->vec[2]);
254         }
255       }
256     }
257   }
258
259   immEnd();
260 }
261
262 /* helper func - draw handle vertices only for an F-Curve (if it is not protected) */
263 static void draw_fcurve_handle_vertices(FCurve *fcu,
264                                         View2D *v2d,
265                                         bool sel_handle_only,
266                                         unsigned pos)
267 {
268   /* smooth outlines for more consistent appearance */
269   immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
270
271   /* set handle size */
272   immUniform1f("size", (1.4f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE)) * U.pixelsize);
273   immUniform1f("outlineWidth", 1.5f * U.pixelsize);
274
275   draw_fcurve_selected_handle_vertices(fcu, v2d, false, sel_handle_only, pos);
276   draw_fcurve_selected_handle_vertices(fcu, v2d, true, sel_handle_only, pos);
277
278   immUnbindProgram();
279 }
280
281 static void draw_fcurve_vertices(ARegion *ar, FCurve *fcu, bool do_handles, bool sel_handle_only)
282 {
283   View2D *v2d = &ar->v2d;
284
285   /* only draw points if curve is visible
286    * - Draw unselected points before selected points as separate passes
287    *    to make sure in the case of overlapping points that the selected is always visible
288    * - Draw handles before keyframes, so that keyframes will overlap handles
289    *   (keyframes are more important for users).
290    */
291
292   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
293
294   GPU_blend(true);
295   GPU_enable_program_point_size();
296
297   /* draw the two handles first (if they're shown, the curve doesn't
298    * have just a single keyframe, and the curve is being edited) */
299   if (do_handles) {
300     draw_fcurve_handle_vertices(fcu, v2d, sel_handle_only, pos);
301   }
302
303   /* draw keyframes over the handles */
304   draw_fcurve_keyframe_vertices(fcu, v2d, !(fcu->flag & FCURVE_PROTECTED), pos);
305
306   GPU_disable_program_point_size();
307   GPU_blend(false);
308 }
309
310 /* Handles ---------------- */
311
312 static bool draw_fcurve_handles_check(SpaceGraph *sipo, FCurve *fcu)
313 {
314   /* don't draw handle lines if handles are not to be shown */
315   if (
316       /* handles shouldn't be shown anywhere */
317       (sipo->flag & SIPO_NOHANDLES) ||
318       /* keyframes aren't editable */
319       (fcu->flag & FCURVE_PROTECTED) ||
320 #if 0 /* handles can still be selected and handle types set, better draw - campbell */
321       /* editing the handles here will cause weird/incorrect interpolation issues */
322       (fcu->flag & FCURVE_INT_VALUES) ||
323 #endif
324       /* group that curve belongs to is not editable */
325       ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) ||
326       /* Do not show handles if there is only 1 keyframe,
327        * otherwise they all clump together in an ugly ball. */
328       (fcu->totvert <= 1)) {
329     return false;
330   }
331   else {
332     return true;
333   }
334 }
335
336 /* draw lines for F-Curve handles only (this is only done in EditMode)
337  * note: draw_fcurve_handles_check must be checked before running this. */
338 static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
339 {
340   int sel, b;
341
342   GPUVertFormat *format = immVertexFormat();
343   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
344   uint color = GPU_vertformat_attr_add(
345       format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
346   immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
347
348   immBeginAtMost(GPU_PRIM_LINES, 4 * 2 * fcu->totvert);
349
350   /* slightly hacky, but we want to draw unselected points before selected ones
351    * so that selected points are clearly visible
352    */
353   for (sel = 0; sel < 2; sel++) {
354     BezTriple *bezt = fcu->bezt, *prevbezt = NULL;
355     int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE;
356     const float *fp;
357     unsigned char col[4];
358
359     for (b = 0; b < fcu->totvert; b++, prevbezt = bezt, bezt++) {
360       /* if only selected keyframes can get their handles shown,
361        * check that keyframe is selected
362        */
363       if (sipo->flag & SIPO_SELVHANDLESONLY) {
364         if (BEZT_ISSEL_ANY(bezt) == 0) {
365           continue;
366         }
367       }
368
369       /* draw handle with appropriate set of colors if selection is ok */
370       if ((bezt->f2 & SELECT) == sel) {
371         fp = bezt->vec[0];
372
373         /* only draw first handle if previous segment had handles */
374         if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
375             (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) {
376           UI_GetThemeColor3ubv(basecol + bezt->h1, col);
377           col[3] = fcurve_display_alpha(fcu) * 255;
378           immAttr4ubv(color, col);
379           immVertex2fv(pos, fp);
380           immAttr4ubv(color, col);
381           immVertex2fv(pos, fp + 3);
382         }
383
384         /* only draw second handle if this segment is bezier */
385         if (bezt->ipo == BEZT_IPO_BEZ) {
386           UI_GetThemeColor3ubv(basecol + bezt->h2, col);
387           col[3] = fcurve_display_alpha(fcu) * 255;
388           immAttr4ubv(color, col);
389           immVertex2fv(pos, fp + 3);
390           immAttr4ubv(color, col);
391           immVertex2fv(pos, fp + 6);
392         }
393       }
394       else {
395         /* only draw first handle if previous segment was had handles, and selection is ok */
396         if (((bezt->f1 & SELECT) == sel) && ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) ||
397                                              (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))) {
398           fp = bezt->vec[0];
399           UI_GetThemeColor3ubv(basecol + bezt->h1, col);
400           col[3] = fcurve_display_alpha(fcu) * 255;
401           immAttr4ubv(color, col);
402           immVertex2fv(pos, fp);
403           immAttr4ubv(color, col);
404           immVertex2fv(pos, fp + 3);
405         }
406
407         /* only draw second handle if this segment is bezier, and selection is ok */
408         if (((bezt->f3 & SELECT) == sel) && (bezt->ipo == BEZT_IPO_BEZ)) {
409           fp = bezt->vec[1];
410           UI_GetThemeColor3ubv(basecol + bezt->h2, col);
411           col[3] = fcurve_display_alpha(fcu) * 255;
412           immAttr4ubv(color, col);
413           immVertex2fv(pos, fp);
414           immAttr4ubv(color, col);
415           immVertex2fv(pos, fp + 3);
416         }
417       }
418     }
419   }
420
421   immEnd();
422   immUnbindProgram();
423 }
424
425 /* Samples ---------------- */
426
427 /* helper func - draw sample-range marker for an F-Curve as a cross
428  * NOTE: the caller MUST HAVE GL_LINE_SMOOTH & GL_BLEND ENABLED, otherwise, the controls don't
429  * have a consistent appearance (due to off-pixel alignments)...
430  */
431 static void draw_fcurve_sample_control(
432     float x, float y, float xscale, float yscale, float hsize, unsigned int pos)
433 {
434   /* adjust view transform before starting */
435   GPU_matrix_push();
436   GPU_matrix_translate_2f(x, y);
437   GPU_matrix_scale_2f(1.0f / xscale * hsize, 1.0f / yscale * hsize);
438
439   /* draw X shape */
440   immBegin(GPU_PRIM_LINES, 4);
441   immVertex2f(pos, -0.7f, -0.7f);
442   immVertex2f(pos, +0.7f, +0.7f);
443
444   immVertex2f(pos, -0.7f, +0.7f);
445   immVertex2f(pos, +0.7f, -0.7f);
446   immEnd();
447
448   /* restore view transform */
449   GPU_matrix_pop();
450 }
451
452 /* helper func - draw keyframe vertices only for an F-Curve */
453 static void draw_fcurve_samples(SpaceGraph *sipo, ARegion *ar, FCurve *fcu)
454 {
455   FPoint *first, *last;
456   float hsize, xscale, yscale;
457
458   /* get view settings */
459   hsize = UI_GetThemeValuef(TH_VERTEX_SIZE);
460   UI_view2d_scale_get(&ar->v2d, &xscale, &yscale);
461
462   /* get verts */
463   first = fcu->fpt;
464   last = (first) ? (first + (fcu->totvert - 1)) : (NULL);
465
466   /* draw */
467   if (first && last) {
468     /* anti-aliased lines for more consistent appearance */
469     if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
470       GPU_line_smooth(true);
471     }
472     GPU_blend(true);
473
474     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
475     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
476
477     immUniformThemeColor((fcu->flag & FCURVE_SELECTED) ? TH_TEXT_HI : TH_TEXT);
478
479     draw_fcurve_sample_control(first->vec[0], first->vec[1], xscale, yscale, hsize, pos);
480     draw_fcurve_sample_control(last->vec[0], last->vec[1], xscale, yscale, hsize, pos);
481
482     immUnbindProgram();
483
484     GPU_blend(false);
485     if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
486       GPU_line_smooth(false);
487     }
488   }
489 }
490
491 /* Curve ---------------- */
492
493 /* Helper func - just draw the F-Curve by sampling the visible region
494  * (for drawing curves with modifiers). */
495 static void draw_fcurve_curve(
496     bAnimContext *ac, ID *id, FCurve *fcu_, View2D *v2d, View2DGrid *grid, unsigned int pos)
497 {
498   SpaceGraph *sipo = (SpaceGraph *)ac->sl;
499   float samplefreq;
500   float stime, etime;
501   float unitFac, offset;
502   float dx, dy;
503   short mapping_flag = ANIM_get_normalization_flags(ac);
504   int i, n;
505
506   /* when opening a blend file on a different sized screen or while dragging the toolbar this can
507    * happen best just bail out in this case. */
508   UI_view2d_grid_size(grid, &dx, &dy);
509   if (dx <= 0.0f) {
510     return;
511   }
512
513   /* disable any drivers */
514   FCurve fcurve_for_draw = *fcu_;
515   fcurve_for_draw.driver = NULL;
516
517   /* compute unit correction factor */
518   unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, &fcurve_for_draw, mapping_flag, &offset);
519
520   /* Note about sampling frequency:
521    * Ideally, this is chosen such that we have 1-2 pixels = 1 segment
522    * which means that our curves can be as smooth as possible. However,
523    * this does mean that curves may not be fully accurate (i.e. if they have
524    * sudden spikes which happen at the sampling point, we may have problems).
525    * Also, this may introduce lower performance on less densely detailed curves,
526    * though it is impossible to predict this from the modifiers!
527    *
528    * If the automatically determined sampling frequency is likely to cause an infinite
529    * loop (i.e. too close to 0), then clamp it to a determined "safe" value. The value
530    *  chosen here is just the coarsest value which still looks reasonable...
531    */
532   /* grid->dx represents the number of 'frames' between gridlines,
533    * but we divide by U.v2d_min_gridsize to get pixels-steps */
534   /* TODO: perhaps we should have 1.0 frames
535    * as upper limit so that curves don't get too distorted? */
536   samplefreq = dx / (U.v2d_min_gridsize * U.pixelsize);
537
538   if (sipo->flag & SIPO_BEAUTYDRAW_OFF) {
539     /* Low Precision = coarse lower-bound clamping
540      *
541      * Although the "Beauty Draw" flag was originally for AA'd
542      * line drawing, the sampling rate here has a much greater
543      * impact on performance (e.g. for T40372)!
544      *
545      * This one still amounts to 10 sample-frames for each 1-frame interval
546      * which should be quite a decent approximation in many situations.
547      */
548     if (samplefreq < 0.1f) {
549       samplefreq = 0.1f;
550     }
551   }
552   else {
553     /* "Higher Precision" but slower - especially on larger windows (e.g. T40372) */
554     if (samplefreq < 0.00001f) {
555       samplefreq = 0.00001f;
556     }
557   }
558
559   /* the start/end times are simply the horizontal extents of the 'cur' rect */
560   stime = v2d->cur.xmin;
561   etime = v2d->cur.xmax + samplefreq; /* + samplefreq here so that last item gets included... */
562
563   /* at each sampling interval, add a new vertex
564    * - apply the unit correction factor to the calculated values so that
565    *   the displayed values appear correctly in the viewport
566    */
567
568   n = (etime - stime) / samplefreq + 0.5f;
569
570   if (n > 0) {
571     immBegin(GPU_PRIM_LINE_STRIP, (n + 1));
572
573     for (i = 0; i <= n; i++) {
574       float ctime = stime + i * samplefreq;
575       immVertex2f(pos, ctime, (evaluate_fcurve(&fcurve_for_draw, ctime) + offset) * unitFac);
576     }
577
578     immEnd();
579   }
580 }
581
582 /* helper func - draw a samples-based F-Curve */
583 static void draw_fcurve_curve_samples(
584     bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, const uint shdr_pos)
585 {
586   FPoint *prevfpt = fcu->fpt;
587   FPoint *fpt = prevfpt + 1;
588   float fac, v[2];
589   int b = fcu->totvert;
590   float unit_scale, offset;
591   short mapping_flag = ANIM_get_normalization_flags(ac);
592   int count = fcu->totvert;
593
594   if (prevfpt->vec[0] > v2d->cur.xmin) {
595     count++;
596   }
597
598   if ((prevfpt + b - 1)->vec[0] < v2d->cur.xmax) {
599     count++;
600   }
601
602   /* apply unit mapping */
603   GPU_matrix_push();
604   unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
605   GPU_matrix_scale_2f(1.0f, unit_scale);
606   GPU_matrix_translate_2f(0.0f, offset);
607
608   immBegin(GPU_PRIM_LINE_STRIP, count);
609
610   /* extrapolate to left? - left-side of view comes before first keyframe? */
611   if (prevfpt->vec[0] > v2d->cur.xmin) {
612     v[0] = v2d->cur.xmin;
613
614     /* y-value depends on the interpolation */
615     if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
616         (fcu->totvert == 1)) {
617       /* just extend across the first keyframe's value */
618       v[1] = prevfpt->vec[1];
619     }
620     else {
621       /* extrapolate linear doesn't use the handle, use the next points center instead */
622       fac = (prevfpt->vec[0] - fpt->vec[0]) / (prevfpt->vec[0] - v[0]);
623       if (fac) {
624         fac = 1.0f / fac;
625       }
626       v[1] = prevfpt->vec[1] - fac * (prevfpt->vec[1] - fpt->vec[1]);
627     }
628
629     immVertex2fv(shdr_pos, v);
630   }
631
632   /* loop over samples, drawing segments */
633   /* draw curve between first and last keyframe (if there are enough to do so) */
634   while (b--) {
635     /* Linear interpolation: just add one point (which should add a new line segment) */
636     immVertex2fv(shdr_pos, prevfpt->vec);
637
638     /* get next pointers */
639     if (b > 0) {
640       prevfpt++;
641     }
642   }
643
644   /* extrapolate to right? (see code for left-extrapolation above too) */
645   if (prevfpt->vec[0] < v2d->cur.xmax) {
646     v[0] = v2d->cur.xmax;
647
648     /* y-value depends on the interpolation */
649     if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
650         (fcu->totvert == 1)) {
651       /* based on last keyframe's value */
652       v[1] = prevfpt->vec[1];
653     }
654     else {
655       /* extrapolate linear doesn't use the handle, use the previous points center instead */
656       fpt = prevfpt - 1;
657       fac = (prevfpt->vec[0] - fpt->vec[0]) / (prevfpt->vec[0] - v[0]);
658       if (fac) {
659         fac = 1.0f / fac;
660       }
661       v[1] = prevfpt->vec[1] - fac * (prevfpt->vec[1] - fpt->vec[1]);
662     }
663
664     immVertex2fv(shdr_pos, v);
665   }
666
667   immEnd();
668
669   GPU_matrix_pop();
670 }
671
672 /* helper func - check if the F-Curve only contains easily drawable segments
673  * (i.e. no easing equation interpolations)
674  */
675 static bool fcurve_can_use_simple_bezt_drawing(FCurve *fcu)
676 {
677   BezTriple *bezt;
678   int i;
679
680   for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
681     if (ELEM(bezt->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN, BEZT_IPO_BEZ) == false) {
682       return false;
683     }
684   }
685
686   return true;
687 }
688
689 /* helper func - draw one repeat of an F-Curve (using Bezier curve approximations) */
690 static void draw_fcurve_curve_bezts(
691     bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, unsigned int pos)
692 {
693   BezTriple *prevbezt = fcu->bezt;
694   BezTriple *bezt = prevbezt + 1;
695   float v1[2], v2[2], v3[2], v4[2];
696   float *fp, data[120];
697   float fac = 0.0f;
698   int b = fcu->totvert - 1;
699   int resol;
700   float unit_scale, offset;
701   short mapping_flag = ANIM_get_normalization_flags(ac);
702
703   /* apply unit mapping */
704   GPU_matrix_push();
705   unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
706   GPU_matrix_scale_2f(1.0f, unit_scale);
707   GPU_matrix_translate_2f(0.0f, offset);
708
709   /* For now, this assumes the worst case scenario, where all the keyframes have
710    * bezier interpolation, and are drawn at full res.
711    * This is tricky to optimize, but maybe can be improved at some point... */
712   immBeginAtMost(GPU_PRIM_LINE_STRIP, (b * 32 + 3));
713
714   /* extrapolate to left? */
715   if (prevbezt->vec[1][0] > v2d->cur.xmin) {
716     /* left-side of view comes before first keyframe, so need to extend as not cyclic */
717     v1[0] = v2d->cur.xmin;
718
719     /* y-value depends on the interpolation */
720     if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (prevbezt->ipo == BEZT_IPO_CONST) ||
721         (fcu->totvert == 1)) {
722       /* just extend across the first keyframe's value */
723       v1[1] = prevbezt->vec[1][1];
724     }
725     else if (prevbezt->ipo == BEZT_IPO_LIN) {
726       /* extrapolate linear dosnt use the handle, use the next points center instead */
727       fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
728       if (fac) {
729         fac = 1.0f / fac;
730       }
731       v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]);
732     }
733     else {
734       /* based on angle of handle 1 (relative to keyframe) */
735       fac = (prevbezt->vec[0][0] - prevbezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
736       if (fac) {
737         fac = 1.0f / fac;
738       }
739       v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[0][1] - prevbezt->vec[1][1]);
740     }
741
742     immVertex2fv(pos, v1);
743   }
744
745   /* if only one keyframe, add it now */
746   if (fcu->totvert == 1) {
747     v1[0] = prevbezt->vec[1][0];
748     v1[1] = prevbezt->vec[1][1];
749     immVertex2fv(pos, v1);
750   }
751
752   /* draw curve between first and last keyframe (if there are enough to do so) */
753   /* TODO: optimize this to not have to calc stuff out of view too? */
754   while (b--) {
755     if (prevbezt->ipo == BEZT_IPO_CONST) {
756       /* Constant-Interpolation: draw segment between previous keyframe and next,
757        * but holding same value */
758       v1[0] = prevbezt->vec[1][0];
759       v1[1] = prevbezt->vec[1][1];
760       immVertex2fv(pos, v1);
761
762       v1[0] = bezt->vec[1][0];
763       v1[1] = prevbezt->vec[1][1];
764       immVertex2fv(pos, v1);
765     }
766     else if (prevbezt->ipo == BEZT_IPO_LIN) {
767       /* Linear interpolation: just add one point (which should add a new line segment) */
768       v1[0] = prevbezt->vec[1][0];
769       v1[1] = prevbezt->vec[1][1];
770       immVertex2fv(pos, v1);
771     }
772     else if (prevbezt->ipo == BEZT_IPO_BEZ) {
773       /* Bezier-Interpolation: draw curve as series of segments between keyframes
774        * - resol determines number of points to sample in between keyframes
775        */
776
777       /* resol depends on distance between points
778        * (not just horizontal) OR is a fixed high res */
779       /* TODO: view scale should factor into this someday too... */
780       if (fcu->driver) {
781         resol = 32;
782       }
783       else {
784         resol = (int)(5.0f * len_v2v2(bezt->vec[1], prevbezt->vec[1]));
785       }
786
787       if (resol < 2) {
788         /* only draw one */
789         v1[0] = prevbezt->vec[1][0];
790         v1[1] = prevbezt->vec[1][1];
791         immVertex2fv(pos, v1);
792       }
793       else {
794         /* clamp resolution to max of 32 */
795         /* NOTE: higher values will crash */
796         if (resol > 32) {
797           resol = 32;
798         }
799
800         v1[0] = prevbezt->vec[1][0];
801         v1[1] = prevbezt->vec[1][1];
802         v2[0] = prevbezt->vec[2][0];
803         v2[1] = prevbezt->vec[2][1];
804
805         v3[0] = bezt->vec[0][0];
806         v3[1] = bezt->vec[0][1];
807         v4[0] = bezt->vec[1][0];
808         v4[1] = bezt->vec[1][1];
809
810         correct_bezpart(v1, v2, v3, v4);
811
812         BKE_curve_forward_diff_bezier(v1[0], v2[0], v3[0], v4[0], data, resol, sizeof(float) * 3);
813         BKE_curve_forward_diff_bezier(
814             v1[1], v2[1], v3[1], v4[1], data + 1, resol, sizeof(float) * 3);
815
816         for (fp = data; resol; resol--, fp += 3) {
817           immVertex2fv(pos, fp);
818         }
819       }
820     }
821
822     /* get next pointers */
823     prevbezt = bezt;
824     bezt++;
825
826     /* last point? */
827     if (b == 0) {
828       v1[0] = prevbezt->vec[1][0];
829       v1[1] = prevbezt->vec[1][1];
830       immVertex2fv(pos, v1);
831     }
832   }
833
834   /* extrapolate to right? (see code for left-extrapolation above too) */
835   if (prevbezt->vec[1][0] < v2d->cur.xmax) {
836     v1[0] = v2d->cur.xmax;
837
838     /* y-value depends on the interpolation */
839     if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
840         (prevbezt->ipo == BEZT_IPO_CONST) || (fcu->totvert == 1)) {
841       /* based on last keyframe's value */
842       v1[1] = prevbezt->vec[1][1];
843     }
844     else if (prevbezt->ipo == BEZT_IPO_LIN) {
845       /* extrapolate linear dosnt use the handle, use the previous points center instead */
846       bezt = prevbezt - 1;
847       fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
848       if (fac) {
849         fac = 1.0f / fac;
850       }
851       v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]);
852     }
853     else {
854       /* based on angle of handle 1 (relative to keyframe) */
855       fac = (prevbezt->vec[2][0] - prevbezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
856       if (fac) {
857         fac = 1.0f / fac;
858       }
859       v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[2][1] - prevbezt->vec[1][1]);
860     }
861
862     immVertex2fv(pos, v1);
863   }
864
865   immEnd();
866
867   GPU_matrix_pop();
868 }
869
870 /* Debugging -------------------------------- */
871
872 /* Draw indicators which show the value calculated from the driver,
873  * and how this is mapped to the value that comes out of it. This
874  * is handy for helping users better understand how to interpret
875  * the graphs, and also facilitates debugging.
876  */
877 static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
878 {
879   ChannelDriver *driver = fcu->driver;
880   View2D *v2d = &ac->ar->v2d;
881   short mapping_flag = ANIM_get_normalization_flags(ac);
882   float offset;
883   float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
884
885   /* for now, only show when debugging driver... */
886   //if ((driver->flag & DRIVER_FLAG_SHOWDEBUG) == 0)
887   //  return;
888
889   const uint shdr_pos = GPU_vertformat_attr_add(
890       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
891   immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
892
893   float viewport_size[4];
894   GPU_viewport_size_get_f(viewport_size);
895   immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
896
897   immUniform1i("colors_len", 0); /* Simple dashes. */
898
899   /* No curve to modify/visualize the result?
900    * => We still want to show the 1-1 default...
901    */
902   if ((fcu->totvert == 0) && BLI_listbase_is_empty(&fcu->modifiers)) {
903     float t;
904
905     /* draw with thin dotted lines in style of what curve would have been */
906     immUniformColor3fv(fcu->color);
907
908     immUniform1f("dash_width", 40.0f);
909     immUniform1f("dash_factor", 0.5f);
910     GPU_line_width(2.0f);
911
912     /* draw 1-1 line, stretching just past the screen limits
913      * NOTE: we need to scale the y-values to be valid for the units
914      */
915     immBegin(GPU_PRIM_LINES, 2);
916
917     t = v2d->cur.xmin;
918     immVertex2f(shdr_pos, t, (t + offset) * unitfac);
919
920     t = v2d->cur.xmax;
921     immVertex2f(shdr_pos, t, (t + offset) * unitfac);
922
923     immEnd();
924   }
925
926   /* draw driver only if actually functional */
927   if ((driver->flag & DRIVER_FLAG_INVALID) == 0) {
928     /* grab "coordinates" for driver outputs */
929     float x = driver->curval;
930     float y = fcu->curval * unitfac;
931
932     /* only draw indicators if the point is in range*/
933     if (x >= v2d->cur.xmin) {
934       float co[2];
935
936       /* draw dotted lines leading towards this point from both axes ....... */
937       immUniformColor3f(0.9f, 0.9f, 0.9f);
938       immUniform1f("dash_width", 10.0f);
939       immUniform1f("dash_factor", 0.5f);
940
941       immBegin(GPU_PRIM_LINES, (y >= v2d->cur.ymin) ? 4 : 2);
942
943       /* x-axis lookup */
944       co[0] = x;
945
946       if (y >= v2d->cur.ymin) {
947         co[1] = v2d->cur.ymin - 1.0f;
948         immVertex2fv(shdr_pos, co);
949
950         co[1] = y;
951         immVertex2fv(shdr_pos, co);
952       }
953
954       /* y-axis lookup */
955       co[1] = y;
956
957       co[0] = v2d->cur.xmin - 1.0f;
958       immVertex2fv(shdr_pos, co);
959
960       co[0] = x;
961       immVertex2fv(shdr_pos, co);
962
963       immEnd();
964
965       immUnbindProgram();
966
967       /* GPU_PRIM_POINTS do not survive dashed line geometry shader... */
968       immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
969
970       /* x marks the spot .................................................... */
971       /* -> outer frame */
972       immUniformColor3f(0.9f, 0.9f, 0.9f);
973       GPU_point_size(7.0);
974
975       immBegin(GPU_PRIM_POINTS, 1);
976       immVertex2f(shdr_pos, x, y);
977       immEnd();
978
979       /* inner frame */
980       immUniformColor3f(0.9f, 0.0f, 0.0f);
981       GPU_point_size(3.0);
982
983       immBegin(GPU_PRIM_POINTS, 1);
984       immVertex2f(shdr_pos, x, y);
985       immEnd();
986     }
987   }
988
989   immUnbindProgram();
990 }
991
992 /* Public Curve-Drawing API  ---------------- */
993
994 /* Draw the 'ghost' F-Curves (i.e. snapshots of the curve)
995  * NOTE: unit mapping has already been applied to the values, so do not try and apply again
996  */
997 void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *ar)
998 {
999   FCurve *fcu;
1000
1001   /* draw with thick dotted lines */
1002   GPU_line_width(3.0f);
1003
1004   /* anti-aliased lines for less jagged appearance */
1005   if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
1006     GPU_line_smooth(true);
1007   }
1008   GPU_blend(true);
1009
1010   const uint shdr_pos = GPU_vertformat_attr_add(
1011       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1012
1013   immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
1014
1015   float viewport_size[4];
1016   GPU_viewport_size_get_f(viewport_size);
1017   immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
1018
1019   immUniform1i("colors_len", 0); /* Simple dashes. */
1020   immUniform1f("dash_width", 20.0f);
1021   immUniform1f("dash_factor", 0.5f);
1022
1023   /* the ghost curves are simply sampled F-Curves stored in sipo->runtime.ghost_curves */
1024   for (fcu = sipo->runtime.ghost_curves.first; fcu; fcu = fcu->next) {
1025     /* set whatever color the curve has set
1026      * - this is set by the function which creates these
1027      * - draw with a fixed opacity of 2
1028      */
1029     immUniformColor3fvAlpha(fcu->color, 0.5f);
1030
1031     /* simply draw the stored samples */
1032     draw_fcurve_curve_samples(ac, NULL, fcu, &ar->v2d, shdr_pos);
1033   }
1034
1035   immUnbindProgram();
1036
1037   if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
1038     GPU_line_smooth(false);
1039   }
1040   GPU_blend(false);
1041 }
1042
1043 /* This is called twice from space_graph.c -> graph_main_region_draw()
1044  * Unselected then selected F-Curves are drawn so that they do not occlude each other.
1045  */
1046 void graph_draw_curves(
1047     bAnimContext *ac, SpaceGraph *sipo, ARegion *ar, View2DGrid *grid, short sel)
1048 {
1049   ListBase anim_data = {NULL, NULL};
1050   bAnimListElem *ale;
1051   int filter;
1052
1053   /* build list of curves to draw */
1054   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE);
1055   filter |= ((sel) ? (ANIMFILTER_SEL) : (ANIMFILTER_UNSEL));
1056   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1057
1058   /* for each curve:
1059    * draw curve, then handle-lines, and finally vertices in this order so that
1060    * the data will be layered correctly
1061    */
1062   for (ale = anim_data.first; ale; ale = ale->next) {
1063     FCurve *fcu = (FCurve *)ale->key_data;
1064     FModifier *fcm = find_active_fmodifier(&fcu->modifiers);
1065     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1066
1067     /* map keyframes for drawing if scaled F-Curve */
1068     if (adt) {
1069       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
1070     }
1071
1072     /* draw curve:
1073      * - curve line may be result of one or more destructive modifiers or just the raw data,
1074      *   so we need to check which method should be used
1075      * - controls from active modifier take precedence over keyframes
1076      *   (XXX! editing tools need to take this into account!)
1077      */
1078
1079     /* 1) draw curve line */
1080     if (((fcu->modifiers.first) || (fcu->flag & FCURVE_INT_VALUES)) ||
1081         (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert))) {
1082       /* set color/drawing style for curve itself */
1083       /* draw active F-Curve thicker than the rest to make it stand out */
1084       if (fcu->flag & FCURVE_ACTIVE) {
1085         GPU_line_width(2.5);
1086       }
1087       else {
1088         GPU_line_width(1.0);
1089       }
1090
1091       /* anti-aliased lines for less jagged appearance */
1092       if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
1093         GPU_line_smooth(true);
1094       }
1095       GPU_blend(true);
1096
1097       const uint shdr_pos = GPU_vertformat_attr_add(
1098           immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1099
1100       immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
1101
1102       float viewport_size[4];
1103       GPU_viewport_size_get_f(viewport_size);
1104       immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
1105
1106       immUniform1i("colors_len", 0); /* Simple dashes. */
1107
1108       if (BKE_fcurve_is_protected(fcu)) {
1109         /* protected curves (non editable) are drawn with dotted lines */
1110         immUniform1f("dash_width", 4.0f);
1111         immUniform1f("dash_factor", 0.5f);
1112       }
1113       else {
1114         immUniform1f("dash_factor", 2.0f); /* solid line */
1115       }
1116
1117       if (((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) || (fcu->flag & FCURVE_MUTED)) {
1118         /* muted curves are drawn in a grayish hue */
1119         /* XXX should we have some variations? */
1120         immUniformThemeColorShade(TH_HEADER, 50);
1121       }
1122       else {
1123         /* set whatever color the curve has set
1124          * - unselected curves draw less opaque to help distinguish the selected ones
1125          */
1126         immUniformColor3fvAlpha(fcu->color, fcurve_display_alpha(fcu));
1127       }
1128
1129       /* draw F-Curve */
1130       if ((fcu->modifiers.first) || (fcu->flag & FCURVE_INT_VALUES)) {
1131         /* draw a curve affected by modifiers or only allowed to have integer values
1132          * by sampling it at various small-intervals over the visible region
1133          */
1134         draw_fcurve_curve(ac, ale->id, fcu, &ar->v2d, grid, shdr_pos);
1135       }
1136       else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
1137         /* just draw curve based on defined data (i.e. no modifiers) */
1138         if (fcu->bezt) {
1139           if (fcurve_can_use_simple_bezt_drawing(fcu)) {
1140             draw_fcurve_curve_bezts(ac, ale->id, fcu, &ar->v2d, shdr_pos);
1141           }
1142           else {
1143             draw_fcurve_curve(ac, ale->id, fcu, &ar->v2d, grid, shdr_pos);
1144           }
1145         }
1146         else if (fcu->fpt) {
1147           draw_fcurve_curve_samples(ac, ale->id, fcu, &ar->v2d, shdr_pos);
1148         }
1149       }
1150
1151       immUnbindProgram();
1152
1153       if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
1154         GPU_line_smooth(false);
1155       }
1156       GPU_blend(false);
1157     }
1158
1159     /* 2) draw handles and vertices as appropriate based on active
1160      * - if the option to only show controls if the F-Curve is selected is enabled, we must obey this
1161      */
1162     if (!(sipo->flag & SIPO_SELCUVERTSONLY) || (fcu->flag & FCURVE_SELECTED)) {
1163       if (!fcurve_are_keyframes_usable(fcu) && !(fcu->fpt && fcu->totvert)) {
1164         /* only draw controls if this is the active modifier */
1165         if ((fcu->flag & FCURVE_ACTIVE) && (fcm)) {
1166           switch (fcm->type) {
1167             case FMODIFIER_TYPE_ENVELOPE: /* envelope */
1168               draw_fcurve_modifier_controls_envelope(fcm, &ar->v2d);
1169               break;
1170           }
1171         }
1172       }
1173       else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) {
1174         short mapping_flag = ANIM_get_normalization_flags(ac);
1175         float offset;
1176         float unit_scale = ANIM_unit_mapping_get_factor(
1177             ac->scene, ale->id, fcu, mapping_flag, &offset);
1178
1179         /* apply unit-scaling to all values via OpenGL */
1180         GPU_matrix_push();
1181         GPU_matrix_scale_2f(1.0f, unit_scale);
1182         GPU_matrix_translate_2f(0.0f, offset);
1183
1184         /* set this once and for all - all handles and handle-verts should use the same thickness */
1185         GPU_line_width(1.0);
1186
1187         if (fcu->bezt) {
1188           bool do_handles = draw_fcurve_handles_check(sipo, fcu);
1189
1190           if (do_handles) {
1191             /* only draw handles/vertices on keyframes */
1192             GPU_blend(true);
1193             draw_fcurve_handles(sipo, fcu);
1194             GPU_blend(false);
1195           }
1196
1197           draw_fcurve_vertices(ar, fcu, do_handles, (sipo->flag & SIPO_SELVHANDLESONLY));
1198         }
1199         else {
1200           /* samples: only draw two indicators at either end as indicators */
1201           draw_fcurve_samples(sipo, ar, fcu);
1202         }
1203
1204         GPU_matrix_pop();
1205       }
1206     }
1207
1208     /* 3) draw driver debugging stuff */
1209     if ((ac->datatype == ANIMCONT_DRIVERS) && (fcu->flag & FCURVE_ACTIVE)) {
1210       graph_draw_driver_debug(ac, ale->id, fcu);
1211     }
1212
1213     /* undo mapping of keyframes for drawing if scaled F-Curve */
1214     if (adt) {
1215       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
1216     }
1217   }
1218
1219   /* free list of curves */
1220   ANIM_animdata_freelist(&anim_data);
1221 }
1222
1223 /* ************************************************************************* */
1224 /* Channel List */
1225
1226 /* left hand part */
1227 void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
1228 {
1229   ListBase anim_data = {NULL, NULL};
1230   bAnimListElem *ale;
1231   int filter;
1232
1233   View2D *v2d = &ar->v2d;
1234   float y = 0.0f, height;
1235   size_t items;
1236   int i = 0;
1237
1238   /* build list of channels to draw */
1239   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
1240   items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1241
1242   /* Update max-extent of channels here (taking into account scrollers):
1243    * - this is done to allow the channel list to be scrollable, but must be done here
1244    *   to avoid regenerating the list again and/or also because channels list is drawn first
1245    * - offset of ACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for
1246    *   start of list offset, and the second is as a correction for the scrollers.
1247    */
1248   height = (float)((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac) * 2));
1249   UI_view2d_totRect_set(v2d, BLI_rcti_size_x(&ar->v2d.mask), height);
1250
1251   /* loop through channels, and set up drawing depending on their type  */
1252   { /* first pass: just the standard GL-drawing for backdrop + text */
1253     size_t channel_index = 0;
1254
1255     y = (float)ACHANNEL_FIRST(ac);
1256
1257     for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
1258       const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
1259       const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
1260
1261       /* check if visible */
1262       if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
1263           IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax)) {
1264         /* draw all channels using standard channel-drawing API */
1265         ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index);
1266       }
1267
1268       /* adjust y-position for next one */
1269       y -= ACHANNEL_STEP(ac);
1270       channel_index++;
1271     }
1272   }
1273   { /* second pass: widgets */
1274     uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1275     size_t channel_index = 0;
1276
1277     y = (float)ACHANNEL_FIRST(ac);
1278
1279     /* set blending again, as may not be set in previous step */
1280     GPU_blend_set_func_separate(
1281         GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
1282     GPU_blend(true);
1283
1284     for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
1285       const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
1286       const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
1287
1288       /* check if visible */
1289       if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
1290           IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax)) {
1291         /* draw all channels using standard channel-drawing API */
1292         rctf channel_rect;
1293         BLI_rctf_init(&channel_rect, 0, v2d->cur.xmax - V2D_SCROLL_WIDTH, yminc, ymaxc);
1294         ANIM_channel_draw_widgets(C, ac, ale, block, &channel_rect, channel_index);
1295       }
1296
1297       /* adjust y-position for next one */
1298       y -= ACHANNEL_STEP(ac);
1299       channel_index++;
1300     }
1301
1302     UI_block_end(C, block);
1303     UI_block_draw(C, block);
1304
1305     GPU_blend(false);
1306   }
1307
1308   /* free tempolary channels */
1309   ANIM_animdata_freelist(&anim_data);
1310 }