GPencil: Change interpolate stroke factor
[blender.git] / source / blender / editors / gpencil / gpencil_edit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung, Antonio Vazquez
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  * Operators for editing Grease Pencil strokes
26  */
27
28 /** \file blender/editors/gpencil/gpencil_edit.c
29  *  \ingroup edgpencil
30  */
31
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stddef.h>
37 #include <math.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_math.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44
45 #include "BLT_translation.h"
46
47 #include "DNA_object_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52 #include "DNA_gpencil_types.h"
53
54 #include "BKE_context.h"
55 #include "BKE_global.h"
56 #include "BKE_gpencil.h"
57 #include "BKE_library.h"
58 #include "BKE_report.h"
59 #include "BKE_screen.h"
60
61 #include "UI_interface.h"
62 #include "UI_resources.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "RNA_access.h"
68 #include "RNA_define.h"
69 #include "RNA_enum_types.h"
70
71 #include "UI_view2d.h"
72
73 #include "ED_gpencil.h"
74 #include "ED_object.h"
75 #include "ED_screen.h"
76 #include "ED_view3d.h"
77 #include "ED_screen.h"
78 #include "ED_space_api.h"
79
80 #include "gpencil_intern.h"
81
82 /* ************************************************ */
83 /* Stroke Edit Mode Management */
84
85 static int gpencil_editmode_toggle_poll(bContext *C)
86 {
87         return ED_gpencil_data_get_active(C) != NULL;
88 }
89
90 static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op))
91 {
92         bGPdata *gpd = ED_gpencil_data_get_active(C);
93         
94         if (gpd == NULL)
95                 return OPERATOR_CANCELLED;
96         
97         /* Just toggle editmode flag... */
98         gpd->flag ^= GP_DATA_STROKE_EDITMODE;
99         
100         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
101         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
102         
103         return OPERATOR_FINISHED;
104 }
105
106 void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
107 {
108         /* identifiers */
109         ot->name = "Strokes Edit Mode Toggle";
110         ot->idname = "GPENCIL_OT_editmode_toggle";
111         ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
112         
113         /* callbacks */
114         ot->exec = gpencil_editmode_toggle_exec;
115         ot->poll = gpencil_editmode_toggle_poll;
116         
117         /* flags */
118         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
119 }
120
121 /* ************************************************ */
122 /* Stroke Editing Operators */
123
124 /* poll callback for all stroke editing operators */
125 static int gp_stroke_edit_poll(bContext *C)
126 {
127         /* NOTE: this is a bit slower, but is the most accurate... */
128         return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
129 }
130
131 /* ************ Stroke Hide selection Toggle ************** */
132
133 static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
134 {
135         ToolSettings *ts = CTX_data_tool_settings(C);
136
137         if (ts == NULL)
138                 return OPERATOR_CANCELLED;
139
140         /* Just toggle alpha... */
141         if (ts->gp_sculpt.alpha > 0.0f) {
142                 ts->gp_sculpt.alpha = 0.0f;
143         }
144         else {
145                 ts->gp_sculpt.alpha = 1.0f;
146         }
147
148         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
149         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
150
151         return OPERATOR_FINISHED;
152 }
153
154 void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
155 {
156         /* identifiers */
157         ot->name = "Hide Selection";
158         ot->idname = "GPENCIL_OT_selection_opacity_toggle";
159         ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
160
161         /* callbacks */
162         ot->exec = gpencil_hideselect_toggle_exec;
163         ot->poll = gp_stroke_edit_poll;
164
165         /* flags */
166         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
167 }
168
169 /* ************** Duplicate Selected Strokes **************** */
170
171 /* Make copies of selected point segments in a selected stroke */
172 static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
173 {
174         bGPDspoint *pt;
175         int i;
176         
177         int start_idx = -1;
178         
179         
180         /* Step through the original stroke's points:
181          * - We accumulate selected points (from start_idx to current index)
182          *   and then convert that to a new stroke
183          */
184         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
185                 /* searching for start, are waiting for end? */
186                 if (start_idx == -1) {
187                         /* is this the first selected point for a new island? */
188                         if (pt->flag & GP_SPOINT_SELECT) {
189                                 start_idx = i;
190                         }
191                 }
192                 else {
193                         size_t len = 0;
194                         
195                         /* is this the end of current island yet?
196                          * 1) Point i-1 was the last one that was selected
197                          * 2) Point i is the last in the array
198                          */
199                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
200                                 len = i - start_idx;
201                         }
202                         else if (i == gps->totpoints - 1) {
203                                 len = i - start_idx + 1;
204                         }
205                         //printf("copying from %d to %d = %d\n", start_idx, i, len);
206                 
207                         /* make copies of the relevant data */
208                         if (len) {
209                                 bGPDstroke *gpsd;
210                                 
211                                 /* make a stupid copy first of the entire stroke (to get the flags too) */
212                                 gpsd = MEM_dupallocN(gps);
213                                 BLI_strncpy(gpsd->tmp_layerinfo, layername, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */
214                                 
215                                 /* initialize triangle memory - will be calculated on next redraw */
216                                 gpsd->triangles = NULL;
217                                 gpsd->flag |= GP_STROKE_RECALC_CACHES;
218                                 gpsd->tot_triangles = 0;
219                                 
220                                 /* now, make a new points array, and copy of the relevant parts */
221                                 gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
222                                 memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
223                                 gpsd->totpoints = len;
224                                 
225                                 /* add to temp buffer */
226                                 gpsd->next = gpsd->prev = NULL;
227                                 BLI_addtail(new_strokes, gpsd);
228                                 
229                                 /* cleanup + reset for next */
230                                 start_idx = -1;
231                         }
232                 }
233         }
234 }
235
236 static int gp_duplicate_exec(bContext *C, wmOperator *op)
237 {
238         bGPdata *gpd = ED_gpencil_data_get_active(C);
239         
240         if (gpd == NULL) {
241                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
242                 return OPERATOR_CANCELLED;
243         }
244         
245         /* for each visible (and editable) layer's selected strokes,
246          * copy the strokes into a temporary buffer, then append
247          * once all done
248          */
249         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
250         {
251                 ListBase new_strokes = {NULL, NULL};
252                 bGPDframe *gpf = gpl->actframe;
253                 bGPDstroke *gps;
254                 
255                 if (gpf == NULL)
256                         continue;
257                 
258                 /* make copies of selected strokes, and deselect these once we're done */
259                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
260                         /* skip strokes that are invalid for current view */
261                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
262                                 continue;
263                         }
264                         
265                         if (gps->flag & GP_STROKE_SELECT) {
266                                 if (gps->totpoints == 1) {
267                                         /* Special Case: If there's just a single point in this stroke... */
268                                         bGPDstroke *gpsd;
269                                         
270                                         /* make direct copies of the stroke and its points */
271                                         gpsd = MEM_dupallocN(gps);
272                                         BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo));
273                                         gpsd->points = MEM_dupallocN(gps->points);
274
275                                         /* triangle information - will be calculated on next redraw */
276                                         gpsd->flag |= GP_STROKE_RECALC_CACHES;
277                                         gpsd->triangles = NULL;
278                                         
279                                         /* add to temp buffer */
280                                         gpsd->next = gpsd->prev = NULL;
281                                         BLI_addtail(&new_strokes, gpsd);
282                                 }
283                                 else {
284                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
285                                         gp_duplicate_points(gps, &new_strokes, gpl->info);
286                                 }
287                                 
288                                 /* deselect original stroke, or else the originals get moved too
289                                  * (when using the copy + move macro)
290                                  */
291                                 gps->flag &= ~GP_STROKE_SELECT;
292                         }
293                 }
294                 
295                 /* add all new strokes in temp buffer to the frame (preventing double-copies) */
296                 BLI_movelisttolist(&gpf->strokes, &new_strokes);
297                 BLI_assert(new_strokes.first == NULL);
298         }
299         CTX_DATA_END;
300         
301         /* updates */
302         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
303         
304         return OPERATOR_FINISHED;
305 }
306
307 void GPENCIL_OT_duplicate(wmOperatorType *ot)
308 {
309         /* identifiers */
310         ot->name = "Duplicate Strokes";
311         ot->idname = "GPENCIL_OT_duplicate";
312         ot->description = "Duplicate the selected Grease Pencil strokes";
313         
314         /* callbacks */
315         ot->exec = gp_duplicate_exec;
316         ot->poll = gp_stroke_edit_poll;
317         
318         /* flags */
319         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
320 }
321
322 /* ******************* Copy/Paste Strokes ************************* */
323 /* Grease Pencil stroke data copy/paste buffer:
324  * - The copy operation collects all segments of selected strokes,
325  *   dumping "ready to be copied" copies of the strokes into the buffer.
326  * - The paste operation makes a copy of those elements, and adds them
327  *   to the active layer. This effectively flattens down the strokes
328  *   from several different layers into a single layer.
329  */
330
331 /* list of bGPDstroke instances */
332 /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
333 ListBase gp_strokes_copypastebuf = {NULL, NULL};
334
335 /* Free copy/paste buffer data */
336 void ED_gpencil_strokes_copybuf_free(void)
337 {
338         bGPDstroke *gps, *gpsn;
339         
340         for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
341                 gpsn = gps->next;
342                 
343                 if (gps->points)    MEM_freeN(gps->points);
344                 if (gps->triangles) MEM_freeN(gps->triangles);
345                 
346                 BLI_freelinkN(&gp_strokes_copypastebuf, gps);
347         }
348         
349         gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
350 }
351
352 /* --------------------- */
353 /* Copy selected strokes */
354
355 static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
356 {
357         bGPdata *gpd = ED_gpencil_data_get_active(C);
358         
359         if (gpd == NULL) {
360                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
361                 return OPERATOR_CANCELLED;
362         }
363         
364         /* clear the buffer first */
365         ED_gpencil_strokes_copybuf_free();
366         
367         /* for each visible (and editable) layer's selected strokes,
368          * copy the strokes into a temporary buffer, then append
369          * once all done
370          */
371         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
372         {
373                 bGPDframe *gpf = gpl->actframe;
374                 bGPDstroke *gps;
375                 
376                 if (gpf == NULL)
377                         continue;
378                 
379                 /* make copies of selected strokes, and deselect these once we're done */
380                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
381                         /* skip strokes that are invalid for current view */
382                         if (ED_gpencil_stroke_can_use(C, gps) == false)
383                                 continue;
384                         
385                         if (gps->flag & GP_STROKE_SELECT) {
386                                 if (gps->totpoints == 1) {
387                                         /* Special Case: If there's just a single point in this stroke... */
388                                         bGPDstroke *gpsd;
389                                         
390                                         /* make direct copies of the stroke and its points */
391                                         gpsd = MEM_dupallocN(gps);
392                                         BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */
393                                         gpsd->points = MEM_dupallocN(gps->points);
394                                         
395                                         /* triangles cache - will be recalculated on next redraw */
396                                         gpsd->flag |= GP_STROKE_RECALC_CACHES;
397                                         gpsd->tot_triangles = 0;
398                                         gpsd->triangles = NULL;
399                                         
400                                         /* add to temp buffer */
401                                         gpsd->next = gpsd->prev = NULL;
402                                         BLI_addtail(&gp_strokes_copypastebuf, gpsd);
403                                 }
404                                 else {
405                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
406                                         gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
407                                 }
408                         }
409                 }
410         }
411         CTX_DATA_END;
412         
413         /* done - no updates needed */
414         return OPERATOR_FINISHED;
415 }
416
417 void GPENCIL_OT_copy(wmOperatorType *ot)
418 {
419         /* identifiers */
420         ot->name = "Copy Strokes";
421         ot->idname = "GPENCIL_OT_copy";
422         ot->description = "Copy selected Grease Pencil points and strokes";
423         
424         /* callbacks */
425         ot->exec = gp_strokes_copy_exec;
426         ot->poll = gp_stroke_edit_poll;
427         
428         /* flags */
429         //ot->flag = OPTYPE_REGISTER;
430 }
431
432 /* --------------------- */
433 /* Paste selected strokes */
434
435 static int gp_strokes_paste_poll(bContext *C)
436 {
437         /* 1) Must have GP datablock to paste to
438          *    - We don't need to have an active layer though, as that can easily get added
439          *    - If the active layer is locked, we can't paste there, but that should prompt a warning instead
440          * 2) Copy buffer must at least have something (though it may be the wrong sort...)
441          */
442         return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
443 }
444
445 typedef enum eGP_PasteMode {
446         GP_COPY_ONLY = -1,
447         GP_COPY_MERGE = 1
448 } eGP_PasteMode;
449
450 static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
451 {
452         Scene *scene = CTX_data_scene(C);
453         bGPdata *gpd = ED_gpencil_data_get_active(C);
454         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
455         bGPDframe *gpf;
456         
457         eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
458         
459         /* check for various error conditions */
460         if (gpd == NULL) {
461                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
462                 return OPERATOR_CANCELLED;
463         }
464         else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
465                 BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
466                 return OPERATOR_CANCELLED;
467         }
468         else if (gpl == NULL) {
469                 /* no active layer - let's just create one */
470                 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
471         }
472         else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
473                 BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
474                 return OPERATOR_CANCELLED;
475         }
476         else {
477                 /* Check that some of the strokes in the buffer can be used */
478                 bGPDstroke *gps;
479                 bool ok = false;
480                 
481                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
482                         if (ED_gpencil_stroke_can_use(C, gps)) {
483                                 ok = true;
484                                 break;
485                         }
486                 }
487                 
488                 if (ok == false) {
489                         /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
490                          * but should be enough to give users a good idea of what's going on
491                          */
492                         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
493                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
494                         else
495                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
496                                 
497                         return OPERATOR_CANCELLED;
498                 }
499         }
500         
501         /* Deselect all strokes first */
502         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
503         {
504                 bGPDspoint *pt;
505                 int i;
506                 
507                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
508                         pt->flag &= ~GP_SPOINT_SELECT;
509                 }
510                 
511                 gps->flag &= ~GP_STROKE_SELECT;
512         }
513         CTX_DATA_END;
514         
515         for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
516                 if (ED_gpencil_stroke_can_use(C, gps)) {
517                         /* Need to verify if layer exists */
518                         if (type != GP_COPY_MERGE) {
519                                 gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info));
520                                 if (gpl == NULL) {
521                                         /* no layer - use active (only if layer deleted before paste) */
522                                         gpl = CTX_data_active_gpencil_layer(C);
523                                 }
524                         }
525                         
526                         /* Ensure we have a frame to draw into
527                          * NOTE: Since this is an op which creates strokes,
528                          *       we are obliged to add a new frame if one
529                          *       doesn't exist already
530                          */
531                         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
532                         if (gpf) {
533                                 bGPDstroke *new_stroke = MEM_dupallocN(gps);
534                                 new_stroke->tmp_layerinfo[0] = '\0';
535                                 
536                                 new_stroke->points = MEM_dupallocN(gps->points);
537                                 
538                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
539                                 new_stroke->triangles = NULL;
540                                 
541                                 new_stroke->next = new_stroke->prev = NULL;
542                                 BLI_addtail(&gpf->strokes, new_stroke);
543                         }
544                 }
545         }
546         
547         /* updates */
548         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
549         
550         return OPERATOR_FINISHED;
551 }
552
553 void GPENCIL_OT_paste(wmOperatorType *ot)
554 {
555         static EnumPropertyItem copy_type[] = {
556                 {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
557                 {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
558                 {0, NULL, 0, NULL, NULL}
559         };
560         
561         /* identifiers */
562         ot->name = "Paste Strokes";
563         ot->idname = "GPENCIL_OT_paste";
564         ot->description = "Paste previously copied strokes or copy and merge in active layer";
565         
566         /* callbacks */
567         ot->exec = gp_strokes_paste_exec;
568         ot->poll = gp_strokes_paste_poll;
569         
570         /* flags */
571         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
572         
573         /* properties */
574         ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
575 }
576
577 /* ******************* Move To Layer ****************************** */
578
579 static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
580 {
581         uiPopupMenu *pup;
582         uiLayout *layout;
583         
584         /* call the menu, which will call this operator again, hence the canceled */
585         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
586         layout = UI_popup_menu_layout(pup);
587         uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
588         UI_popup_menu_end(C, pup);
589         
590         return OPERATOR_INTERFACE;
591 }
592
593 // FIXME: allow moving partial strokes
594 static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
595 {
596         bGPdata *gpd = CTX_data_gpencil_data(C);
597         bGPDlayer *target_layer = NULL;
598         ListBase strokes = {NULL, NULL};
599         int layer_num = RNA_enum_get(op->ptr, "layer");
600         
601         /* Get layer or create new one */
602         if (layer_num == -1) {
603                 /* Create layer */
604                 target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
605         }
606         else {
607                 /* Try to get layer */
608                 target_layer = BLI_findlink(&gpd->layers, layer_num);
609                 
610                 if (target_layer == NULL) {
611                         BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
612                         return OPERATOR_CANCELLED;
613                 }
614         }
615         
616         /* Extract all strokes to move to this layer
617          * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
618          *       getting repeatedly moved
619          */
620         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
621         {
622                 bGPDframe *gpf = gpl->actframe;
623                 bGPDstroke *gps, *gpsn;
624                 
625                 /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
626                 if ((gpl == target_layer) || (gpf == NULL))
627                         continue;
628                 
629                 /* make copies of selected strokes, and deselect these once we're done */
630                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
631                         gpsn = gps->next;
632                         
633                         /* skip strokes that are invalid for current view */
634                         if (ED_gpencil_stroke_can_use(C, gps) == false)
635                                 continue;
636                         
637                         /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
638                         if (gps->flag & GP_STROKE_SELECT) {
639                                 BLI_remlink(&gpf->strokes, gps);
640                                 BLI_addtail(&strokes, gps);
641                         }
642                 }
643         }
644         CTX_DATA_END;
645         
646         /* Paste them all in one go */
647         if (strokes.first) {
648                 Scene *scene = CTX_data_scene(C);
649                 bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true);
650                 
651                 BLI_movelisttolist(&gpf->strokes, &strokes);
652                 BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
653         }
654         
655         /* updates */
656         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
657         
658         return OPERATOR_FINISHED;
659 }
660
661 void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
662 {
663         /* identifiers */
664         ot->name = "Move Strokes to Layer";
665         ot->idname = "GPENCIL_OT_move_to_layer";
666         ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
667         
668         /* callbacks */
669         ot->invoke = gp_move_to_layer_invoke;
670         ot->exec = gp_move_to_layer_exec;
671         ot->poll = gp_stroke_edit_poll; // XXX?
672         
673         /* flags */
674         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
675         
676         /* gp layer to use (dynamic enum) */
677         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
678         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
679 }
680
681 /* ******************* Delete Active Frame ************************ */
682
683 static int gp_actframe_delete_poll(bContext *C)
684 {
685         bGPdata *gpd = ED_gpencil_data_get_active(C);
686         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
687         
688         /* only if there's an active layer with an active frame */
689         return (gpl && gpl->actframe);
690 }
691
692 /* delete active frame - wrapper around API calls */
693 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
694 {
695         Scene *scene = CTX_data_scene(C);
696         bGPdata *gpd = ED_gpencil_data_get_active(C);
697         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
698         bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
699         
700         /* if there's no existing Grease-Pencil data there, add some */
701         if (gpd == NULL) {
702                 BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
703                 return OPERATOR_CANCELLED;
704         }
705         if (ELEM(NULL, gpl, gpf)) {
706                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
707                 return OPERATOR_CANCELLED;
708         }
709         
710         /* delete it... */
711         BKE_gpencil_layer_delframe(gpl, gpf);
712         
713         /* notifiers */
714         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
715         
716         return OPERATOR_FINISHED;
717 }
718
719 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
720 {
721         /* identifiers */
722         ot->name = "Delete Active Frame";
723         ot->idname = "GPENCIL_OT_active_frame_delete";
724         ot->description = "Delete the active frame for the active Grease Pencil Layer";
725         
726         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
727         
728         /* callbacks */
729         ot->exec = gp_actframe_delete_exec;
730         ot->poll = gp_actframe_delete_poll;
731 }
732
733 /* **************** Delete All Active Frames ****************** */
734
735 static int gp_actframe_delete_all_poll(bContext *C)
736 {
737         bGPdata *gpd = ED_gpencil_data_get_active(C);
738         
739         /* 1) There must be grease pencil data
740          * 2) Hopefully some of the layers have stuff we can use
741          */
742         return (gpd && gpd->layers.first);
743 }
744
745 static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
746 {
747         Scene *scene = CTX_data_scene(C);
748         bool success = false;
749         
750         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
751         {
752                 /* try to get the "active" frame - but only if it actually occurs on this frame */
753                 bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
754                 
755                 if (gpf == NULL)
756                         continue;
757                 
758                 /* delete it... */
759                 BKE_gpencil_layer_delframe(gpl, gpf);
760                 
761                 /* we successfully modified something */
762                 success = true;
763         }
764         CTX_DATA_END;
765         
766         /* updates */
767         if (success) {
768                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);               
769                 return OPERATOR_FINISHED;
770         }
771         else {
772                 BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
773                 return OPERATOR_CANCELLED;
774         }
775 }
776
777 void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
778 {
779         /* identifiers */
780         ot->name = "Delete All Active Frames";
781         ot->idname = "GPENCIL_OT_active_frames_delete_all";
782         ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
783         
784         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
785         
786         /* callbacks */
787         ot->exec = gp_actframe_delete_all_exec;
788         ot->poll = gp_actframe_delete_all_poll;
789 }
790
791 /* ******************* Delete Operator ************************ */
792
793 typedef enum eGP_DeleteMode {
794         /* delete selected stroke points */
795         GP_DELETEOP_POINTS          = 0,
796         /* delete selected strokes */
797         GP_DELETEOP_STROKES         = 1,
798         /* delete active frame */
799         GP_DELETEOP_FRAME           = 2,
800 } eGP_DeleteMode;
801
802 /* ----------------------------------- */
803
804 /* Delete selected strokes */
805 static int gp_delete_selected_strokes(bContext *C)
806 {
807         bool changed = false;
808         
809         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
810         {
811                 bGPDframe *gpf = gpl->actframe;
812                 bGPDstroke *gps, *gpsn;
813                 
814                 if (gpf == NULL)
815                         continue;
816                 
817                 /* simply delete strokes which are selected */
818                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
819                         gpsn = gps->next;
820                         
821                         /* skip strokes that are invalid for current view */
822                         if (ED_gpencil_stroke_can_use(C, gps) == false)
823                                 continue;
824                         
825                         /* free stroke if selected */
826                         if (gps->flag & GP_STROKE_SELECT) {
827                                 /* free stroke memory arrays, then stroke itself */
828                                 if (gps->points) MEM_freeN(gps->points);
829                                 if (gps->triangles) MEM_freeN(gps->triangles);
830                                 BLI_freelinkN(&gpf->strokes, gps);
831                                 
832                                 changed = true;
833                         }
834                 }
835         }
836         CTX_DATA_END;
837         
838         if (changed) {
839                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
840                 return OPERATOR_FINISHED;
841         }
842         else {
843                 return OPERATOR_CANCELLED;
844         }
845 }
846
847 /* ----------------------------------- */
848
849 /* Delete selected points but keep the stroke */
850 static int gp_dissolve_selected_points(bContext *C)
851 {
852         bool changed = false;
853         
854         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
855         {
856                 bGPDframe *gpf = gpl->actframe;
857                 bGPDstroke *gps, *gpsn;
858                 
859                 if (gpf == NULL)
860                         continue;
861                 
862                 /* simply delete points from selected strokes
863                  * NOTE: we may still have to remove the stroke if it ends up having no points!
864                  */
865                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
866                         gpsn = gps->next;
867                         
868                         /* skip strokes that are invalid for current view */
869                         if (ED_gpencil_stroke_can_use(C, gps) == false)
870                                 continue;
871                         
872                         if (gps->flag & GP_STROKE_SELECT) {
873                                 bGPDspoint *pt;
874                                 int i;
875                                 
876                                 int tot = gps->totpoints; /* number of points in new buffer */
877                                 
878                                 /* First Pass: Count how many points are selected (i.e. how many to remove) */
879                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
880                                         if (pt->flag & GP_SPOINT_SELECT) {
881                                                 /* selected point - one of the points to remove */
882                                                 tot--;
883                                         }
884                                 }
885                                 
886                                 /* if no points are left, we simply delete the entire stroke */
887                                 if (tot <= 0) {
888                                         /* remove the entire stroke */
889                                         MEM_freeN(gps->points);
890                                         if (gps->triangles) {
891                                                 MEM_freeN(gps->triangles);
892                                         }
893                                         BLI_freelinkN(&gpf->strokes, gps);
894                                 }
895                                 else {
896                                         /* just copy all unselected into a smaller buffer */
897                                         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
898                                         bGPDspoint *npt        = new_points;
899                                         
900                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
901                                                 if ((pt->flag & GP_SPOINT_SELECT) == 0) {
902                                                         *npt = *pt;
903                                                         npt++;
904                                                 }
905                                         }
906                                         
907                                         /* free the old buffer */
908                                         MEM_freeN(gps->points);
909                                         
910                                         /* save the new buffer */
911                                         gps->points = new_points;
912                                         gps->totpoints = tot;
913                                         
914                                         /* triangles cache needs to be recalculated */
915                                         gps->flag |= GP_STROKE_RECALC_CACHES;
916                                         gps->tot_triangles = 0;
917                                         
918                                         /* deselect the stroke, since none of its selected points will still be selected */
919                                         gps->flag &= ~GP_STROKE_SELECT;
920                                 }
921                                 
922                                 changed = true;
923                         }
924                 }
925         }
926         CTX_DATA_END;
927         
928         if (changed) {
929                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
930                 return OPERATOR_FINISHED;
931         }
932         else {
933                 return OPERATOR_CANCELLED;
934         }
935 }
936
937 /* ----------------------------------- */
938
939 /* Temp data for storing information about an "island" of points
940  * that should be kept when splitting up a stroke. Used in:
941  * gp_stroke_delete_tagged_points()
942  */
943 typedef struct tGPDeleteIsland {
944         int start_idx;
945         int end_idx;
946 } tGPDeleteIsland;
947
948
949 /* Split the given stroke into several new strokes, partitioning
950  * it based on whether the stroke points have a particular flag
951  * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
952  *
953  * The algorithm used here is as follows:
954  * 1) We firstly identify the number of "islands" of non-tagged points
955  *    which will all end up being in new strokes.
956  *    - In the most extreme case (i.e. every other vert is a 1-vert island),
957  *      we have at most n / 2 islands
958  *    - Once we start having larger islands than that, the number required
959  *      becomes much less
960  * 2) Each island gets converted to a new stroke
961  */
962 void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags)
963 {
964         tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
965         bool in_island  = false;
966         int num_islands = 0;
967         
968         bGPDspoint *pt;
969         int i;
970         
971         /* First Pass: Identify start/end of islands */
972         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
973                 if (pt->flag & tag_flags) {
974                         /* selected - stop accumulating to island */
975                         in_island = false;
976                 }
977                 else {
978                         /* unselected - start of a new island? */
979                         int idx;
980                         
981                         if (in_island) {
982                                 /* extend existing island */
983                                 idx = num_islands - 1;
984                                 islands[idx].end_idx = i;
985                         }
986                         else {
987                                 /* start of new island */
988                                 in_island = true;
989                                 num_islands++;
990                                 
991                                 idx = num_islands - 1;
992                                 islands[idx].start_idx = islands[idx].end_idx = i;
993                         }
994                 }
995         }
996         
997         /* Watch out for special case where No islands = All points selected = Delete Stroke only */
998         if (num_islands) {
999                 /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
1000                 int idx;
1001                 
1002                 /* Create each new stroke... */
1003                 for (idx = 0; idx < num_islands; idx++) {
1004                         tGPDeleteIsland *island = &islands[idx];
1005                         bGPDstroke *new_stroke  = MEM_dupallocN(gps);
1006                         
1007                         /* initialize triangle memory  - to be calculated on next redraw */
1008                         new_stroke->triangles = NULL;
1009                         new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1010                         new_stroke->tot_triangles = 0;
1011                         
1012                         /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
1013                         new_stroke->totpoints = island->end_idx - island->start_idx + 1;
1014                         new_stroke->points    = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
1015                         
1016                         /* Copy over the relevant points */
1017                         memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
1018                         
1019                         
1020                         /* Each island corresponds to a new stroke. We must adjust the 
1021                          * timings of these new strokes:
1022                          *
1023                          * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
1024                          * the start of the stroke, we have to offset this inittime and all remaining points' delta values.
1025                          * This way we get a new stroke with exactly the same timing as if user had started drawing from
1026                          * the first non-removed point...
1027                          */
1028                         {
1029                                 bGPDspoint *pts;
1030                                 float delta = gps->points[island->start_idx].time;
1031                                 int j;
1032                                 
1033                                 new_stroke->inittime += (double)delta;
1034                                 
1035                                 pts = new_stroke->points;
1036                                 for (j = 0; j < new_stroke->totpoints; j++, pts++) {
1037                                         pts->time -= delta;
1038                                 }
1039                         }
1040                         
1041                         /* Add new stroke to the frame */
1042                         if (next_stroke) {
1043                                 BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
1044                         }
1045                         else {
1046                                 BLI_addtail(&gpf->strokes, new_stroke);
1047                         }
1048                 }
1049         }
1050         
1051         /* free islands */
1052         MEM_freeN(islands);
1053         
1054         /* Delete the old stroke */
1055         MEM_freeN(gps->points);
1056         if (gps->triangles) {
1057                 MEM_freeN(gps->triangles);
1058         }
1059         BLI_freelinkN(&gpf->strokes, gps);
1060 }
1061
1062
1063 /* Split selected strokes into segments, splitting on selected points */
1064 static int gp_delete_selected_points(bContext *C)
1065 {
1066         bool changed = false;
1067         
1068         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1069         {
1070                 bGPDframe *gpf = gpl->actframe;
1071                 bGPDstroke *gps, *gpsn;
1072                 
1073                 if (gpf == NULL)
1074                         continue;
1075                 
1076                 /* simply delete strokes which are selected */
1077                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1078                         gpsn = gps->next;
1079                         
1080                         /* skip strokes that are invalid for current view */
1081                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1082                                 continue;
1083                         
1084                         
1085                         if (gps->flag & GP_STROKE_SELECT) {
1086                                 /* deselect old stroke, since it will be used as template for the new strokes */
1087                                 gps->flag &= ~GP_STROKE_SELECT;
1088                                 
1089                                 /* delete unwanted points by splitting stroke into several smaller ones */
1090                                 gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT);
1091                                 
1092                                 changed = true;
1093                         }
1094                 }
1095         }
1096         CTX_DATA_END;
1097         
1098         if (changed) {
1099                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1100                 return OPERATOR_FINISHED;
1101         }
1102         else {
1103                 return OPERATOR_CANCELLED;
1104         }
1105 }
1106
1107 /* ----------------------------------- */
1108
1109 static int gp_delete_exec(bContext *C, wmOperator *op)
1110 {
1111         eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
1112         int result = OPERATOR_CANCELLED;
1113         
1114         switch (mode) {
1115                 case GP_DELETEOP_STROKES:       /* selected strokes */
1116                         result = gp_delete_selected_strokes(C);
1117                         break;
1118                 
1119                 case GP_DELETEOP_POINTS:        /* selected points (breaks the stroke into segments) */
1120                         result = gp_delete_selected_points(C);
1121                         break;
1122
1123                 case GP_DELETEOP_FRAME:         /* active frame */
1124                         result = gp_actframe_delete_exec(C, op);
1125                         break;
1126         }
1127         
1128         return result;
1129 }
1130
1131 void GPENCIL_OT_delete(wmOperatorType *ot)
1132 {
1133         static EnumPropertyItem prop_gpencil_delete_types[] = {
1134                 {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
1135                 {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
1136                 {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
1137                 {0, NULL, 0, NULL, NULL}
1138         };
1139         
1140         /* identifiers */
1141         ot->name = "Delete";
1142         ot->idname = "GPENCIL_OT_delete";
1143         ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
1144         
1145         /* callbacks */
1146         ot->invoke = WM_menu_invoke;
1147         ot->exec = gp_delete_exec;
1148         ot->poll = gp_stroke_edit_poll;
1149         
1150         /* flags */
1151         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1152         
1153         /* props */
1154         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
1155 }
1156
1157 static int gp_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
1158 {
1159         return gp_dissolve_selected_points(C);
1160 }
1161
1162 void GPENCIL_OT_dissolve(wmOperatorType *ot)
1163 {
1164         /* identifiers */
1165         ot->name = "Dissolve";
1166         ot->idname = "GPENCIL_OT_dissolve";
1167         ot->description = "Delete selected points without splitting strokes";
1168
1169         /* callbacks */
1170         ot->exec = gp_dissolve_exec;
1171         ot->poll = gp_stroke_edit_poll;
1172
1173         /* flags */
1174         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1175 }
1176
1177 /* ****************** Snapping - Strokes <-> Cursor ************************ */
1178
1179 /* Poll callback for snap operators */
1180 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
1181  *       define a cursor or gridstep which can be used
1182  */
1183 static int gp_snap_poll(bContext *C)
1184 {
1185         bGPdata *gpd = CTX_data_gpencil_data(C);
1186         ScrArea *sa = CTX_wm_area(C);
1187         
1188         return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
1189 }
1190
1191 /* --------------------------------- */
1192
1193 static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
1194 {
1195         bGPdata *gpd = ED_gpencil_data_get_active(C);
1196         RegionView3D *rv3d = CTX_wm_region_data(C);
1197         const float gridf = rv3d->gridview;
1198         
1199         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1200                 /* only editable and visible layers are considered */
1201                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1202                         bGPDframe *gpf = gpl->actframe;
1203                         float diff_mat[4][4];
1204                         
1205                         /* calculate difference matrix if parent object */
1206                         if (gpl->parent != NULL) {
1207                                 ED_gpencil_parent_location(gpl, diff_mat);
1208                         }
1209                         
1210                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1211                                 bGPDspoint *pt;
1212                                 int i;
1213                                 
1214                                 /* skip strokes that are invalid for current view */
1215                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1216                                         continue;
1217                                 /* check if the color is editable */
1218                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1219                                         continue;
1220                                 
1221                                 // TODO: if entire stroke is selected, offset entire stroke by same amount?
1222                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1223                                         /* only if point is selected */
1224                                         if (pt->flag & GP_SPOINT_SELECT) {
1225                                                 if (gpl->parent == NULL) {
1226                                                         pt->x = gridf * floorf(0.5f + pt->x / gridf);
1227                                                         pt->y = gridf * floorf(0.5f + pt->y / gridf);
1228                                                         pt->z = gridf * floorf(0.5f + pt->z / gridf);
1229                                                 }
1230                                                 else {
1231                                                         /* apply parent transformations */
1232                                                         float fpt[3];
1233                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
1234                                                         
1235                                                         fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
1236                                                         fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
1237                                                         fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
1238                                                         
1239                                                         /* return data */
1240                                                         copy_v3_v3(&pt->x, fpt);
1241                                                         gp_apply_parent_point(gpl, pt);
1242                                                 }
1243                                         }
1244                                 }
1245                         }
1246                 }
1247         }
1248         
1249         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1250         return OPERATOR_FINISHED;
1251 }
1252
1253 void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
1254 {
1255         /* identifiers */
1256         ot->name = "Snap Selection to Grid";
1257         ot->idname = "GPENCIL_OT_snap_to_grid";
1258         ot->description = "Snap selected points to the nearest grid points";
1259         
1260         /* callbacks */
1261         ot->exec = gp_snap_to_grid;
1262         ot->poll = gp_snap_poll;
1263         
1264         /* flags */
1265         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1266 }
1267
1268 /* ------------------------------- */
1269
1270 static int gp_snap_to_cursor(bContext *C, wmOperator *op)
1271 {
1272         bGPdata *gpd = ED_gpencil_data_get_active(C);
1273         
1274         Scene *scene = CTX_data_scene(C);
1275         View3D *v3d = CTX_wm_view3d(C);
1276         
1277         const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
1278         const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d);
1279         
1280         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1281                 /* only editable and visible layers are considered */
1282                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1283                         bGPDframe *gpf = gpl->actframe;
1284                         float diff_mat[4][4];
1285                         
1286                         /* calculate difference matrix if parent object */
1287                         if (gpl->parent != NULL) {
1288                                 ED_gpencil_parent_location(gpl, diff_mat);
1289                         }
1290                         
1291                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1292                                 bGPDspoint *pt;
1293                                 int i;
1294                                 
1295                                 /* skip strokes that are invalid for current view */
1296                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1297                                         continue;
1298                                 /* check if the color is editable */
1299                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1300                                         continue;
1301                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
1302                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
1303                                         continue;
1304                                 
1305                                 if (use_offset) {
1306                                         float offset[3];
1307                                         
1308                                         /* compute offset from first point of stroke to cursor */
1309                                         /* TODO: Allow using midpoint instead? */
1310                                         sub_v3_v3v3(offset, cursor_global, &gps->points->x);
1311                                         
1312                                         /* apply offset to all points in the stroke */
1313                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1314                                                 add_v3_v3(&pt->x, offset);
1315                                         }
1316                                 }
1317                                 else {
1318                                         /* affect each selected point */
1319                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1320                                                 if (pt->flag & GP_SPOINT_SELECT) {
1321                                                         copy_v3_v3(&pt->x, cursor_global);
1322                                                         if (gpl->parent != NULL) {
1323                                                                 gp_apply_parent_point(gpl, pt);
1324                                                         }
1325                                                 }
1326                                         }
1327                                 }
1328                         }
1329                         
1330                 }
1331         }
1332         
1333         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1334         return OPERATOR_FINISHED;
1335 }
1336
1337 void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
1338 {
1339         /* identifiers */
1340         ot->name = "Snap Selection to Cursor";
1341         ot->idname = "GPENCIL_OT_snap_to_cursor";
1342         ot->description = "Snap selected points/strokes to the cursor";
1343         
1344         /* callbacks */
1345         ot->exec = gp_snap_to_cursor;
1346         ot->poll = gp_snap_poll;
1347         
1348         /* flags */
1349         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1350         
1351         /* props */
1352         ot->prop = RNA_def_boolean(ot->srna, "use_offset", true, "With Offset",
1353                                    "Offset the entire stroke instead of selected points only");
1354 }
1355
1356 /* ------------------------------- */
1357
1358 static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
1359 {
1360         bGPdata *gpd = ED_gpencil_data_get_active(C);
1361         
1362         Scene *scene = CTX_data_scene(C);
1363         View3D *v3d = CTX_wm_view3d(C);
1364         
1365         float *cursor = ED_view3d_cursor3d_get(scene, v3d);
1366         float centroid[3] = {0.0f};
1367         float min[3], max[3];
1368         size_t count = 0;
1369         
1370         INIT_MINMAX(min, max);
1371         
1372         /* calculate midpoints from selected points */
1373         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1374                 /* only editable and visible layers are considered */
1375                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1376                         bGPDframe *gpf = gpl->actframe;
1377                         float diff_mat[4][4];
1378                         
1379                         /* calculate difference matrix if parent object */
1380                         if (gpl->parent != NULL) {
1381                                 ED_gpencil_parent_location(gpl, diff_mat);
1382                         }
1383                         
1384                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1385                                 bGPDspoint *pt;
1386                                 int i;
1387                                 
1388                                 /* skip strokes that are invalid for current view */
1389                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1390                                         continue;
1391                                 /* check if the color is editable */
1392                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1393                                         continue;
1394                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
1395                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
1396                                         continue;
1397                                 
1398                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1399                                         if (pt->flag & GP_SPOINT_SELECT) {
1400                                                 if (gpl->parent == NULL) {
1401                                                         add_v3_v3(centroid, &pt->x);
1402                                                         minmax_v3v3_v3(min, max, &pt->x);
1403                                                 }
1404                                                 else {
1405                                                         /* apply parent transformations */
1406                                                         float fpt[3];
1407                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
1408                                                         
1409                                                         add_v3_v3(centroid, fpt);
1410                                                         minmax_v3v3_v3(min, max, fpt);
1411                                                 }
1412                                                 count++;
1413                                         }
1414                                 }
1415                                 
1416                         }
1417                 }
1418         }
1419         
1420         if (v3d->around == V3D_AROUND_CENTER_MEAN && count) {
1421                 mul_v3_fl(centroid, 1.0f / (float)count);
1422                 copy_v3_v3(cursor, centroid);
1423         }
1424         else {
1425                 mid_v3_v3v3(cursor, min, max);
1426         }
1427
1428         
1429         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1430         return OPERATOR_FINISHED;
1431 }
1432
1433 void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
1434 {
1435         /* identifiers */
1436         ot->name = "Snap Cursor to Selected Points";
1437         ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
1438         ot->description = "Snap cursor to center of selected points";
1439         
1440         /* callbacks */
1441         ot->exec = gp_snap_cursor_to_sel;
1442         ot->poll = gp_snap_poll;
1443         
1444         /* flags */
1445         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1446 }
1447
1448 /* ******************* Apply layer thickness change to strokes ************************** */
1449
1450 static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
1451 {
1452         bGPdata *gpd = ED_gpencil_data_get_active(C);
1453         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1454
1455         /* sanity checks */
1456         if (ELEM(NULL, gpd, gpl, gpl->frames.first))
1457                 return OPERATOR_CANCELLED;
1458
1459         /* loop all strokes */
1460         for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
1461                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1462                         /* Apply thickness */
1463                         gps->thickness = gps->thickness + gpl->thickness;
1464                 }
1465         }
1466         /* clear value */
1467         gpl->thickness = 0.0f;
1468
1469         /* notifiers */
1470         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1471
1472         return OPERATOR_FINISHED;
1473 }
1474
1475 void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
1476 {
1477         /* identifiers */
1478         ot->name = "Apply Stroke Thickness";
1479         ot->idname = "GPENCIL_OT_stroke_apply_thickness";
1480         ot->description = "Apply the thickness change of the layer to its strokes";
1481
1482         /* api callbacks */
1483         ot->exec = gp_stroke_apply_thickness_exec;
1484         ot->poll = gp_active_layer_poll;
1485 }
1486
1487 /* ******************* Close Strokes ************************** */
1488
1489 enum {
1490         GP_STROKE_CYCLIC_CLOSE = 1,
1491         GP_STROKE_CYCLIC_OPEN = 2,
1492         GP_STROKE_CYCLIC_TOGGLE = 3
1493 };
1494
1495 static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
1496 {
1497         bGPdata *gpd = ED_gpencil_data_get_active(C);
1498         const int type = RNA_enum_get(op->ptr, "type");
1499         
1500         /* sanity checks */
1501         if (ELEM(NULL, gpd))
1502                 return OPERATOR_CANCELLED;
1503         
1504         /* loop all selected strokes */
1505         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1506         {
1507                 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1508                         bGPDpalettecolor *palcolor = gps->palcolor;
1509                         
1510                         /* skip strokes that are not selected or invalid for current view */
1511                         if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
1512                                 continue;
1513                         /* skip hidden or locked colors */
1514                         if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
1515                                 continue;
1516                         
1517                         switch (type) {
1518                                 case GP_STROKE_CYCLIC_CLOSE:
1519                                         /* Close all (enable) */
1520                                         gps->flag |= GP_STROKE_CYCLIC;
1521                                         break;
1522                                 case GP_STROKE_CYCLIC_OPEN:
1523                                         /* Open all (disable) */
1524                                         gps->flag &= ~GP_STROKE_CYCLIC;
1525                                         break;
1526                                 case GP_STROKE_CYCLIC_TOGGLE:
1527                                         /* Just toggle flag... */
1528                                         gps->flag ^= GP_STROKE_CYCLIC;
1529                                         break;
1530                                 default:
1531                                         BLI_assert(0);
1532                                         break;
1533                         }
1534                 }
1535         }
1536         CTX_DATA_END;
1537         
1538         /* notifiers */
1539         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1540         
1541         return OPERATOR_FINISHED;
1542 }
1543
1544 /**
1545  * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
1546  * option to force opened/closed strokes instead of just toggle behavior.
1547  */
1548 void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
1549 {
1550         static EnumPropertyItem cyclic_type[] = {
1551                 {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
1552                 {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
1553                 {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1554                 {0, NULL, 0, NULL, NULL}
1555         };
1556         
1557         /* identifiers */
1558         ot->name = "Set Cyclical State";
1559         ot->idname = "GPENCIL_OT_stroke_cyclical_set";
1560         ot->description = "Close or open the selected stroke adding an edge from last to first point";
1561         
1562         /* api callbacks */
1563         ot->exec = gp_stroke_cyclical_set_exec;
1564         ot->poll = gp_active_layer_poll;
1565         
1566         /* flags */
1567         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1568         
1569         /* properties */
1570         ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
1571 }
1572
1573 /* ******************* Stroke join ************************** */
1574
1575 /* Helper: flip stroke */
1576 static void gpencil_flip_stroke(bGPDstroke *gps)
1577 {
1578         int end = gps->totpoints - 1;
1579         
1580         for (int i = 0; i < gps->totpoints / 2; i++) {
1581                 bGPDspoint *point, *point2;
1582                 bGPDspoint pt;
1583         
1584                 /* save first point */
1585                 point = &gps->points[i];
1586                 pt.x = point->x;
1587                 pt.y = point->y;
1588                 pt.z = point->z;
1589                 pt.flag = point->flag;
1590                 pt.pressure = point->pressure;
1591                 pt.strength = point->strength;
1592                 pt.time = point->time;
1593                 
1594                 /* replace first point with last point */
1595                 point2 = &gps->points[end];
1596                 point->x = point2->x;
1597                 point->y = point2->y;
1598                 point->z = point2->z;
1599                 point->flag = point2->flag;
1600                 point->pressure = point2->pressure;
1601                 point->strength = point2->strength;
1602                 point->time = point2->time;
1603                 
1604                 /* replace last point with first saved before */
1605                 point = &gps->points[end];
1606                 point->x = pt.x;
1607                 point->y = pt.y;
1608                 point->z = pt.z;
1609                 point->flag = pt.flag;
1610                 point->pressure = pt.pressure;
1611                 point->strength = pt.strength;
1612                 point->time = pt.time;
1613                 
1614                 end--;
1615         }
1616 }
1617
1618 /* Helper: copy point between strokes */
1619 static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
1620                                       float pressure, float strength, float deltatime)
1621 {
1622         bGPDspoint *newpoint;
1623         
1624         gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
1625         gps->totpoints++;
1626         
1627         newpoint = &gps->points[gps->totpoints - 1];
1628         newpoint->x = point->x * delta[0];
1629         newpoint->y = point->y * delta[1];
1630         newpoint->z = point->z * delta[2];
1631         newpoint->flag = point->flag;
1632         newpoint->pressure = pressure;
1633         newpoint->strength = strength;
1634         newpoint->time = point->time + deltatime;
1635 }
1636
1637 /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
1638 static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
1639 {
1640         bGPDspoint point;
1641         bGPDspoint *pt;
1642         int i;
1643         float delta[3] = {1.0f, 1.0f, 1.0f};
1644         float deltatime = 0.0f;
1645         
1646         /* sanity checks */
1647         if (ELEM(NULL, gps_a, gps_b))
1648                 return;
1649         
1650         if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
1651                 return;
1652         
1653         /* define start and end points of each stroke */
1654         float sa[3], sb[3], ea[3], eb[3];
1655         pt = &gps_a->points[0];
1656         copy_v3_v3(sa, &pt->x);
1657         
1658         pt = &gps_a->points[gps_a->totpoints - 1];
1659         copy_v3_v3(ea, &pt->x);
1660         
1661         pt = &gps_b->points[0];
1662         copy_v3_v3(sb, &pt->x);
1663         
1664         pt = &gps_b->points[gps_b->totpoints - 1];
1665         copy_v3_v3(eb, &pt->x);
1666         
1667         /* review if need flip stroke B */
1668         float ea_sb = len_squared_v3v3(ea, sb);
1669         float ea_eb = len_squared_v3v3(ea, eb);
1670         /* flip if distance to end point is shorter */
1671         if (ea_eb < ea_sb) {
1672                 gpencil_flip_stroke(gps_b);
1673         }
1674         
1675         /* don't visibly link the first and last points? */
1676         if (leave_gaps) {
1677                 /* 1st: add one tail point to start invisible area */
1678                 point = gps_a->points[gps_a->totpoints - 1];
1679                 deltatime = point.time;
1680                 gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
1681                 
1682                 /* 2nd: add one head point to finish invisible area */
1683                 point = gps_b->points[0];
1684                 gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
1685         }
1686         
1687         /* 3rd: add all points */
1688         for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
1689                 /* check if still room in buffer */
1690                 if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
1691                         gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
1692                 }
1693         }
1694 }
1695
1696 static int gp_stroke_join_exec(bContext *C, wmOperator *op)
1697 {
1698         bGPdata *gpd = ED_gpencil_data_get_active(C);
1699         bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
1700         bGPDstroke *gps, *gpsn;
1701         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1702         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1703         
1704         bGPDframe *gpf_a = NULL;
1705         bGPDstroke *stroke_a = NULL;
1706         bGPDstroke *stroke_b = NULL;
1707         bGPDstroke *new_stroke = NULL;
1708         
1709         const int type = RNA_enum_get(op->ptr, "type");
1710         const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
1711         
1712         /* sanity checks */
1713         if (ELEM(NULL, gpd))
1714                 return OPERATOR_CANCELLED;
1715         
1716         if (activegpl->flag & GP_LAYER_LOCKED)
1717                 return OPERATOR_CANCELLED;
1718         
1719         BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
1720         
1721         
1722         /* read all selected strokes */
1723         bool first = false;
1724         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1725         {
1726                 bGPDframe *gpf = gpl->actframe;
1727                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1728                         gpsn = gps->next;
1729                         if (gps->flag & GP_STROKE_SELECT) {
1730                                 /* skip strokes that are invalid for current view */
1731                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1732                                         continue;
1733                                 }
1734                                 /* check if the color is editable */
1735                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
1736                                         continue;
1737                                 }
1738                                 
1739                                 /* to join strokes, cyclic must be disabled */
1740                                 gps->flag &= ~GP_STROKE_CYCLIC;
1741                                 
1742                                 /* saves first frame and stroke */
1743                                 if (!first) {
1744                                         first = true;
1745                                         gpf_a = gpf;
1746                                         stroke_a = gps;
1747                                 }
1748                                 else {
1749                                         stroke_b = gps;
1750                                         
1751                                         /* create a new stroke if was not created before (only created if something to join) */
1752                                         if (new_stroke == NULL) {
1753                                                 new_stroke = MEM_dupallocN(stroke_a);
1754                                                 new_stroke->points = MEM_dupallocN(stroke_a->points);
1755                                                 new_stroke->triangles = NULL;
1756                                                 new_stroke->tot_triangles = 0;
1757                                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1758                                                 
1759                                                 /* if new, set current color */
1760                                                 if (type == GP_STROKE_JOINCOPY) {
1761                                                         new_stroke->palcolor = palcolor;
1762                                                         BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
1763                                                         new_stroke->flag |= GP_STROKE_RECALC_COLOR;
1764                                                 }
1765                                         }
1766                                         
1767                                         /* join new_stroke and stroke B. New stroke will contain all the previous data */
1768                                         gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
1769                                         
1770                                         /* if join only, delete old strokes */
1771                                         if (type == GP_STROKE_JOIN) {
1772                                                 if (stroke_a) {
1773                                                         BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
1774                                                         BLI_remlink(&gpf->strokes, stroke_a);
1775                                                         BKE_gpencil_free_stroke(stroke_a);
1776                                                         stroke_a = NULL;
1777                                                 }
1778                                                 if (stroke_b) {
1779                                                         BLI_remlink(&gpf->strokes, stroke_b);
1780                                                         BKE_gpencil_free_stroke(stroke_b);
1781                                                         stroke_b = NULL;
1782                                                 }
1783                                         }
1784                                 }
1785                         }
1786                 }
1787         }
1788         CTX_DATA_END;
1789         
1790         /* add new stroke if was not added before */
1791         if (type == GP_STROKE_JOINCOPY) {
1792                 if (new_stroke) {
1793                         /* Add a new frame if needed */
1794                         if (activegpl->actframe == NULL)
1795                                 activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
1796                         
1797                         BLI_addtail(&activegpl->actframe->strokes, new_stroke);
1798                 }
1799         }
1800         
1801         /* notifiers */
1802         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1803         
1804         return OPERATOR_FINISHED;
1805 }
1806
1807 void GPENCIL_OT_stroke_join(wmOperatorType *ot)
1808 {
1809         static EnumPropertyItem join_type[] = {
1810                 {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
1811                 {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
1812                 {0, NULL, 0, NULL, NULL}
1813         };
1814         
1815         /* identifiers */
1816         ot->name = "Join Strokes";
1817         ot->idname = "GPENCIL_OT_stroke_join";
1818         ot->description = "Join selected strokes (optionally as new stroke)";
1819         
1820         /* api callbacks */
1821         ot->exec = gp_stroke_join_exec;
1822         ot->poll = gp_active_layer_poll;
1823         
1824         /* flags */
1825         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1826         
1827         /* properties */
1828         ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
1829         RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
1830 }
1831
1832 /* ******************* Stroke flip ************************** */
1833
1834 static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
1835 {
1836         bGPdata *gpd = ED_gpencil_data_get_active(C);
1837
1838         /* sanity checks */
1839         if (ELEM(NULL, gpd))
1840                 return OPERATOR_CANCELLED;
1841
1842         /* read all selected strokes */
1843         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1844         {
1845                 bGPDframe *gpf = gpl->actframe;
1846                 if (gpf == NULL)
1847                         continue;
1848                         
1849                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1850                         if (gps->flag & GP_STROKE_SELECT) {
1851                                 /* skip strokes that are invalid for current view */
1852                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1853                                         continue;
1854                                 }
1855                                 /* check if the color is editable */
1856                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
1857                                         continue;
1858                                 }
1859                                 
1860                                 /* flip stroke */
1861                                 gpencil_flip_stroke(gps);
1862                         }
1863                 }
1864         }
1865         CTX_DATA_END;
1866         
1867         /* notifiers */
1868         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1869         
1870         return OPERATOR_FINISHED;
1871 }
1872
1873 void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
1874 {
1875         /* identifiers */
1876         ot->name = "Flip Stroke";
1877         ot->idname = "GPENCIL_OT_stroke_flip";
1878         ot->description = "Change direction of the points of the selected strokes";
1879         
1880         /* api callbacks */
1881         ot->exec = gp_stroke_flip_exec;
1882         ot->poll = gp_active_layer_poll;
1883         
1884         /* flags */
1885         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1886 }
1887
1888 /* ***************** Reproject Strokes ********************** */
1889
1890 static int gp_strokes_reproject_poll(bContext *C)
1891 {
1892         /* 2 Requirements:
1893          *  - 1) Editable GP data
1894          *  - 2) 3D View only (2D editors don't have projection issues)
1895          */
1896         return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C));
1897 }
1898
1899 static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op))
1900 {
1901         Scene *scene = CTX_data_scene(C);
1902         GP_SpaceConversion gsc = {NULL};
1903         
1904         /* init space conversion stuff */
1905         gp_point_conversion_init(C, &gsc);
1906         
1907         /* Go through each editable + selected stroke, adjusting each of its points one by one... */
1908         GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
1909         {
1910                 if (gps->flag & GP_STROKE_SELECT) {
1911                         bGPDspoint *pt;
1912                         int i;
1913                         float inverse_diff_mat[4][4];
1914                         
1915                         /* Compute inverse matrix for unapplying parenting once instead of doing per-point */
1916                         /* TODO: add this bit to the iteration macro? */
1917                         if (gpl->parent) {
1918                                 invert_m4_m4(inverse_diff_mat, diff_mat);
1919                         }
1920                         
1921                         /* Adjust each point */
1922                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1923                                 float xy[2];
1924                                 
1925                                 /* 3D to Screenspace */
1926                                 /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace
1927                                  *       coordinates, resulting in lost precision, which in turn causes stairstepping
1928                                  *       artifacts in the final points.
1929                                  */
1930                                 if (gpl->parent == NULL) {
1931                                         gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]);
1932                                 }
1933                                 else {
1934                                         bGPDspoint pt2;
1935                                         gp_point_to_parent_space(pt, diff_mat, &pt2);
1936                                         gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
1937                                 }
1938                                 
1939                                 /* Project screenspace back to 3D space (from current perspective)
1940                                  * so that all points have been treated the same way
1941                                  */
1942                                 gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
1943                                 
1944                                 /* Unapply parent corrections */
1945                                 if (gpl->parent) {
1946                                         mul_m4_v3(inverse_diff_mat, &pt->x);
1947                                 }
1948                         }
1949                 }
1950         }
1951         GP_EDITABLE_STROKES_END;
1952         
1953         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1954         return OPERATOR_FINISHED;
1955 }
1956
1957 void GPENCIL_OT_reproject(wmOperatorType *ot)
1958 {
1959         /* identifiers */
1960         ot->name = "Reproject Strokes";
1961         ot->idname = "GPENCIL_OT_reproject";
1962         ot->description = "Reproject the selected strokes from the current viewpoint to get all points on the same plane again "
1963                           "(e.g. to fix problems from accidental 3D cursor movement, or viewport changes)";
1964         
1965         /* callbacks */
1966         ot->exec = gp_strokes_reproject_exec;
1967         ot->poll = gp_strokes_reproject_poll;
1968         
1969         /* flags */
1970         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1971 }
1972
1973 /* =========  Interpolation operators ========================== */
1974 /* Helper: Update point with interpolation */
1975 static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor)
1976 {
1977         bGPDspoint *prev, *pt, *next;
1978
1979         /* update points */
1980         for (int i = 0; i < new_stroke->totpoints; i++) {
1981                 prev = &gps_from->points[i];
1982                 pt = &new_stroke->points[i];
1983                 next = &gps_to->points[i];
1984
1985                 /* Interpolate all values */
1986                 interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor);
1987                 pt->pressure = interpf(prev->pressure, next->pressure, factor);
1988                 pt->strength = interpf(prev->strength, next->strength, factor);
1989                 CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
1990         }
1991 }
1992
1993 /* Helper: Update all strokes interpolated */
1994 static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi)
1995 {
1996         tGPDinterpolate_layer *tgpil;
1997         bGPDstroke *new_stroke, *gps_from, *gps_to;
1998         int cStroke;
1999         float factor;
2000         float shift = tgpi->shift;
2001
2002         for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
2003                 factor = tgpil->factor + shift;
2004                 for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) {
2005                         if (new_stroke->totpoints == 0) {
2006                                 continue;
2007                         }
2008                         /* get strokes to interpolate */
2009                         cStroke = BLI_findindex(&tgpil->interFrame->strokes, new_stroke);
2010                         gps_from = BLI_findlink(&tgpil->prevFrame->strokes, cStroke);
2011                         gps_to = BLI_findlink(&tgpil->nextFrame->strokes, cStroke);
2012                         /* update points position */
2013                         if ((gps_from) && (gps_to)) {
2014                                 gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
2015                         }
2016                 }
2017         }
2018
2019         WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
2020 }
2021
2022 /* Helper: Verify valid strokes for interpolation */
2023 static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd)
2024 {
2025         ToolSettings *ts = CTX_data_tool_settings(C);
2026         int flag = ts->gp_sculpt.flag;
2027
2028         bGPDlayer *gpl;
2029         bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
2030         bGPDstroke *gps_from, *gps_to;
2031         int fFrame;
2032
2033         /* get layers */
2034         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2035                 /* all layers or only active */
2036                 if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
2037                         continue;
2038                 }
2039                 /* only editable and visible layers are considered */
2040                 if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
2041                         continue;
2042                 }
2043                 /* read strokes */
2044                 for (gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) {
2045                         /* only selected */
2046                         if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
2047                                 continue;
2048                         }
2049                         /* skip strokes that are invalid for current view */
2050                         if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
2051                                 continue;
2052                         }
2053                         /* check if the color is editable */
2054                         if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) {
2055                                 continue;
2056                         }
2057                         /* get final stroke to interpolate */
2058                         fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from);
2059                         gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame);
2060                         if (gps_to == NULL) {
2061                                 continue;
2062                         }
2063                         return 1;
2064                 }
2065         }
2066         return 0;
2067 }
2068
2069 /* Helper: Create internal strokes interpolated */
2070 static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
2071 {
2072         bGPDlayer *gpl;
2073         bGPdata *gpd = tgpi->gpd;
2074         tGPDinterpolate_layer *tgpil;
2075         bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
2076         bGPDstroke *gps_from, *gps_to, *new_stroke;
2077         int fFrame;
2078
2079         /* save initial factor for active layer to define shift limits */
2080         tgpi->init_factor = (float)(tgpi->cframe - active_gpl->actframe->framenum) / (active_gpl->actframe->next->framenum - active_gpl->actframe->framenum + 1);
2081         /* limits are 100% below 0 and 100% over the 100% */
2082         tgpi->low_limit = -1.0f - tgpi->init_factor;
2083         tgpi->high_limit = 2.0f - tgpi->init_factor;
2084
2085         /* set layers */
2086         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2087                 /* all layers or only active */
2088                 if (((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
2089                         continue;
2090                 }
2091                 /* only editable and visible layers are considered */
2092                 if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
2093                         continue;
2094                 }
2095                 /* create temp data for each layer */
2096                 tgpil = NULL;
2097                 tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
2098
2099                 tgpil->gpl = gpl;
2100                 tgpil->prevFrame = gpl->actframe;
2101                 tgpil->nextFrame = gpl->actframe->next;
2102
2103                 BLI_addtail(&tgpi->ilayers, tgpil);
2104                 /* create a new temporary frame */
2105                 tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe");
2106                 tgpil->interFrame->framenum = tgpi->cframe;
2107
2108                 /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */
2109                 tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1);
2110                 /* create new strokes data with interpolated points reading original stroke */
2111                 for (gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
2112                         bool valid = true;
2113                         /* only selected */
2114                         if ((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
2115                                 valid = false;
2116                         }
2117
2118                         /* skip strokes that are invalid for current view */
2119                         if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
2120                                 valid = false;
2121                         }
2122                         /* check if the color is editable */
2123                         if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) {
2124                                 valid = false;
2125                         }
2126                         /* get final stroke to interpolate */
2127                         fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from);
2128                         gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame);
2129                         if (gps_to == NULL) {
2130                                 valid = false;
2131                         }
2132                         /* create new stroke */
2133                         new_stroke = MEM_dupallocN(gps_from);
2134                         new_stroke->points = MEM_dupallocN(gps_from->points);
2135                         new_stroke->triangles = MEM_dupallocN(gps_from->triangles);
2136                         if (valid) {
2137                                 /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
2138                                 if (gps_from->totpoints > gps_to->totpoints) {
2139                                         new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints);
2140                                         new_stroke->totpoints = gps_to->totpoints;
2141                                         new_stroke->tot_triangles = 0;
2142                                         new_stroke->flag |= GP_STROKE_RECALC_CACHES;
2143                                 }
2144                                 /* update points position */
2145                                 gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
2146                         }
2147                         else {
2148                                 /* need an empty stroke to keep index correct for lookup, but resize to smallest size */
2149                                 new_stroke->totpoints = 0;
2150                                 new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points));
2151                                 new_stroke->tot_triangles = 0;
2152                                 new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles));
2153                         }
2154                         /* add to strokes */
2155                         BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
2156                 }
2157         }
2158 }
2159
2160 /* Helper: calculate shift based on position of mouse (we only use x-axis for now.
2161 * since this is more convenient for users to do), and store new shift value
2162 */
2163 static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event)
2164 {
2165         float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f;
2166         float mpos = event->x - tgpi->ar->winrct.xmin;
2167         if (mpos >= mid) {
2168                 tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid;
2169         }
2170         else {
2171                 tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid);
2172         }
2173
2174         CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
2175         RNA_float_set(op->ptr, "shift", tgpi->shift);
2176 }
2177
2178 /* Helper: Draw status message while the user is running the operator */
2179 static void gpencil_interpolate_status_indicators(tGPDinterpolate *p)
2180 {
2181         Scene *scene = p->scene;
2182         char status_str[UI_MAX_DRAW_STR];
2183         char msg_str[UI_MAX_DRAW_STR];
2184         BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust, Factor"), UI_MAX_DRAW_STR);
2185
2186         if (hasNumInput(&p->num)) {
2187                 char str_offs[NUM_STR_REP_LEN];
2188
2189                 outputNumInput(&p->num, str_offs, &scene->unit);
2190
2191                 BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs);
2192         }
2193         else {
2194                 BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", msg_str, (int)((p->init_factor + p->shift)  * 100.0f));
2195         }
2196
2197         ED_area_headerprint(p->sa, status_str);
2198 }
2199
2200 /* Helper: Update screen and stroke */
2201 static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
2202 {
2203         /* update shift indicator in header */
2204         gpencil_interpolate_status_indicators(tgpi);
2205         /* apply... */
2206         tgpi->shift = RNA_float_get(op->ptr, "shift");
2207         /* update points position */
2208         gp_interpolate_update_strokes(C, tgpi);
2209 }
2210
2211 /* init new temporary interpolation data */
2212 static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
2213 {
2214         ToolSettings *ts = CTX_data_tool_settings(C);
2215         bGPdata *gpd = CTX_data_gpencil_data(C);
2216
2217         /* set current scene and window */
2218         tgpi->scene = CTX_data_scene(C);
2219         tgpi->sa = CTX_wm_area(C);
2220         tgpi->ar = CTX_wm_region(C);
2221         tgpi->flag = ts->gp_sculpt.flag;
2222
2223         /* set current frame number */
2224         tgpi->cframe = tgpi->scene->r.cfra;
2225
2226         /* set GP datablock */
2227         tgpi->gpd = gpd;
2228
2229         /* set interpolation weight */
2230         tgpi->shift = RNA_float_get(op->ptr, "shift");
2231         /* set layers */
2232         gp_interpolate_set_points(C, tgpi);
2233
2234         return 1;
2235 }
2236
2237 /* Poll handler: check if context is suitable for interpolation */
2238 static int gpencil_interpolate_poll(bContext *C)
2239 {
2240         bGPdata * gpd = CTX_data_gpencil_data(C);
2241         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
2242         /* only 3D view */
2243         if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) {
2244                 return 0;
2245         }
2246         /* need data to interpolate */
2247         if (ELEM(NULL, gpd, gpl)) {
2248                 return 0;
2249         }
2250
2251         return 1;
2252 }
2253
2254 /* Allocate memory and initialize values */
2255 static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op)
2256 {
2257         tGPDinterpolate *tgpi = NULL;
2258
2259         /* create new context data */
2260         tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data");
2261
2262         /* define initial values */
2263         gp_interpolate_set_init_values(C, op, tgpi);
2264
2265         /* return context data for running operator */
2266         return tgpi;
2267 }
2268
2269 /* Exit and free memory */
2270 static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
2271 {
2272         tGPDinterpolate *tgpi = op->customdata;
2273         tGPDinterpolate_layer *tgpil;
2274
2275         /* don't assume that operator data exists at all */
2276         if (tgpi) {
2277                 /* remove drawing handler */
2278                 if (tgpi->draw_handle_screen) {
2279                         ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen);
2280                 }
2281                 if (tgpi->draw_handle_3d) {
2282                         ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d);
2283                 }
2284                 /* clear status message area */
2285                 ED_area_headerprint(tgpi->sa, NULL);
2286                 /* finally, free memory used by temp data */
2287                 for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
2288                         BKE_gpencil_free_strokes(tgpil->interFrame);
2289                         MEM_freeN(tgpil->interFrame);
2290                 }
2291
2292                 BLI_freelistN(&tgpi->ilayers);
2293                 MEM_freeN(tgpi);
2294         }
2295         WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
2296
2297         /* clear pointer */
2298         op->customdata = NULL;
2299 }
2300
2301 /* Cancel handler */
2302 static void gpencil_interpolate_cancel(bContext *C, wmOperator *op)
2303 {
2304         /* this is just a wrapper around exit() */
2305         gpencil_interpolate_exit(C, op);
2306 }
2307
2308 /* Init interpolation: Allocate memory and set init values         */
2309 static int gpencil_interpolate_init(bContext *C, wmOperator *op)
2310 {
2311         tGPDinterpolate *tgpi;
2312         /* check context */
2313         tgpi = op->customdata = gp_session_init_interpolation(C, op);
2314         if (tgpi == NULL) {
2315                 /* something wasn't set correctly in context */
2316                 gpencil_interpolate_exit(C, op);
2317                 return 0;
2318         }
2319
2320         /* everything is now setup ok */
2321         return 1;
2322 }
2323
2324 /* ********************** custom drawcall api ***************** */
2325 /* Helper: drawing callback for modal operator in screen mode */
2326 static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
2327 {
2328         wmOperator *op = arg;
2329         struct tGPDinterpolate *tgpi = op->customdata;
2330         ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL);
2331 }
2332
2333 /* Helper: drawing callback for modal operator in 3d mode */
2334 static void gpencil_interpolate_draw_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
2335 {
2336         wmOperator *op = arg;
2337         struct tGPDinterpolate *tgpi = op->customdata;
2338         ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW);
2339 }
2340
2341 /* Invoke handler: Initialize the operator */
2342 static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2343 {
2344         wmWindow *win = CTX_wm_window(C);
2345         Scene *scene = CTX_data_scene(C);
2346         bGPdata * gpd = CTX_data_gpencil_data(C);
2347         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
2348         tGPDinterpolate *tgpi = NULL;
2349
2350         /* cannot interpolate if not between 2 frames */
2351         if ((gpl->actframe == NULL) || (gpl->actframe->next == NULL)) {
2352                 BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames in active layer");
2353                 return OPERATOR_CANCELLED;
2354         }
2355
2356         /* cannot interpolate in extremes */
2357         if ((gpl->actframe->framenum == scene->r.cfra) || (gpl->actframe->next->framenum == scene->r.cfra)) {
2358                 BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames in active layer");
2359                 return OPERATOR_CANCELLED;
2360         }
2361
2362         /* need editable strokes */
2363         if (!gp_interpolate_check_todo(C, gpd)) {
2364                 BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable stroke");
2365                 return OPERATOR_CANCELLED;
2366         }
2367
2368         /* try to initialize context data needed */
2369         if (!gpencil_interpolate_init(C, op)) {
2370                 if (op->customdata)
2371                         MEM_freeN(op->customdata);
2372                 return OPERATOR_CANCELLED;
2373         }
2374         else
2375                 tgpi = op->customdata;
2376
2377         /* enable custom drawing handlers. It needs 2 handlers because can be strokes in 3d space and screen space and each handler use different
2378            coord system */
2379         tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, op, REGION_DRAW_POST_PIXEL);
2380         tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, op, REGION_DRAW_POST_VIEW);
2381         /* set cursor to indicate modal */
2382         WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR);
2383         /* update shift indicator in header */
2384         gpencil_interpolate_status_indicators(tgpi);
2385         WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
2386
2387         /* add a modal handler for this operator */
2388         WM_event_add_modal_handler(C, op);
2389
2390         return OPERATOR_RUNNING_MODAL;
2391 }
2392
2393 /* Modal handler: Events handling during interactive part */
2394 static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event)
2395 {
2396         tGPDinterpolate *tgpi = op->customdata;
2397         wmWindow *win = CTX_wm_window(C);
2398         bGPDframe *gpf_dst;
2399         bGPDstroke *gps_src, *gps_dst;
2400         tGPDinterpolate_layer *tgpil;
2401         const bool has_numinput = hasNumInput(&tgpi->num);
2402
2403         switch (event->type) {
2404                 case LEFTMOUSE: /* confirm */
2405                 case RETKEY:
2406                 {
2407                         /* return to normal cursor and header status */
2408                         ED_area_headerprint(tgpi->sa, NULL);
2409                         WM_cursor_modal_restore(win);
2410
2411                         /* insert keyframes as required... */
2412                         for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) {
2413                                 gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
2414                                 gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN;
2415                                 
2416                                 /* copy strokes */
2417                                 BLI_listbase_clear(&gpf_dst->strokes);
2418                                 for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) {
2419                                         if (gps_src->totpoints == 0) {
2420                                                 continue;
2421                                         }
2422                                         /* make copy of source stroke, then adjust pointer to points too */
2423                                         gps_dst = MEM_dupallocN(gps_src);
2424                                         gps_dst->points = MEM_dupallocN(gps_src->points);
2425                                         gps_dst->triangles = MEM_dupallocN(gps_src->triangles);
2426                                         gps_dst->flag |= GP_STROKE_RECALC_CACHES;
2427                                         BLI_addtail(&gpf_dst->strokes, gps_dst);
2428                                 }
2429                         }
2430                         /* clean up temp data */
2431                         gpencil_interpolate_exit(C, op);
2432
2433                         /* done! */
2434                         return OPERATOR_FINISHED;
2435                 }
2436
2437                 case ESCKEY:    /* cancel */
2438                 case RIGHTMOUSE:
2439                 {
2440                         /* return to normal cursor and header status */
2441                         ED_area_headerprint(tgpi->sa, NULL);
2442                         WM_cursor_modal_restore(win);
2443
2444                         /* clean up temp data */
2445                         gpencil_interpolate_exit(C, op);
2446
2447                         /* canceled! */
2448                         return OPERATOR_CANCELLED;
2449                 }
2450                 case WHEELUPMOUSE:
2451                 {
2452                         tgpi->shift = tgpi->shift + 0.01f;
2453                         CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
2454                         RNA_float_set(op->ptr, "shift", tgpi->shift);
2455                         /* update screen */
2456                         gpencil_interpolate_update(C, op, tgpi);
2457                         break;
2458                 }
2459                 case WHEELDOWNMOUSE:
2460                 {
2461                         tgpi->shift = tgpi->shift - 0.01f;
2462                         CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
2463                         RNA_float_set(op->ptr, "shift", tgpi->shift);
2464                         /* update screen */
2465                         gpencil_interpolate_update(C, op, tgpi);
2466                         break;
2467                 }
2468                 case MOUSEMOVE: /* calculate new position */
2469                 {
2470                         /* only handle mousemove if not doing numinput */
2471                         if (has_numinput == false) {
2472                                 /* update shift based on position of mouse */
2473                                 gpencil_mouse_update_shift(tgpi, op, event);
2474                                 /* update screen */
2475                                 gpencil_interpolate_update(C, op, tgpi);
2476                         }
2477                         break;
2478                 }
2479                 default:
2480                         if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
2481                                 float value;
2482                                 float factor = tgpi->init_factor;
2483
2484                                 /* Grab shift from numeric input, and store this new value (the user see an int) */
2485                                 value = (factor + tgpi->shift) * 100.0f;
2486                                 applyNumInput(&tgpi->num, &value);
2487                                 tgpi->shift = value / 100.0f;
2488                                 /* recalculate the shift to get the right value in the frame scale */
2489                                 tgpi->shift = tgpi->shift - factor;
2490
2491                                 CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit);
2492                                 RNA_float_set(op->ptr, "shift", tgpi->shift);
2493
2494                                 /* update screen */
2495                                 gpencil_interpolate_update(C, op, tgpi);
2496
2497                                 break;
2498                         }
2499                         else {
2500                                 /* unhandled event - allow to pass through */
2501                                 return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
2502                         }
2503         }
2504
2505         /* still running... */
2506         return OPERATOR_RUNNING_MODAL;
2507 }
2508
2509 /* Define modal operator for interpolation */
2510 void GPENCIL_OT_interpolate(wmOperatorType *ot)
2511 {
2512         /* identifiers */
2513         ot->name = "Grease Pencil Interpolation";
2514         ot->idname = "GPENCIL_OT_interpolate";
2515         ot->description = "Interpolate grease pencil strokes between frames";
2516
2517         /* api callbacks */
2518         ot->invoke = gpencil_interpolate_invoke;
2519         ot->modal = gpencil_interpolate_modal;
2520         ot->cancel = gpencil_interpolate_cancel;
2521         ot->poll = gpencil_interpolate_poll;
2522
2523         /* flags */
2524         ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
2525
2526         RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Displacement factor for the interpolate operation", -0.9f, 0.9f);
2527 }
2528
2529 /* =============== Interpolate sequence ===============*/
2530 /* Create Sequence Interpolation */
2531 static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
2532 {
2533         Scene *scene = CTX_data_scene(C);
2534         ToolSettings *ts = CTX_data_tool_settings(C);
2535         bGPdata * gpd = CTX_data_gpencil_data(C);
2536         bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
2537         bGPDlayer *gpl;
2538         bGPDframe *prevFrame, *nextFrame, *interFrame;
2539         bGPDstroke *gps_from, *gps_to, *new_stroke;
2540         float factor;
2541         int cframe, fFrame;
2542         int flag = ts->gp_sculpt.flag;
2543
2544         /* cannot interpolate if not between 2 frames */
2545         if ((active_gpl->actframe == NULL) || (active_gpl->actframe->next == NULL)) {
2546                 BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames");
2547                 return OPERATOR_CANCELLED;
2548         }
2549         /* cannot interpolate in extremes */
2550         if ((active_gpl->actframe->framenum == scene->r.cfra) || (active_gpl->actframe->next->framenum == scene->r.cfra)) {
2551                 BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames");
2552                 return OPERATOR_CANCELLED;
2553         }
2554         
2555         /* loop all layer to check if need interpolation */
2556         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2557                 /* all layers or only active */
2558                 if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
2559                         continue;
2560                 }
2561                 /* only editable and visible layers are considered */
2562                 if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
2563                         continue;
2564                 }
2565                 /* store extremes */
2566                 prevFrame = gpl->actframe;
2567                 nextFrame = gpl->actframe->next;
2568                 /* Loop over intermediary frames and create the interpolation */
2569                 for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) {
2570                         interFrame = NULL;
2571
2572                         /* get interpolation factor */
2573                         factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1);
2574
2575                         /* create new strokes data with interpolated points reading original stroke */
2576                         for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
2577                                 /* only selected */
2578                                 if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
2579                                         continue;
2580                                 }
2581                                 /* skip strokes that are invalid for current view */
2582                                 if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
2583                                         continue;
2584                                 }
2585                                 /* check if the color is editable */
2586                                 if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) {
2587                                         continue;
2588                                 }
2589                                 /* get final stroke to interpolate */
2590                                 fFrame = BLI_findindex(&prevFrame->strokes, gps_from);
2591                                 gps_to = BLI_findlink(&nextFrame->strokes, fFrame);
2592                                 if (gps_to == NULL) {
2593                                         continue;
2594                                 }
2595                                 /* create a new frame if needed */
2596                                 if (interFrame == NULL) {
2597                                         interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW);
2598                                         interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
2599                                 }
2600                                 /* create new stroke */
2601                                 new_stroke = MEM_dupallocN(gps_from);
2602                                 new_stroke->points = MEM_dupallocN(gps_from->points);
2603                                 new_stroke->triangles = MEM_dupallocN(gps_from->triangles);
2604                                 /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
2605                                 if (gps_from->totpoints > gps_to->totpoints) {
2606                                         new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints);
2607                                         new_stroke->totpoints = gps_to->totpoints;
2608                                         new_stroke->tot_triangles = 0;
2609                                         new_stroke->flag |= GP_STROKE_RECALC_CACHES;
2610                                 }
2611                                 /* update points position */
2612                                 gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
2613
2614                                 /* add to strokes */
2615                                 BLI_addtail(&interFrame->strokes, new_stroke);
2616                         }
2617                 }
2618         }
2619
2620         /* notifiers */
2621         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2622
2623         return OPERATOR_FINISHED;
2624 }
2625
2626 /* Define sequence interpolation               */
2627 void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
2628 {
2629         /* identifiers */
2630         ot->name = "Grease Pencil Sequence Interpolation";
2631         ot->idname = "GPENCIL_OT_interpolate_sequence";
2632         ot->description = "Interpolate full grease pencil strokes sequence between frames";
2633
2634         /* api callbacks */
2635         ot->exec = gpencil_interpolate_seq_exec;
2636         ot->poll = gpencil_interpolate_poll;
2637
2638         /* flags */
2639         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2640 }
2641 /* =========  End Interpolation operators ========================== */