1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2008, Blender Foundation
17 * This is a new part of Blender
18 */
19
20 /** \file
21 * \ingroup edgpencil
22 */
23
24 #include <math.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_blenlib.h"
33 #include "BLI_utildefines.h"
34
35 #include "DNA_gpencil_types.h"
36 #include "DNA_scene_types.h"
37
38 #include "BKE_fcurve.h"
39 #include "BKE_gpencil.h"
40 #include "BKE_report.h"
41
42 #include "ED_anim_api.h"
43 #include "ED_gpencil.h"
44 #include "ED_keyframes_edit.h"
45 #include "ED_markers.h"
46
47 #include "WM_api.h"
48
49 /* ***************************************** */
50 /* NOTE ABOUT THIS FILE:
51 * This file contains code for editing Grease Pencil data in the Action Editor
52 * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
53 * Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
54 */
55 /* ***************************************** */
56 /* Generics - Loopers */
57
58 /* Loops over the gp-frames for a gp-layer, and applies the given callback */
ED_gpencil_layer_frames_looper(bGPDlayer * gpl,Scene * scene,bool (* gpf_cb)(bGPDframe *,Scene *))59 bool ED_gpencil_layer_frames_looper(bGPDlayer *gpl,
60 Scene *scene,
61 bool (*gpf_cb)(bGPDframe *, Scene *))
62 {
63 /* error checker */
64 if (gpl == NULL) {
65 return false;
66 }
67
68 /* do loop */
69 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
70 /* execute callback */
71 if (gpf_cb(gpf, scene)) {
72 return true;
73 }
74 }
75
76 /* nothing to return */
77 return false;
78 }
79
80 /* ****************************************** */
81 /* Data Conversion Tools */
82
83 /* make a listing all the gp-frames in a layer as cfraelems */
ED_gpencil_layer_make_cfra_list(bGPDlayer * gpl,ListBase * elems,bool onlysel)84 void ED_gpencil_layer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel)
85 {
86 CfraElem *ce;
87
88 /* error checking */
89 if (ELEM(NULL, gpl, elems)) {
90 return;
91 }
92
93 /* loop through gp-frames, adding */
94 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
95 if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
96 ce = MEM_callocN(sizeof(CfraElem), "CfraElem");
97
98 ce->cfra = (float)gpf->framenum;
99 ce->sel = (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
100
101 BLI_addtail(elems, ce);
102 }
103 }
104 }
105
106 /* ***************************************** */
107 /* Selection Tools */
108
109 /* check if one of the frames in this layer is selected */
ED_gpencil_layer_frame_select_check(bGPDlayer * gpl)110 bool ED_gpencil_layer_frame_select_check(bGPDlayer *gpl)
111 {
112 /* error checking */
113 if (gpl == NULL) {
114 return false;
115 }
116
117 /* stop at the first one found */
118 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
119 if (gpf->flag & GP_FRAME_SELECT) {
120 return true;
121 }
122 }
123
124 /* not found */
125 return false;
126 }
127
128 /* helper function - select gp-frame based on SELECT_* mode */
gpencil_frame_select(bGPDframe * gpf,short select_mode)129 static void gpencil_frame_select(bGPDframe *gpf, short select_mode)
130 {
131 if (gpf == NULL) {
132 return;
133 }
134
135 switch (select_mode) {
136 case SELECT_ADD:
137 gpf->flag |= GP_FRAME_SELECT;
138 break;
139 case SELECT_SUBTRACT:
140 gpf->flag &= ~GP_FRAME_SELECT;
141 break;
142 case SELECT_INVERT:
143 gpf->flag ^= GP_FRAME_SELECT;
144 break;
145 }
146 }
147
148 /* set all/none/invert select (like above, but with SELECT_* modes) */
ED_gpencil_select_frames(bGPDlayer * gpl,short select_mode)149 void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode)
150 {
151 /* error checking */
152 if (gpl == NULL) {
153 return;
154 }
155
156 /* handle according to mode */
157 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
158 gpencil_frame_select(gpf, select_mode);
159 }
160 }
161
162 /* set all/none/invert select */
ED_gpencil_layer_frame_select_set(bGPDlayer * gpl,short mode)163 void ED_gpencil_layer_frame_select_set(bGPDlayer *gpl, short mode)
164 {
165 /* error checking */
166 if (gpl == NULL) {
167 return;
168 }
169
170 /* now call the standard function */
171 ED_gpencil_select_frames(gpl, mode);
172 }
173
174 /* select the frame in this layer that occurs on this frame (there should only be one at most) */
ED_gpencil_select_frame(bGPDlayer * gpl,int selx,short select_mode)175 void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode)
176 {
177 bGPDframe *gpf;
178
179 if (gpl == NULL) {
180 return;
181 }
182
183 gpf = BKE_gpencil_layer_frame_find(gpl, selx);
184
185 if (gpf) {
186 gpencil_frame_select(gpf, select_mode);
187 }
188 }
189
190 /* select the frames in this layer that occur within the bounds specified */
ED_gpencil_layer_frames_select_box(bGPDlayer * gpl,float min,float max,short select_mode)191 void ED_gpencil_layer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode)
192 {
193 if (gpl == NULL) {
194 return;
195 }
196
197 /* only select those frames which are in bounds */
198 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
199 if (IN_RANGE(gpf->framenum, min, max)) {
200 gpencil_frame_select(gpf, select_mode);
201 }
202 }
203 }
204
205 /* select the frames in this layer that occur within the lasso/circle region specified */
ED_gpencil_layer_frames_select_region(KeyframeEditData * ked,bGPDlayer * gpl,short tool,short select_mode)206 void ED_gpencil_layer_frames_select_region(KeyframeEditData *ked,
207 bGPDlayer *gpl,
208 short tool,
209 short select_mode)
210 {
211 if (gpl == NULL) {
212 return;
213 }
214
215 /* only select frames which are within the region */
216 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
217 /* construct a dummy point coordinate to do this testing with */
218 float pt[2] = {0};
219
220 pt[0] = gpf->framenum;
221 pt[1] = ked->channel_y;
222
223 /* check the necessary regions */
224 if (tool == BEZT_OK_CHANNEL_LASSO) {
225 /* Lasso */
226 if (keyframe_region_lasso_test(ked->data, pt)) {
227 gpencil_frame_select(gpf, select_mode);
228 }
229 }
230 else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
231 /* Circle */
232 if (keyframe_region_circle_test(ked->data, pt)) {
233 gpencil_frame_select(gpf, select_mode);
234 }
235 }
236 }
237 }
238
239 /* ***************************************** */
240 /* Frame Editing Tools */
241
242 /* Delete selected frames */
ED_gpencil_layer_frames_delete(bGPDlayer * gpl)243 bool ED_gpencil_layer_frames_delete(bGPDlayer *gpl)
244 {
245 bool changed = false;
246
247 /* error checking */
248 if (gpl == NULL) {
249 return false;
250 }
251
252 /* check for frames to delete */
253 LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
254 if (gpf->flag & GP_FRAME_SELECT) {
255 BKE_gpencil_layer_frame_delete(gpl, gpf);
256 changed = true;
257 }
258 }
259
260 return changed;
261 }
262
263 /* Duplicate selected frames from given gp-layer */
ED_gpencil_layer_frames_duplicate(bGPDlayer * gpl)264 void ED_gpencil_layer_frames_duplicate(bGPDlayer *gpl)
265 {
266 /* error checking */
267 if (gpl == NULL) {
268 return;
269 }
270
271 /* duplicate selected frames */
272 LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) {
273
274 /* duplicate this frame */
275 if (gpf->flag & GP_FRAME_SELECT) {
276 bGPDframe *gpfd;
277
278 /* duplicate frame, and deselect self */
279 gpfd = BKE_gpencil_frame_duplicate(gpf);
280 gpf->flag &= ~GP_FRAME_SELECT;
281
282 BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
283 }
284 }
285 }
286
287 /**
288 * Set keyframe type for selected frames from given gp-layer
289 *
290 * \param type: The type of keyframe (#eBezTriple_KeyframeType) to set selected frames to.
291 */
ED_gpencil_layer_frames_keytype_set(bGPDlayer * gpl,short type)292 void ED_gpencil_layer_frames_keytype_set(bGPDlayer *gpl, short type)
293 {
294 if (gpl == NULL) {
295 return;
296 }
297
298 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
299 if (gpf->flag & GP_FRAME_SELECT) {
300 gpf->key_type = type;
301 }
302 }
303 }
304
305 /* -------------------------------------- */
306 /* Copy and Paste Tools:
307 * - The copy/paste buffer currently stores a set of GP_Layers, with temporary
308 * GP_Frames with the necessary strokes
309 * - Unless there is only one element in the buffer,
310 * names are also tested to check for compatibility.
311 * - All pasted frames are offset by the same amount.
312 * This is calculated as the difference in the times of the current frame and the
313 * 'first keyframe' (i.e. the earliest one in all channels).
314 * - The earliest frame is calculated per copy operation.
315 */
316
317 /* globals for copy/paste data (like for other copy/paste buffers) */
318 static ListBase gpencil_anim_copybuf = {NULL, NULL};
319 static int gpencil_anim_copy_firstframe = 999999999;
320 static int gpencil_anim_copy_lastframe = -999999999;
321 static int gpencil_anim_copy_cfra = 0;
322
323 /* This function frees any MEM_calloc'ed copy/paste buffer data */
ED_gpencil_anim_copybuf_free(void)324 void ED_gpencil_anim_copybuf_free(void)
325 {
326 BKE_gpencil_free_layers(&gpencil_anim_copybuf);
327 BLI_listbase_clear(&gpencil_anim_copybuf);
328
329 gpencil_anim_copy_firstframe = 999999999;
330 gpencil_anim_copy_lastframe = -999999999;
331 gpencil_anim_copy_cfra = 0;
332 }
333
334 /* This function adds data to the copy/paste buffer, freeing existing data first
335 * Only the selected GP-layers get their selected keyframes copied.
336 *
337 * Returns whether the copy operation was successful or not
338 */
ED_gpencil_anim_copybuf_copy(bAnimContext * ac)339 bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac)
340 {
341 ListBase anim_data = {NULL, NULL};
342 bAnimListElem *ale;
343 int filter;
344
345 Scene *scene = ac->scene;
346
347 /* clear buffer first */
348 ED_gpencil_anim_copybuf_free();
349
350 /* filter data */
351 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS);
352 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
353
354 /* assume that each of these is a GP layer */
355 for (ale = anim_data.first; ale; ale = ale->next) {
356 ListBase copied_frames = {NULL, NULL};
357 bGPDlayer *gpl = (bGPDlayer *)ale->data;
358
359 /* loop over frames, and copy only selected frames */
360 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
361 /* if frame is selected, make duplicate it and its strokes */
362 if (gpf->flag & GP_FRAME_SELECT) {
363 /* make a copy of this frame */
364 bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf);
365 BLI_addtail(&copied_frames, new_frame);
366
367 /* extend extents for keyframes encountered */
368 if (gpf->framenum < gpencil_anim_copy_firstframe) {
369 gpencil_anim_copy_firstframe = gpf->framenum;
370 }
371 if (gpf->framenum > gpencil_anim_copy_lastframe) {
372 gpencil_anim_copy_lastframe = gpf->framenum;
373 }
374 }
375 }
376
377 /* create a new layer in buffer if there were keyframes here */
378 if (BLI_listbase_is_empty(&copied_frames) == false) {
379 bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
380 BLI_addtail(&gpencil_anim_copybuf, new_layer);
381
382 /* move over copied frames */
383 BLI_movelisttolist(&new_layer->frames, &copied_frames);
384 BLI_assert(copied_frames.first == NULL);
385
386 /* make a copy of the layer's name - for name-based matching later... */
387 BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info));
388 }
389 }
390
391 /* in case 'relative' paste method is used */
392 gpencil_anim_copy_cfra = CFRA;
393
394 /* clean up */
395 ANIM_animdata_freelist(&anim_data);
396
397 /* check if anything ended up in the buffer */
398 if (ELEM(NULL, gpencil_anim_copybuf.first, gpencil_anim_copybuf.last)) {
399 BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
400 return false;
401 }
402
403 /* report success */
404 return true;
405 }
406
407 /* Pastes keyframes from buffer, and reports success */
ED_gpencil_anim_copybuf_paste(bAnimContext * ac,const short offset_mode)408 bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
409 {
410 ListBase anim_data = {NULL, NULL};
411 bAnimListElem *ale;
412 int filter;
413
414 Scene *scene = ac->scene;
415 bool no_name = false;
416 int offset = 0;
417
418 /* check if buffer is empty */
419 if (BLI_listbase_is_empty(&gpencil_anim_copybuf)) {
420 BKE_report(ac->reports, RPT_ERROR, "No data in buffer to paste");
421 return false;
422 }
423
424 /* check if single channel in buffer (disregard names if so) */
425 if (gpencil_anim_copybuf.first == gpencil_anim_copybuf.last) {
426 no_name = true;
427 }
428
429 /* methods of offset (eKeyPasteOffset) */
430 switch (offset_mode) {
431 case KEYFRAME_PASTE_OFFSET_CFRA_START:
432 offset = (CFRA - gpencil_anim_copy_firstframe);
433 break;
434 case KEYFRAME_PASTE_OFFSET_CFRA_END:
435 offset = (CFRA - gpencil_anim_copy_lastframe);
436 break;
437 case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
438 offset = (CFRA - gpencil_anim_copy_cfra);
439 break;
440 case KEYFRAME_PASTE_OFFSET_NONE:
441 offset = 0;
442 break;
443 }
444
445 /* filter data */
446 /* TODO: try doing it with selection, then without selection limits. */
447 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
448 ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
449 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
450
451 /* from selected channels */
452 for (ale = anim_data.first; ale; ale = ale->next) {
453 bGPDlayer *gpld = (bGPDlayer *)ale->data;
454 bGPDlayer *gpls = NULL;
455 bGPDframe *gpfs, *gpf;
456
457 /* find suitable layer from buffer to use to paste from */
458 for (gpls = gpencil_anim_copybuf.first; gpls; gpls = gpls->next) {
459 /* check if layer name matches */
460 if ((no_name) || STREQ(gpls->info, gpld->info)) {
461 break;
462 }
463 }
464
465 /* this situation might occur! */
466 if (gpls == NULL) {
467 continue;
468 }
469
470 /* add frames from buffer */
471 for (gpfs = gpls->frames.first; gpfs; gpfs = gpfs->next) {
472 /* temporarily apply offset to buffer-frame while copying */
473 gpfs->framenum += offset;
474
475 /* get frame to copy data into (if no frame returned, then just ignore) */
476 gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW);
477 if (gpf) {
478 bGPDstroke *gps, *gpsn;
479
480 /* This should be the right frame... as it may be a pre-existing frame,
481 * must make sure that only compatible stroke types get copied over
482 * - We cannot just add a duplicate frame, as that would cause errors
483 * - For now, we don't check if the types will be compatible since we
484 * don't have enough info to do so. Instead, we simply just paste,
485 * if it works, it will show up.
486 */
487 for (gps = gpfs->strokes.first; gps; gps = gps->next) {
488 /* make a copy of stroke, then of its points array */
489 gpsn = BKE_gpencil_stroke_duplicate(gps, true);
490
491 /* append stroke to frame */
492 BLI_addtail(&gpf->strokes, gpsn);
493 }
494
495 /* if no strokes (i.e. new frame) added, free gpf */
496 if (BLI_listbase_is_empty(&gpf->strokes)) {
497 BKE_gpencil_layer_frame_delete(gpld, gpf);
498 }
499 }
500
501 /* unapply offset from buffer-frame */
502 gpfs->framenum -= offset;
503 }
504 }
505
506 /* clean up */
507 ANIM_animdata_freelist(&anim_data);
508 return true;
509 }
510
511 /* -------------------------------------- */
512 /* Snap Tools */
513
gpencil_frame_snap_nearest(bGPDframe * UNUSED (gpf),Scene * UNUSED (scene))514 static bool gpencil_frame_snap_nearest(bGPDframe *UNUSED(gpf), Scene *UNUSED(scene))
515 {
516 #if 0 /* note: gpf->framenum is already an int! */
517 if (gpf->flag & GP_FRAME_SELECT) {
518 gpf->framenum = (int)(floor(gpf->framenum + 0.5));
519 }
520 #endif
521 return false;
522 }
523
gpencil_frame_snap_nearestsec(bGPDframe * gpf,Scene * scene)524 static bool gpencil_frame_snap_nearestsec(bGPDframe *gpf, Scene *scene)
525 {
526 float secf = (float)FPS;
527 if (gpf->flag & GP_FRAME_SELECT) {
528 gpf->framenum = (int)(floorf(gpf->framenum / secf + 0.5f) * secf);
529 }
530 return false;
531 }
532
gpencil_frame_snap_cframe(bGPDframe * gpf,Scene * scene)533 static bool gpencil_frame_snap_cframe(bGPDframe *gpf, Scene *scene)
534 {
535 if (gpf->flag & GP_FRAME_SELECT) {
536 gpf->framenum = (int)CFRA;
537 }
538 return false;
539 }
540
gpencil_frame_snap_nearmarker(bGPDframe * gpf,Scene * scene)541 static bool gpencil_frame_snap_nearmarker(bGPDframe *gpf, Scene *scene)
542 {
543 if (gpf->flag & GP_FRAME_SELECT) {
544 gpf->framenum = (int)ED_markers_find_nearest_marker_time(&scene->markers,
545 (float)gpf->framenum);
546 }
547 return false;
548 }
549
550 /* snap selected frames to ... */
ED_gpencil_layer_snap_frames(bGPDlayer * gpl,Scene * scene,short mode)551 void ED_gpencil_layer_snap_frames(bGPDlayer *gpl, Scene *scene, short mode)
552 {
553 switch (mode) {
554 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
555 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearest);
556 break;
557 case SNAP_KEYS_CURFRAME: /* snap to current frame */
558 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_cframe);
559 break;
560 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
561 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearmarker);
562 break;
563 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
564 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_snap_nearestsec);
565 break;
566 default: /* just in case */
567 break;
568 }
569 }
570
571 /* -------------------------------------- */
572 /* Mirror Tools */
573
gpencil_frame_mirror_cframe(bGPDframe * gpf,Scene * scene)574 static bool gpencil_frame_mirror_cframe(bGPDframe *gpf, Scene *scene)
575 {
576 int diff;
577
578 if (gpf->flag & GP_FRAME_SELECT) {
579 diff = CFRA - gpf->framenum;
580 gpf->framenum = CFRA + diff;
581 }
582
583 return false;
584 }
585
gpencil_frame_mirror_yaxis(bGPDframe * gpf,Scene * UNUSED (scene))586 static bool gpencil_frame_mirror_yaxis(bGPDframe *gpf, Scene *UNUSED(scene))
587 {
588 int diff;
589
590 if (gpf->flag & GP_FRAME_SELECT) {
591 diff = -gpf->framenum;
592 gpf->framenum = diff;
593 }
594
595 return false;
596 }
597
gpencil_frame_mirror_xaxis(bGPDframe * gpf,Scene * UNUSED (scene))598 static bool gpencil_frame_mirror_xaxis(bGPDframe *gpf, Scene *UNUSED(scene))
599 {
600 int diff;
601
602 /* NOTE: since we can't really do this, we just do the same as for yaxis... */
603 if (gpf->flag & GP_FRAME_SELECT) {
604 diff = -gpf->framenum;
605 gpf->framenum = diff;
606 }
607
608 return false;
609 }
610
gpencil_frame_mirror_marker(bGPDframe * gpf,Scene * scene)611 static bool gpencil_frame_mirror_marker(bGPDframe *gpf, Scene *scene)
612 {
613 static TimeMarker *marker;
614 static short initialized = 0;
615 int diff;
616
617 /* In order for this mirror function to work without
618 * any extra arguments being added, we use the case
619 * of gpf==NULL to denote that we should find the
620 * marker to mirror over. The static pointer is safe
621 * to use this way, as it will be set to null after
622 * each cycle in which this is called.
623 */
624
625 if (gpf != NULL) {
626 /* mirroring time */
627 if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
628 diff = (marker->frame - gpf->framenum);
629 gpf->framenum = (marker->frame + diff);
630 }
631 }
632 else {
633 /* initialization time */
634 if (initialized) {
635 /* reset everything for safety */
636 marker = NULL;
637 initialized = 0;
638 }
639 else {
640 /* try to find a marker */
641 marker = ED_markers_get_first_selected(&scene->markers);
642 if (marker) {
643 initialized = 1;
644 }
645 }
646 }
647
648 return false;
649 }
650
651 /* mirror selected gp-frames on... */
652 /* TODO: mirror over a specific time */
ED_gpencil_layer_mirror_frames(bGPDlayer * gpl,Scene * scene,short mode)653 void ED_gpencil_layer_mirror_frames(bGPDlayer *gpl, Scene *scene, short mode)
654 {
655 switch (mode) {
656 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
657 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_cframe);
658 break;
659 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
660 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_yaxis);
661 break;
662 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
663 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_xaxis);
664 break;
665 case MIRROR_KEYS_MARKER: /* mirror over marker */
666 gpencil_frame_mirror_marker(NULL, scene);
667 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_marker);
668 gpencil_frame_mirror_marker(NULL, scene);
669 break;
670 default: /* just in case */
671 ED_gpencil_layer_frames_looper(gpl, scene, gpencil_frame_mirror_yaxis);
672 break;
673 }
674 }
675
676 /* ***************************************** */
677