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