Cleanup: code-style, duplicate header
[blender-staging.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_space_api.h"
78
79 #include "gpencil_intern.h"
80
81 /* ************************************************ */
82 /* Stroke Edit Mode Management */
83
84 static int gpencil_editmode_toggle_poll(bContext *C)
85 {
86         return ED_gpencil_data_get_active(C) != NULL;
87 }
88
89 static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op))
90 {
91         bGPdata *gpd = ED_gpencil_data_get_active(C);
92         
93         if (gpd == NULL)
94                 return OPERATOR_CANCELLED;
95         
96         /* Just toggle editmode flag... */
97         gpd->flag ^= GP_DATA_STROKE_EDITMODE;
98         /* recalculate parent matrix */
99         if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
100                 ED_gpencil_reset_layers_parent(gpd);
101         }
102
103         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
104         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
105         
106         return OPERATOR_FINISHED;
107 }
108
109 void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
110 {
111         /* identifiers */
112         ot->name = "Strokes Edit Mode Toggle";
113         ot->idname = "GPENCIL_OT_editmode_toggle";
114         ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
115         
116         /* callbacks */
117         ot->exec = gpencil_editmode_toggle_exec;
118         ot->poll = gpencil_editmode_toggle_poll;
119         
120         /* flags */
121         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
122 }
123
124 /* ************************************************ */
125 /* Stroke Editing Operators */
126
127 /* poll callback for all stroke editing operators */
128 static int gp_stroke_edit_poll(bContext *C)
129 {
130         /* NOTE: this is a bit slower, but is the most accurate... */
131         return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
132 }
133
134 /* ************ Stroke Hide selection Toggle ************** */
135
136 static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
137 {
138         ToolSettings *ts = CTX_data_tool_settings(C);
139
140         if (ts == NULL)
141                 return OPERATOR_CANCELLED;
142
143         /* Just toggle alpha... */
144         if (ts->gp_sculpt.alpha > 0.0f) {
145                 ts->gp_sculpt.alpha = 0.0f;
146         }
147         else {
148                 ts->gp_sculpt.alpha = 1.0f;
149         }
150
151         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
152         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
153
154         return OPERATOR_FINISHED;
155 }
156
157 void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
158 {
159         /* identifiers */
160         ot->name = "Hide Selection";
161         ot->idname = "GPENCIL_OT_selection_opacity_toggle";
162         ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
163
164         /* callbacks */
165         ot->exec = gpencil_hideselect_toggle_exec;
166         ot->poll = gp_stroke_edit_poll;
167
168         /* flags */
169         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
170 }
171
172 /* ************** Duplicate Selected Strokes **************** */
173
174 /* Make copies of selected point segments in a selected stroke */
175 static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
176 {
177         bGPDspoint *pt;
178         int i;
179         
180         int start_idx = -1;
181         
182         
183         /* Step through the original stroke's points:
184          * - We accumulate selected points (from start_idx to current index)
185          *   and then convert that to a new stroke
186          */
187         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
188                 /* searching for start, are waiting for end? */
189                 if (start_idx == -1) {
190                         /* is this the first selected point for a new island? */
191                         if (pt->flag & GP_SPOINT_SELECT) {
192                                 start_idx = i;
193                         }
194                 }
195                 else {
196                         size_t len = 0;
197                         
198                         /* is this the end of current island yet?
199                          * 1) Point i-1 was the last one that was selected
200                          * 2) Point i is the last in the array
201                          */
202                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
203                                 len = i - start_idx;
204                         }
205                         else if (i == gps->totpoints - 1) {
206                                 len = i - start_idx + 1;
207                         }
208                         //printf("copying from %d to %d = %d\n", start_idx, i, len);
209                 
210                         /* make copies of the relevant data */
211                         if (len) {
212                                 bGPDstroke *gpsd;
213                                 
214                                 /* make a stupid copy first of the entire stroke (to get the flags too) */
215                                 gpsd = MEM_dupallocN(gps);
216                                 BLI_strncpy(gpsd->tmp_layerinfo, layername, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */
217                                 
218                                 /* initialize triangle memory - will be calculated on next redraw */
219                                 gpsd->triangles = NULL;
220                                 gpsd->flag |= GP_STROKE_RECALC_CACHES;
221                                 gpsd->tot_triangles = 0;
222                                 
223                                 /* now, make a new points array, and copy of the relevant parts */
224                                 gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
225                                 memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
226                                 gpsd->totpoints = len;
227                                 
228                                 /* add to temp buffer */
229                                 gpsd->next = gpsd->prev = NULL;
230                                 BLI_addtail(new_strokes, gpsd);
231                                 
232                                 /* cleanup + reset for next */
233                                 start_idx = -1;
234                         }
235                 }
236         }
237 }
238
239 static int gp_duplicate_exec(bContext *C, wmOperator *op)
240 {
241         bGPdata *gpd = ED_gpencil_data_get_active(C);
242         
243         if (gpd == NULL) {
244                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
245                 return OPERATOR_CANCELLED;
246         }
247         
248         /* for each visible (and editable) layer's selected strokes,
249          * copy the strokes into a temporary buffer, then append
250          * once all done
251          */
252         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
253         {
254                 ListBase new_strokes = {NULL, NULL};
255                 bGPDframe *gpf = gpl->actframe;
256                 bGPDstroke *gps;
257                 
258                 if (gpf == NULL)
259                         continue;
260                 
261                 /* make copies of selected strokes, and deselect these once we're done */
262                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
263                         /* skip strokes that are invalid for current view */
264                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
265                                 continue;
266                         }
267                         
268                         if (gps->flag & GP_STROKE_SELECT) {
269                                 if (gps->totpoints == 1) {
270                                         /* Special Case: If there's just a single point in this stroke... */
271                                         bGPDstroke *gpsd;
272                                         
273                                         /* make direct copies of the stroke and its points */
274                                         gpsd = MEM_dupallocN(gps);
275                                         BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo));
276                                         gpsd->points = MEM_dupallocN(gps->points);
277
278                                         /* triangle information - will be calculated on next redraw */
279                                         gpsd->flag |= GP_STROKE_RECALC_CACHES;
280                                         gpsd->triangles = NULL;
281                                         
282                                         /* add to temp buffer */
283                                         gpsd->next = gpsd->prev = NULL;
284                                         BLI_addtail(&new_strokes, gpsd);
285                                 }
286                                 else {
287                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
288                                         gp_duplicate_points(gps, &new_strokes, gpl->info);
289                                 }
290                                 
291                                 /* deselect original stroke, or else the originals get moved too
292                                  * (when using the copy + move macro)
293                                  */
294                                 gps->flag &= ~GP_STROKE_SELECT;
295                         }
296                 }
297                 
298                 /* add all new strokes in temp buffer to the frame (preventing double-copies) */
299                 BLI_movelisttolist(&gpf->strokes, &new_strokes);
300                 BLI_assert(new_strokes.first == NULL);
301         }
302         CTX_DATA_END;
303         
304         /* updates */
305         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
306         
307         return OPERATOR_FINISHED;
308 }
309
310 void GPENCIL_OT_duplicate(wmOperatorType *ot)
311 {
312         /* identifiers */
313         ot->name = "Duplicate Strokes";
314         ot->idname = "GPENCIL_OT_duplicate";
315         ot->description = "Duplicate the selected Grease Pencil strokes";
316         
317         /* callbacks */
318         ot->exec = gp_duplicate_exec;
319         ot->poll = gp_stroke_edit_poll;
320         
321         /* flags */
322         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
323 }
324
325 /* ******************* Copy/Paste Strokes ************************* */
326 /* Grease Pencil stroke data copy/paste buffer:
327  * - The copy operation collects all segments of selected strokes,
328  *   dumping "ready to be copied" copies of the strokes into the buffer.
329  * - The paste operation makes a copy of those elements, and adds them
330  *   to the active layer. This effectively flattens down the strokes
331  *   from several different layers into a single layer.
332  */
333
334 /* list of bGPDstroke instances */
335 /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
336 ListBase gp_strokes_copypastebuf = {NULL, NULL};
337
338 /* Free copy/paste buffer data */
339 void ED_gpencil_strokes_copybuf_free(void)
340 {
341         bGPDstroke *gps, *gpsn;
342         
343         for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
344                 gpsn = gps->next;
345                 
346                 if (gps->points)    MEM_freeN(gps->points);
347                 if (gps->triangles) MEM_freeN(gps->triangles);
348                 
349                 BLI_freelinkN(&gp_strokes_copypastebuf, gps);
350         }
351         
352         gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
353 }
354
355 /* --------------------- */
356 /* Copy selected strokes */
357
358 static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
359 {
360         bGPdata *gpd = ED_gpencil_data_get_active(C);
361         
362         if (gpd == NULL) {
363                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
364                 return OPERATOR_CANCELLED;
365         }
366         
367         /* clear the buffer first */
368         ED_gpencil_strokes_copybuf_free();
369         
370         /* for each visible (and editable) layer's selected strokes,
371          * copy the strokes into a temporary buffer, then append
372          * once all done
373          */
374         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
375         {
376                 bGPDframe *gpf = gpl->actframe;
377                 bGPDstroke *gps;
378                 
379                 if (gpf == NULL)
380                         continue;
381                 
382                 /* make copies of selected strokes, and deselect these once we're done */
383                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
384                         /* skip strokes that are invalid for current view */
385                         if (ED_gpencil_stroke_can_use(C, gps) == false)
386                                 continue;
387                         
388                         if (gps->flag & GP_STROKE_SELECT) {
389                                 if (gps->totpoints == 1) {
390                                         /* Special Case: If there's just a single point in this stroke... */
391                                         bGPDstroke *gpsd;
392                                         
393                                         /* make direct copies of the stroke and its points */
394                                         gpsd = MEM_dupallocN(gps);
395                                         BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */
396                                         gpsd->points = MEM_dupallocN(gps->points);
397                                         
398                                         /* triangles cache - will be recalculated on next redraw */
399                                         gpsd->flag |= GP_STROKE_RECALC_CACHES;
400                                         gpsd->tot_triangles = 0;
401                                         gpsd->triangles = NULL;
402                                         
403                                         /* add to temp buffer */
404                                         gpsd->next = gpsd->prev = NULL;
405                                         BLI_addtail(&gp_strokes_copypastebuf, gpsd);
406                                 }
407                                 else {
408                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
409                                         gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
410                                 }
411                         }
412                 }
413         }
414         CTX_DATA_END;
415         
416         /* done - no updates needed */
417         return OPERATOR_FINISHED;
418 }
419
420 void GPENCIL_OT_copy(wmOperatorType *ot)
421 {
422         /* identifiers */
423         ot->name = "Copy Strokes";
424         ot->idname = "GPENCIL_OT_copy";
425         ot->description = "Copy selected Grease Pencil points and strokes";
426         
427         /* callbacks */
428         ot->exec = gp_strokes_copy_exec;
429         ot->poll = gp_stroke_edit_poll;
430         
431         /* flags */
432         //ot->flag = OPTYPE_REGISTER;
433 }
434
435 /* --------------------- */
436 /* Paste selected strokes */
437
438 static int gp_strokes_paste_poll(bContext *C)
439 {
440         /* 1) Must have GP datablock to paste to
441          *    - We don't need to have an active layer though, as that can easily get added
442          *    - If the active layer is locked, we can't paste there, but that should prompt a warning instead
443          * 2) Copy buffer must at least have something (though it may be the wrong sort...)
444          */
445         return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
446 }
447
448 typedef enum eGP_PasteMode {
449         GP_COPY_ONLY = -1,
450         GP_COPY_MERGE = 1
451 } eGP_PasteMode;
452
453 static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
454 {
455         Scene *scene = CTX_data_scene(C);
456         bGPdata *gpd = ED_gpencil_data_get_active(C);
457         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
458         bGPDframe *gpf;
459         
460         eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
461         
462         /* check for various error conditions */
463         if (gpd == NULL) {
464                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
465                 return OPERATOR_CANCELLED;
466         }
467         else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
468                 BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
469                 return OPERATOR_CANCELLED;
470         }
471         else if (gpl == NULL) {
472                 /* no active layer - let's just create one */
473                 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
474         }
475         else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
476                 BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
477                 return OPERATOR_CANCELLED;
478         }
479         else {
480                 /* Check that some of the strokes in the buffer can be used */
481                 bGPDstroke *gps;
482                 bool ok = false;
483                 
484                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
485                         if (ED_gpencil_stroke_can_use(C, gps)) {
486                                 ok = true;
487                                 break;
488                         }
489                 }
490                 
491                 if (ok == false) {
492                         /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
493                          * but should be enough to give users a good idea of what's going on
494                          */
495                         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
496                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
497                         else
498                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
499                                 
500                         return OPERATOR_CANCELLED;
501                 }
502         }
503         
504         /* Deselect all strokes first */
505         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
506         {
507                 bGPDspoint *pt;
508                 int i;
509                 
510                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
511                         pt->flag &= ~GP_SPOINT_SELECT;
512                 }
513                 
514                 gps->flag &= ~GP_STROKE_SELECT;
515         }
516         CTX_DATA_END;
517         
518         for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
519                 if (ED_gpencil_stroke_can_use(C, gps)) {
520                         /* Need to verify if layer exists */
521                         if (type != GP_COPY_MERGE) {
522                                 gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info));
523                                 if (gpl == NULL) {
524                                         /* no layer - use active (only if layer deleted before paste) */
525                                         gpl = CTX_data_active_gpencil_layer(C);
526                                 }
527                         }
528                         
529                         /* Ensure we have a frame to draw into
530                          * NOTE: Since this is an op which creates strokes,
531                          *       we are obliged to add a new frame if one
532                          *       doesn't exist already
533                          */
534                         gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
535                         if (gpf) {
536                                 bGPDstroke *new_stroke = MEM_dupallocN(gps);
537                                 new_stroke->tmp_layerinfo[0] = '\0';
538                                 
539                                 new_stroke->points = MEM_dupallocN(gps->points);
540                                 
541                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
542                                 new_stroke->triangles = NULL;
543                                 
544                                 new_stroke->next = new_stroke->prev = NULL;
545                                 BLI_addtail(&gpf->strokes, new_stroke);
546                         }
547                 }
548         }
549         
550         /* updates */
551         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
552         
553         return OPERATOR_FINISHED;
554 }
555
556 void GPENCIL_OT_paste(wmOperatorType *ot)
557 {
558         static EnumPropertyItem copy_type[] = {
559                 {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
560                 {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
561                 {0, NULL, 0, NULL, NULL}
562         };
563         
564         /* identifiers */
565         ot->name = "Paste Strokes";
566         ot->idname = "GPENCIL_OT_paste";
567         ot->description = "Paste previously copied strokes or copy and merge in active layer";
568         
569         /* callbacks */
570         ot->exec = gp_strokes_paste_exec;
571         ot->poll = gp_strokes_paste_poll;
572         
573         /* flags */
574         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
575         
576         /* properties */
577         ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
578 }
579
580 /* ******************* Move To Layer ****************************** */
581
582 static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
583 {
584         uiPopupMenu *pup;
585         uiLayout *layout;
586         
587         /* call the menu, which will call this operator again, hence the canceled */
588         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
589         layout = UI_popup_menu_layout(pup);
590         uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
591         UI_popup_menu_end(C, pup);
592         
593         return OPERATOR_INTERFACE;
594 }
595
596 // FIXME: allow moving partial strokes
597 static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
598 {
599         bGPdata *gpd = CTX_data_gpencil_data(C);
600         bGPDlayer *target_layer = NULL;
601         ListBase strokes = {NULL, NULL};
602         int layer_num = RNA_enum_get(op->ptr, "layer");
603         
604         /* Get layer or create new one */
605         if (layer_num == -1) {
606                 /* Create layer */
607                 target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
608         }
609         else {
610                 /* Try to get layer */
611                 target_layer = BLI_findlink(&gpd->layers, layer_num);
612                 
613                 if (target_layer == NULL) {
614                         BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
615                         return OPERATOR_CANCELLED;
616                 }
617         }
618         
619         /* Extract all strokes to move to this layer
620          * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
621          *       getting repeatedly moved
622          */
623         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
624         {
625                 bGPDframe *gpf = gpl->actframe;
626                 bGPDstroke *gps, *gpsn;
627                 
628                 /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
629                 if ((gpl == target_layer) || (gpf == NULL))
630                         continue;
631                 
632                 /* make copies of selected strokes, and deselect these once we're done */
633                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
634                         gpsn = gps->next;
635                         
636                         /* skip strokes that are invalid for current view */
637                         if (ED_gpencil_stroke_can_use(C, gps) == false)
638                                 continue;
639                         
640                         /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
641                         if (gps->flag & GP_STROKE_SELECT) {
642                                 BLI_remlink(&gpf->strokes, gps);
643                                 BLI_addtail(&strokes, gps);
644                         }
645                 }
646         }
647         CTX_DATA_END;
648         
649         /* Paste them all in one go */
650         if (strokes.first) {
651                 Scene *scene = CTX_data_scene(C);
652                 bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true);
653                 
654                 BLI_movelisttolist(&gpf->strokes, &strokes);
655                 BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
656         }
657         
658         /* updates */
659         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
660         
661         return OPERATOR_FINISHED;
662 }
663
664 void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
665 {
666         /* identifiers */
667         ot->name = "Move Strokes to Layer";
668         ot->idname = "GPENCIL_OT_move_to_layer";
669         ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
670         
671         /* callbacks */
672         ot->invoke = gp_move_to_layer_invoke;
673         ot->exec = gp_move_to_layer_exec;
674         ot->poll = gp_stroke_edit_poll; // XXX?
675         
676         /* flags */
677         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
678         
679         /* gp layer to use (dynamic enum) */
680         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
681         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
682 }
683
684 /* ********************* Add Blank Frame *************************** */
685
686 /* Basically the same as the drawing op */
687 static int UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C)
688 {
689         if (ED_operator_regionactive(C)) {
690                 /* check if current context can support GPencil data */
691                 if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
692                         return 1;
693                 }
694                 else {
695                         CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
696                 }
697         }
698         else {
699                 CTX_wm_operator_poll_msg_set(C, "Active region not set");
700         }
701         
702         return 0;
703 }
704
705 static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
706 {
707         Scene *scene = CTX_data_scene(C);
708         bGPdata *gpd = ED_gpencil_data_get_active(C);
709         bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd);
710
711         const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
712
713         /* Initialise datablock and an active layer if nothing exists yet */
714         if (ELEM(NULL, gpd, active_gpl)) {
715                 /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */
716                 WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL);
717         }
718         
719         /* Go through each layer, adding a frame after the active one 
720          * and/or shunting all the others out of the way
721          */
722         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
723         {
724                 if ((all_layers == false) && (gpl != active_gpl)) {
725                         continue;
726                 }
727
728                 /* 1) Check for an existing frame on the current frame */
729                 bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, CFRA);
730                 if (gpf) {
731                         /* Shunt all frames after (and including) the existing one later by 1-frame */
732                         for (; gpf; gpf = gpf->next) {
733                                 gpf->framenum += 1;
734                         }
735                 }
736                 
737                 /* 2) Now add a new frame, with nothing in it */
738                 gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW);
739         }
740         CTX_DATA_END;
741         
742         /* notifiers */
743         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
744         
745         return OPERATOR_FINISHED;
746 }
747
748 void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
749 {
750         /* identifiers */
751         ot->name = "Add Blank Frame";
752         ot->idname = "GPENCIL_OT_blank_frame_add";
753         ot->description = "Add a new frame with nothing in it on the current frame. "
754                           "If there is already a frame, all existing frames are shifted one frame later";
755         
756         /* callbacks */
757         ot->exec = gp_blank_frame_add_exec;
758         ot->poll = gp_add_poll;
759         
760         /* properties */
761         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
762         RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active");
763 }
764
765 /* ******************* Delete Active Frame ************************ */
766
767 static int gp_actframe_delete_poll(bContext *C)
768 {
769         bGPdata *gpd = ED_gpencil_data_get_active(C);
770         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
771         
772         /* only if there's an active layer with an active frame */
773         return (gpl && gpl->actframe);
774 }
775
776 /* delete active frame - wrapper around API calls */
777 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
778 {
779         Scene *scene = CTX_data_scene(C);
780         bGPdata *gpd = ED_gpencil_data_get_active(C);
781         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
782         bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
783         
784         /* if there's no existing Grease-Pencil data there, add some */
785         if (gpd == NULL) {
786                 BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
787                 return OPERATOR_CANCELLED;
788         }
789         if (ELEM(NULL, gpl, gpf)) {
790                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
791                 return OPERATOR_CANCELLED;
792         }
793         
794         /* delete it... */
795         BKE_gpencil_layer_delframe(gpl, gpf);
796         
797         /* notifiers */
798         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
799         
800         return OPERATOR_FINISHED;
801 }
802
803 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
804 {
805         /* identifiers */
806         ot->name = "Delete Active Frame";
807         ot->idname = "GPENCIL_OT_active_frame_delete";
808         ot->description = "Delete the active frame for the active Grease Pencil Layer";
809         
810         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
811         
812         /* callbacks */
813         ot->exec = gp_actframe_delete_exec;
814         ot->poll = gp_actframe_delete_poll;
815 }
816
817 /* **************** Delete All Active Frames ****************** */
818
819 static int gp_actframe_delete_all_poll(bContext *C)
820 {
821         bGPdata *gpd = ED_gpencil_data_get_active(C);
822         
823         /* 1) There must be grease pencil data
824          * 2) Hopefully some of the layers have stuff we can use
825          */
826         return (gpd && gpd->layers.first);
827 }
828
829 static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
830 {
831         Scene *scene = CTX_data_scene(C);
832         bool success = false;
833         
834         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
835         {
836                 /* try to get the "active" frame - but only if it actually occurs on this frame */
837                 bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
838                 
839                 if (gpf == NULL)
840                         continue;
841                 
842                 /* delete it... */
843                 BKE_gpencil_layer_delframe(gpl, gpf);
844                 
845                 /* we successfully modified something */
846                 success = true;
847         }
848         CTX_DATA_END;
849         
850         /* updates */
851         if (success) {
852                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);               
853                 return OPERATOR_FINISHED;
854         }
855         else {
856                 BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
857                 return OPERATOR_CANCELLED;
858         }
859 }
860
861 void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
862 {
863         /* identifiers */
864         ot->name = "Delete All Active Frames";
865         ot->idname = "GPENCIL_OT_active_frames_delete_all";
866         ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
867         
868         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
869         
870         /* callbacks */
871         ot->exec = gp_actframe_delete_all_exec;
872         ot->poll = gp_actframe_delete_all_poll;
873 }
874
875 /* ******************* Delete Operator ************************ */
876
877 typedef enum eGP_DeleteMode {
878         /* delete selected stroke points */
879         GP_DELETEOP_POINTS          = 0,
880         /* delete selected strokes */
881         GP_DELETEOP_STROKES         = 1,
882         /* delete active frame */
883         GP_DELETEOP_FRAME           = 2,
884 } eGP_DeleteMode;
885
886 /* ----------------------------------- */
887
888 /* Delete selected strokes */
889 static int gp_delete_selected_strokes(bContext *C)
890 {
891         bool changed = false;
892         
893         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
894         {
895                 bGPDframe *gpf = gpl->actframe;
896                 bGPDstroke *gps, *gpsn;
897                 
898                 if (gpf == NULL)
899                         continue;
900                 
901                 /* simply delete strokes which are selected */
902                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
903                         gpsn = gps->next;
904                         
905                         /* skip strokes that are invalid for current view */
906                         if (ED_gpencil_stroke_can_use(C, gps) == false)
907                                 continue;
908                         
909                         /* free stroke if selected */
910                         if (gps->flag & GP_STROKE_SELECT) {
911                                 /* free stroke memory arrays, then stroke itself */
912                                 if (gps->points) MEM_freeN(gps->points);
913                                 if (gps->triangles) MEM_freeN(gps->triangles);
914                                 BLI_freelinkN(&gpf->strokes, gps);
915                                 
916                                 changed = true;
917                         }
918                 }
919         }
920         CTX_DATA_END;
921         
922         if (changed) {
923                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
924                 return OPERATOR_FINISHED;
925         }
926         else {
927                 return OPERATOR_CANCELLED;
928         }
929 }
930
931 /* ----------------------------------- */
932
933 /* Delete selected points but keep the stroke */
934 static int gp_dissolve_selected_points(bContext *C)
935 {
936         bool changed = false;
937         
938         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
939         {
940                 bGPDframe *gpf = gpl->actframe;
941                 bGPDstroke *gps, *gpsn;
942                 
943                 if (gpf == NULL)
944                         continue;
945                 
946                 /* simply delete points from selected strokes
947                  * NOTE: we may still have to remove the stroke if it ends up having no points!
948                  */
949                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
950                         gpsn = gps->next;
951                         
952                         /* skip strokes that are invalid for current view */
953                         if (ED_gpencil_stroke_can_use(C, gps) == false)
954                                 continue;
955                         /* check if the color is editable */
956                         if (ED_gpencil_stroke_color_use(gpl, gps) == false)
957                                 continue;
958                         
959                         if (gps->flag & GP_STROKE_SELECT) {
960                                 bGPDspoint *pt;
961                                 int i;
962                                 
963                                 int tot = gps->totpoints; /* number of points in new buffer */
964                                 
965                                 /* First Pass: Count how many points are selected (i.e. how many to remove) */
966                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
967                                         if (pt->flag & GP_SPOINT_SELECT) {
968                                                 /* selected point - one of the points to remove */
969                                                 tot--;
970                                         }
971                                 }
972                                 
973                                 /* if no points are left, we simply delete the entire stroke */
974                                 if (tot <= 0) {
975                                         /* remove the entire stroke */
976                                         MEM_freeN(gps->points);
977                                         if (gps->triangles) {
978                                                 MEM_freeN(gps->triangles);
979                                         }
980                                         BLI_freelinkN(&gpf->strokes, gps);
981                                 }
982                                 else {
983                                         /* just copy all unselected into a smaller buffer */
984                                         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
985                                         bGPDspoint *npt        = new_points;
986                                         
987                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
988                                                 if ((pt->flag & GP_SPOINT_SELECT) == 0) {
989                                                         *npt = *pt;
990                                                         npt++;
991                                                 }
992                                         }
993                                         
994                                         /* free the old buffer */
995                                         MEM_freeN(gps->points);
996                                         
997                                         /* save the new buffer */
998                                         gps->points = new_points;
999                                         gps->totpoints = tot;
1000                                         
1001                                         /* triangles cache needs to be recalculated */
1002                                         gps->flag |= GP_STROKE_RECALC_CACHES;
1003                                         gps->tot_triangles = 0;
1004                                         
1005                                         /* deselect the stroke, since none of its selected points will still be selected */
1006                                         gps->flag &= ~GP_STROKE_SELECT;
1007                                 }
1008                                 
1009                                 changed = true;
1010                         }
1011                 }
1012         }
1013         CTX_DATA_END;
1014         
1015         if (changed) {
1016                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1017                 return OPERATOR_FINISHED;
1018         }
1019         else {
1020                 return OPERATOR_CANCELLED;
1021         }
1022 }
1023
1024 /* ----------------------------------- */
1025
1026 /* Temp data for storing information about an "island" of points
1027  * that should be kept when splitting up a stroke. Used in:
1028  * gp_stroke_delete_tagged_points()
1029  */
1030 typedef struct tGPDeleteIsland {
1031         int start_idx;
1032         int end_idx;
1033 } tGPDeleteIsland;
1034
1035
1036 /* Split the given stroke into several new strokes, partitioning
1037  * it based on whether the stroke points have a particular flag
1038  * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
1039  *
1040  * The algorithm used here is as follows:
1041  * 1) We firstly identify the number of "islands" of non-tagged points
1042  *    which will all end up being in new strokes.
1043  *    - In the most extreme case (i.e. every other vert is a 1-vert island),
1044  *      we have at most n / 2 islands
1045  *    - Once we start having larger islands than that, the number required
1046  *      becomes much less
1047  * 2) Each island gets converted to a new stroke
1048  */
1049 void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags)
1050 {
1051         tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
1052         bool in_island  = false;
1053         int num_islands = 0;
1054         
1055         bGPDspoint *pt;
1056         int i;
1057         
1058         /* First Pass: Identify start/end of islands */
1059         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1060                 if (pt->flag & tag_flags) {
1061                         /* selected - stop accumulating to island */
1062                         in_island = false;
1063                 }
1064                 else {
1065                         /* unselected - start of a new island? */
1066                         int idx;
1067                         
1068                         if (in_island) {
1069                                 /* extend existing island */
1070                                 idx = num_islands - 1;
1071                                 islands[idx].end_idx = i;
1072                         }
1073                         else {
1074                                 /* start of new island */
1075                                 in_island = true;
1076                                 num_islands++;
1077                                 
1078                                 idx = num_islands - 1;
1079                                 islands[idx].start_idx = islands[idx].end_idx = i;
1080                         }
1081                 }
1082         }
1083         
1084         /* Watch out for special case where No islands = All points selected = Delete Stroke only */
1085         if (num_islands) {
1086                 /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
1087                 int idx;
1088                 
1089                 /* Create each new stroke... */
1090                 for (idx = 0; idx < num_islands; idx++) {
1091                         tGPDeleteIsland *island = &islands[idx];
1092                         bGPDstroke *new_stroke  = MEM_dupallocN(gps);
1093                         
1094                         /* initialize triangle memory  - to be calculated on next redraw */
1095                         new_stroke->triangles = NULL;
1096                         new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1097                         new_stroke->tot_triangles = 0;
1098                         
1099                         /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
1100                         new_stroke->totpoints = island->end_idx - island->start_idx + 1;
1101                         new_stroke->points    = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
1102                         
1103                         /* Copy over the relevant points */
1104                         memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
1105                         
1106                         
1107                         /* Each island corresponds to a new stroke. We must adjust the 
1108                          * timings of these new strokes:
1109                          *
1110                          * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
1111                          * the start of the stroke, we have to offset this inittime and all remaining points' delta values.
1112                          * This way we get a new stroke with exactly the same timing as if user had started drawing from
1113                          * the first non-removed point...
1114                          */
1115                         {
1116                                 bGPDspoint *pts;
1117                                 float delta = gps->points[island->start_idx].time;
1118                                 int j;
1119                                 
1120                                 new_stroke->inittime += (double)delta;
1121                                 
1122                                 pts = new_stroke->points;
1123                                 for (j = 0; j < new_stroke->totpoints; j++, pts++) {
1124                                         pts->time -= delta;
1125                                 }
1126                         }
1127                         
1128                         /* Add new stroke to the frame */
1129                         if (next_stroke) {
1130                                 BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
1131                         }
1132                         else {
1133                                 BLI_addtail(&gpf->strokes, new_stroke);
1134                         }
1135                 }
1136         }
1137         
1138         /* free islands */
1139         MEM_freeN(islands);
1140         
1141         /* Delete the old stroke */
1142         MEM_freeN(gps->points);
1143         if (gps->triangles) {
1144                 MEM_freeN(gps->triangles);
1145         }
1146         BLI_freelinkN(&gpf->strokes, gps);
1147 }
1148
1149
1150 /* Split selected strokes into segments, splitting on selected points */
1151 static int gp_delete_selected_points(bContext *C)
1152 {
1153         bool changed = false;
1154         
1155         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1156         {
1157                 bGPDframe *gpf = gpl->actframe;
1158                 bGPDstroke *gps, *gpsn;
1159                 
1160                 if (gpf == NULL)
1161                         continue;
1162                 
1163                 /* simply delete strokes which are selected */
1164                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1165                         gpsn = gps->next;
1166                         
1167                         /* skip strokes that are invalid for current view */
1168                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1169                                 continue;
1170                         /* check if the color is editable */
1171                         if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1172                                 continue;
1173                         
1174                         
1175                         if (gps->flag & GP_STROKE_SELECT) {
1176                                 /* deselect old stroke, since it will be used as template for the new strokes */
1177                                 gps->flag &= ~GP_STROKE_SELECT;
1178                                 
1179                                 /* delete unwanted points by splitting stroke into several smaller ones */
1180                                 gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT);
1181                                 
1182                                 changed = true;
1183                         }
1184                 }
1185         }
1186         CTX_DATA_END;
1187         
1188         if (changed) {
1189                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1190                 return OPERATOR_FINISHED;
1191         }
1192         else {
1193                 return OPERATOR_CANCELLED;
1194         }
1195 }
1196
1197 /* ----------------------------------- */
1198
1199 static int gp_delete_exec(bContext *C, wmOperator *op)
1200 {
1201         eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
1202         int result = OPERATOR_CANCELLED;
1203         
1204         switch (mode) {
1205                 case GP_DELETEOP_STROKES:       /* selected strokes */
1206                         result = gp_delete_selected_strokes(C);
1207                         break;
1208                 
1209                 case GP_DELETEOP_POINTS:        /* selected points (breaks the stroke into segments) */
1210                         result = gp_delete_selected_points(C);
1211                         break;
1212                 
1213                 case GP_DELETEOP_FRAME:         /* active frame */
1214                         result = gp_actframe_delete_exec(C, op);
1215                         break;
1216         }
1217         
1218         return result;
1219 }
1220
1221 void GPENCIL_OT_delete(wmOperatorType *ot)
1222 {
1223         static EnumPropertyItem prop_gpencil_delete_types[] = {
1224                 {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
1225                 {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
1226                 {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
1227                 {0, NULL, 0, NULL, NULL}
1228         };
1229         
1230         /* identifiers */
1231         ot->name = "Delete";
1232         ot->idname = "GPENCIL_OT_delete";
1233         ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
1234         
1235         /* callbacks */
1236         ot->invoke = WM_menu_invoke;
1237         ot->exec = gp_delete_exec;
1238         ot->poll = gp_stroke_edit_poll;
1239         
1240         /* flags */
1241         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1242         
1243         /* props */
1244         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
1245 }
1246
1247 static int gp_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
1248 {
1249         return gp_dissolve_selected_points(C);
1250 }
1251
1252 void GPENCIL_OT_dissolve(wmOperatorType *ot)
1253 {
1254         /* identifiers */
1255         ot->name = "Dissolve";
1256         ot->idname = "GPENCIL_OT_dissolve";
1257         ot->description = "Delete selected points without splitting strokes";
1258
1259         /* callbacks */
1260         ot->exec = gp_dissolve_exec;
1261         ot->poll = gp_stroke_edit_poll;
1262
1263         /* flags */
1264         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1265 }
1266
1267 /* ****************** Snapping - Strokes <-> Cursor ************************ */
1268
1269 /* Poll callback for snap operators */
1270 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
1271  *       define a cursor or gridstep which can be used
1272  */
1273 static int gp_snap_poll(bContext *C)
1274 {
1275         bGPdata *gpd = CTX_data_gpencil_data(C);
1276         ScrArea *sa = CTX_wm_area(C);
1277         
1278         return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
1279 }
1280
1281 /* --------------------------------- */
1282
1283 static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
1284 {
1285         bGPdata *gpd = ED_gpencil_data_get_active(C);
1286         RegionView3D *rv3d = CTX_wm_region_data(C);
1287         const float gridf = rv3d->gridview;
1288         
1289         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1290                 /* only editable and visible layers are considered */
1291                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1292                         bGPDframe *gpf = gpl->actframe;
1293                         float diff_mat[4][4];
1294                         
1295                         /* calculate difference matrix if parent object */
1296                         if (gpl->parent != NULL) {
1297                                 ED_gpencil_parent_location(gpl, diff_mat);
1298                         }
1299                         
1300                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1301                                 bGPDspoint *pt;
1302                                 int i;
1303                                 
1304                                 /* skip strokes that are invalid for current view */
1305                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1306                                         continue;
1307                                 /* check if the color is editable */
1308                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1309                                         continue;
1310                                 
1311                                 // TODO: if entire stroke is selected, offset entire stroke by same amount?
1312                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1313                                         /* only if point is selected */
1314                                         if (pt->flag & GP_SPOINT_SELECT) {
1315                                                 if (gpl->parent == NULL) {
1316                                                         pt->x = gridf * floorf(0.5f + pt->x / gridf);
1317                                                         pt->y = gridf * floorf(0.5f + pt->y / gridf);
1318                                                         pt->z = gridf * floorf(0.5f + pt->z / gridf);
1319                                                 }
1320                                                 else {
1321                                                         /* apply parent transformations */
1322                                                         float fpt[3];
1323                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
1324                                                         
1325                                                         fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
1326                                                         fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
1327                                                         fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
1328                                                         
1329                                                         /* return data */
1330                                                         copy_v3_v3(&pt->x, fpt);
1331                                                         gp_apply_parent_point(gpl, pt);
1332                                                 }
1333                                         }
1334                                 }
1335                         }
1336                 }
1337         }
1338         
1339         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1340         return OPERATOR_FINISHED;
1341 }
1342
1343 void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
1344 {
1345         /* identifiers */
1346         ot->name = "Snap Selection to Grid";
1347         ot->idname = "GPENCIL_OT_snap_to_grid";
1348         ot->description = "Snap selected points to the nearest grid points";
1349         
1350         /* callbacks */
1351         ot->exec = gp_snap_to_grid;
1352         ot->poll = gp_snap_poll;
1353         
1354         /* flags */
1355         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1356 }
1357
1358 /* ------------------------------- */
1359
1360 static int gp_snap_to_cursor(bContext *C, wmOperator *op)
1361 {
1362         bGPdata *gpd = ED_gpencil_data_get_active(C);
1363         
1364         Scene *scene = CTX_data_scene(C);
1365         View3D *v3d = CTX_wm_view3d(C);
1366         
1367         const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
1368         const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d);
1369         
1370         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1371                 /* only editable and visible layers are considered */
1372                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1373                         bGPDframe *gpf = gpl->actframe;
1374                         float diff_mat[4][4];
1375                         
1376                         /* calculate difference matrix if parent object */
1377                         if (gpl->parent != NULL) {
1378                                 ED_gpencil_parent_location(gpl, diff_mat);
1379                         }
1380                         
1381                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1382                                 bGPDspoint *pt;
1383                                 int i;
1384                                 
1385                                 /* skip strokes that are invalid for current view */
1386                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1387                                         continue;
1388                                 /* check if the color is editable */
1389                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1390                                         continue;
1391                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
1392                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
1393                                         continue;
1394                                 
1395                                 if (use_offset) {
1396                                         float offset[3];
1397                                         
1398                                         /* compute offset from first point of stroke to cursor */
1399                                         /* TODO: Allow using midpoint instead? */
1400                                         sub_v3_v3v3(offset, cursor_global, &gps->points->x);
1401                                         
1402                                         /* apply offset to all points in the stroke */
1403                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1404                                                 add_v3_v3(&pt->x, offset);
1405                                         }
1406                                 }
1407                                 else {
1408                                         /* affect each selected point */
1409                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1410                                                 if (pt->flag & GP_SPOINT_SELECT) {
1411                                                         copy_v3_v3(&pt->x, cursor_global);
1412                                                         if (gpl->parent != NULL) {
1413                                                                 gp_apply_parent_point(gpl, pt);
1414                                                         }
1415                                                 }
1416                                         }
1417                                 }
1418                         }
1419                         
1420                 }
1421         }
1422         
1423         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1424         return OPERATOR_FINISHED;
1425 }
1426
1427 void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
1428 {
1429         /* identifiers */
1430         ot->name = "Snap Selection to Cursor";
1431         ot->idname = "GPENCIL_OT_snap_to_cursor";
1432         ot->description = "Snap selected points/strokes to the cursor";
1433         
1434         /* callbacks */
1435         ot->exec = gp_snap_to_cursor;
1436         ot->poll = gp_snap_poll;
1437         
1438         /* flags */
1439         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1440         
1441         /* props */
1442         ot->prop = RNA_def_boolean(ot->srna, "use_offset", true, "With Offset",
1443                                    "Offset the entire stroke instead of selected points only");
1444 }
1445
1446 /* ------------------------------- */
1447
1448 static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
1449 {
1450         bGPdata *gpd = ED_gpencil_data_get_active(C);
1451         
1452         Scene *scene = CTX_data_scene(C);
1453         View3D *v3d = CTX_wm_view3d(C);
1454         
1455         float *cursor = ED_view3d_cursor3d_get(scene, v3d);
1456         float centroid[3] = {0.0f};
1457         float min[3], max[3];
1458         size_t count = 0;
1459         
1460         INIT_MINMAX(min, max);
1461         
1462         /* calculate midpoints from selected points */
1463         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1464                 /* only editable and visible layers are considered */
1465                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1466                         bGPDframe *gpf = gpl->actframe;
1467                         float diff_mat[4][4];
1468                         
1469                         /* calculate difference matrix if parent object */
1470                         if (gpl->parent != NULL) {
1471                                 ED_gpencil_parent_location(gpl, diff_mat);
1472                         }
1473                         
1474                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1475                                 bGPDspoint *pt;
1476                                 int i;
1477                                 
1478                                 /* skip strokes that are invalid for current view */
1479                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
1480                                         continue;
1481                                 /* check if the color is editable */
1482                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false)
1483                                         continue;
1484                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
1485                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
1486                                         continue;
1487                                 
1488                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1489                                         if (pt->flag & GP_SPOINT_SELECT) {
1490                                                 if (gpl->parent == NULL) {
1491                                                         add_v3_v3(centroid, &pt->x);
1492                                                         minmax_v3v3_v3(min, max, &pt->x);
1493                                                 }
1494                                                 else {
1495                                                         /* apply parent transformations */
1496                                                         float fpt[3];
1497                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
1498                                                         
1499                                                         add_v3_v3(centroid, fpt);
1500                                                         minmax_v3v3_v3(min, max, fpt);
1501                                                 }
1502                                                 count++;
1503                                         }
1504                                 }
1505                                 
1506                         }
1507                 }
1508         }
1509         
1510         if (v3d->around == V3D_AROUND_CENTER_MEAN && count) {
1511                 mul_v3_fl(centroid, 1.0f / (float)count);
1512                 copy_v3_v3(cursor, centroid);
1513         }
1514         else {
1515                 mid_v3_v3v3(cursor, min, max);
1516         }
1517
1518         
1519         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1520         return OPERATOR_FINISHED;
1521 }
1522
1523 void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
1524 {
1525         /* identifiers */
1526         ot->name = "Snap Cursor to Selected Points";
1527         ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
1528         ot->description = "Snap cursor to center of selected points";
1529         
1530         /* callbacks */
1531         ot->exec = gp_snap_cursor_to_sel;
1532         ot->poll = gp_snap_poll;
1533         
1534         /* flags */
1535         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1536 }
1537
1538 /* ******************* Apply layer thickness change to strokes ************************** */
1539
1540 static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
1541 {
1542         bGPdata *gpd = ED_gpencil_data_get_active(C);
1543         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1544
1545         /* sanity checks */
1546         if (ELEM(NULL, gpd, gpl, gpl->frames.first))
1547                 return OPERATOR_CANCELLED;
1548
1549         /* loop all strokes */
1550         for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
1551                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1552                         /* Apply thickness */
1553                         gps->thickness = gps->thickness + gpl->thickness;
1554                 }
1555         }
1556         /* clear value */
1557         gpl->thickness = 0.0f;
1558
1559         /* notifiers */
1560         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1561
1562         return OPERATOR_FINISHED;
1563 }
1564
1565 void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
1566 {
1567         /* identifiers */
1568         ot->name = "Apply Stroke Thickness";
1569         ot->idname = "GPENCIL_OT_stroke_apply_thickness";
1570         ot->description = "Apply the thickness change of the layer to its strokes";
1571
1572         /* api callbacks */
1573         ot->exec = gp_stroke_apply_thickness_exec;
1574         ot->poll = gp_active_layer_poll;
1575 }
1576
1577 /* ******************* Close Strokes ************************** */
1578
1579 enum {
1580         GP_STROKE_CYCLIC_CLOSE = 1,
1581         GP_STROKE_CYCLIC_OPEN = 2,
1582         GP_STROKE_CYCLIC_TOGGLE = 3
1583 };
1584
1585 static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
1586 {
1587         bGPdata *gpd = ED_gpencil_data_get_active(C);
1588         const int type = RNA_enum_get(op->ptr, "type");
1589         
1590         /* sanity checks */
1591         if (ELEM(NULL, gpd))
1592                 return OPERATOR_CANCELLED;
1593         
1594         /* loop all selected strokes */
1595         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1596         {
1597                 if (gpl->actframe == NULL)
1598                         continue;
1599                         
1600                 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
1601                         bGPDpalettecolor *palcolor = gps->palcolor;
1602                         
1603                         /* skip strokes that are not selected or invalid for current view */
1604                         if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
1605                                 continue;
1606                         /* skip hidden or locked colors */
1607                         if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
1608                                 continue;
1609                         
1610                         switch (type) {
1611                                 case GP_STROKE_CYCLIC_CLOSE:
1612                                         /* Close all (enable) */
1613                                         gps->flag |= GP_STROKE_CYCLIC;
1614                                         break;
1615                                 case GP_STROKE_CYCLIC_OPEN:
1616                                         /* Open all (disable) */
1617                                         gps->flag &= ~GP_STROKE_CYCLIC;
1618                                         break;
1619                                 case GP_STROKE_CYCLIC_TOGGLE:
1620                                         /* Just toggle flag... */
1621                                         gps->flag ^= GP_STROKE_CYCLIC;
1622                                         break;
1623                                 default:
1624                                         BLI_assert(0);
1625                                         break;
1626                         }
1627                 }
1628         }
1629         CTX_DATA_END;
1630         
1631         /* notifiers */
1632         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1633         
1634         return OPERATOR_FINISHED;
1635 }
1636
1637 /**
1638  * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
1639  * option to force opened/closed strokes instead of just toggle behavior.
1640  */
1641 void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
1642 {
1643         static EnumPropertyItem cyclic_type[] = {
1644                 {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
1645                 {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
1646                 {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
1647                 {0, NULL, 0, NULL, NULL}
1648         };
1649         
1650         /* identifiers */
1651         ot->name = "Set Cyclical State";
1652         ot->idname = "GPENCIL_OT_stroke_cyclical_set";
1653         ot->description = "Close or open the selected stroke adding an edge from last to first point";
1654         
1655         /* api callbacks */
1656         ot->exec = gp_stroke_cyclical_set_exec;
1657         ot->poll = gp_active_layer_poll;
1658         
1659         /* flags */
1660         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1661         
1662         /* properties */
1663         ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
1664 }
1665
1666 /* ******************* Stroke join ************************** */
1667
1668 /* Helper: flip stroke */
1669 static void gpencil_flip_stroke(bGPDstroke *gps)
1670 {
1671         int end = gps->totpoints - 1;
1672         
1673         for (int i = 0; i < gps->totpoints / 2; i++) {
1674                 bGPDspoint *point, *point2;
1675                 bGPDspoint pt;
1676         
1677                 /* save first point */
1678                 point = &gps->points[i];
1679                 pt.x = point->x;
1680                 pt.y = point->y;
1681                 pt.z = point->z;
1682                 pt.flag = point->flag;
1683                 pt.pressure = point->pressure;
1684                 pt.strength = point->strength;
1685                 pt.time = point->time;
1686                 
1687                 /* replace first point with last point */
1688                 point2 = &gps->points[end];
1689                 point->x = point2->x;
1690                 point->y = point2->y;
1691                 point->z = point2->z;
1692                 point->flag = point2->flag;
1693                 point->pressure = point2->pressure;
1694                 point->strength = point2->strength;
1695                 point->time = point2->time;
1696                 
1697                 /* replace last point with first saved before */
1698                 point = &gps->points[end];
1699                 point->x = pt.x;
1700                 point->y = pt.y;
1701                 point->z = pt.z;
1702                 point->flag = pt.flag;
1703                 point->pressure = pt.pressure;
1704                 point->strength = pt.strength;
1705                 point->time = pt.time;
1706                 
1707                 end--;
1708         }
1709 }
1710
1711 /* Helper: copy point between strokes */
1712 static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
1713                                       float pressure, float strength, float deltatime)
1714 {
1715         bGPDspoint *newpoint;
1716         
1717         gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
1718         gps->totpoints++;
1719         
1720         newpoint = &gps->points[gps->totpoints - 1];
1721         newpoint->x = point->x * delta[0];
1722         newpoint->y = point->y * delta[1];
1723         newpoint->z = point->z * delta[2];
1724         newpoint->flag = point->flag;
1725         newpoint->pressure = pressure;
1726         newpoint->strength = strength;
1727         newpoint->time = point->time + deltatime;
1728 }
1729
1730 /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
1731 static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
1732 {
1733         bGPDspoint point;
1734         bGPDspoint *pt;
1735         int i;
1736         float delta[3] = {1.0f, 1.0f, 1.0f};
1737         float deltatime = 0.0f;
1738         
1739         /* sanity checks */
1740         if (ELEM(NULL, gps_a, gps_b))
1741                 return;
1742         
1743         if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
1744                 return;
1745         
1746         /* define start and end points of each stroke */
1747         float sa[3], sb[3], ea[3], eb[3];
1748         pt = &gps_a->points[0];
1749         copy_v3_v3(sa, &pt->x);
1750         
1751         pt = &gps_a->points[gps_a->totpoints - 1];
1752         copy_v3_v3(ea, &pt->x);
1753         
1754         pt = &gps_b->points[0];
1755         copy_v3_v3(sb, &pt->x);
1756         
1757         pt = &gps_b->points[gps_b->totpoints - 1];
1758         copy_v3_v3(eb, &pt->x);
1759         
1760         /* review if need flip stroke B */
1761         float ea_sb = len_squared_v3v3(ea, sb);
1762         float ea_eb = len_squared_v3v3(ea, eb);
1763         /* flip if distance to end point is shorter */
1764         if (ea_eb < ea_sb) {
1765                 gpencil_flip_stroke(gps_b);
1766         }
1767         
1768         /* don't visibly link the first and last points? */
1769         if (leave_gaps) {
1770                 /* 1st: add one tail point to start invisible area */
1771                 point = gps_a->points[gps_a->totpoints - 1];
1772                 deltatime = point.time;
1773                 gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
1774                 
1775                 /* 2nd: add one head point to finish invisible area */
1776                 point = gps_b->points[0];
1777                 gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
1778         }
1779         
1780         /* 3rd: add all points */
1781         for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
1782                 /* check if still room in buffer */
1783                 if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
1784                         gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
1785                 }
1786         }
1787 }
1788
1789 static int gp_stroke_join_exec(bContext *C, wmOperator *op)
1790 {
1791         bGPdata *gpd = ED_gpencil_data_get_active(C);
1792         bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
1793         bGPDstroke *gps, *gpsn;
1794         bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
1795         bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
1796         
1797         bGPDframe *gpf_a = NULL;
1798         bGPDstroke *stroke_a = NULL;
1799         bGPDstroke *stroke_b = NULL;
1800         bGPDstroke *new_stroke = NULL;
1801         
1802         const int type = RNA_enum_get(op->ptr, "type");
1803         const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
1804         
1805         /* sanity checks */
1806         if (ELEM(NULL, gpd))
1807                 return OPERATOR_CANCELLED;
1808         
1809         if (activegpl->flag & GP_LAYER_LOCKED)
1810                 return OPERATOR_CANCELLED;
1811         
1812         BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
1813         
1814         
1815         /* read all selected strokes */
1816         bool first = false;
1817         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1818         {
1819                 bGPDframe *gpf = gpl->actframe;
1820                 if (gpf == NULL)
1821                         continue;
1822                 
1823                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1824                         gpsn = gps->next;
1825                         if (gps->flag & GP_STROKE_SELECT) {
1826                                 /* skip strokes that are invalid for current view */
1827                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1828                                         continue;
1829                                 }
1830                                 /* check if the color is editable */
1831                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
1832                                         continue;
1833                                 }
1834                                 
1835                                 /* to join strokes, cyclic must be disabled */
1836                                 gps->flag &= ~GP_STROKE_CYCLIC;
1837                                 
1838                                 /* saves first frame and stroke */
1839                                 if (!first) {
1840                                         first = true;
1841                                         gpf_a = gpf;
1842                                         stroke_a = gps;
1843                                 }
1844                                 else {
1845                                         stroke_b = gps;
1846                                         
1847                                         /* create a new stroke if was not created before (only created if something to join) */
1848                                         if (new_stroke == NULL) {
1849                                                 new_stroke = MEM_dupallocN(stroke_a);
1850                                                 new_stroke->points = MEM_dupallocN(stroke_a->points);
1851                                                 new_stroke->triangles = NULL;
1852                                                 new_stroke->tot_triangles = 0;
1853                                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1854                                                 
1855                                                 /* if new, set current color */
1856                                                 if (type == GP_STROKE_JOINCOPY) {
1857                                                         new_stroke->palcolor = palcolor;
1858                                                         BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
1859                                                         new_stroke->flag |= GP_STROKE_RECALC_COLOR;
1860                                                 }
1861                                         }
1862                                         
1863                                         /* join new_stroke and stroke B. New stroke will contain all the previous data */
1864                                         gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
1865                                         
1866                                         /* if join only, delete old strokes */
1867                                         if (type == GP_STROKE_JOIN) {
1868                                                 if (stroke_a) {
1869                                                         BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
1870                                                         BLI_remlink(&gpf->strokes, stroke_a);
1871                                                         BKE_gpencil_free_stroke(stroke_a);
1872                                                         stroke_a = NULL;
1873                                                 }
1874                                                 if (stroke_b) {
1875                                                         BLI_remlink(&gpf->strokes, stroke_b);
1876                                                         BKE_gpencil_free_stroke(stroke_b);
1877                                                         stroke_b = NULL;
1878                                                 }
1879                                         }
1880                                 }
1881                         }
1882                 }
1883         }
1884         CTX_DATA_END;
1885         
1886         /* add new stroke if was not added before */
1887         if (type == GP_STROKE_JOINCOPY) {
1888                 if (new_stroke) {
1889                         /* Add a new frame if needed */
1890                         if (activegpl->actframe == NULL)
1891                                 activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
1892                         
1893                         BLI_addtail(&activegpl->actframe->strokes, new_stroke);
1894                 }
1895         }
1896         
1897         /* notifiers */
1898         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1899         
1900         return OPERATOR_FINISHED;
1901 }
1902
1903 void GPENCIL_OT_stroke_join(wmOperatorType *ot)
1904 {
1905         static EnumPropertyItem join_type[] = {
1906                 {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
1907                 {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
1908                 {0, NULL, 0, NULL, NULL}
1909         };
1910         
1911         /* identifiers */
1912         ot->name = "Join Strokes";
1913         ot->idname = "GPENCIL_OT_stroke_join";
1914         ot->description = "Join selected strokes (optionally as new stroke)";
1915         
1916         /* api callbacks */
1917         ot->exec = gp_stroke_join_exec;
1918         ot->poll = gp_active_layer_poll;
1919         
1920         /* flags */
1921         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1922         
1923         /* properties */
1924         ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
1925         RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
1926 }
1927
1928 /* ******************* Stroke flip ************************** */
1929
1930 static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
1931 {
1932         bGPdata *gpd = ED_gpencil_data_get_active(C);
1933
1934         /* sanity checks */
1935         if (ELEM(NULL, gpd))
1936                 return OPERATOR_CANCELLED;
1937
1938         /* read all selected strokes */
1939         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1940         {
1941                 bGPDframe *gpf = gpl->actframe;
1942                 if (gpf == NULL)
1943                         continue;
1944                         
1945                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1946                         if (gps->flag & GP_STROKE_SELECT) {
1947                                 /* skip strokes that are invalid for current view */
1948                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1949                                         continue;
1950                                 }
1951                                 /* check if the color is editable */
1952                                 if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
1953                                         continue;
1954                                 }
1955                                 
1956                                 /* flip stroke */
1957                                 gpencil_flip_stroke(gps);
1958                         }
1959                 }
1960         }
1961         CTX_DATA_END;
1962         
1963         /* notifiers */
1964         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1965         
1966         return OPERATOR_FINISHED;
1967 }
1968
1969 void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
1970 {
1971         /* identifiers */
1972         ot->name = "Flip Stroke";
1973         ot->idname = "GPENCIL_OT_stroke_flip";
1974         ot->description = "Change direction of the points of the selected strokes";
1975         
1976         /* api callbacks */
1977         ot->exec = gp_stroke_flip_exec;
1978         ot->poll = gp_active_layer_poll;
1979         
1980         /* flags */
1981         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1982 }
1983
1984 /* ***************** Reproject Strokes ********************** */
1985
1986 typedef enum eGP_ReprojectModes {
1987         /* On same plane, parallel to viewplane */
1988         GP_REPROJECT_PLANAR = 0,
1989         /* Reprojected on to the scene geometry */
1990         GP_REPROJECT_SURFACE,
1991 } eGP_ReprojectModes;
1992
1993 static int gp_strokes_reproject_poll(bContext *C)
1994 {
1995         /* 2 Requirements:
1996          *  - 1) Editable GP data
1997          *  - 2) 3D View only (2D editors don't have projection issues)
1998          */
1999         return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C));
2000 }
2001
2002 static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
2003 {
2004         Scene *scene = CTX_data_scene(C);
2005         GP_SpaceConversion gsc = {NULL};
2006         eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type");
2007         
2008         /* init space conversion stuff */
2009         gp_point_conversion_init(C, &gsc);
2010         
2011         /* init autodist for geometry projection */
2012         if (mode == GP_REPROJECT_SURFACE) {
2013                 view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar);
2014                 ED_view3d_autodist_init(scene, gsc.ar, CTX_wm_view3d(C), 0);
2015         }
2016         
2017         // TODO: For deforming geometry workflow, create new frames?
2018         
2019         /* Go through each editable + selected stroke, adjusting each of its points one by one... */
2020         GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
2021         {
2022                 if (gps->flag & GP_STROKE_SELECT) {
2023                         bGPDspoint *pt;
2024                         int i;
2025                         float inverse_diff_mat[4][4];
2026                         
2027                         /* Compute inverse matrix for unapplying parenting once instead of doing per-point */
2028                         /* TODO: add this bit to the iteration macro? */
2029                         if (gpl->parent) {
2030                                 invert_m4_m4(inverse_diff_mat, diff_mat);
2031                         }
2032                         
2033                         /* Adjust each point */
2034                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2035                                 float xy[2];
2036                                 
2037                                 /* 3D to Screenspace */
2038                                 /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace
2039                                  *       coordinates, resulting in lost precision, which in turn causes stairstepping
2040                                  *       artifacts in the final points.
2041                                  */
2042                                 if (gpl->parent == NULL) {
2043                                         gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]);
2044                                 }
2045                                 else {
2046                                         bGPDspoint pt2;
2047                                         gp_point_to_parent_space(pt, diff_mat, &pt2);
2048                                         gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
2049                                 }
2050                                 
2051                                 /* Project screenspace back to 3D space (from current perspective)
2052                                  * so that all points have been treated the same way
2053                                  */
2054                                 if (mode == GP_REPROJECT_PLANAR) {
2055                                         /* Planar - All on same plane parallel to the viewplane */
2056                                         gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
2057                                 }
2058                                 else {
2059                                         /* Geometry - Snap to surfaces of visible geometry */
2060                                         /* XXX: There will be precision loss (possible stairstep artifacts) from this conversion to satisfy the API's */
2061                                         const int screen_co[2] = {(int)xy[0], (int)xy[1]};
2062                                         
2063                                         int depth_margin = 0; // XXX: 4 for strokes, 0 for normal
2064                                         float depth;
2065                                         
2066                                         /* XXX: The proper procedure computes the depths into an array, to have smooth transitions when all else fails... */
2067                                         if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) {
2068                                                 ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth);
2069                                         }
2070                                         else {
2071                                                 /* Default to planar */
2072                                                 gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
2073                                         }
2074                                 }
2075                                 
2076                                 /* Unapply parent corrections */
2077                                 if (gpl->parent) {
2078                                         mul_m4_v3(inverse_diff_mat, &pt->x);
2079                                 }
2080                         }
2081                 }
2082         }
2083         GP_EDITABLE_STROKES_END;
2084         
2085         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2086         return OPERATOR_FINISHED;
2087 }
2088
2089 void GPENCIL_OT_reproject(wmOperatorType *ot)
2090 {
2091         static EnumPropertyItem reproject_type[] = {
2092                 {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", 
2093                  "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
2094                  "using 'Cursor' Stroke Placement"},
2095                 {GP_REPROJECT_SURFACE, "SURFACE", 0, "Surface",
2096                  "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
2097                 {0, NULL, 0, NULL, NULL}
2098         };
2099         
2100         /* identifiers */
2101         ot->name = "Reproject Strokes";
2102         ot->idname = "GPENCIL_OT_reproject";
2103         ot->description = "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
2104                           "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
2105                           "or for matching deforming geometry)";
2106         
2107         /* callbacks */
2108         ot->invoke = WM_menu_invoke;
2109         ot->exec = gp_strokes_reproject_exec;
2110         ot->poll = gp_strokes_reproject_poll;
2111         
2112         /* flags */
2113         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2114         
2115         /* properties */
2116         ot->prop = RNA_def_enum(ot->srna, "type", reproject_type, GP_REPROJECT_PLANAR, "Projection Type", "");
2117 }
2118
2119 /* ******************* Stroke subdivide ************************** */
2120
2121 /* helper: Count how many points need to be inserted */
2122 static int gp_count_subdivision_cuts(bGPDstroke *gps)
2123 {
2124         bGPDspoint *pt;
2125         int i;
2126         int totnewpoints = 0;
2127         for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
2128                 if (pt->flag & GP_SPOINT_SELECT) {
2129                         if (i + 1 < gps->totpoints){
2130                                 if (gps->points[i + 1].flag & GP_SPOINT_SELECT) {
2131                                         ++totnewpoints;
2132                                 };
2133                         }
2134                 }
2135         }
2136
2137         return totnewpoints;
2138 }
2139 static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
2140 {
2141         bGPdata *gpd = ED_gpencil_data_get_active(C);
2142         bGPDspoint *temp_points;
2143         const int cuts = RNA_int_get(op->ptr, "number_cuts");
2144
2145         int totnewpoints, oldtotpoints;
2146         int i2;
2147
2148         /* sanity checks */
2149         if (ELEM(NULL, gpd))
2150                 return OPERATOR_CANCELLED;
2151
2152         /* Go through each editable + selected stroke */
2153         GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
2154         {
2155                 if (gps->flag & GP_STROKE_SELECT) {
2156                         /* loop as many times as cuts */
2157                         for (int s = 0; s < cuts; s++) {
2158                                 totnewpoints = gp_count_subdivision_cuts(gps);
2159                                 if (totnewpoints == 0) {
2160                                         continue;
2161                                 }
2162                                 /* duplicate points in a temp area */
2163                                 temp_points = MEM_dupallocN(gps->points);
2164                                 oldtotpoints = gps->totpoints;
2165
2166                                 /* resize the points arrys */
2167                                 gps->totpoints += totnewpoints;
2168                                 gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
2169                                 gps->flag |= GP_STROKE_RECALC_CACHES;
2170
2171                                 /* loop and interpolate */
2172                                 i2 = 0;
2173                                 for (int i = 0; i < oldtotpoints; i++) {
2174                                         bGPDspoint *pt = &temp_points[i];
2175                                         bGPDspoint *pt_final = &gps->points[i2];
2176
2177                                         /* copy current point */
2178                                         copy_v3_v3(&pt_final->x, &pt->x);
2179                                         pt_final->pressure = pt->pressure;
2180                                         pt_final->strength = pt->strength;
2181                                         pt_final->time = pt->time;
2182                                         pt_final->flag = pt->flag;
2183                                         ++i2;
2184
2185                                         /* if next point is selected add a half way point */
2186                                         if (pt->flag & GP_SPOINT_SELECT) {
2187                                                 if (i + 1 < oldtotpoints){
2188                                                         if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
2189                                                                 pt_final = &gps->points[i2];
2190                                                                 /* Interpolate all values */
2191                                                                 bGPDspoint *next = &temp_points[i + 1];
2192                                                                 interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
2193                                                                 pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
2194                                                                 pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
2195                                                                 CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
2196                                                                 pt_final->time = interpf(pt->time, next->time, 0.5f);
2197                                                                 pt_final->flag |= GP_SPOINT_SELECT;
2198                                                                 ++i2;
2199                                                         };
2200                                                 }
2201                                         }
2202                                 }
2203                                 /* free temp memory */
2204                                 MEM_freeN(temp_points);
2205                         }
2206                 }
2207         }
2208         GP_EDITABLE_STROKES_END;
2209
2210         /* notifiers */
2211         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2212
2213         return OPERATOR_FINISHED;
2214 }
2215
2216 void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
2217 {
2218         PropertyRNA *prop;
2219
2220         /* identifiers */
2221         ot->name = "Subdivide Stroke";
2222         ot->idname = "GPENCIL_OT_stroke_subdivide";
2223         ot->description = "Subdivide between continuous selected points of the stroke adding a point half way between them";
2224
2225         /* api callbacks */
2226         ot->exec = gp_stroke_subdivide_exec;
2227         ot->poll = gp_active_layer_poll;
2228
2229         /* flags */
2230         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
2231
2232         /* properties */
2233         prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5);
2234         /* avoid re-using last var because it can cause _very_ high value and annoy users */
2235         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2236
2237 }