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