Blender  V3.3
editaction_gpencil.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. */
3 
8 #include <math.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "MEM_guardedalloc.h"
15 
16 #include "BLI_blenlib.h"
17 #include "BLI_utildefines.h"
18 
19 #include "DNA_gpencil_types.h"
20 #include "DNA_scene_types.h"
21 
22 #include "BKE_fcurve.h"
23 #include "BKE_gpencil.h"
24 #include "BKE_report.h"
25 
26 #include "ED_anim_api.h"
27 #include "ED_gpencil.h"
28 #include "ED_keyframes_edit.h"
29 #include "ED_markers.h"
30 
31 #include "WM_api.h"
32 
33 #include "DEG_depsgraph.h"
34 
35 /* ***************************************** */
36 /* NOTE ABOUT THIS FILE:
37  * This file contains code for editing Grease Pencil data in the Action Editor
38  * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
39  * Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
40  */
41 /* ***************************************** */
42 /* Generics - Loopers */
43 
45  Scene *scene,
46  bool (*gpf_cb)(bGPDframe *, Scene *))
47 {
48  /* error checker */
49  if (gpl == NULL) {
50  return false;
51  }
52 
53  /* do loop */
54  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
55  /* execute callback */
56  if (gpf_cb(gpf, scene)) {
57  return true;
58  }
59  }
60 
61  /* nothing to return */
62  return false;
63 }
64 
65 /* ****************************************** */
66 /* Data Conversion Tools */
67 
68 void ED_gpencil_layer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
69 {
70  CfraElem *ce;
71 
72  /* error checking */
73  if (ELEM(NULL, gpl, elems)) {
74  return;
75  }
76 
77  /* loop through gp-frames, adding */
78  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
79  if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
80  ce = MEM_callocN(sizeof(CfraElem), "CfraElem");
81 
82  ce->cfra = (float)gpf->framenum;
83  ce->sel = (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
84 
85  BLI_addtail(elems, ce);
86  }
87  }
88 }
89 
90 /* ***************************************** */
91 /* Selection Tools */
92 
94 {
95  /* error checking */
96  if (gpl == NULL) {
97  return false;
98  }
99 
100  /* stop at the first one found */
101  LISTBASE_FOREACH (const bGPDframe *, gpf, &gpl->frames) {
102  if (gpf->flag & GP_FRAME_SELECT) {
103  return true;
104  }
105  }
106 
107  /* not found */
108  return false;
109 }
110 
111 /* helper function - select gp-frame based on SELECT_* mode */
112 static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
113 {
114  if (gpf == NULL) {
115  return;
116  }
117 
118  switch (select_mode) {
119  case SELECT_ADD:
120  gpf->flag |= GP_FRAME_SELECT;
121  break;
122  case SELECT_SUBTRACT:
123  gpf->flag &= ~GP_FRAME_SELECT;
124  break;
125  case SELECT_INVERT:
126  gpf->flag ^= GP_FRAME_SELECT;
127  break;
128  }
129 }
130 
131 void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
132 {
133  /* error checking */
134  if (gpl == NULL) {
135  return;
136  }
137 
138  /* handle according to mode */
139  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
140  gpencil_frame_select(gpf, select_mode);
141  }
142 }
143 
145 {
146  /* error checking */
147  if (gpl == NULL) {
148  return;
149  }
150 
151  /* now call the standard function */
152  ED_gpencil_select_frames(gpl, mode);
153 }
154 
155 void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
156 {
157  bGPDframe *gpf;
158 
159  if (gpl == NULL) {
160  return;
161  }
162 
163  gpf = BKE_gpencil_layer_frame_find(gpl, selx);
164 
165  if (gpf) {
166  gpencil_frame_select(gpf, select_mode);
167  }
168 }
169 
170 void ED_gpencil_layer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
171 {
172  if (gpl == NULL) {
173  return;
174  }
175 
176  /* only select those frames which are in bounds */
177  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
178  if (IN_RANGE(gpf->framenum, min, max)) {
179  gpencil_frame_select(gpf, select_mode);
180  }
181  }
182 }
183 
185  bGPDlayer *gpl,
186  short tool,
187  short select_mode)
188 {
189  if (gpl == NULL) {
190  return;
191  }
192 
193  /* only select frames which are within the region */
194  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
195  /* construct a dummy point coordinate to do this testing with */
196  float pt[2] = {0};
197 
198  pt[0] = gpf->framenum;
199  pt[1] = ked->channel_y;
200 
201  /* check the necessary regions */
202  if (tool == BEZT_OK_CHANNEL_LASSO) {
203  /* Lasso */
204  if (keyframe_region_lasso_test(ked->data, pt)) {
205  gpencil_frame_select(gpf, select_mode);
206  }
207  }
208  else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
209  /* Circle */
210  if (keyframe_region_circle_test(ked->data, pt)) {
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) {
224  BKE_gpencil_layer_autolock_set(gpd, false);
226  }
227 }
228 
229 /* ***************************************** */
230 /* Frame Editing Tools */
231 
233 {
234  bool changed = false;
235 
236  /* error checking */
237  if (gpl == NULL) {
238  return false;
239  }
240 
241  /* check for frames to delete */
242  LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
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 == NULL) {
256  return;
257  }
258 
259  /* Duplicate selected frames. */
260  LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->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 == NULL) {
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) */
302 static int gpencil_anim_copy_firstframe = 999999999;
303 static int gpencil_anim_copy_lastframe = -999999999;
304 static int gpencil_anim_copy_cfra = 0;
305 
307 {
310 
311  gpencil_anim_copy_firstframe = 999999999;
312  gpencil_anim_copy_lastframe = -999999999;
314 }
315 
317 {
318  ListBase anim_data = {NULL, NULL};
319  bAnimListElem *ale;
320  int filter;
321 
322  Scene *scene = ac->scene;
323 
324  /* clear buffer first */
326 
327  /* filter data */
329  ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
330 
331  for (ale = anim_data.first; ale; ale = ale->next) {
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 = {NULL, NULL};
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 = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
362  BLI_addtail(&gpencil_anim_copybuf, new_layer);
363 
364  /* move over copied frames */
365  BLI_movelisttolist(&new_layer->frames, &copied_frames);
366  BLI_assert(copied_frames.first == NULL);
367 
368  /* make a copy of the layer's name - for name-based matching later... */
369  BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info));
370  }
371  }
372 
373  /* in case 'relative' paste method is used */
375 
376  /* clean up */
377  ANIM_animdata_freelist(&anim_data);
378 
379  /* report success */
381 }
382 
383 bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
384 {
385  ListBase anim_data = {NULL, NULL};
386  bAnimListElem *ale;
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) {
407  break;
410  break;
413  break;
415  offset = 0;
416  break;
417  }
418 
419  /* filter data */
420  /* TODO: try doing it with selection, then without selection limits. */
423  ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
424 
425  /* from selected channels */
426  for (ale = anim_data.first; ale; ale = ale->next) {
427  /* only deal with GPlayers (case of calls from general dopesheet) */
428  if (ale->type != ANIMTYPE_GPLAYER) {
429  continue;
430  }
431 
432  bGPDlayer *gpld = (bGPDlayer *)ale->data;
433  bGPDlayer *gpls = NULL;
434  bGPDframe *gpfs, *gpf;
435 
436  /* find suitable layer from buffer to use to paste from */
437  for (gpls = gpencil_anim_copybuf.first; gpls; gpls = gpls->next) {
438  /* check if layer name matches */
439  if ((no_name) || STREQ(gpls->info, gpld->info)) {
440  break;
441  }
442  }
443 
444  /* this situation might occur! */
445  if (gpls == NULL) {
446  continue;
447  }
448 
449  /* add frames from buffer */
450  for (gpfs = gpls->frames.first; gpfs; gpfs = gpfs->next) {
451  /* temporarily apply offset to buffer-frame while copying */
452  gpfs->framenum += offset;
453 
454  /* get frame to copy data into (if no frame returned, then just ignore) */
455  gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
456  if (gpf) {
457  /* Ensure to use same keyframe type. */
458  gpf->key_type = gpfs->key_type;
459 
460  bGPDstroke *gps, *gpsn;
461 
462  /* This should be the right frame... as it may be a pre-existing frame,
463  * must make sure that only compatible stroke types get copied over
464  * - We cannot just add a duplicate frame, as that would cause errors
465  * - For now, we don't check if the types will be compatible since we
466  * don't have enough info to do so. Instead, we simply just paste,
467  * if it works, it will show up.
468  */
469  for (gps = gpfs->strokes.first; gps; gps = gps->next) {
470  /* make a copy of stroke, then of its points array */
471  gpsn = BKE_gpencil_stroke_duplicate(gps, true, true);
472 
473  /* append stroke to frame */
474  BLI_addtail(&gpf->strokes, gpsn);
475  }
476 
477  /* if no strokes (i.e. new frame) added, free gpf */
478  if (BLI_listbase_is_empty(&gpf->strokes)) {
480  }
481  }
482 
483  /* unapply offset from buffer-frame */
484  gpfs->framenum -= offset;
485  }
486 
487  /* Tag destination datablock. */
489  }
490 
491  /* clean up */
492  ANIM_animdata_freelist(&anim_data);
493  return true;
494 }
495 
496 /* -------------------------------------- */
497 /* Snap Tools */
498 
500 {
501 #if 0 /* NOTE: gpf->framenum is already an int! */
502  if (gpf->flag & GP_FRAME_SELECT) {
503  gpf->framenum = (int)(floor(gpf->framenum + 0.5));
504  }
505 #endif
506  return false;
507 }
508 
510 {
511  float secf = (float)FPS;
512  if (gpf->flag & GP_FRAME_SELECT) {
513  gpf->framenum = (int)(floorf(gpf->framenum / secf + 0.5f) * secf);
514  }
515  return false;
516 }
517 
519 {
520  if (gpf->flag & GP_FRAME_SELECT) {
521  gpf->framenum = (int)scene->r.cfra;
522  }
523  return false;
524 }
525 
527 {
528  if (gpf->flag & GP_FRAME_SELECT) {
530  (float)gpf->framenum);
531  }
532  return false;
533 }
534 
536 {
537  switch (mode) {
538  case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
540  break;
541  case SNAP_KEYS_CURFRAME: /* snap to current frame */
543  break;
544  case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
546  break;
547  case SNAP_KEYS_NEARSEC: /* snap to nearest second */
549  break;
550  default: /* just in case */
551  break;
552  }
553 }
554 
555 /* -------------------------------------- */
556 /* Mirror Tools */
557 
559 {
560  int diff;
561 
562  if (gpf->flag & GP_FRAME_SELECT) {
563  diff = scene->r.cfra - gpf->framenum;
564  gpf->framenum = scene->r.cfra + diff;
565  }
566 
567  return false;
568 }
569 
571 {
572  int diff;
573 
574  if (gpf->flag & GP_FRAME_SELECT) {
575  diff = -gpf->framenum;
576  gpf->framenum = diff;
577  }
578 
579  return false;
580 }
581 
583 {
584  int diff;
585 
586  /* NOTE: since we can't really do this, we just do the same as for yaxis... */
587  if (gpf->flag & GP_FRAME_SELECT) {
588  diff = -gpf->framenum;
589  gpf->framenum = diff;
590  }
591 
592  return false;
593 }
594 
596 {
597  static TimeMarker *marker;
598  static short initialized = 0;
599  int diff;
600 
601  /* In order for this mirror function to work without
602  * any extra arguments being added, we use the case
603  * of gpf==NULL to denote that we should find the
604  * marker to mirror over. The static pointer is safe
605  * to use this way, as it will be set to null after
606  * each cycle in which this is called.
607  */
608 
609  if (gpf != NULL) {
610  /* mirroring time */
611  if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
612  diff = (marker->frame - gpf->framenum);
613  gpf->framenum = (marker->frame + diff);
614  }
615  }
616  else {
617  /* initialization time */
618  if (initialized) {
619  /* reset everything for safety */
620  marker = NULL;
621  initialized = 0;
622  }
623  else {
624  /* try to find a marker */
626  if (marker) {
627  initialized = 1;
628  }
629  }
630  }
631 
632  return false;
633 }
634 
636 {
637  switch (mode) {
638  case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
640  break;
641  case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
643  break;
644  case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
646  break;
647  case MIRROR_KEYS_MARKER: /* mirror over marker */
651  break;
652  default: /* just in case */
654  break;
655  }
656 }
657 
658 /* ***************************************** */
typedef float(TangentPoint)[2]
void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active)
Definition: gpencil.c:1601
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
Definition: gpencil.c:855
struct bGPDframe * BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src, bool dup_strokes)
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
Definition: gpencil.c:1558
bool BKE_gpencil_layer_frame_delete(struct bGPDlayer *gpl, struct bGPDframe *gpf)
Definition: gpencil.c:1396
struct bGPDframe * BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:1216
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
void BKE_gpencil_free_layers(struct ListBase *list)
Definition: gpencil.c:456
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:341
void BKE_gpencil_layer_autolock_set(struct bGPdata *gpd, bool unlock)
Definition: gpencil.c:1623
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
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.c:80
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
#define IN_RANGE(a, b, c)
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_TRANSFORM
Definition: DNA_ID.h:771
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
@ GP_LAYER_SELECT
@ GP_FRAME_SELECT
#define FPS
@ ANIMTYPE_GPLAYER
Definition: ED_anim_api.h:233
@ ANIMFILTER_FOREDIT
Definition: ED_anim_api.h:312
@ ANIMFILTER_DATA_VISIBLE
Definition: ED_anim_api.h:292
@ ANIMFILTER_LIST_VISIBLE
Definition: ED_anim_api.h:295
@ ANIMFILTER_NODUPLIS
Definition: ED_anim_api.h:325
@ ANIMFILTER_SEL
Definition: ED_anim_api.h:308
@ 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
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
#define ND_DATA
Definition: WM_types.h:456
#define NA_EDITED
Definition: WM_types.h:523
#define NC_GPENCIL
Definition: WM_types.h:349
void ANIM_animdata_freelist(ListBase *anim_data)
Definition: anim_deps.c:397
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode, void *data, eAnimCont_Types datatype)
Definition: anim_filter.c:3447
TimeMarker * ED_markers_get_first_selected(ListBase *markers)
Definition: anim_markers.c:344
int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
Definition: anim_markers.c:163
Scene scene
static bool gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *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 bool gpencil_frame_snap_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene))
static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene *UNUSED(scene))
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)
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
static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene *UNUSED(scene))
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)
static int gpencil_anim_copy_firstframe
bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
static bool gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene)
void ED_gpencil_anim_copybuf_free(void)
static ListBase gpencil_anim_copybuf
static bool initialized
Definition: gpu_init_exit.c:22
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
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.c:31
#define floorf(x)
Definition: metal/compat.h:224
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt=1)
T floor(const T &a)
#define min(a, b)
Definition: sort.c:35
float cfra
Definition: BKE_fcurve.h:40
int sel
Definition: BKE_fcurve.h:41
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
struct RenderData r
ListBase markers
struct Scene * scene
Definition: ED_anim_api.h:84
short datatype
Definition: ED_anim_api.h:62
void * data
Definition: ED_anim_api.h:60
struct bAnimListElem * next
Definition: ED_anim_api.h:127
struct ID * id
Definition: ED_anim_api.h:160
char info[128]
ListBase frames
struct bGPDstroke * next
float max
void WM_main_add_notifier(unsigned int type, void *reference)