Blender V4.5
editaction_gpencil.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cmath>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_listbase.h"
17#include "BLI_string.h"
18#include "BLI_utildefines.h"
19
21#include "DNA_scene_types.h"
22
23#include "BKE_gpencil_legacy.h"
24
25#include "ED_anim_api.hh"
26#include "ED_gpencil_legacy.hh"
27#include "ED_keyframes_edit.hh"
28#include "ED_markers.hh"
29
30#include "WM_api.hh"
31
32#include "DEG_depsgraph.hh"
33
34/* ***************************************** */
35/* NOTE ABOUT THIS FILE:
36 * This file contains code for editing Grease Pencil data in the Action Editor
37 * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
38 * Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
39 */
40/* ***************************************** */
41/* Generics - Loopers */
42
44 Scene *scene,
45 bool (*gpf_cb)(bGPDframe *, Scene *))
46{
47 /* error checker */
48 if (gpl == nullptr) {
49 return false;
50 }
51
52 /* do loop */
53 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
54 /* execute callback */
55 if (gpf_cb(gpf, scene)) {
56 return true;
57 }
58 }
59
60 /* nothing to return */
61 return false;
62}
63
64/* ****************************************** */
65/* Data Conversion Tools */
66
67void ED_gpencil_layer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
68{
69 CfraElem *ce;
70
71 /* error checking */
72 if (ELEM(nullptr, gpl, elems)) {
73 return;
74 }
75
76 /* loop through gp-frames, adding */
77 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
78 if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
79 ce = MEM_callocN<CfraElem>("CfraElem");
80
81 ce->cfra = float(gpf->framenum);
82 ce->sel = (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
83
84 BLI_addtail(elems, ce);
85 }
86 }
87}
88
89/* ***************************************** */
90/* Selection Tools */
91
93{
94 /* error checking */
95 if (gpl == nullptr) {
96 return false;
97 }
98
99 /* stop at the first one found */
100 LISTBASE_FOREACH (const bGPDframe *, gpf, &gpl->frames) {
101 if (gpf->flag & GP_FRAME_SELECT) {
102 return true;
103 }
104 }
105
106 /* not found */
107 return false;
108}
109
110/* Helper function: select GP-frame based on SELECT_* mode. */
111static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
112{
113 if (gpf == nullptr) {
114 return;
115 }
116
117 switch (select_mode) {
118 case SELECT_ADD:
119 gpf->flag |= GP_FRAME_SELECT;
120 break;
121 case SELECT_SUBTRACT:
122 gpf->flag &= ~GP_FRAME_SELECT;
123 break;
124 case SELECT_INVERT:
125 gpf->flag ^= GP_FRAME_SELECT;
126 break;
127 }
128}
129
130void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
131{
132 /* error checking */
133 if (gpl == nullptr) {
134 return;
135 }
136
137 /* handle according to mode */
138 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
139 gpencil_frame_select(gpf, select_mode);
140 }
141}
142
144{
145 /* error checking */
146 if (gpl == nullptr) {
147 return;
148 }
149
150 /* now call the standard function */
151 ED_gpencil_select_frames(gpl, mode);
152}
153
154void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
155{
156 bGPDframe *gpf;
157
158 if (gpl == nullptr) {
159 return;
160 }
161
162 gpf = BKE_gpencil_layer_frame_find(gpl, selx);
163
164 if (gpf) {
165 gpencil_frame_select(gpf, select_mode);
166 }
167}
168
169void ED_gpencil_layer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
170{
171 if (gpl == nullptr) {
172 return;
173 }
174
175 /* only select those frames which are in bounds */
176 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
177 if (IN_RANGE(gpf->framenum, min, max)) {
178 gpencil_frame_select(gpf, select_mode);
179 }
180 }
181}
182
184 bGPDlayer *gpl,
185 short tool,
186 short select_mode)
187{
188 if (gpl == nullptr) {
189 return;
190 }
191
192 /* only select frames which are within the region */
193 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
194 /* construct a dummy point coordinate to do this testing with */
195 float pt[2] = {0};
196
197 pt[0] = gpf->framenum;
198 pt[1] = ked->channel_y;
199
200 /* check the necessary regions */
201 if (tool == BEZT_OK_CHANNEL_LASSO) {
202 /* Lasso */
203 if (keyframe_region_lasso_test(static_cast<const KeyframeEdit_LassoData *>(ked->data), pt)) {
204 gpencil_frame_select(gpf, select_mode);
205 }
206 }
207 else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
208 /* Circle */
209 if (keyframe_region_circle_test(static_cast<const KeyframeEdit_CircleData *>(ked->data), pt))
210 {
211 gpencil_frame_select(gpf, select_mode);
212 }
213 }
214 }
215}
216
218{
219 gpl->flag |= GP_LAYER_SELECT;
220
221 /* Update other layer status. */
222 if (BKE_gpencil_layer_active_get(gpd) != gpl) {
226 }
227}
228
229/* ***************************************** */
230/* Frame Editing Tools */
231
233{
234 bool changed = false;
235
236 /* error checking */
237 if (gpl == nullptr) {
238 return false;
239 }
240
241 /* check for frames to delete */
243 if (gpf->flag & GP_FRAME_SELECT) {
245 changed = true;
246 }
247 }
248
249 return changed;
250}
251
253{
254 /* error checking */
255 if (gpl == nullptr) {
256 return;
257 }
258
259 /* Duplicate selected frames. */
261
262 /* duplicate this frame */
263 if (gpf->flag & GP_FRAME_SELECT) {
264 bGPDframe *gpfd;
265
266 /* duplicate frame, and deselect self */
267 gpfd = BKE_gpencil_frame_duplicate(gpf, true);
268 gpf->flag &= ~GP_FRAME_SELECT;
269
270 BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
271 }
272 }
273}
274
276{
277 if (gpl == nullptr) {
278 return;
279 }
280
281 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
282 if (gpf->flag & GP_FRAME_SELECT) {
283 gpf->key_type = type;
284 }
285 }
286}
287
288/* -------------------------------------- */
289/* Copy and Paste Tools:
290 * - The copy/paste buffer currently stores a set of GP_Layers, with temporary
291 * GP_Frames with the necessary strokes
292 * - Unless there is only one element in the buffer,
293 * names are also tested to check for compatibility.
294 * - All pasted frames are offset by the same amount.
295 * This is calculated as the difference in the times of the current frame and the
296 * 'first keyframe' (i.e. the earliest one in all channels).
297 * - The earliest frame is calculated per copy operation.
298 */
299
300/* globals for copy/paste data (like for other copy/paste buffers) */
301static ListBase gpencil_anim_copybuf = {nullptr, nullptr};
302static int gpencil_anim_copy_firstframe = 999999999;
303static int gpencil_anim_copy_lastframe = -999999999;
305
315
317{
318 ListBase anim_data = {nullptr, nullptr};
319 int filter;
320
321 Scene *scene = ac->scene;
322
323 /* clear buffer first */
325
326 /* filter data */
329 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
330
331 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
332 /* This function only deals with grease pencil layer frames.
333 * This check is needed in the case of a call from the main dope-sheet. */
334 if (ale->type != ANIMTYPE_GPLAYER) {
335 continue;
336 }
337
338 ListBase copied_frames = {nullptr, nullptr};
339 bGPDlayer *gpl = (bGPDlayer *)ale->data;
340
341 /* loop over frames, and copy only selected frames */
342 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
343 /* if frame is selected, make duplicate it and its strokes */
344 if (gpf->flag & GP_FRAME_SELECT) {
345 /* make a copy of this frame */
346 bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf, true);
347 BLI_addtail(&copied_frames, new_frame);
348
349 /* extend extents for keyframes encountered */
352 }
353 }
354
355 /* create a new layer in buffer if there were keyframes here */
356 if (BLI_listbase_is_empty(&copied_frames) == false) {
357 bGPDlayer *new_layer = MEM_callocN<bGPDlayer>("GPCopyPasteLayer");
359
360 /* move over copied frames */
361 BLI_movelisttolist(&new_layer->frames, &copied_frames);
362 BLI_assert(copied_frames.first == nullptr);
363
364 /* make a copy of the layer's name - for name-based matching later... */
365 STRNCPY(new_layer->info, gpl->info);
366 }
367 }
368
369 /* in case 'relative' paste method is used */
371
372 /* clean up */
373 ANIM_animdata_freelist(&anim_data);
374
375 /* report success */
377}
378
379bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
380{
381 ListBase anim_data = {nullptr, nullptr};
382 int filter;
383
384 Scene *scene = ac->scene;
385 bool no_name = false;
386 int offset = 0;
387
388 /* check if buffer is empty */
390 return false;
391 }
392
393 /* Check if single channel in buffer (disregard names if so). */
394 if (gpencil_anim_copybuf.first == gpencil_anim_copybuf.last) {
395 no_name = true;
396 }
397
398 /* methods of offset (eKeyPasteOffset) */
399 switch (offset_mode) {
401 offset = (scene->r.cfra - gpencil_anim_copy_firstframe);
402 break;
404 offset = (scene->r.cfra - gpencil_anim_copy_lastframe);
405 break;
407 offset = (scene->r.cfra - gpencil_anim_copy_cfra);
408 break;
410 offset = 0;
411 break;
412 }
413
414 /* filter data */
415 /* TODO: try doing it with selection, then without selection limits. */
419 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
420
421 /* from selected channels */
422 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
423 /* Only deal with GPlayers (case of calls from general dope-sheet). */
424 if (ale->type != ANIMTYPE_GPLAYER) {
425 continue;
426 }
427
428 bGPDlayer *gpld = (bGPDlayer *)ale->data;
429 bGPDlayer *gpls = nullptr;
430 bGPDframe *gpf;
431
432 /* find suitable layer from buffer to use to paste from */
433 for (gpls = static_cast<bGPDlayer *>(gpencil_anim_copybuf.first); gpls; gpls = gpls->next) {
434 /* check if layer name matches */
435 if ((no_name) || STREQ(gpls->info, gpld->info)) {
436 break;
437 }
438 }
439
440 /* this situation might occur! */
441 if (gpls == nullptr) {
442 continue;
443 }
444
445 /* add frames from buffer */
446 LISTBASE_FOREACH (bGPDframe *, gpfs, &gpls->frames) {
447 /* temporarily apply offset to buffer-frame while copying */
448 gpfs->framenum += offset;
449
450 /* get frame to copy data into (if no frame returned, then just ignore) */
451 gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
452 if (gpf) {
453 /* Ensure to use same keyframe type. */
454 gpf->key_type = gpfs->key_type;
455
456 /* This should be the right frame... as it may be a pre-existing frame,
457 * must make sure that only compatible stroke types get copied over
458 * - We cannot just add a duplicate frame, as that would cause errors
459 * - For now, we don't check if the types will be compatible since we
460 * don't have enough info to do so. Instead, we simply just paste,
461 * if it works, it will show up.
462 */
463 LISTBASE_FOREACH (bGPDstroke *, gps, &gpfs->strokes) {
464 /* make a copy of stroke, then of its points array */
465 bGPDstroke *gpsn = BKE_gpencil_stroke_duplicate(gps, true, true);
466
467 /* append stroke to frame */
468 BLI_addtail(&gpf->strokes, gpsn);
469 }
470
471 /* if no strokes (i.e. new frame) added, free gpf */
472 if (BLI_listbase_is_empty(&gpf->strokes)) {
474 }
475 }
476
477 /* unapply offset from buffer-frame */
478 gpfs->framenum -= offset;
479 }
480
481 /* Tag destination datablock. */
483 }
484
485 /* clean up */
486 ANIM_animdata_freelist(&anim_data);
487 return true;
488}
489
490/* -------------------------------------- */
491/* Snap Tools */
492
493static bool gpencil_frame_snap_nearest(bGPDframe * /*gpf*/, Scene * /*scene*/)
494{
495#if 0 /* NOTE: gpf->framenum is already an int! */
496 if (gpf->flag & GP_FRAME_SELECT) {
497 gpf->framenum = int(floor(gpf->framenum + 0.5));
498 }
499#endif
500 return false;
501}
502
504{
505 float secf = float(FPS);
506 if (gpf->flag & GP_FRAME_SELECT) {
507 gpf->framenum = int(floorf(gpf->framenum / secf + 0.5f) * secf);
508 }
509 return false;
510}
511
513{
514 if (gpf->flag & GP_FRAME_SELECT) {
515 gpf->framenum = scene->r.cfra;
516 }
517 return false;
518}
519
521{
522 if (gpf->flag & GP_FRAME_SELECT) {
524 }
525 return false;
526}
527
528void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
529{
530 switch (mode) {
531 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
533 break;
534 case SNAP_KEYS_CURFRAME: /* snap to current frame */
536 break;
537 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
539 break;
540 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
542 break;
543 default: /* just in case */
544 break;
545 }
546}
547
548/* -------------------------------------- */
549/* Mirror Tools */
550
552{
553 int diff;
554
555 if (gpf->flag & GP_FRAME_SELECT) {
556 diff = scene->r.cfra - gpf->framenum;
557 gpf->framenum = scene->r.cfra + diff;
558 }
559
560 return false;
561}
562
563static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene * /*scene*/)
564{
565 int diff;
566
567 if (gpf->flag & GP_FRAME_SELECT) {
568 diff = -gpf->framenum;
569 gpf->framenum = diff;
570 }
571
572 return false;
573}
574
575static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene * /*scene*/)
576{
577 int diff;
578
579 /* NOTE: since we can't really do this, we just do the same as for yaxis... */
580 if (gpf->flag & GP_FRAME_SELECT) {
581 diff = -gpf->framenum;
582 gpf->framenum = diff;
583 }
584
585 return false;
586}
587
589{
590 static TimeMarker *marker;
591 static short initialized = 0;
592 int diff;
593
594 /* In order for this mirror function to work without
595 * any extra arguments being added, we use the case
596 * of gpf==nullptr to denote that we should find the
597 * marker to mirror over. The static pointer is safe
598 * to use this way, as it will be set to null after
599 * each cycle in which this is called.
600 */
601
602 if (gpf != nullptr) {
603 /* mirroring time */
604 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
605 diff = (marker->frame - gpf->framenum);
606 gpf->framenum = (marker->frame + diff);
607 }
608 }
609 else {
610 /* initialization time */
611 if (initialized) {
612 /* reset everything for safety */
613 marker = nullptr;
614 initialized = 0;
615 }
616 else {
617 /* try to find a marker */
618 marker = ED_markers_get_first_selected(&scene->markers);
619 if (marker) {
620 initialized = 1;
621 }
622 }
623 }
624
625 return false;
626}
627
628void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
629{
630 switch (mode) {
631 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
633 break;
634 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
636 break;
637 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
639 break;
640 case MIRROR_KEYS_MARKER: /* mirror over marker */
641 gpencil_frame_mirror_marker(nullptr, scene);
643 gpencil_frame_mirror_marker(nullptr, scene);
644 break;
645 default: /* just in case */
647 break;
648 }
649}
650
651/* ***************************************** */
void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active)
bool BKE_gpencil_layer_frame_delete(struct bGPDlayer *gpl, struct bGPDframe *gpf)
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
void BKE_gpencil_free_layers(struct ListBase *list)
struct bGPDframe * BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src, bool dup_strokes)
@ GP_GETFRAME_ADD_NEW
void BKE_gpencil_layer_autolock_set(struct bGPdata *gpd, bool unlock)
struct bGPDframe * BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe)
#define BLI_assert(a)
Definition BLI_assert.h:46
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define IN_RANGE(a, b, c)
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:962
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
#define FPS
@ ANIMTYPE_GPLAYER
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_SEL
@ MIRROR_KEYS_YAXIS
@ MIRROR_KEYS_MARKER
@ MIRROR_KEYS_CURFRAME
@ MIRROR_KEYS_XAXIS
@ KEYFRAME_PASTE_OFFSET_NONE
@ KEYFRAME_PASTE_OFFSET_CFRA_END
@ KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE
@ KEYFRAME_PASTE_OFFSET_CFRA_START
@ BEZT_OK_CHANNEL_CIRCLE
@ BEZT_OK_CHANNEL_LASSO
@ SNAP_KEYS_CURFRAME
@ SNAP_KEYS_NEARFRAME
@ SNAP_KEYS_NEARMARKER
@ SNAP_KEYS_NEARSEC
@ SELECT_INVERT
@ SELECT_SUBTRACT
@ SELECT_ADD
Read Guarded memory(de)allocation.
#define ND_DATA
Definition WM_types.hh:506
#define NA_EDITED
Definition WM_types.hh:581
#define NC_GPENCIL
Definition WM_types.hh:396
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
TimeMarker * ED_markers_get_first_selected(ListBase *markers)
#define floorf(x)
static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene *)
static bool gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *scene)
static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene *)
static bool gpencil_frame_mirror_marker(bGPDframe *gpf, Scene *scene)
static bool gpencil_frame_snap_nearmarker(bGPDframe *gpf, Scene *scene)
void ED_gpencil_layer_frame_select_set(bGPDlayer *gpl, short mode)
void ED_gpencil_layer_frames_keytype_set(bGPDlayer *gpl, short type)
void ED_gpencil_layer_frames_duplicate(bGPDlayer *gpl)
static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
void ED_gpencil_set_active_channel(bGPdata *gpd, bGPDlayer *gpl)
bool ED_gpencil_layer_frames_delete(bGPDlayer *gpl)
bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
static bool gpencil_frame_snap_nearest(bGPDframe *, Scene *)
bool ED_gpencil_layer_frame_select_check(const bGPDlayer *gpl)
void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
static int gpencil_anim_copy_lastframe
void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
static int gpencil_anim_copy_cfra
bool ED_gpencil_layer_frames_looper(bGPDlayer *gpl, Scene *scene, bool(*gpf_cb)(bGPDframe *, Scene *))
void ED_gpencil_layer_frames_select_region(KeyframeEditData *ked, bGPDlayer *gpl, short tool, short select_mode)
static bool gpencil_frame_mirror_cframe(bGPDframe *gpf, Scene *scene)
void ED_gpencil_layer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
void ED_gpencil_layer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
void ED_gpencil_anim_copybuf_free()
static int gpencil_anim_copy_firstframe
bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
static bool gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene)
static ListBase gpencil_anim_copybuf
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
#define floor
#define filter
static bool initialized
bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const float xy[2])
bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, const float xy[2])
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
#define min(a, b)
Definition sort.cc:36
void * first
struct RenderData r
ListBase markers
eAnimCont_Types datatype
struct bGPDlayer * next
max
Definition text_draw.cc:251
void WM_main_add_notifier(uint type, void *reference)