Blender V4.3
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
9#include <cmath>
10#include <cstddef>
11#include <cstdio>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_blenlib.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 = static_cast<CfraElem *>(MEM_callocN(sizeof(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 dopesheet. */
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 */
350 if (gpf->framenum < gpencil_anim_copy_firstframe) {
351 gpencil_anim_copy_firstframe = gpf->framenum;
352 }
353 if (gpf->framenum > gpencil_anim_copy_lastframe) {
354 gpencil_anim_copy_lastframe = gpf->framenum;
355 }
356 }
357 }
358
359 /* create a new layer in buffer if there were keyframes here */
360 if (BLI_listbase_is_empty(&copied_frames) == false) {
361 bGPDlayer *new_layer = static_cast<bGPDlayer *>(
362 MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer"));
364
365 /* move over copied frames */
366 BLI_movelisttolist(&new_layer->frames, &copied_frames);
367 BLI_assert(copied_frames.first == nullptr);
368
369 /* make a copy of the layer's name - for name-based matching later... */
370 STRNCPY(new_layer->info, gpl->info);
371 }
372 }
373
374 /* in case 'relative' paste method is used */
375 gpencil_anim_copy_cfra = scene->r.cfra;
376
377 /* clean up */
378 ANIM_animdata_freelist(&anim_data);
379
380 /* report success */
382}
383
384bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
385{
386 ListBase anim_data = {nullptr, nullptr};
387 int filter;
388
389 Scene *scene = ac->scene;
390 bool no_name = false;
391 int offset = 0;
392
393 /* check if buffer is empty */
395 return false;
396 }
397
398 /* Check if single channel in buffer (disregard names if so). */
400 no_name = true;
401 }
402
403 /* methods of offset (eKeyPasteOffset) */
404 switch (offset_mode) {
406 offset = (scene->r.cfra - gpencil_anim_copy_firstframe);
407 break;
409 offset = (scene->r.cfra - gpencil_anim_copy_lastframe);
410 break;
412 offset = (scene->r.cfra - gpencil_anim_copy_cfra);
413 break;
415 offset = 0;
416 break;
417 }
418
419 /* filter data */
420 /* TODO: try doing it with selection, then without selection limits. */
424 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
425
426 /* from selected channels */
427 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
428 /* only deal with GPlayers (case of calls from general dopesheet) */
429 if (ale->type != ANIMTYPE_GPLAYER) {
430 continue;
431 }
432
433 bGPDlayer *gpld = (bGPDlayer *)ale->data;
434 bGPDlayer *gpls = nullptr;
435 bGPDframe *gpf;
436
437 /* find suitable layer from buffer to use to paste from */
438 for (gpls = static_cast<bGPDlayer *>(gpencil_anim_copybuf.first); gpls; gpls = gpls->next) {
439 /* check if layer name matches */
440 if ((no_name) || STREQ(gpls->info, gpld->info)) {
441 break;
442 }
443 }
444
445 /* this situation might occur! */
446 if (gpls == nullptr) {
447 continue;
448 }
449
450 /* add frames from buffer */
451 LISTBASE_FOREACH (bGPDframe *, gpfs, &gpls->frames) {
452 /* temporarily apply offset to buffer-frame while copying */
453 gpfs->framenum += offset;
454
455 /* get frame to copy data into (if no frame returned, then just ignore) */
456 gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
457 if (gpf) {
458 /* Ensure to use same keyframe type. */
459 gpf->key_type = gpfs->key_type;
460
461 /* This should be the right frame... as it may be a pre-existing frame,
462 * must make sure that only compatible stroke types get copied over
463 * - We cannot just add a duplicate frame, as that would cause errors
464 * - For now, we don't check if the types will be compatible since we
465 * don't have enough info to do so. Instead, we simply just paste,
466 * if it works, it will show up.
467 */
468 LISTBASE_FOREACH (bGPDstroke *, gps, &gpfs->strokes) {
469 /* make a copy of stroke, then of its points array */
470 bGPDstroke *gpsn = BKE_gpencil_stroke_duplicate(gps, true, true);
471
472 /* append stroke to frame */
473 BLI_addtail(&gpf->strokes, gpsn);
474 }
475
476 /* if no strokes (i.e. new frame) added, free gpf */
477 if (BLI_listbase_is_empty(&gpf->strokes)) {
479 }
480 }
481
482 /* unapply offset from buffer-frame */
483 gpfs->framenum -= offset;
484 }
485
486 /* Tag destination datablock. */
488 }
489
490 /* clean up */
491 ANIM_animdata_freelist(&anim_data);
492 return true;
493}
494
495/* -------------------------------------- */
496/* Snap Tools */
497
498static bool gpencil_frame_snap_nearest(bGPDframe * /*gpf*/, Scene * /*scene*/)
499{
500#if 0 /* NOTE: gpf->framenum is already an int! */
501 if (gpf->flag & GP_FRAME_SELECT) {
502 gpf->framenum = int(floor(gpf->framenum + 0.5));
503 }
504#endif
505 return false;
506}
507
509{
510 float secf = float(FPS);
511 if (gpf->flag & GP_FRAME_SELECT) {
512 gpf->framenum = int(floorf(gpf->framenum / secf + 0.5f) * secf);
513 }
514 return false;
515}
516
518{
519 if (gpf->flag & GP_FRAME_SELECT) {
520 gpf->framenum = int(scene->r.cfra);
521 }
522 return false;
523}
524
526{
527 if (gpf->flag & GP_FRAME_SELECT) {
528 gpf->framenum = int(
529 ED_markers_find_nearest_marker_time(&scene->markers, float(gpf->framenum)));
530 }
531 return false;
532}
533
534void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
535{
536 switch (mode) {
537 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
539 break;
540 case SNAP_KEYS_CURFRAME: /* snap to current frame */
542 break;
543 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
545 break;
546 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
548 break;
549 default: /* just in case */
550 break;
551 }
552}
553
554/* -------------------------------------- */
555/* Mirror Tools */
556
558{
559 int diff;
560
561 if (gpf->flag & GP_FRAME_SELECT) {
562 diff = scene->r.cfra - gpf->framenum;
563 gpf->framenum = scene->r.cfra + diff;
564 }
565
566 return false;
567}
568
569static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene * /*scene*/)
570{
571 int diff;
572
573 if (gpf->flag & GP_FRAME_SELECT) {
574 diff = -gpf->framenum;
575 gpf->framenum = diff;
576 }
577
578 return false;
579}
580
581static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene * /*scene*/)
582{
583 int diff;
584
585 /* NOTE: since we can't really do this, we just do the same as for yaxis... */
586 if (gpf->flag & GP_FRAME_SELECT) {
587 diff = -gpf->framenum;
588 gpf->framenum = diff;
589 }
590
591 return false;
592}
593
595{
596 static TimeMarker *marker;
597 static short initialized = 0;
598 int diff;
599
600 /* In order for this mirror function to work without
601 * any extra arguments being added, we use the case
602 * of gpf==nullptr to denote that we should find the
603 * marker to mirror over. The static pointer is safe
604 * to use this way, as it will be set to null after
605 * each cycle in which this is called.
606 */
607
608 if (gpf != nullptr) {
609 /* mirroring time */
610 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
611 diff = (marker->frame - gpf->framenum);
612 gpf->framenum = (marker->frame + diff);
613 }
614 }
615 else {
616 /* initialization time */
617 if (initialized) {
618 /* reset everything for safety */
619 marker = nullptr;
620 initialized = 0;
621 }
622 else {
623 /* try to find a marker */
624 marker = ED_markers_get_first_selected(&scene->markers);
625 if (marker) {
626 initialized = 1;
627 }
628 }
629 }
630
631 return false;
632}
633
634void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
635{
636 switch (mode) {
637 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
639 break;
640 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
642 break;
643 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
645 break;
646 case MIRROR_KEYS_MARKER: /* mirror over marker */
647 gpencil_frame_mirror_marker(nullptr, scene);
649 gpencil_frame_mirror_marker(nullptr, scene);
650 break;
651 default: /* just in case */
653 break;
654 }
655}
656
657/* ***************************************** */
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:50
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:331
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#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:1021
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#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:475
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
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
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
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
static bool initialized
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
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:42
ccl_device_inline float2 floor(const float2 a)
#define min(a, b)
Definition sort.c:32
void * last
void * first
eAnimCont_Types datatype
struct bGPDlayer * next
void WM_main_add_notifier(uint type, void *reference)