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