Blender V5.0
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_utf8.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) {
225 }
226}
227
228/* ***************************************** */
229/* Frame Editing Tools */
230
232{
233 bool changed = false;
234
235 /* error checking */
236 if (gpl == nullptr) {
237 return false;
238 }
239
240 /* check for frames to delete */
242 if (gpf->flag & GP_FRAME_SELECT) {
244 changed = true;
245 }
246 }
247
248 return changed;
249}
250
252{
253 /* error checking */
254 if (gpl == nullptr) {
255 return;
256 }
257
258 /* Duplicate selected frames. */
260
261 /* duplicate this frame */
262 if (gpf->flag & GP_FRAME_SELECT) {
263 bGPDframe *gpfd;
264
265 /* duplicate frame, and deselect self */
266 gpfd = BKE_gpencil_frame_duplicate(gpf, true);
267 gpf->flag &= ~GP_FRAME_SELECT;
268
269 BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
270 }
271 }
272}
273
275{
276 if (gpl == nullptr) {
277 return;
278 }
279
280 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
281 if (gpf->flag & GP_FRAME_SELECT) {
282 gpf->key_type = type;
283 }
284 }
285}
286
287/* -------------------------------------- */
288/* Copy and Paste Tools:
289 * - The copy/paste buffer currently stores a set of GP_Layers, with temporary
290 * GP_Frames with the necessary strokes
291 * - Unless there is only one element in the buffer,
292 * names are also tested to check for compatibility.
293 * - All pasted frames are offset by the same amount.
294 * This is calculated as the difference in the times of the current frame and the
295 * 'first keyframe' (i.e. the earliest one in all channels).
296 * - The earliest frame is calculated per copy operation.
297 */
298
299/* globals for copy/paste data (like for other copy/paste buffers) */
300static ListBase gpencil_anim_copybuf = {nullptr, nullptr};
301static int gpencil_anim_copy_firstframe = 999999999;
302static int gpencil_anim_copy_lastframe = -999999999;
304
314
316{
317 ListBase anim_data = {nullptr, nullptr};
318 int filter;
319
320 Scene *scene = ac->scene;
321
322 /* clear buffer first */
324
325 /* filter data */
328 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
329
330 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
331 /* This function only deals with grease pencil layer frames.
332 * This check is needed in the case of a call from the main dope-sheet. */
333 if (ale->type != ANIMTYPE_GPLAYER) {
334 continue;
335 }
336
337 ListBase copied_frames = {nullptr, nullptr};
338 bGPDlayer *gpl = (bGPDlayer *)ale->data;
339
340 /* loop over frames, and copy only selected frames */
341 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
342 /* if frame is selected, make duplicate it and its strokes */
343 if (gpf->flag & GP_FRAME_SELECT) {
344 /* make a copy of this frame */
345 bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf, true);
346 BLI_addtail(&copied_frames, new_frame);
347
348 /* extend extents for keyframes encountered */
351 }
352 }
353
354 /* create a new layer in buffer if there were keyframes here */
355 if (BLI_listbase_is_empty(&copied_frames) == false) {
356 bGPDlayer *new_layer = MEM_callocN<bGPDlayer>("GPCopyPasteLayer");
358
359 /* move over copied frames */
360 BLI_movelisttolist(&new_layer->frames, &copied_frames);
361 BLI_assert(copied_frames.first == nullptr);
362
363 /* make a copy of the layer's name - for name-based matching later... */
364 STRNCPY_UTF8(new_layer->info, gpl->info);
365 }
366 }
367
368 /* in case 'relative' paste method is used */
370
371 /* clean up */
372 ANIM_animdata_freelist(&anim_data);
373
374 /* report success */
376}
377
378bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
379{
380 ListBase anim_data = {nullptr, nullptr};
381 int filter;
382
383 Scene *scene = ac->scene;
384 bool no_name = false;
385 int offset = 0;
386
387 /* check if buffer is empty */
389 return false;
390 }
391
392 /* Check if single channel in buffer (disregard names if so). */
393 if (gpencil_anim_copybuf.first == gpencil_anim_copybuf.last) {
394 no_name = true;
395 }
396
397 /* methods of offset (eKeyPasteOffset) */
398 switch (offset_mode) {
400 offset = (scene->r.cfra - gpencil_anim_copy_firstframe);
401 break;
403 offset = (scene->r.cfra - gpencil_anim_copy_lastframe);
404 break;
406 offset = (scene->r.cfra - gpencil_anim_copy_cfra);
407 break;
409 offset = 0;
410 break;
411 }
412
413 /* filter data */
414 /* TODO: try doing it with selection, then without selection limits. */
418 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
419
420 /* from selected channels */
421 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
422 /* Only deal with GPlayers (case of calls from general dope-sheet). */
423 if (ale->type != ANIMTYPE_GPLAYER) {
424 continue;
425 }
426
427 bGPDlayer *gpld = (bGPDlayer *)ale->data;
428 bGPDlayer *gpls = nullptr;
429 bGPDframe *gpf;
430
431 /* find suitable layer from buffer to use to paste from */
432 for (gpls = static_cast<bGPDlayer *>(gpencil_anim_copybuf.first); gpls; gpls = gpls->next) {
433 /* check if layer name matches */
434 if ((no_name) || STREQ(gpls->info, gpld->info)) {
435 break;
436 }
437 }
438
439 /* this situation might occur! */
440 if (gpls == nullptr) {
441 continue;
442 }
443
444 /* add frames from buffer */
445 LISTBASE_FOREACH (bGPDframe *, gpfs, &gpls->frames) {
446 /* temporarily apply offset to buffer-frame while copying */
447 gpfs->framenum += offset;
448
449 /* get frame to copy data into (if no frame returned, then just ignore) */
450 gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
451 if (gpf) {
452 /* Ensure to use same keyframe type. */
453 gpf->key_type = gpfs->key_type;
454
455 /* This should be the right frame... as it may be a pre-existing frame,
456 * must make sure that only compatible stroke types get copied over
457 * - We cannot just add a duplicate frame, as that would cause errors
458 * - For now, we don't check if the types will be compatible since we
459 * don't have enough info to do so. Instead, we simply just paste,
460 * if it works, it will show up.
461 */
462 LISTBASE_FOREACH (bGPDstroke *, gps, &gpfs->strokes) {
463 /* Make a copy of stroke, then a copy of its points array. */
464 bGPDstroke *gpsn = BKE_gpencil_stroke_duplicate(gps, true, true);
465
466 /* append stroke to frame */
467 BLI_addtail(&gpf->strokes, gpsn);
468 }
469
470 /* if no strokes (i.e. new frame) added, free gpf */
471 if (BLI_listbase_is_empty(&gpf->strokes)) {
473 }
474 }
475
476 /* unapply offset from buffer-frame */
477 gpfs->framenum -= offset;
478 }
479
480 /* Tag destination datablock. */
482 }
483
484 /* clean up */
485 ANIM_animdata_freelist(&anim_data);
486 return true;
487}
488
489/* -------------------------------------- */
490/* Snap Tools */
491
492static bool gpencil_frame_snap_nearest(bGPDframe * /*gpf*/, Scene * /*scene*/)
493{
494#if 0 /* NOTE: gpf->framenum is already an int! */
495 if (gpf->flag & GP_FRAME_SELECT) {
496 gpf->framenum = int(floor(gpf->framenum + 0.5));
497 }
498#endif
499 return false;
500}
501
503{
504 float secf = float(scene->frames_per_second());
505 if (gpf->flag & GP_FRAME_SELECT) {
506 gpf->framenum = int(floorf(gpf->framenum / secf + 0.5f) * secf);
507 }
508 return false;
509}
510
512{
513 if (gpf->flag & GP_FRAME_SELECT) {
514 gpf->framenum = scene->r.cfra;
515 }
516 return false;
517}
518
520{
521 if (gpf->flag & GP_FRAME_SELECT) {
523 }
524 return false;
525}
526
527void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
528{
529 switch (mode) {
530 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
532 break;
533 case SNAP_KEYS_CURFRAME: /* snap to current frame */
535 break;
536 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
538 break;
539 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
541 break;
542 default: /* just in case */
543 break;
544 }
545}
546
547/* -------------------------------------- */
548/* Mirror Tools */
549
551{
552 int diff;
553
554 if (gpf->flag & GP_FRAME_SELECT) {
555 diff = scene->r.cfra - gpf->framenum;
556 gpf->framenum = scene->r.cfra + diff;
557 }
558
559 return false;
560}
561
562static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene * /*scene*/)
563{
564 int diff;
565
566 if (gpf->flag & GP_FRAME_SELECT) {
567 diff = -gpf->framenum;
568 gpf->framenum = diff;
569 }
570
571 return false;
572}
573
574static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene * /*scene*/)
575{
576 int diff;
577
578 /* NOTE: since we can't really do this, we just do the same as for yaxis... */
579 if (gpf->flag & GP_FRAME_SELECT) {
580 diff = -gpf->framenum;
581 gpf->framenum = diff;
582 }
583
584 return false;
585}
586
588{
589 static TimeMarker *marker;
590 static short initialized = 0;
591 int diff;
592
593 /* In order for this mirror function to work without
594 * any extra arguments being added, we use the case
595 * of gpf==nullptr to denote that we should find the
596 * marker to mirror over. The static pointer is safe
597 * to use this way, as it will be set to null after
598 * each cycle in which this is called.
599 */
600
601 if (gpf != nullptr) {
602 /* mirroring time */
603 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
604 diff = (marker->frame - gpf->framenum);
605 gpf->framenum = (marker->frame + diff);
606 }
607 }
608 else {
609 /* initialization time */
610 if (initialized) {
611 /* reset everything for safety */
612 marker = nullptr;
613 initialized = 0;
614 }
615 else {
616 /* try to find a marker */
617 marker = ED_markers_get_first_selected(&scene->markers);
618 if (marker) {
619 initialized = 1;
620 }
621 }
622 }
623
624 return false;
625}
626
627void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
628{
629 switch (mode) {
630 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
632 break;
633 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
635 break;
636 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
638 break;
639 case MIRROR_KEYS_MARKER: /* mirror over marker */
640 gpencil_frame_mirror_marker(nullptr, scene);
642 gpencil_frame_mirror_marker(nullptr, scene);
643 break;
644 default: /* just in case */
646 break;
647 }
648}
649
650/* ***************************************** */
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
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
#define STRNCPY_UTF8(dst, src)
#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:1054
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ 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:509
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
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)
nullptr float
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
static bool initialized
#define filter
#define floor
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 floorf
#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)