Blender V4.3
graph_view.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_listbase.h"
14#include "BLI_rect.h"
15
16#include "DNA_anim_types.h"
17#include "DNA_scene_types.h"
18#include "DNA_space_types.h"
19
20#include "RNA_access.hh"
21#include "RNA_define.hh"
22
23#include "BKE_context.hh"
24#include "BKE_fcurve.hh"
25#include "BKE_nla.hh"
26
27#include "UI_view2d.hh"
28
29#include "ED_anim_api.hh"
30#include "ED_markers.hh"
31#include "ED_screen.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#include "graph_intern.hh"
37
38/* -------------------------------------------------------------------- */
43 float *xmin,
44 float *xmax,
45 float *ymin,
46 float *ymax,
47 const bool do_sel_only,
48 const bool include_handles)
49{
50 Scene *scene = ac->scene;
51
52 ListBase anim_data = {nullptr, nullptr};
53 int filter;
54
55 /* Get data to filter, from Dopesheet. */
58 if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
59 filter |= ANIMFILTER_SEL;
60 }
61
63 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
64
65 /* Set large values initial values that will be easy to override. */
66 if (xmin) {
67 *xmin = 999999999.0f;
68 }
69 if (xmax) {
70 *xmax = -999999999.0f;
71 }
72 if (ymin) {
73 *ymin = 999999999.0f;
74 }
75 if (ymax) {
76 *ymax = -999999999.0f;
77 }
78
79 /* Check if any channels to set range with. */
80 if (anim_data.first) {
81 bool foundBounds = false;
82
83 /* Go through channels, finding max extents. */
84 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
85 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
86 FCurve *fcu = (FCurve *)ale->key_data;
88 float unitFac, offset;
89
90 /* Get range. */
91 if (BKE_fcurve_calc_bounds(fcu, do_sel_only, include_handles, nullptr, &bounds)) {
92 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
93
94 /* Apply NLA scaling. */
95 if (adt) {
98 }
99
100 /* Apply unit corrections. */
101 unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset);
102 bounds.ymin += offset;
103 bounds.ymax += offset;
104 bounds.ymin *= unitFac;
105 bounds.ymax *= unitFac;
106
107 /* Try to set cur using these values, if they're more extreme than previously set values.
108 */
109 if ((xmin) && (bounds.xmin < *xmin)) {
110 *xmin = bounds.xmin;
111 }
112 if ((xmax) && (bounds.xmax > *xmax)) {
113 *xmax = bounds.xmax;
114 }
115 if ((ymin) && (bounds.ymin < *ymin)) {
116 *ymin = bounds.ymin;
117 }
118 if ((ymax) && (bounds.ymax > *ymax)) {
119 *ymax = bounds.ymax;
120 }
121
122 foundBounds = true;
123 }
124 }
125
126 /* Ensure that the extents are not too extreme that view implodes. */
127 if (foundBounds) {
128 if ((xmin && xmax) && (fabsf(*xmax - *xmin) < 0.001f)) {
129 *xmin -= 0.0005f;
130 *xmax += 0.0005f;
131 }
132 if ((ymin && ymax) && (fabsf(*ymax - *ymin) < 0.001f)) {
133 *ymin -= 0.0005f;
134 *ymax += 0.0005f;
135 }
136 }
137 else {
138 if (xmin) {
139 *xmin = float(PSFRA);
140 }
141 if (xmax) {
142 *xmax = float(PEFRA);
143 }
144 if (ymin) {
145 *ymin = -5;
146 }
147 if (ymax) {
148 *ymax = 5;
149 }
150 }
151
152 /* Free memory. */
153 ANIM_animdata_freelist(&anim_data);
154 }
155 else {
156 /* Set default range. */
157 if (ac->scene) {
158 if (xmin) {
159 *xmin = float(PSFRA);
160 }
161 if (xmax) {
162 *xmax = float(PEFRA);
163 }
164 }
165 else {
166 if (xmin) {
167 *xmin = -5;
168 }
169 if (xmax) {
170 *xmax = 100;
171 }
172 }
173
174 if (ymin) {
175 *ymin = -5;
176 }
177 if (ymax) {
178 *ymax = 5;
179 }
180 }
181}
182
185/* -------------------------------------------------------------------- */
190{
191 bAnimContext ac;
192 Scene *scene;
193 float min, max;
194
195 /* Get editor data. */
196 if (ANIM_animdata_get_context(C, &ac) == 0) {
197 return OPERATOR_CANCELLED;
198 }
199 if (ac.scene == nullptr) {
200 return OPERATOR_CANCELLED;
201 }
202
203 scene = ac.scene;
204
205 /* Set the range directly. */
206 get_graph_keyframe_extents(&ac, &min, &max, nullptr, nullptr, true, false);
207 scene->r.flag |= SCER_PRV_RANGE;
208 scene->r.psfra = round_fl_to_int(min);
209 scene->r.pefra = round_fl_to_int(max);
210
211 /* Set notifier that things have changed. */
212 /* XXX: Err... there's nothing for frame ranges yet, but this should do fine too. */
214
215 return OPERATOR_FINISHED;
216}
217
219{
220 /* Identifiers */
221 ot->name = "Set Preview Range to Selected";
222 ot->idname = "GRAPH_OT_previewrange_set";
223 ot->description = "Set Preview Range based on range of selected keyframes";
224
225 /* API callbacks */
227 /* XXX: unchecked poll to get F-samples working too, but makes modifier damage trickier. */
229
230 /* Flags */
232}
233
236/* -------------------------------------------------------------------- */
241 const bool do_sel_only,
242 const bool include_handles,
243 const int smooth_viewtx)
244{
245 bAnimContext ac;
246 rctf cur_new;
247
248 /* Get editor data. */
249 if (ANIM_animdata_get_context(C, &ac) == 0) {
250 return OPERATOR_CANCELLED;
251 }
252
253 /* Set the horizontal range, with an extra offset so that the extreme keys will be in view. */
255 &cur_new.xmin,
256 &cur_new.xmax,
257 &cur_new.ymin,
258 &cur_new.ymax,
259 do_sel_only,
260 include_handles);
261
262 /* Give some more space at the borders. */
263 cur_new = ANIM_frame_range_view2d_add_xmargin(ac.region->v2d, cur_new);
264 BLI_rctf_resize_y(&cur_new, 1.1f * BLI_rctf_size_y(&cur_new));
265
266 /* Take regions into account, that could block the view.
267 * Marker region is supposed to be larger than the scroll-bar, so prioritize it. */
268 float pad_top = UI_TIME_SCRUB_MARGIN_Y;
271 BLI_rctf_pad_y(&cur_new, ac.region->winy, pad_bottom, pad_top);
272
273 UI_view2d_smooth_view(C, ac.region, &cur_new, smooth_viewtx);
274 return OPERATOR_FINISHED;
275}
276
277/* ......... */
278
280{
281 const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
282 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
283
284 /* Whole range */
285 return graphkeys_viewall(C, false, include_handles, smooth_viewtx);
286}
287
289{
290 const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
291 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
292
293 /* Only selected. */
294 return graphkeys_viewall(C, true, include_handles, smooth_viewtx);
295}
296
297/* ......... */
298
300{
301 /* Identifiers */
302 ot->name = "Frame All";
303 ot->idname = "GRAPH_OT_view_all";
304 ot->description = "Reset viewable area to show full keyframe range";
305
306 /* API callbacks */
308 /* XXX: Unchecked poll to get F-samples working too, but makes modifier damage trickier. */
310
311 /* Flags */
312 ot->flag = 0;
313
314 /* Props */
316 "include_handles",
317 true,
318 "Include Handles",
319 "Include handles of keyframes when calculating extents");
320}
321
323{
324 /* Identifiers */
325 ot->name = "Frame Selected";
326 ot->idname = "GRAPH_OT_view_selected";
327 ot->description = "Reset viewable area to show selected keyframe range";
328
329 /* API callbacks */
331 /* XXX: Unchecked poll to get F-samples working too, but makes modifier damage trickier. */
333
334 /* Flags */
335 ot->flag = 0;
336
337 /* Props */
339 "include_handles",
340 true,
341 "Include Handles",
342 "Include handles of keyframes when calculating extents");
343}
344
347/* -------------------------------------------------------------------- */
352{
353 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
354 ANIM_center_frame(C, smooth_viewtx);
355 return OPERATOR_FINISHED;
356}
357
359{
360 /* Identifiers */
361 ot->name = "Go to Current Frame";
362 ot->idname = "GRAPH_OT_view_frame";
363 ot->description = "Move the view to the current frame";
364
365 /* API callbacks */
368
369 /* Flags */
370 ot->flag = 0;
371}
372
375/* -------------------------------------------------------------------- */
382/* Bake each F-Curve into a set of samples, and store as a ghost curve. */
383static void create_ghost_curves(bAnimContext *ac, int start, int end)
384{
385 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
386 ListBase anim_data = {nullptr, nullptr};
387 int filter;
388
389 /* Free existing ghost curves. */
391
392 /* Sanity check. */
393 if (start >= end) {
394 printf("Error: Frame range for Ghost F-Curve creation is inappropriate\n");
395 return;
396 }
397
398 /* Filter data. */
402 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
403
404 /* Loop through filtered data and add keys between selected keyframes on every frame. */
405 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
406 FCurve *fcu = (FCurve *)ale->key_data;
407 FCurve *gcu = BKE_fcurve_create();
408 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
409 ChannelDriver *driver = fcu->driver;
410 FPoint *fpt;
411 float unitFac, offset;
412 int cfra;
413 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
414
415 /* Disable driver so that it don't muck up the sampling process. */
416 fcu->driver = nullptr;
417
418 /* Calculate unit-mapping factor. */
419 unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset);
420
421 /* Create samples, but store them in a new curve
422 * - we cannot use fcurve_store_samples() as that will only overwrite the original curve.
423 */
424 gcu->fpt = fpt = static_cast<FPoint *>(
425 MEM_callocN(sizeof(FPoint) * (end - start + 1), "Ghost FPoint Samples"));
426 gcu->totvert = end - start + 1;
427
428 /* Use the sampling callback at 1-frame intervals from start to end frames. */
429 for (cfra = start; cfra <= end; cfra++, fpt++) {
430 float cfrae = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
431
432 fpt->vec[0] = cfrae;
433 fpt->vec[1] = (fcurve_samplingcb_evalcurve(fcu, nullptr, cfrae) + offset) * unitFac;
434 }
435
436 /* Set color of ghost curve
437 * - make the color slightly darker.
438 */
439 gcu->color[0] = fcu->color[0] - 0.07f;
440 gcu->color[1] = fcu->color[1] - 0.07f;
441 gcu->color[2] = fcu->color[2] - 0.07f;
442
443 /* Store new ghost curve. */
444 BLI_addtail(&sipo->runtime.ghost_curves, gcu);
445
446 /* Restore driver. */
447 fcu->driver = driver;
448 }
449
450 /* Admin and redraws. */
451 ANIM_animdata_freelist(&anim_data);
452}
453
454/* ------------------- */
455
457{
458 bAnimContext ac;
459 View2D *v2d;
460 int start, end;
461
462 /* Get editor data. */
463 if (ANIM_animdata_get_context(C, &ac) == 0) {
464 return OPERATOR_CANCELLED;
465 }
466
467 /* Ghost curves are snapshots of the visible portions of the curves,
468 * so set range to be the visible range. */
469 v2d = &ac.region->v2d;
470 start = int(v2d->cur.xmin);
471 end = int(v2d->cur.xmax);
472
473 /* Bake selected curves into a ghost curve. */
474 create_ghost_curves(&ac, start, end);
475
476 /* Update this editor only. */
478
479 return OPERATOR_FINISHED;
480}
481
483{
484 /* Identifiers */
485 ot->name = "Create Ghost Curves";
486 ot->idname = "GRAPH_OT_ghost_curves_create";
487 ot->description =
488 "Create snapshot (Ghosts) of selected F-Curves as background aid for active Graph Editor";
489
490 /* API callbacks */
493
494 /* Flags */
496
497 /* TODO: add props for start/end frames */
498}
499
502/* -------------------------------------------------------------------- */
509{
510 bAnimContext ac;
511 SpaceGraph *sipo;
512
513 /* Get editor data. */
514 if (ANIM_animdata_get_context(C, &ac) == 0) {
515 return OPERATOR_CANCELLED;
516 }
517 sipo = (SpaceGraph *)ac.sl;
518
519 /* If no ghost curves, don't do anything. */
521 return OPERATOR_CANCELLED;
522 }
523 /* Free ghost curves. */
525
526 /* Update this editor only. */
528
529 return OPERATOR_FINISHED;
530}
531
533{
534 /* Identifiers */
535 ot->name = "Clear Ghost Curves";
536 ot->idname = "GRAPH_OT_ghost_curves_clear";
537 ot->description = "Clear F-Curve snapshots (Ghosts) for active Graph Editor";
538
539 /* API callbacks */
542
543 /* Flags */
545}
546
ScrArea * CTX_wm_area(const bContext *C)
FCurve * BKE_fcurve_create(void)
void BKE_fcurves_free(ListBase *list)
float fcurve_samplingcb_evalcurve(FCurve *fcu, void *data, float evaltime)
bool BKE_fcurve_calc_bounds(const FCurve *fcu, bool selected_keys_only, bool include_handles, const float frame_range[2], rctf *r_bounds)
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)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
MINLINE int round_fl_to_int(float a)
void BLI_rctf_resize_y(struct rctf *rect, float y)
Definition rct.c:645
void BLI_rctf_pad_y(struct rctf *rect, float boundary_size, float pad_min, float pad_max)
Definition rct.c:683
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
@ SCER_PRV_RANGE
#define PSFRA
#define PEFRA
@ USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_FCURVESONLY
@ ANIMFILTER_SEL
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:708
bool ED_operator_graphedit_active(bContext *C)
Read Guarded memory(de)allocation.
void UI_view2d_smooth_view(const bContext *C, ARegion *region, const rctf *cur, int smooth_viewtx)
#define UI_MARKER_MARGIN_Y
Definition UI_view2d.hh:471
#define V2D_SCROLL_HANDLE_HEIGHT
Definition UI_view2d.hh:67
#define UI_TIME_SCRUB_MARGIN_Y
Definition UI_view2d.hh:472
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_SCENE
Definition WM_types.hh:345
#define ND_FRAME
Definition WM_types.hh:401
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_center_frame(bContext *C, int smooth_viewtx)
Definition anim_draw.cc:663
rctf ANIM_frame_range_view2d_add_xmargin(const View2D &view_2d, const rctf view_rect)
Definition anim_draw.cc:706
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
Definition anim_draw.cc:529
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
ListBase * ED_context_get_markers(const bContext *C)
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
unsigned int U
Definition btGjkEpa3.h:78
#define printf
#define fabsf(x)
draw_view in_light_buf[] float
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
bool graphop_visible_keyframes_poll(bContext *C)
static int graphkeys_previewrange_exec(bContext *C, wmOperator *)
static int graphkeys_view_selected_exec(bContext *C, wmOperator *op)
static int graphkeys_view_frame_exec(bContext *C, wmOperator *op)
static int graphkeys_clear_ghostcurves_exec(bContext *C, wmOperator *)
void GRAPH_OT_ghost_curves_create(wmOperatorType *ot)
void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, float *ymin, float *ymax, const bool do_sel_only, const bool include_handles)
Definition graph_view.cc:42
void GRAPH_OT_view_all(wmOperatorType *ot)
void GRAPH_OT_view_frame(wmOperatorType *ot)
void GRAPH_OT_view_selected(wmOperatorType *ot)
void GRAPH_OT_previewrange_set(wmOperatorType *ot)
void GRAPH_OT_ghost_curves_clear(wmOperatorType *ot)
static int graphkeys_viewall_exec(bContext *C, wmOperator *op)
static int graphkeys_create_ghostcurves_exec(bContext *C, wmOperator *)
static void create_ghost_curves(bAnimContext *ac, int start, int end)
static int graphkeys_viewall(bContext *C, const bool do_sel_only, const bool include_handles, const int smooth_viewtx)
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
#define min(a, b)
Definition sort.c:32
FPoint * fpt
ChannelDriver * driver
float color[3]
unsigned int totvert
float vec[2]
void * first
SpaceGraph_Runtime runtime
SpaceLink * sl
eAnimCont_Types datatype
ARegion * region
float xmax
float xmin
float ymax
float ymin
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_operator_smooth_viewtx_get(const wmOperator *op)