72f65ce8a3782451fe2635807eeff20db0ee356d
[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_lasso_2d.h"
44 #include "BLI_math.h"
45 #include "BLI_string.h"
46 #include "BLI_string_utils.h"
47 #include "BLI_utildefines.h"
48
49 #include "BLT_translation.h"
50
51 #include "DNA_meshdata_types.h"
52 #include "DNA_object_types.h"
53 #include "DNA_scene_types.h"
54 #include "DNA_screen_types.h"
55 #include "DNA_space_types.h"
56 #include "DNA_view3d_types.h"
57 #include "DNA_gpencil_types.h"
58
59 #include "BKE_brush.h"
60 #include "BKE_context.h"
61 #include "BKE_global.h"
62 #include "BKE_gpencil.h"
63 #include "BKE_library.h"
64 #include "BKE_main.h"
65 #include "BKE_material.h"
66 #include "BKE_object.h"
67 #include "BKE_paint.h"
68 #include "BKE_report.h"
69 #include "BKE_screen.h"
70 #include "BKE_workspace.h"
71
72 #include "UI_interface.h"
73 #include "UI_resources.h"
74
75 #include "WM_api.h"
76 #include "WM_types.h"
77 #include "WM_message.h"
78 #include "WM_toolsystem.h"
79
80 #include "RNA_access.h"
81 #include "RNA_define.h"
82 #include "RNA_enum_types.h"
83
84 #include "UI_view2d.h"
85
86 #include "ED_gpencil.h"
87 #include "ED_object.h"
88 #include "ED_screen.h"
89 #include "ED_view3d.h"
90 #include "ED_select_utils.h"
91 #include "ED_space_api.h"
92
93 #include "DEG_depsgraph.h"
94 #include "DEG_depsgraph_build.h"
95 #include "DEG_depsgraph_query.h"
96
97 #include "gpencil_intern.h"
98
99   /* ************************************************ */
100   /* Stroke Edit Mode Management */
101
102 /* poll callback for all stroke editing operators */
103 static bool gp_stroke_edit_poll(bContext *C)
104 {
105         /* edit only supported with grease pencil objects */
106         Object *ob = CTX_data_active_object(C);
107         if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
108                 return false;
109         }
110
111         /* NOTE: this is a bit slower, but is the most accurate... */
112         return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
113 }
114
115 /* poll callback to verify edit mode in 3D view only */
116 static bool gp_strokes_edit3d_poll(bContext *C)
117 {
118         /* edit only supported with grease pencil objects */
119         Object *ob = CTX_data_active_object(C);
120         if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
121                 return false;
122         }
123
124
125         /* 2 Requirements:
126          * - 1) Editable GP data
127          * - 2) 3D View only
128          */
129         return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C));
130 }
131
132 static bool gpencil_editmode_toggle_poll(bContext *C)
133 {
134         /* edit only supported with grease pencil objects */
135         Object *ob = CTX_data_active_object(C);
136         if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
137                 return false;
138         }
139
140         /* if using gpencil object, use this gpd */
141         if (ob->type == OB_GPENCIL) {
142                 return ob->data != NULL;
143         }
144
145         return ED_gpencil_data_get_active(C) != NULL;
146 }
147
148 static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
149 {
150         const int back = RNA_boolean_get(op->ptr, "back");
151
152         struct wmMsgBus *mbus = CTX_wm_message_bus(C);
153         Depsgraph *depsgraph = CTX_data_depsgraph(C);
154         bGPdata *gpd = ED_gpencil_data_get_active(C);
155         bool is_object = false;
156         short mode;
157         /* if using a gpencil object, use this datablock */
158         Object *ob = CTX_data_active_object(C);
159         if ((ob) && (ob->type == OB_GPENCIL)) {
160                 gpd = ob->data;
161                 is_object = true;
162         }
163
164         if (gpd == NULL) {
165                 BKE_report(op->reports, RPT_ERROR, "No active GP data");
166                 return OPERATOR_CANCELLED;
167         }
168
169         /* Just toggle editmode flag... */
170         gpd->flag ^= GP_DATA_STROKE_EDITMODE;
171         /* recalculate parent matrix */
172         if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
173                 ED_gpencil_reset_layers_parent(depsgraph, ob, gpd);
174         }
175         /* set mode */
176         if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
177                 mode = OB_MODE_EDIT_GPENCIL;
178         }
179         else {
180                 mode = OB_MODE_OBJECT;
181         }
182
183         if (is_object) {
184                 /* try to back previous mode */
185                 if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) {
186                         mode = ob->restore_mode;
187                 }
188                 ob->restore_mode = ob->mode;
189                 ob->mode = mode;
190         }
191
192         /* setup other modes */
193         ED_gpencil_setup_modes(C, gpd, mode);
194         /* set cache as dirty */
195         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
196
197         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
198         WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL);
199         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
200
201         if (is_object) {
202                 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
203         }
204         if (G.background == false) {
205                 WM_toolsystem_update_from_context_view3d(C);
206         }
207
208         return OPERATOR_FINISHED;
209 }
210
211 void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
212 {
213         PropertyRNA *prop;
214
215         /* identifiers */
216         ot->name = "Strokes Edit Mode Toggle";
217         ot->idname = "GPENCIL_OT_editmode_toggle";
218         ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
219
220         /* callbacks */
221         ot->exec = gpencil_editmode_toggle_exec;
222         ot->poll = gpencil_editmode_toggle_poll;
223
224         /* flags */
225         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
226
227         /* properties */
228         prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
229         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
230 }
231
232 /* set select mode */
233 static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op)
234 {
235         Scene *scene = CTX_data_scene(C);
236         ToolSettings *ts = CTX_data_tool_settings(C);
237         const int mode = RNA_int_get(op->ptr, "mode");
238
239         /* Just set mode */
240         ts->gpencil_selectmode = mode;
241
242         WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
243         DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
244
245         return OPERATOR_FINISHED;
246 }
247
248 void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot)
249 {
250         PropertyRNA *prop;
251
252         /* identifiers */
253         ot->name = "Select Mode Toggle";
254         ot->idname = "GPENCIL_OT_selectmode_toggle";
255         ot->description = "Set selection mode for Grease Pencil strokes";
256
257         /* callbacks */
258         ot->exec = gpencil_selectmode_toggle_exec;
259         ot->poll = gp_strokes_edit3d_poll;
260
261         /* flags */
262         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
263
264         /* properties */
265         prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select mode", "Select mode", 0, 2);
266         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
267 }
268
269 /* Stroke Paint Mode Management */
270
271 static bool gpencil_paintmode_toggle_poll(bContext *C)
272 {
273         /* if using gpencil object, use this gpd */
274         Object *ob = CTX_data_active_object(C);
275         if ((ob) && (ob->type == OB_GPENCIL)) {
276                 return ob->data != NULL;
277         }
278         return ED_gpencil_data_get_active(C) != NULL;
279 }
280
281 static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
282 {
283         const bool back = RNA_boolean_get(op->ptr, "back");
284
285         struct wmMsgBus *mbus = CTX_wm_message_bus(C);
286         Main *bmain = CTX_data_main(C);
287         bGPdata *gpd = ED_gpencil_data_get_active(C);
288         ToolSettings *ts = CTX_data_tool_settings(C);
289
290         bool is_object = false;
291         short mode;
292         /* if using a gpencil object, use this datablock */
293         Object *ob = CTX_data_active_object(C);
294         if ((ob) && (ob->type == OB_GPENCIL)) {
295                 gpd = ob->data;
296                 is_object = true;
297         }
298
299         if (gpd == NULL)
300                 return OPERATOR_CANCELLED;
301
302         /* Just toggle paintmode flag... */
303         gpd->flag ^= GP_DATA_STROKE_PAINTMODE;
304         /* set mode */
305         if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
306                 mode = OB_MODE_PAINT_GPENCIL;
307         }
308         else {
309                 mode = OB_MODE_OBJECT;
310         }
311
312         if (is_object) {
313                 /* try to back previous mode */
314                 if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) {
315                         mode = ob->restore_mode;
316                 }
317                 ob->restore_mode = ob->mode;
318                 ob->mode = mode;
319         }
320
321         if (mode == OB_MODE_PAINT_GPENCIL) {
322                 /* be sure we have brushes */
323                 BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
324                 Paint *paint = &ts->gp_paint->paint;
325                 /* if not exist, create a new one */
326                 if (paint->brush == NULL) {
327                         BKE_brush_gpencil_presets(C);
328                 }
329                 BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint);
330         }
331
332         /* setup other modes */
333         ED_gpencil_setup_modes(C, gpd, mode);
334         /* set cache as dirty */
335         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
336
337         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
338         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
339
340         if (is_object) {
341                 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
342         }
343         if (G.background == false) {
344                 WM_toolsystem_update_from_context_view3d(C);
345         }
346
347         return OPERATOR_FINISHED;
348 }
349
350 void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot)
351 {
352         PropertyRNA *prop;
353
354         /* identifiers */
355         ot->name = "Strokes Paint Mode Toggle";
356         ot->idname = "GPENCIL_OT_paintmode_toggle";
357         ot->description = "Enter/Exit paint mode for Grease Pencil strokes";
358
359         /* callbacks */
360         ot->exec = gpencil_paintmode_toggle_exec;
361         ot->poll = gpencil_paintmode_toggle_poll;
362
363         /* flags */
364         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
365
366         /* properties */
367         prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
368         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
369 }
370
371 /* Stroke Sculpt Mode Management */
372
373 static bool gpencil_sculptmode_toggle_poll(bContext *C)
374 {
375         /* if using gpencil object, use this gpd */
376         Object *ob = CTX_data_active_object(C);
377         if ((ob) && (ob->type == OB_GPENCIL)) {
378                 return ob->data != NULL;
379         }
380         return ED_gpencil_data_get_active(C) != NULL;
381 }
382
383 static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
384 {
385         const bool back = RNA_boolean_get(op->ptr, "back");
386
387         struct wmMsgBus *mbus = CTX_wm_message_bus(C);
388         bGPdata *gpd = ED_gpencil_data_get_active(C);
389         bool is_object = false;
390         short mode;
391         /* if using a gpencil object, use this datablock */
392         Object *ob = CTX_data_active_object(C);
393         if ((ob) && (ob->type == OB_GPENCIL)) {
394                 gpd = ob->data;
395                 is_object = true;
396         }
397
398         if (gpd == NULL)
399                 return OPERATOR_CANCELLED;
400
401         /* Just toggle sculptmode flag... */
402         gpd->flag ^= GP_DATA_STROKE_SCULPTMODE;
403         /* set mode */
404         if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
405                 mode = OB_MODE_SCULPT_GPENCIL;
406         }
407         else {
408                 mode = OB_MODE_OBJECT;
409         }
410
411         if (is_object) {
412                 /* try to back previous mode */
413                 if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) {
414                         mode = ob->restore_mode;
415                 }
416                 ob->restore_mode = ob->mode;
417                 ob->mode = mode;
418         }
419
420         /* setup other modes */
421         ED_gpencil_setup_modes(C, gpd, mode);
422         /* set cache as dirty */
423         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
424
425         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
426         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
427
428         if (is_object) {
429                 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
430         }
431         if (G.background == false) {
432                 WM_toolsystem_update_from_context_view3d(C);
433         }
434
435         return OPERATOR_FINISHED;
436 }
437
438 void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot)
439 {
440         PropertyRNA *prop;
441
442         /* identifiers */
443         ot->name = "Strokes Sculpt Mode Toggle";
444         ot->idname = "GPENCIL_OT_sculptmode_toggle";
445         ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes";
446
447         /* callbacks */
448         ot->exec = gpencil_sculptmode_toggle_exec;
449         ot->poll = gpencil_sculptmode_toggle_poll;
450
451         /* flags */
452         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
453
454         /* properties */
455         prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
456         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
457 }
458
459 /* Stroke Weight Paint Mode Management */
460
461 static bool gpencil_weightmode_toggle_poll(bContext *C)
462 {
463         /* if using gpencil object, use this gpd */
464         Object *ob = CTX_data_active_object(C);
465         if ((ob) && (ob->type == OB_GPENCIL)) {
466                 return ob->data != NULL;
467         }
468         return ED_gpencil_data_get_active(C) != NULL;
469 }
470
471 static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
472 {
473         const bool back = RNA_boolean_get(op->ptr, "back");
474
475         struct wmMsgBus *mbus = CTX_wm_message_bus(C);
476         bGPdata *gpd = ED_gpencil_data_get_active(C);
477         bool is_object = false;
478         short mode;
479         /* if using a gpencil object, use this datablock */
480         Object *ob = CTX_data_active_object(C);
481         if ((ob) && (ob->type == OB_GPENCIL)) {
482                 gpd = ob->data;
483                 is_object = true;
484         }
485
486         if (gpd == NULL)
487                 return OPERATOR_CANCELLED;
488
489         /* Just toggle weightmode flag... */
490         gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE;
491         /* set mode */
492         if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) {
493                 mode = OB_MODE_WEIGHT_GPENCIL;
494         }
495         else {
496                 mode = OB_MODE_OBJECT;
497         }
498
499         if (is_object) {
500                 /* try to back previous mode */
501                 if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) {
502                         mode = ob->restore_mode;
503                 }
504                 ob->restore_mode = ob->mode;
505                 ob->mode = mode;
506         }
507
508         /* setup other modes */
509         ED_gpencil_setup_modes(C, gpd, mode);
510         /* set cache as dirty */
511         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
512
513         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
514         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
515
516         if (is_object) {
517                 WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
518         }
519         if (G.background == false) {
520                 WM_toolsystem_update_from_context_view3d(C);
521         }
522
523         return OPERATOR_FINISHED;
524 }
525
526 void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot)
527 {
528         PropertyRNA *prop;
529
530         /* identifiers */
531         ot->name = "Strokes Weight Mode Toggle";
532         ot->idname = "GPENCIL_OT_weightmode_toggle";
533         ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes";
534
535         /* callbacks */
536         ot->exec = gpencil_weightmode_toggle_exec;
537         ot->poll = gpencil_weightmode_toggle_poll;
538
539         /* flags */
540         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
541
542         /* properties */
543         prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
544         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
545 }
546
547 /* ************************************************ */
548 /* Stroke Editing Operators */
549
550 /* ************ Stroke Hide selection Toggle ************** */
551
552 static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
553 {
554         View3D *v3d = CTX_wm_view3d(C);
555         if (v3d == NULL)
556                 return OPERATOR_CANCELLED;
557
558         /* Just toggle alpha... */
559         if (v3d->vertex_opacity > 0.0f) {
560                 v3d->vertex_opacity = 0.0f;
561         }
562         else {
563                 v3d->vertex_opacity = 1.0f;
564         }
565
566         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
567         WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL);
568         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
569
570         return OPERATOR_FINISHED;
571 }
572
573 void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
574 {
575         /* identifiers */
576         ot->name = "Hide Selection";
577         ot->idname = "GPENCIL_OT_selection_opacity_toggle";
578         ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
579
580         /* callbacks */
581         ot->exec = gpencil_hideselect_toggle_exec;
582         ot->poll = gp_stroke_edit_poll;
583
584         /* flags */
585         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
586 }
587
588 /* ************** Duplicate Selected Strokes **************** */
589
590 /* Make copies of selected point segments in a selected stroke */
591 static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
592 {
593         bGPDspoint *pt;
594         int i;
595
596         int start_idx = -1;
597
598
599         /* Step through the original stroke's points:
600          * - We accumulate selected points (from start_idx to current index)
601          *   and then convert that to a new stroke
602          */
603         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
604                 /* searching for start, are waiting for end? */
605                 if (start_idx == -1) {
606                         /* is this the first selected point for a new island? */
607                         if (pt->flag & GP_SPOINT_SELECT) {
608                                 start_idx = i;
609                         }
610                 }
611                 else {
612                         size_t len = 0;
613
614                         /* is this the end of current island yet?
615                          * 1) Point i-1 was the last one that was selected
616                          * 2) Point i is the last in the array
617                          */
618                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
619                                 len = i - start_idx;
620                         }
621                         else if (i == gps->totpoints - 1) {
622                                 len = i - start_idx + 1;
623                         }
624                         //printf("copying from %d to %d = %d\n", start_idx, i, len);
625
626                         /* make copies of the relevant data */
627                         if (len) {
628                                 bGPDstroke *gpsd;
629
630                                 /* make a stupid copy first of the entire stroke (to get the flags too) */
631                                 gpsd = MEM_dupallocN(gps);
632                                 BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */
633
634                                 /* initialize triangle memory - will be calculated on next redraw */
635                                 gpsd->triangles = NULL;
636                                 gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
637                                 gpsd->tot_triangles = 0;
638
639                                 /* now, make a new points array, and copy of the relevant parts */
640                                 gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
641                                 memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
642                                 gpsd->totpoints = len;
643
644                                 if (gps->dvert != NULL) {
645                                         gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
646                                         memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
647
648                                         /* Copy weights */
649                                         int e = start_idx;
650                                         for (int j = 0; j < gpsd->totpoints; j++) {
651                                                 MDeformVert *dvert_dst = &gps->dvert[e];
652                                                 MDeformVert *dvert_src = &gps->dvert[j];
653                                                 dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
654                                                 e++;
655                                         }
656                                 }
657
658                                 /* add to temp buffer */
659                                 gpsd->next = gpsd->prev = NULL;
660                                 BLI_addtail(new_strokes, gpsd);
661
662                                 /* cleanup + reset for next */
663                                 start_idx = -1;
664                         }
665                 }
666         }
667 }
668
669 static int gp_duplicate_exec(bContext *C, wmOperator *op)
670 {
671         bGPdata *gpd = ED_gpencil_data_get_active(C);
672
673         if (gpd == NULL) {
674                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
675                 return OPERATOR_CANCELLED;
676         }
677
678         if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
679                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
680                 return OPERATOR_CANCELLED;
681         }
682
683         /* for each visible (and editable) layer's selected strokes,
684          * copy the strokes into a temporary buffer, then append
685          * once all done
686          */
687         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
688         {
689                 ListBase new_strokes = {NULL, NULL};
690                 bGPDframe *gpf = gpl->actframe;
691                 bGPDstroke *gps;
692
693                 if (gpf == NULL)
694                         continue;
695
696                 /* make copies of selected strokes, and deselect these once we're done */
697                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
698                         /* skip strokes that are invalid for current view */
699                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
700                                 continue;
701                         }
702
703                         if (gps->flag & GP_STROKE_SELECT) {
704                                 if (gps->totpoints == 1) {
705                                         /* Special Case: If there's just a single point in this stroke... */
706                                         bGPDstroke *gpsd;
707
708                                         /* make direct copies of the stroke and its points */
709                                         gpsd = MEM_dupallocN(gps);
710                                         BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
711                                         gpsd->points = MEM_dupallocN(gps->points);
712                                         if (gps->dvert != NULL) {
713                                                 gpsd->dvert = MEM_dupallocN(gps->dvert);
714                                                 BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
715                                         }
716
717                                         /* triangle information - will be calculated on next redraw */
718                                         gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
719                                         gpsd->triangles = NULL;
720
721                                         /* add to temp buffer */
722                                         gpsd->next = gpsd->prev = NULL;
723                                         BLI_addtail(&new_strokes, gpsd);
724                                 }
725                                 else {
726                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
727                                         gp_duplicate_points(gps, &new_strokes, gpl->info);
728                                 }
729
730                                 /* deselect original stroke, or else the originals get moved too
731                                  * (when using the copy + move macro)
732                                  */
733                                 gps->flag &= ~GP_STROKE_SELECT;
734                         }
735                 }
736
737                 /* add all new strokes in temp buffer to the frame (preventing double-copies) */
738                 BLI_movelisttolist(&gpf->strokes, &new_strokes);
739                 BLI_assert(new_strokes.first == NULL);
740         }
741         CTX_DATA_END;
742
743         /* updates */
744         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
745         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
746
747         return OPERATOR_FINISHED;
748 }
749
750 void GPENCIL_OT_duplicate(wmOperatorType *ot)
751 {
752         /* identifiers */
753         ot->name = "Duplicate Strokes";
754         ot->idname = "GPENCIL_OT_duplicate";
755         ot->description = "Duplicate the selected Grease Pencil strokes";
756
757         /* callbacks */
758         ot->exec = gp_duplicate_exec;
759         ot->poll = gp_stroke_edit_poll;
760
761         /* flags */
762         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
763 }
764
765 /* ******************* Copy/Paste Strokes ************************* */
766 /* Grease Pencil stroke data copy/paste buffer:
767  * - The copy operation collects all segments of selected strokes,
768  *   dumping "ready to be copied" copies of the strokes into the buffer.
769  * - The paste operation makes a copy of those elements, and adds them
770  *   to the active layer. This effectively flattens down the strokes
771  *   from several different layers into a single layer.
772  */
773
774  /* list of bGPDstroke instances */
775  /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
776 ListBase gp_strokes_copypastebuf = {NULL, NULL};
777
778 /* Hash for hanging on to all the colors used by strokes in the buffer
779  *
780  * This is needed to prevent dangling and unsafe pointers when pasting across datablocks,
781  * or after a color used by a stroke in the buffer gets deleted (via user action or undo).
782  */
783 static GHash *gp_strokes_copypastebuf_colors = NULL;
784
785 static GHash *gp_strokes_copypastebuf_colors_material_to_name_create(Main *bmain)
786 {
787         GHash *ma_to_name = BLI_ghash_ptr_new(__func__);
788
789         for (Material *ma = bmain->mat.first; ma != NULL; ma = ma->id.next) {
790                 char *name = BKE_id_to_unique_string_key(&ma->id);
791                 BLI_ghash_insert(ma_to_name, ma, name);
792         }
793
794         return ma_to_name;
795 }
796
797 static void gp_strokes_copypastebuf_colors_material_to_name_free(GHash *ma_to_name)
798 {
799         BLI_ghash_free(ma_to_name, NULL, MEM_freeN);
800 }
801
802 static GHash *gp_strokes_copypastebuf_colors_name_to_material_create(Main *bmain)
803 {
804         GHash *name_to_ma = BLI_ghash_str_new(__func__);
805
806         for (Material *ma = bmain->mat.first; ma != NULL; ma = ma->id.next) {
807                 char *name = BKE_id_to_unique_string_key(&ma->id);
808                 BLI_ghash_insert(name_to_ma, name, ma);
809         }
810
811         return name_to_ma;
812 }
813
814 static void gp_strokes_copypastebuf_colors_name_to_material_free(GHash *name_to_ma)
815 {
816         BLI_ghash_free(name_to_ma, MEM_freeN, NULL);
817 }
818
819 /* Free copy/paste buffer data */
820 void ED_gpencil_strokes_copybuf_free(void)
821 {
822         bGPDstroke *gps, *gpsn;
823
824         /* Free the colors buffer
825          * NOTE: This is done before the strokes so that the ptrs are still safe
826          */
827         if (gp_strokes_copypastebuf_colors) {
828                 BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN);
829                 gp_strokes_copypastebuf_colors = NULL;
830         }
831
832         /* Free the stroke buffer */
833         for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
834                 gpsn = gps->next;
835
836                 if (gps->points) {
837                         MEM_freeN(gps->points);
838                 }
839                 if (gps->dvert) {
840                         BKE_gpencil_free_stroke_weights(gps);
841                         MEM_freeN(gps->dvert);
842                 }
843
844                 MEM_SAFE_FREE(gps->triangles);
845
846                 BLI_freelinkN(&gp_strokes_copypastebuf, gps);
847         }
848
849         gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
850 }
851
852 /* Ensure that destination datablock has all the colours the pasted strokes need
853  * Helper function for copy-pasting strokes
854  */
855 GHash *gp_copybuf_validate_colormap(bContext *C)
856 {
857         Main *bmain = CTX_data_main(C);
858         Object *ob = CTX_data_active_object(C);
859         GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors");
860         GHashIterator gh_iter;
861
862         /* For each color, check if exist and add if not */
863         GHash *name_to_ma = gp_strokes_copypastebuf_colors_name_to_material_create(bmain);
864
865         GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) {
866                 int *key = BLI_ghashIterator_getKey(&gh_iter);
867                 char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
868                 Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
869
870                 if (ma != NULL && BKE_gpencil_get_material_index(ob, ma) == 0) {
871                         BKE_object_material_slot_add(bmain, ob);
872                         assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
873                 }
874
875                 /* Store this mapping (for use later when pasting) */
876                 if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) {
877                         BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma);
878                 }
879         }
880
881         gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma);
882
883         return new_colors;
884 }
885
886 /* --------------------- */
887 /* Copy selected strokes */
888
889 static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
890 {
891         Main *bmain = CTX_data_main(C);
892         Object *ob = CTX_data_active_object(C);
893         bGPdata *gpd = ED_gpencil_data_get_active(C);
894
895         if (gpd == NULL) {
896                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
897                 return OPERATOR_CANCELLED;
898         }
899
900         if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
901                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
902                 return OPERATOR_CANCELLED;
903         }
904
905         /* clear the buffer first */
906         ED_gpencil_strokes_copybuf_free();
907
908         /* for each visible (and editable) layer's selected strokes,
909          * copy the strokes into a temporary buffer, then append
910          * once all done
911          */
912         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
913         {
914                 bGPDframe *gpf = gpl->actframe;
915                 bGPDstroke *gps;
916
917                 if (gpf == NULL)
918                         continue;
919
920                 /* make copies of selected strokes, and deselect these once we're done */
921                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
922                         /* skip strokes that are invalid for current view */
923                         if (ED_gpencil_stroke_can_use(C, gps) == false)
924                                 continue;
925
926                         if (gps->flag & GP_STROKE_SELECT) {
927                                 if (gps->totpoints == 1) {
928                                         /* Special Case: If there's just a single point in this stroke... */
929                                         bGPDstroke *gpsd;
930
931                                         /* make direct copies of the stroke and its points */
932                                         gpsd = MEM_dupallocN(gps);
933                                         BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */
934                                         gpsd->points = MEM_dupallocN(gps->points);
935                                         if (gps->dvert != NULL) {
936                                                 gpsd->dvert = MEM_dupallocN(gps->dvert);
937                                                 BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
938                                         }
939
940                                         /* triangles cache - will be recalculated on next redraw */
941                                         gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
942                                         gpsd->tot_triangles = 0;
943                                         gpsd->triangles = NULL;
944
945                                         /* add to temp buffer */
946                                         gpsd->next = gpsd->prev = NULL;
947                                         BLI_addtail(&gp_strokes_copypastebuf, gpsd);
948                                 }
949                                 else {
950                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
951                                         gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
952                                 }
953                         }
954                 }
955         }
956         CTX_DATA_END;
957
958         /* Build up hash of material colors used in these strokes */
959         if (gp_strokes_copypastebuf.first) {
960                 gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
961                 GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain);
962                 for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
963                         if (ED_gpencil_stroke_can_use(C, gps)) {
964                                 char **ma_name_val;
965                                 if (!BLI_ghash_ensure_p(gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
966                                         Material *ma = give_current_material(ob, gps->mat_nr + 1);
967                                         char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
968                                         *ma_name_val = MEM_dupallocN(ma_name);
969                                 }
970                         }
971                 }
972                 gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name);
973         }
974
975         /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
976         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX?
977
978         /* done */
979         return OPERATOR_FINISHED;
980 }
981
982 void GPENCIL_OT_copy(wmOperatorType *ot)
983 {
984         /* identifiers */
985         ot->name = "Copy Strokes";
986         ot->idname = "GPENCIL_OT_copy";
987         ot->description = "Copy selected Grease Pencil points and strokes";
988
989         /* callbacks */
990         ot->exec = gp_strokes_copy_exec;
991         ot->poll = gp_stroke_edit_poll;
992
993         /* flags */
994         //ot->flag = OPTYPE_REGISTER;
995 }
996
997 /* --------------------- */
998 /* Paste selected strokes */
999
1000 static bool gp_strokes_paste_poll(bContext *C)
1001 {
1002         /* 1) Must have GP datablock to paste to
1003          *    - We don't need to have an active layer though, as that can easily get added
1004          *    - If the active layer is locked, we can't paste there, but that should prompt a warning instead
1005          * 2) Copy buffer must at least have something (though it may be the wrong sort...)
1006          */
1007         return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
1008 }
1009
1010 typedef enum eGP_PasteMode {
1011         GP_COPY_ONLY = -1,
1012         GP_COPY_MERGE = 1
1013 } eGP_PasteMode;
1014
1015 static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
1016 {
1017         Object *ob = CTX_data_active_object(C);
1018         bGPdata *gpd = ED_gpencil_data_get_active(C);
1019         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
1020         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1021         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1022         bGPDframe *gpf;
1023
1024         eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
1025         GHash *new_colors;
1026
1027         /* check for various error conditions */
1028         if (gpd == NULL) {
1029                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
1030                 return OPERATOR_CANCELLED;
1031         }
1032         else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1033                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1034                 return OPERATOR_CANCELLED;
1035         }
1036         else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
1037                 BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
1038                 return OPERATOR_CANCELLED;
1039         }
1040         else if (gpl == NULL) {
1041                 /* no active layer - let's just create one */
1042                 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
1043         }
1044         else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
1045                 BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
1046                 return OPERATOR_CANCELLED;
1047         }
1048         else {
1049                 /* Check that some of the strokes in the buffer can be used */
1050                 bGPDstroke *gps;
1051                 bool ok = false;
1052
1053                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
1054                         if (ED_gpencil_stroke_can_use(C, gps)) {
1055                                 ok = true;
1056                                 break;
1057                         }
1058                 }
1059
1060                 if (ok == false) {
1061                         /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
1062                          * but should be enough to give users a good idea of what's going on
1063                          */
1064                         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
1065                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
1066                         else
1067                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
1068
1069                         return OPERATOR_CANCELLED;
1070                 }
1071         }
1072
1073         /* Deselect all strokes first */
1074         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
1075         {
1076                 bGPDspoint *pt;
1077                 int i;
1078
1079                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1080                         pt->flag &= ~GP_SPOINT_SELECT;
1081                 }
1082
1083                 gps->flag &= ~GP_STROKE_SELECT;
1084         }
1085         CTX_DATA_END;
1086
1087         /* Ensure that all the necessary colors exist */
1088         new_colors = gp_copybuf_validate_colormap(C);
1089
1090         /* Copy over the strokes from the buffer (and adjust the colors) */
1091         for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
1092                 if (ED_gpencil_stroke_can_use(C, gps)) {
1093                         /* Need to verify if layer exists */
1094                         if (type != GP_COPY_MERGE) {
1095                                 gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
1096                                 if (gpl == NULL) {
1097                                         /* no layer - use active (only if layer deleted before paste) */
1098                                         gpl = CTX_data_active_gpencil_layer(C);
1099                                 }
1100                         }
1101
1102                         /* Ensure we have a frame to draw into
1103                          * NOTE: Since this is an op which creates strokes,
1104                          *       we are obliged to add a new frame if one
1105                          *       doesn't exist already
1106                          */
1107                         gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
1108                         if (gpf) {
1109                                 /* Create new stroke */
1110                                 bGPDstroke *new_stroke = MEM_dupallocN(gps);
1111                                 new_stroke->runtime.tmp_layerinfo[0] = '\0';
1112
1113                                 new_stroke->points = MEM_dupallocN(gps->points);
1114                                 if (gps->dvert != NULL) {
1115                                         new_stroke->dvert = MEM_dupallocN(gps->dvert);
1116                                         BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
1117                                 }
1118                                 new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
1119                                 new_stroke->triangles = NULL;
1120
1121                                 new_stroke->next = new_stroke->prev = NULL;
1122                                 BLI_addtail(&gpf->strokes, new_stroke);
1123
1124                                 /* Remap material */
1125                                 Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
1126                                 if ((ma) && (BKE_gpencil_get_material_index(ob, ma) > 0)) {
1127                                         new_stroke->mat_nr = BKE_gpencil_get_material_index(ob, ma) - 1;
1128                                         CLAMP_MIN(new_stroke->mat_nr, 0);
1129                                 }
1130                                 else {
1131                                         new_stroke->mat_nr = 0; /* only if the color is not found */
1132                                 }
1133
1134                         }
1135                 }
1136         }
1137
1138         /* free temp data */
1139         BLI_ghash_free(new_colors, NULL, NULL);
1140
1141         /* updates */
1142         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1143         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1144
1145         return OPERATOR_FINISHED;
1146 }
1147
1148 void GPENCIL_OT_paste(wmOperatorType *ot)
1149 {
1150         static const EnumPropertyItem copy_type[] = {
1151                 {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
1152                 {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
1153                 {0, NULL, 0, NULL, NULL}
1154         };
1155
1156         /* identifiers */
1157         ot->name = "Paste Strokes";
1158         ot->idname = "GPENCIL_OT_paste";
1159         ot->description = "Paste previously copied strokes or copy and merge in active layer";
1160
1161         /* callbacks */
1162         ot->exec = gp_strokes_paste_exec;
1163         ot->poll = gp_strokes_paste_poll;
1164
1165         /* flags */
1166         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
1167
1168         /* properties */
1169         ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
1170 }
1171
1172 /* ******************* Move To Layer ****************************** */
1173
1174 static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1175 {
1176         uiPopupMenu *pup;
1177         uiLayout *layout;
1178
1179         /* call the menu, which will call this operator again, hence the canceled */
1180         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1181         layout = UI_popup_menu_layout(pup);
1182         uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
1183         UI_popup_menu_end(C, pup);
1184
1185         return OPERATOR_INTERFACE;
1186 }
1187
1188 // FIXME: allow moving partial strokes
1189 static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
1190 {
1191         bGPdata *gpd = CTX_data_gpencil_data(C);
1192         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1193         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1194         bGPDlayer *target_layer = NULL;
1195         ListBase strokes = {NULL, NULL};
1196         int layer_num = RNA_enum_get(op->ptr, "layer");
1197
1198         if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1199                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1200                 return OPERATOR_CANCELLED;
1201         }
1202
1203         /* Get layer or create new one */
1204         if (layer_num == -1) {
1205                 /* Create layer */
1206                 target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
1207         }
1208         else {
1209                 /* Try to get layer */
1210                 target_layer = BLI_findlink(&gpd->layers, layer_num);
1211
1212                 if (target_layer == NULL) {
1213                         BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
1214                         return OPERATOR_CANCELLED;
1215                 }
1216         }
1217
1218         /* Extract all strokes to move to this layer
1219          * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
1220          *       getting repeatedly moved
1221          */
1222         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1223         {
1224                 bGPDframe *gpf = gpl->actframe;
1225                 bGPDstroke *gps, *gpsn;
1226
1227                 /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
1228                 if ((gpl == target_layer) || (gpf == NULL))
1229                         continue;
1230
1231                 /* make copies of selected strokes, and deselect these once we're done */
1232                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1233                         gpsn = gps->next;
1234
1235                         /* skip strokes that are invalid for current view */
1236                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1237                                 continue;
1238
1239                         /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
1240                         if (gps->flag & GP_STROKE_SELECT) {
1241                                 BLI_remlink(&gpf->strokes, gps);
1242                                 BLI_addtail(&strokes, gps);
1243                         }
1244                 }
1245         }
1246         CTX_DATA_END;
1247
1248         /* Paste them all in one go */
1249         if (strokes.first) {
1250                 bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, GP_GETFRAME_ADD_NEW);
1251
1252                 BLI_movelisttolist(&gpf->strokes, &strokes);
1253                 BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
1254         }
1255
1256         /* updates */
1257         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1258         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1259
1260         return OPERATOR_FINISHED;
1261 }
1262
1263 void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
1264 {
1265         /* identifiers */
1266         ot->name = "Move Strokes to Layer";
1267         ot->idname = "GPENCIL_OT_move_to_layer";
1268         ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
1269
1270         /* callbacks */
1271         ot->invoke = gp_move_to_layer_invoke;
1272         ot->exec = gp_move_to_layer_exec;
1273         ot->poll = gp_stroke_edit_poll; // XXX?
1274
1275         /* flags */
1276         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1277
1278         /* gp layer to use (dynamic enum) */
1279         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
1280         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
1281 }
1282
1283 /* ********************* Add Blank Frame *************************** */
1284
1285 /* Basically the same as the drawing op */
1286 static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C)
1287 {
1288         if (ED_operator_regionactive(C)) {
1289                 /* check if current context can support GPencil data */
1290                 if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
1291                         return 1;
1292                 }
1293                 else {
1294                         CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
1295                 }
1296         }
1297         else {
1298                 CTX_wm_operator_poll_msg_set(C, "Active region not set");
1299         }
1300
1301         return 0;
1302 }
1303
1304 static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
1305 {
1306         bGPdata *gpd = ED_gpencil_data_get_active(C);
1307         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1308         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1309
1310         bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd);
1311
1312         const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
1313
1314         /* Initialise datablock and an active layer if nothing exists yet */
1315         if (ELEM(NULL, gpd, active_gpl)) {
1316                 /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */
1317                 WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL);
1318         }
1319
1320         /* Go through each layer, adding a frame after the active one
1321          * and/or shunting all the others out of the way
1322          */
1323         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1324         {
1325                 if ((all_layers == false) && (gpl != active_gpl)) {
1326                         continue;
1327                 }
1328
1329                 /* 1) Check for an existing frame on the current frame */
1330                 bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval);
1331                 if (gpf) {
1332                         /* Shunt all frames after (and including) the existing one later by 1-frame */
1333                         for (; gpf; gpf = gpf->next) {
1334                                 gpf->framenum += 1;
1335                         }
1336                 }
1337
1338                 /* 2) Now add a new frame, with nothing in it */
1339                 gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
1340         }
1341         CTX_DATA_END;
1342
1343         /* notifiers */
1344         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1345         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1346
1347         return OPERATOR_FINISHED;
1348 }
1349
1350 void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
1351 {
1352         /* identifiers */
1353         ot->name = "Insert Blank Frame";
1354         ot->idname = "GPENCIL_OT_blank_frame_add";
1355         ot->description = "Insert a blank frame on the current frame "
1356                 "(all subsequently existing frames, if any, are shifted right by one frame)";
1357
1358         /* callbacks */
1359         ot->exec = gp_blank_frame_add_exec;
1360         ot->poll = gp_add_poll;
1361
1362         /* properties */
1363         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1364         RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active");
1365 }
1366
1367 /* ******************* Delete Active Frame ************************ */
1368
1369 static bool gp_actframe_delete_poll(bContext *C)
1370 {
1371         bGPdata *gpd = ED_gpencil_data_get_active(C);
1372         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1373
1374         /* only if there's an active layer with an active frame */
1375         return (gpl && gpl->actframe);
1376 }
1377
1378 /* delete active frame - wrapper around API calls */
1379 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
1380 {
1381         bGPdata *gpd = ED_gpencil_data_get_active(C);
1382         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1383
1384         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1385         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1386
1387         bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
1388
1389         /* if there's no existing Grease-Pencil data there, add some */
1390         if (gpd == NULL) {
1391                 BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
1392                 return OPERATOR_CANCELLED;
1393         }
1394         if (ELEM(NULL, gpl, gpf)) {
1395                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
1396                 return OPERATOR_CANCELLED;
1397         }
1398
1399         /* delete it... */
1400         BKE_gpencil_layer_delframe(gpl, gpf);
1401
1402         /* notifiers */
1403         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1404         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1405
1406         return OPERATOR_FINISHED;
1407 }
1408
1409 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
1410 {
1411         /* identifiers */
1412         ot->name = "Delete Active Frame";
1413         ot->idname = "GPENCIL_OT_active_frame_delete";
1414         ot->description = "Delete the active frame for the active Grease Pencil Layer";
1415
1416         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1417
1418         /* callbacks */
1419         ot->exec = gp_actframe_delete_exec;
1420         ot->poll = gp_actframe_delete_poll;
1421 }
1422
1423 /* **************** Delete All Active Frames ****************** */
1424
1425 static bool gp_actframe_delete_all_poll(bContext *C)
1426 {
1427         bGPdata *gpd = ED_gpencil_data_get_active(C);
1428
1429         /* 1) There must be grease pencil data
1430          * 2) Hopefully some of the layers have stuff we can use
1431          */
1432         return (gpd && gpd->layers.first);
1433 }
1434
1435 static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
1436 {
1437         bGPdata *gpd = ED_gpencil_data_get_active(C);
1438         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1439         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1440
1441         bool success = false;
1442
1443         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1444         {
1445                 /* try to get the "active" frame - but only if it actually occurs on this frame */
1446                 bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
1447
1448                 if (gpf == NULL)
1449                         continue;
1450
1451                 /* delete it... */
1452                 BKE_gpencil_layer_delframe(gpl, gpf);
1453
1454                 /* we successfully modified something */
1455                 success = true;
1456         }
1457         CTX_DATA_END;
1458
1459         /* updates */
1460         if (success) {
1461                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1462                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1463                 return OPERATOR_FINISHED;
1464         }
1465         else {
1466                 BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
1467                 return OPERATOR_CANCELLED;
1468         }
1469 }
1470
1471 void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
1472 {
1473         /* identifiers */
1474         ot->name = "Delete All Active Frames";
1475         ot->idname = "GPENCIL_OT_active_frames_delete_all";
1476         ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
1477
1478         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1479
1480         /* callbacks */
1481         ot->exec = gp_actframe_delete_all_exec;
1482         ot->poll = gp_actframe_delete_all_poll;
1483 }
1484
1485 /* ******************* Delete Operator ************************ */
1486
1487 typedef enum eGP_DeleteMode {
1488         /* delete selected stroke points */
1489         GP_DELETEOP_POINTS          = 0,
1490         /* delete selected strokes */
1491         GP_DELETEOP_STROKES         = 1,
1492         /* delete active frame */
1493         GP_DELETEOP_FRAME           = 2,
1494 } eGP_DeleteMode;
1495
1496 typedef enum eGP_DissolveMode {
1497         /* dissolve all selected points */
1498         GP_DISSOLVE_POINTS = 0,
1499         /* dissolve between selected points */
1500         GP_DISSOLVE_BETWEEN = 1,
1501         /* dissolve unselected points */
1502         GP_DISSOLVE_UNSELECT = 2,
1503 } eGP_DissolveMode;
1504
1505 /* ----------------------------------- */
1506
1507 /* Delete selected strokes */
1508 static int gp_delete_selected_strokes(bContext *C)
1509 {
1510         bool changed = false;
1511         bGPdata *gpd = ED_gpencil_data_get_active(C);
1512         const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1513
1514         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1515         {
1516                 bGPDframe *init_gpf = gpl->actframe;
1517                 if (is_multiedit) {
1518                         init_gpf = gpl->frames.first;
1519                 }
1520
1521                 for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1522                         if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1523                                 bGPDstroke *gps, *gpsn;
1524
1525                                 if (gpf == NULL)
1526                                         continue;
1527
1528                                 /* simply delete strokes which are selected */
1529                                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1530                                         gpsn = gps->next;
1531
1532                                         /* skip strokes that are invalid for current view */
1533                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1534                                                 continue;
1535
1536                                         /* free stroke if selected */
1537                                         if (gps->flag & GP_STROKE_SELECT) {
1538                                                 /* free stroke memory arrays, then stroke itself */
1539                                                 if (gps->points) {
1540                                                         MEM_freeN(gps->points);
1541                                                 }
1542                                                 if (gps->dvert) {
1543                                                         BKE_gpencil_free_stroke_weights(gps);
1544                                                         MEM_freeN(gps->dvert);
1545                                                 }
1546                                                 MEM_SAFE_FREE(gps->triangles);
1547                                                 BLI_freelinkN(&gpf->strokes, gps);
1548
1549                                                 changed = true;
1550                                         }
1551                                 }
1552                         }
1553                 }
1554         }
1555         CTX_DATA_END;
1556
1557         if (changed) {
1558                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1559                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1560                 return OPERATOR_FINISHED;
1561         }
1562         else {
1563                 return OPERATOR_CANCELLED;
1564         }
1565 }
1566
1567 /* ----------------------------------- */
1568
1569 /* Delete selected points but keep the stroke */
1570 static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
1571 {
1572         Object *ob = CTX_data_active_object(C);
1573         bGPdata *gpd = ED_gpencil_data_get_active(C);
1574         const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1575         bool changed = false;
1576         int first = 0;
1577         int last = 0;
1578
1579         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1580         {
1581                 bGPDframe *init_gpf = gpl->actframe;
1582                 if (is_multiedit) {
1583                         init_gpf = gpl->frames.first;
1584                 }
1585
1586                 for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1587                         if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1588
1589                                 bGPDstroke *gps, *gpsn;
1590
1591                                 if (gpf == NULL)
1592                                         continue;
1593
1594                                 /* simply delete points from selected strokes
1595                                  * NOTE: we may still have to remove the stroke if it ends up having no points!
1596                                  */
1597                                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1598                                         gpsn = gps->next;
1599
1600                                         /* skip strokes that are invalid for current view */
1601                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1602                                                 continue;
1603                                         /* check if the color is editable */
1604                                         if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false)
1605                                                 continue;
1606
1607                                         /* the stroke must have at least one point selected for any operator */
1608                                         if (gps->flag & GP_STROKE_SELECT) {
1609                                                 bGPDspoint *pt;
1610                                                 MDeformVert *dvert = NULL;
1611                                                 int i;
1612
1613                                                 int tot = gps->totpoints; /* number of points in new buffer */
1614
1615                                                 /* first pass: count points to remove */
1616                                                 switch (mode) {
1617                                                         case GP_DISSOLVE_POINTS:
1618                                                                 /* Count how many points are selected (i.e. how many to remove) */
1619                                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1620                                                                         if (pt->flag & GP_SPOINT_SELECT) {
1621                                                                                 /* selected point - one of the points to remove */
1622                                                                                 tot--;
1623                                                                         }
1624                                                                 }
1625                                                                 break;
1626                                                         case GP_DISSOLVE_BETWEEN:
1627                                                                 /* need to find first and last point selected */
1628                                                                 first = -1;
1629                                                                 last = 0;
1630                                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1631                                                                         if (pt->flag & GP_SPOINT_SELECT) {
1632                                                                                 if (first < 0) {
1633                                                                                         first = i;
1634                                                                                 }
1635                                                                                 last = i;
1636                                                                         }
1637                                                                 }
1638                                                                 /* count unselected points in the range */
1639                                                                 for (i = first, pt = gps->points + first; i < last; i++, pt++) {
1640                                                                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1641                                                                                 tot--;
1642                                                                         }
1643                                                                 }
1644                                                                 break;
1645                                                         case GP_DISSOLVE_UNSELECT:
1646                                                                 /* count number of unselected points */
1647                                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1648                                                                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1649                                                                                 tot--;
1650                                                                         }
1651                                                                 }
1652                                                                 break;
1653                                                         default:
1654                                                                 return false;
1655                                                                 break;
1656                                                 }
1657
1658                                                 /* if no points are left, we simply delete the entire stroke */
1659                                                 if (tot <= 0) {
1660                                                         /* remove the entire stroke */
1661                                                         if (gps->points) {
1662                                                                 MEM_freeN(gps->points);
1663                                                         }
1664                                                         if (gps->dvert) {
1665                                                                 BKE_gpencil_free_stroke_weights(gps);
1666                                                                 MEM_freeN(gps->dvert);
1667                                                         }
1668                                                         if (gps->triangles) {
1669                                                                 MEM_freeN(gps->triangles);
1670                                                         }
1671                                                         BLI_freelinkN(&gpf->strokes, gps);
1672                                                         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1673                                                 }
1674                                                 else {
1675                                                         /* just copy all points to keep into a smaller buffer */
1676                                                         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
1677                                                         bGPDspoint *npt = new_points;
1678
1679                                                         MDeformVert *new_dvert = NULL;
1680                                                         MDeformVert *ndvert = NULL;
1681
1682                                                         if (gps->dvert != NULL) {
1683                                                                 new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
1684                                                                 ndvert = new_dvert;
1685                                                         }
1686
1687                                                         switch (mode) {
1688                                                                 case GP_DISSOLVE_POINTS:
1689                                                                         (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1690                                                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1691                                                                                 if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1692                                                                                         *npt = *pt;
1693                                                                                         npt++;
1694
1695                                                                                         if (gps->dvert != NULL) {
1696                                                                                                 *ndvert = *dvert;
1697                                                                                                 ndvert->dw = MEM_dupallocN(dvert->dw);
1698                                                                                                 ndvert++;
1699                                                                                                 dvert++;
1700                                                                                         }
1701                                                                                 }
1702                                                                         }
1703                                                                         break;
1704                                                                 case GP_DISSOLVE_BETWEEN:
1705                                                                         /* copy first segment */
1706                                                                         (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1707                                                                         for (i = 0, pt = gps->points; i < first; i++, pt++) {
1708                                                                                 *npt = *pt;
1709                                                                                 npt++;
1710
1711                                                                                 if (gps->dvert != NULL) {
1712                                                                                         *ndvert = *dvert;
1713                                                                                         ndvert->dw = MEM_dupallocN(dvert->dw);
1714                                                                                         ndvert++;
1715                                                                                         dvert++;
1716                                                                                 }
1717                                                                         }
1718                                                                         /* copy segment (selected points) */
1719                                                                         (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
1720                                                                         for (i = first, pt = gps->points + first; i < last; i++, pt++) {
1721                                                                                 if (pt->flag & GP_SPOINT_SELECT) {
1722                                                                                         *npt = *pt;
1723                                                                                         npt++;
1724
1725                                                                                         if (gps->dvert != NULL) {
1726                                                                                                 *ndvert = *dvert;
1727                                                                                                 ndvert->dw = MEM_dupallocN(dvert->dw);
1728                                                                                                 ndvert++;
1729                                                                                                 dvert++;
1730                                                                                         }
1731                                                                                 }
1732                                                                         }
1733                                                                         /* copy last segment */
1734                                                                         (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
1735                                                                         for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
1736                                                                                 *npt = *pt;
1737                                                                                 npt++;
1738
1739                                                                                 if (gps->dvert != NULL) {
1740                                                                                         *ndvert = *dvert;
1741                                                                                         ndvert->dw = MEM_dupallocN(dvert->dw);
1742                                                                                         ndvert++;
1743                                                                                         dvert++;
1744                                                                                 }
1745                                                                         }
1746
1747                                                                         break;
1748                                                                 case GP_DISSOLVE_UNSELECT:
1749                                                                         /* copy any selected point */
1750                                                                         (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1751                                                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1752                                                                                 if (pt->flag & GP_SPOINT_SELECT) {
1753                                                                                         *npt = *pt;
1754                                                                                         npt++;
1755
1756                                                                                         if (gps->dvert != NULL) {
1757                                                                                                 *ndvert = *dvert;
1758                                                                                                 ndvert->dw = MEM_dupallocN(dvert->dw);
1759                                                                                                 ndvert++;
1760                                                                                                 dvert++;
1761                                                                                         }
1762                                                                                 }
1763                                                                         }
1764                                                                         break;
1765                                                         }
1766
1767                                                         /* free the old buffer */
1768                                                         if (gps->points) {
1769                                                                 MEM_freeN(gps->points);
1770                                                         }
1771                                                         if (gps->dvert) {
1772                                                                 BKE_gpencil_free_stroke_weights(gps);
1773                                                                 MEM_freeN(gps->dvert);
1774                                                         }
1775
1776                                                         /* save the new buffer */
1777                                                         gps->points = new_points;
1778                                                         gps->dvert = new_dvert;
1779                                                         gps->totpoints = tot;
1780
1781                                                         /* triangles cache needs to be recalculated */
1782                                                         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
1783                                                         gps->tot_triangles = 0;
1784
1785                                                         /* deselect the stroke, since none of its selected points will still be selected */
1786                                                         gps->flag &= ~GP_STROKE_SELECT;
1787                                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1788                                                                 pt->flag &= ~GP_SPOINT_SELECT;
1789                                                         }
1790                                                 }
1791
1792                                                 changed = true;
1793                                         }
1794                                 }
1795                         }
1796                 }
1797         }
1798         CTX_DATA_END;
1799
1800         if (changed) {
1801                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1802                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1803                 return OPERATOR_FINISHED;
1804         }
1805         else {
1806                 return OPERATOR_CANCELLED;
1807         }
1808 }
1809
1810 /* ----------------------------------- */
1811
1812 /* Temp data for storing information about an "island" of points
1813  * that should be kept when splitting up a stroke. Used in:
1814  * gp_stroke_delete_tagged_points()
1815  */
1816 typedef struct tGPDeleteIsland {
1817         int start_idx;
1818         int end_idx;
1819 } tGPDeleteIsland;
1820
1821
1822 /* Split the given stroke into several new strokes, partitioning
1823  * it based on whether the stroke points have a particular flag
1824  * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
1825  *
1826  * The algorithm used here is as follows:
1827  * 1) We firstly identify the number of "islands" of non-tagged points
1828  *    which will all end up being in new strokes.
1829  *    - In the most extreme case (i.e. every other vert is a 1-vert island),
1830  *      we have at most n / 2 islands
1831  *    - Once we start having larger islands than that, the number required
1832  *      becomes much less
1833  * 2) Each island gets converted to a new stroke
1834  * If the number of points is <= limit, the stroke is deleted
1835  */
1836 void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke,
1837         int tag_flags, bool select, int limit)
1838 {
1839         tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
1840         bool in_island  = false;
1841         int num_islands = 0;
1842
1843
1844         /* First Pass: Identify start/end of islands */
1845         bGPDspoint *pt = gps->points;
1846         for (int i = 0; i < gps->totpoints; i++, pt++) {
1847                 if (pt->flag & tag_flags) {
1848                         /* selected - stop accumulating to island */
1849                         in_island = false;
1850                 }
1851                 else {
1852                         /* unselected - start of a new island? */
1853                         int idx;
1854
1855                         if (in_island) {
1856                                 /* extend existing island */
1857                                 idx = num_islands - 1;
1858                                 islands[idx].end_idx = i;
1859                         }
1860                         else {
1861                                 /* start of new island */
1862                                 in_island = true;
1863                                 num_islands++;
1864
1865                                 idx = num_islands - 1;
1866                                 islands[idx].start_idx = islands[idx].end_idx = i;
1867                         }
1868                 }
1869         }
1870
1871         /* Watch out for special case where No islands = All points selected = Delete Stroke only */
1872         if (num_islands) {
1873                 /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
1874                 int idx;
1875
1876                 /* Create each new stroke... */
1877                 for (idx = 0; idx < num_islands; idx++) {
1878                         tGPDeleteIsland *island = &islands[idx];
1879                         bGPDstroke *new_stroke = MEM_dupallocN(gps);
1880
1881                         /* initialize triangle memory  - to be calculated on next redraw */
1882                         new_stroke->triangles = NULL;
1883                         new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
1884                         new_stroke->tot_triangles = 0;
1885
1886                         /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
1887                         new_stroke->totpoints = island->end_idx - island->start_idx + 1;
1888
1889                         /* Copy over the relevant point data */
1890                         new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
1891                         memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
1892
1893                         /* Copy over vertex weight data (if available) */
1894                         if (new_stroke->dvert != NULL) {
1895                                 /* Copy over the relevant vertex-weight points */
1896                                 new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, "gp delete stroke fragment weight");
1897                                 memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints);
1898
1899                                 /* Copy weights */
1900                                 int e = island->start_idx;
1901                                 for (int i = 0; i < new_stroke->totpoints; i++) {
1902                                         MDeformVert *dvert_dst = &gps->dvert[e];
1903                                         MDeformVert *dvert_src = &new_stroke->dvert[i];
1904                                         dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
1905                                         e++;
1906                                 }
1907                         }
1908
1909                         /* Each island corresponds to a new stroke. We must adjust the
1910                          * timings of these new strokes:
1911                          *
1912                          * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
1913                          * the start of the stroke, we have to offset this inittime and all remaining points' delta values.
1914                          * This way we get a new stroke with exactly the same timing as if user had started drawing from
1915                          * the first non-removed point...
1916                          */
1917                         {
1918                                 bGPDspoint *pts;
1919                                 float delta = gps->points[island->start_idx].time;
1920                                 int j;
1921
1922                                 new_stroke->inittime += (double)delta;
1923
1924                                 pts = new_stroke->points;
1925                                 for (j = 0; j < new_stroke->totpoints; j++, pts++) {
1926                                         pts->time -= delta;
1927                                         /* set flag for select again later */
1928                                         if (select == true) {
1929                                                 pts->flag &= ~GP_SPOINT_SELECT;
1930                                                 pts->flag |= GP_SPOINT_TAG;
1931                                         }
1932                                 }
1933                         }
1934
1935                         /* Add new stroke to the frame or delete if below limit */
1936                         if ((limit > 0) && (new_stroke->totpoints <= limit)) {
1937                                 BKE_gpencil_free_stroke(new_stroke);
1938                         }
1939                         else {
1940                                 if (next_stroke) {
1941                                         BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
1942                                 }
1943                                 else {
1944                                         BLI_addtail(&gpf->strokes, new_stroke);
1945                                 }
1946                         }
1947                 }
1948         }
1949
1950         /* free islands */
1951         MEM_freeN(islands);
1952
1953         /* Delete the old stroke */
1954         if (gps->points) {
1955                 MEM_freeN(gps->points);
1956         }
1957         if (gps->dvert) {
1958                 BKE_gpencil_free_stroke_weights(gps);
1959                 MEM_freeN(gps->dvert);
1960         }
1961         if (gps->triangles) {
1962                 MEM_freeN(gps->triangles);
1963         }
1964         BLI_freelinkN(&gpf->strokes, gps);
1965 }
1966
1967 /* Split selected strokes into segments, splitting on selected points */
1968 static int gp_delete_selected_points(bContext *C)
1969 {
1970         Object *ob = CTX_data_active_object(C);
1971         bGPdata *gpd = ED_gpencil_data_get_active(C);
1972         const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1973         bool changed = false;
1974
1975         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1976         {
1977                 bGPDframe *init_gpf = gpl->actframe;
1978                 if (is_multiedit) {
1979                         init_gpf = gpl->frames.first;
1980                 }
1981
1982                 for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1983                         if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1984                                 bGPDstroke *gps, *gpsn;
1985
1986                                 if (gpf == NULL)
1987                                         continue;
1988
1989                                 /* simply delete strokes which are selected */
1990                                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1991                                         gpsn = gps->next;
1992
1993                                         /* skip strokes that are invalid for current view */
1994                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1995                                                 continue;
1996                                         /* check if the color is editable */
1997                                         if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false)
1998                                                 continue;
1999
2000
2001                                         if (gps->flag & GP_STROKE_SELECT) {
2002                                                 /* deselect old stroke, since it will be used as template for the new strokes */
2003                                                 gps->flag &= ~GP_STROKE_SELECT;
2004
2005                                                 /* delete unwanted points by splitting stroke into several smaller ones */
2006                                                 gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
2007
2008                                                 changed = true;
2009                                         }
2010                                 }
2011                         }
2012                 }
2013         }
2014         CTX_DATA_END;
2015
2016         if (changed) {
2017                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2018                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2019                 return OPERATOR_FINISHED;
2020         }
2021         else {
2022                 return OPERATOR_CANCELLED;
2023         }
2024 }
2025
2026 /* simple wrapper to external call */
2027 int gp_delete_selected_point_wrap(bContext *C)
2028 {
2029         return gp_delete_selected_points(C);
2030 }
2031
2032 /* ----------------------------------- */
2033
2034 static int gp_delete_exec(bContext *C, wmOperator *op)
2035 {
2036         eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
2037         int result = OPERATOR_CANCELLED;
2038
2039         switch (mode) {
2040                 case GP_DELETEOP_STROKES:       /* selected strokes */
2041                         result = gp_delete_selected_strokes(C);
2042                         break;
2043
2044                 case GP_DELETEOP_POINTS:        /* selected points (breaks the stroke into segments) */
2045                         result = gp_delete_selected_points(C);
2046                         break;
2047
2048                 case GP_DELETEOP_FRAME:         /* active frame */
2049                         result = gp_actframe_delete_exec(C, op);
2050                         break;
2051         }
2052
2053         return result;
2054 }
2055
2056 void GPENCIL_OT_delete(wmOperatorType *ot)
2057 {
2058         static const EnumPropertyItem prop_gpencil_delete_types[] = {
2059                 {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
2060                 {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
2061                 {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
2062                 {0, NULL, 0, NULL, NULL}
2063         };
2064
2065         /* identifiers */
2066         ot->name = "Delete";
2067         ot->idname = "GPENCIL_OT_delete";
2068         ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
2069
2070         /* callbacks */
2071         ot->invoke = WM_menu_invoke;
2072         ot->exec = gp_delete_exec;
2073         ot->poll = gp_stroke_edit_poll;
2074
2075         /* flags */
2076         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
2077
2078         /* props */
2079         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
2080 }
2081
2082 static int gp_dissolve_exec(bContext *C, wmOperator *op)
2083 {
2084         eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
2085
2086         return gp_dissolve_selected_points(C, mode);
2087 }
2088
2089 void GPENCIL_OT_dissolve(wmOperatorType *ot)
2090 {
2091         static EnumPropertyItem prop_gpencil_dissolve_types[] = {
2092                 {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"},
2093                 {GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points"},
2094                 {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"},
2095                 {0, NULL, 0, NULL, NULL}
2096         };
2097
2098         /* identifiers */
2099         ot->name = "Dissolve";
2100         ot->idname = "GPENCIL_OT_dissolve";
2101         ot->description = "Delete selected points without splitting strokes";
2102
2103         /* callbacks */
2104         ot->invoke = WM_menu_invoke;
2105         ot->exec = gp_dissolve_exec;
2106         ot->poll = gp_stroke_edit_poll;
2107
2108         /* flags */
2109         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
2110
2111         /* props */
2112         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0,
2113                                 "Type", "Method used for dissolving Stroke points");
2114 }
2115
2116 /* ****************** Snapping - Strokes <-> Cursor ************************ */
2117
2118 /* Poll callback for snap operators */
2119 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
2120  *       define a cursor or gridstep which can be used
2121  */
2122 static bool gp_snap_poll(bContext *C)
2123 {
2124         bGPdata *gpd = CTX_data_gpencil_data(C);
2125         ScrArea *sa = CTX_wm_area(C);
2126
2127         return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
2128 }
2129
2130 /* --------------------------------- */
2131
2132 static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
2133 {
2134         bGPdata *gpd = ED_gpencil_data_get_active(C);
2135         RegionView3D *rv3d = CTX_wm_region_data(C);
2136         View3D *v3d = CTX_wm_view3d(C);
2137         Scene *scene = CTX_data_scene(C);
2138         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2139         Object *obact = CTX_data_active_object(C);
2140         const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL);
2141
2142         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2143                 /* only editable and visible layers are considered */
2144                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2145                         bGPDframe *gpf = gpl->actframe;
2146                         float diff_mat[4][4];
2147
2148                         /* calculate difference matrix object */
2149                         ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
2150
2151                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2152                                 bGPDspoint *pt;
2153                                 int i;
2154
2155                                 /* skip strokes that are invalid for current view */
2156                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2157                                         continue;
2158                                 /* check if the color is editable */
2159                                 if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false)
2160                                         continue;
2161
2162                                 // TODO: if entire stroke is selected, offset entire stroke by same amount?
2163                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2164                                         /* only if point is selected */
2165                                         if (pt->flag & GP_SPOINT_SELECT) {
2166                                                 /* apply parent transformations */
2167                                                 float fpt[3];
2168                                                 mul_v3_m4v3(fpt, diff_mat, &pt->x);
2169
2170                                                 fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
2171                                                 fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
2172                                                 fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
2173
2174                                                 /* return data */
2175                                                 copy_v3_v3(&pt->x, fpt);
2176                                                 gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
2177                                         }
2178                                 }
2179                         }
2180                 }
2181         }
2182
2183         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2184         DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
2185         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2186         return OPERATOR_FINISHED;
2187 }
2188
2189 void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
2190 {
2191         /* identifiers */
2192         ot->name = "Snap Selection to Grid";
2193         ot->idname = "GPENCIL_OT_snap_to_grid";
2194         ot->description = "Snap selected points to the nearest grid points";
2195
2196         /* callbacks */
2197         ot->exec = gp_snap_to_grid;
2198         ot->poll = gp_snap_poll;
2199
2200         /* flags */
2201         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2202 }
2203
2204 /* ------------------------------- */
2205
2206 static int gp_snap_to_cursor(bContext *C, wmOperator *op)
2207 {
2208         bGPdata *gpd = ED_gpencil_data_get_active(C);
2209
2210         Scene *scene = CTX_data_scene(C);
2211         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2212         Object *obact = CTX_data_active_object(C);
2213
2214         const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
2215         const float *cursor_global = scene->cursor.location;
2216
2217         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2218                 /* only editable and visible layers are considered */
2219                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2220                         bGPDframe *gpf = gpl->actframe;
2221                         float diff_mat[4][4];
2222
2223                         /* calculate difference matrix */
2224                         ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
2225
2226                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2227                                 bGPDspoint *pt;
2228                                 int i;
2229
2230                                 /* skip strokes that are invalid for current view */
2231                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2232                                         continue;
2233                                 /* check if the color is editable */
2234                                 if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false)
2235                                         continue;
2236                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
2237                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
2238                                         continue;
2239
2240                                 if (use_offset) {
2241                                         float offset[3];
2242
2243                                         /* compute offset from first point of stroke to cursor */
2244                                         /* TODO: Allow using midpoint instead? */
2245                                         sub_v3_v3v3(offset, cursor_global, &gps->points->x);
2246
2247                                         /* apply offset to all points in the stroke */
2248                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2249                                                 add_v3_v3(&pt->x, offset);
2250                                         }
2251                                 }
2252                                 else {
2253                                         /* affect each selected point */
2254                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2255                                                 if (pt->flag & GP_SPOINT_SELECT) {
2256                                                         copy_v3_v3(&pt->x, cursor_global);
2257                                                         gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
2258                                                 }
2259                                         }
2260                                 }
2261                         }
2262
2263                 }
2264         }
2265
2266         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2267         DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
2268         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2269         return OPERATOR_FINISHED;
2270 }
2271
2272 void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
2273 {
2274         /* identifiers */
2275         ot->name = "Snap Selection to Cursor";
2276         ot->idname = "GPENCIL_OT_snap_to_cursor";
2277         ot->description = "Snap selected points/strokes to the cursor";
2278
2279         /* callbacks */
2280         ot->exec = gp_snap_to_cursor;
2281         ot->poll = gp_snap_poll;
2282
2283         /* flags */
2284         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2285
2286         /* props */
2287         ot->prop = RNA_def_boolean(ot->srna, "use_offset", true, "With Offset",
2288                 "Offset the entire stroke instead of selected points only");
2289 }
2290
2291 /* ------------------------------- */
2292
2293 static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
2294 {
2295         bGPdata *gpd = ED_gpencil_data_get_active(C);
2296
2297         Scene *scene = CTX_data_scene(C);
2298         View3D *v3d = CTX_wm_view3d(C);
2299         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2300         Object *obact = CTX_data_active_object(C);
2301
2302         float *cursor = scene->cursor.location;
2303         float centroid[3] = {0.0f};
2304         float min[3], max[3];
2305         size_t count = 0;
2306
2307         INIT_MINMAX(min, max);
2308
2309         /* calculate midpoints from selected points */
2310         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2311                 /* only editable and visible layers are considered */
2312                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2313                         bGPDframe *gpf = gpl->actframe;
2314                         float diff_mat[4][4];
2315
2316                         /* calculate difference matrix */
2317                         ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
2318
2319                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2320                                 bGPDspoint *pt;
2321                                 int i;
2322
2323                                 /* skip strokes that are invalid for current view */
2324                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2325                                         continue;
2326                                 /* check if the color is editable */
2327                                 if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false)
2328                                         continue;
2329                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
2330                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
2331                                         continue;
2332
2333                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2334                                         if (pt->flag & GP_SPOINT_SELECT) {
2335                                                 /* apply parent transformations */
2336                                                 float fpt[3];
2337                                                 mul_v3_m4v3(fpt, diff_mat, &pt->x);
2338
2339                                                 add_v3_v3(centroid, fpt);
2340                                                 minmax_v3v3_v3(min, max, fpt);
2341
2342                                                 count++;
2343                                         }
2344                                 }
2345
2346                         }
2347                 }
2348         }
2349
2350         if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) {
2351                 mul_v3_fl(centroid, 1.0f / (float)count);
2352                 copy_v3_v3(cursor, centroid);
2353         }
2354         else {
2355                 mid_v3_v3v3(cursor, min, max);
2356         }
2357
2358
2359         DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
2360         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
2361
2362         return OPERATOR_FINISHED;
2363 }
2364
2365 void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
2366 {
2367         /* identifiers */
2368         ot->name = "Snap Cursor to Selected Points";
2369         ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
2370         ot->description = "Snap cursor to center of selected points";
2371
2372         /* callbacks */
2373         ot->exec = gp_snap_cursor_to_sel;
2374         ot->poll = gp_snap_poll;
2375
2376         /* flags */
2377         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2378 }
2379
2380 /* ******************* Apply layer thickness change to strokes ************************** */
2381
2382 static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
2383 {
2384         bGPdata *gpd = ED_gpencil_data_get_active(C);
2385         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
2386
2387         /* sanity checks */
2388         if (ELEM(NULL, gpd, gpl, gpl->frames.first))
2389                 return OPERATOR_CANCELLED;
2390
2391         /* loop all strokes */
2392         for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
2393                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2394                         /* Apply thickness */
2395                         if ((gps->thickness == 0) && (gpl->line_change == 0)) {
2396                                 gps->thickness = gpl->thickness;
2397                         }
2398                         else {
2399                                 gps->thickness = gps->thickness + gpl->line_change;
2400                         }
2401                 }
2402         }
2403         /* clear value */
2404         gpl->thickness = 0.0f;
2405         gpl->line_change = 0;
2406
2407         /* notifiers */
2408         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2409         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2410
2411         return OPERATOR_FINISHED;
2412 }
2413
2414 void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
2415 {
2416         /* identifiers */
2417         ot->name = "Apply Stroke Thickness";
2418         ot->idname = "GPENCIL_OT_stroke_apply_thickness";
2419         ot->description = "Apply the thickness change of the layer to its strokes";
2420
2421         /* api callbacks */
2422         ot->exec = gp_stroke_apply_thickness_exec;
2423         ot->poll = gp_active_layer_poll;
2424 }
2425
2426 /* ******************* Close Strokes ************************** */
2427
2428 enum {
2429         GP_STROKE_CYCLIC_CLOSE = 1,
2430         GP_STROKE_CYCLIC_OPEN = 2,
2431         GP_STROKE_CYCLIC_TOGGLE = 3
2432 };
2433
2434 static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
2435 {
2436         bGPdata *gpd = ED_gpencil_data_get_active(C);
2437         Object *ob = CTX_data_active_object(C);
2438
2439         const int type = RNA_enum_get(op->ptr, "type");
2440
2441         /* sanity checks */
2442         if (ELEM(NULL, gpd))
2443                 return OPERATOR_CANCELLED;
2444
2445         /* loop all selected strokes */
2446         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2447         {
2448                 if (gpl->actframe == NULL)
2449                         continue;
2450
2451                 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
2452                         MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
2453
2454                         /* skip strokes that are not selected or invalid for current view */
2455                         if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
2456                                 continue;
2457                         /* skip hidden or locked colors */
2458                         if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED))
2459                                 continue;
2460
2461                         switch (type) {
2462                                 case GP_STROKE_CYCLIC_CLOSE:
2463                                         /* Close all (enable) */
2464                                         gps->flag |= GP_STROKE_CYCLIC;
2465                                         break;
2466                                 case GP_STROKE_CYCLIC_OPEN:
2467                                         /* Open all (disable) */
2468                                         gps->flag &= ~GP_STROKE_CYCLIC;
2469                                         break;
2470                                 case GP_STROKE_CYCLIC_TOGGLE:
2471                                         /* Just toggle flag... */
2472                                         gps->flag ^= GP_STROKE_CYCLIC;
2473                                         break;
2474                                 default:
2475                                         BLI_assert(0);
2476                                         break;
2477                         }
2478                 }
2479         }
2480         CTX_DATA_END;
2481
2482         /* notifiers */
2483         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2484         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2485
2486         return OPERATOR_FINISHED;
2487 }
2488
2489 /**
2490  * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
2491  * option to force opened/closed strokes instead of just toggle behavior.
2492  */
2493 void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
2494 {
2495         static const EnumPropertyItem cyclic_type[] = {
2496                 {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
2497                 {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
2498                 {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
2499                 {0, NULL, 0, NULL, NULL}
2500         };
2501
2502         /* identifiers */
2503         ot->name = "Set Cyclical State";
2504         ot->idname = "GPENCIL_OT_stroke_cyclical_set";
2505         ot->description = "Close or open the selected stroke adding an edge from last to first point";
2506
2507         /* api callbacks */
2508         ot->exec = gp_stroke_cyclical_set_exec;
2509         ot->poll = gp_active_layer_poll;
2510
2511         /* flags */
2512         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2513
2514         /* properties */
2515         ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
2516 }
2517
2518 /* ******************* Flat Stroke Caps ************************** */
2519
2520 enum {
2521         GP_STROKE_CAPS_TOGGLE_BOTH    = 0,
2522         GP_STROKE_CAPS_TOGGLE_START   = 1,
2523         GP_STROKE_CAPS_TOGGLE_END     = 2,
2524         GP_STROKE_CAPS_TOGGLE_DEFAULT = 3
2525 };
2526
2527 static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op)
2528 {
2529         bGPdata *gpd = ED_gpencil_data_get_active(C);
2530         Object *ob = CTX_data_active_object(C);
2531
2532         const int type = RNA_enum_get(op->ptr, "type");
2533
2534         /* sanity checks */
2535         if (ELEM(NULL, gpd))
2536                 return OPERATOR_CANCELLED;
2537
2538         /* loop all selected strokes */
2539         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2540         {
2541                 if (gpl->actframe == NULL)
2542                         continue;
2543
2544                 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
2545                         MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
2546
2547                         /* skip strokes that are not selected or invalid for current view */
2548                         if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
2549                                 continue;
2550                         /* skip hidden or locked colors */
2551                         if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED))
2552                                 continue;
2553
2554                         if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) ||
2555                                 (type == GP_STROKE_CAPS_TOGGLE_START))
2556                         {
2557                                 ++gps->caps[0];
2558                                 if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
2559                                         gps->caps[0] = GP_STROKE_CAP_ROUND;
2560                                 }
2561                         }
2562                         if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) ||
2563                                 (type == GP_STROKE_CAPS_TOGGLE_END))
2564                         {
2565                                 ++gps->caps[1];
2566                                 if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
2567                                         gps->caps[1] = GP_STROKE_CAP_ROUND;
2568                                 }
2569                         }
2570                         if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
2571                                 gps->caps[0] = GP_STROKE_CAP_ROUND;
2572                                 gps->caps[1] = GP_STROKE_CAP_ROUND;
2573                         }
2574                 }
2575         }
2576         CTX_DATA_END;
2577
2578         /* notifiers */
2579         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2580         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2581
2582         return OPERATOR_FINISHED;
2583 }
2584
2585 /**
2586  * Change Stroke caps mode Rounded or Flat
2587  */
2588 void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
2589 {
2590         static const EnumPropertyItem toggle_type[] = {
2591                 {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
2592                 {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
2593                 {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
2594                 {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
2595                 {0, NULL, 0, NULL, NULL}
2596         };
2597
2598         /* identifiers */
2599         ot->name = "Set Caps Mode";
2600         ot->idname = "GPENCIL_OT_stroke_caps_set";
2601         ot->description = "Change Stroke caps mode (rounded or flat)";
2602
2603         /* api callbacks */
2604         ot->exec = gp_stroke_caps_set_exec;
2605         ot->poll = gp_active_layer_poll;
2606
2607         /* flags */
2608         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2609
2610         /* properties */
2611         ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", "");
2612 }
2613
2614 /* ******************* Stroke join ************************** */
2615
2616 /* Helper: flip stroke */
2617 static void gpencil_flip_stroke(bGPDstroke *gps)
2618 {
2619         int end = gps->totpoints - 1;
2620
2621         for (int i = 0; i < gps->totpoints / 2; i++) {
2622                 bGPDspoint *point, *point2;
2623                 bGPDspoint pt;
2624
2625                 /* save first point */
2626                 point = &gps->points[i];
2627                 pt.x = point->x;
2628                 pt.y = point->y;
2629                 pt.z = point->z;
2630                 pt.flag = point->flag;
2631                 pt.pressure = point->pressure;
2632                 pt.strength = point->strength;
2633                 pt.time = point->time;
2634
2635                 /* replace first point with last point */
2636                 point2 = &gps->points[end];
2637                 point->x = point2->x;
2638                 point->y = point2->y;
2639                 point->z = point2->z;
2640                 point->flag = point2->flag;
2641                 point->pressure = point2->pressure;
2642                 point->strength = point2->strength;
2643                 point->time = point2->time;
2644
2645                 /* replace last point with first saved before */
2646                 point = &gps->points[end];
2647                 point->x = pt.x;
2648                 point->y = pt.y;
2649                 point->z = pt.z;
2650                 point->flag = pt.flag;
2651                 point->pressure = pt.pressure;
2652                 point->strength = pt.strength;
2653                 point->time = pt.time;
2654
2655                 end--;
2656         }
2657 }
2658
2659 /* Helper: copy point between strokes */
2660 static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, int idx, float delta[3],
2661         float pressure, float strength, float deltatime)
2662 {
2663         bGPDspoint *newpoint;
2664
2665         gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
2666         if (gps->dvert != NULL) {
2667                 gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1));
2668         }
2669         gps->totpoints++;
2670         newpoint = &gps->points[gps->totpoints - 1];
2671
2672         newpoint->x = point->x * delta[0];
2673         newpoint->y = point->y * delta[1];
2674         newpoint->z = point->z * delta[2];
2675         newpoint->flag = point->flag;
2676         newpoint->pressure = pressure;
2677         newpoint->strength = strength;
2678         newpoint->time = point->time + deltatime;
2679
2680         if (gps->dvert != NULL) {
2681                 MDeformVert *dvert = &gps->dvert[idx];
2682                 MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
2683
2684                 newdvert->totweight = dvert->totweight;
2685                 newdvert->dw = MEM_dupallocN(dvert->dw);
2686         }
2687 }
2688
2689 /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
2690 static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
2691 {
2692         bGPDspoint point;
2693         bGPDspoint *pt;
2694         int i;
2695         float delta[3] = {1.0f, 1.0f, 1.0f};
2696         float deltatime = 0.0f;
2697
2698         /* sanity checks */
2699         if (ELEM(NULL, gps_a, gps_b))
2700                 return;
2701
2702         if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
2703                 return;
2704
2705         /* define start and end points of each stroke */
2706         float sa[3], sb[3], ea[3], eb[3];
2707         pt = &gps_a->points[0];
2708         copy_v3_v3(sa, &pt->x);
2709
2710         pt = &gps_a->points[gps_a->totpoints - 1];
2711         copy_v3_v3(ea, &pt->x);
2712
2713         pt = &gps_b->points[0];
2714         copy_v3_v3(sb, &pt->x);
2715
2716         pt = &gps_b->points[gps_b->totpoints - 1];
2717         copy_v3_v3(eb, &pt->x);
2718
2719         /* review if need flip stroke B */
2720         float ea_sb = len_squared_v3v3(ea, sb);
2721         float ea_eb = len_squared_v3v3(ea, eb);
2722         /* flip if distance to end point is shorter */
2723         if (ea_eb < ea_sb) {
2724                 gpencil_flip_stroke(gps_b);
2725         }
2726
2727         /* don't visibly link the first and last points? */
2728         if (leave_gaps) {
2729                 /* 1st: add one tail point to start invisible area */
2730                 point = gps_a->points[gps_a->totpoints - 1];
2731                 deltatime = point.time;
2732                 gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, delta, 0.0f, 0.0f, 0.0f);
2733
2734                 /* 2nd: add one head point to finish invisible area */
2735                 point = gps_b->points[0];
2736                 gpencil_stroke_copy_point(gps_a, &point, 0, delta, 0.0f, 0.0f, deltatime);
2737         }
2738
2739         /* 3rd: add all points */
2740         for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
2741                 /* check if still room in buffer */
2742                 if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
2743                         gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime);
2744                 }
2745         }
2746 }
2747
2748 static int gp_stroke_join_exec(bContext *C, wmOperator *op)
2749 {
2750         bGPdata *gpd = ED_gpencil_data_get_active(C);
2751         bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
2752         bGPDstroke *gps, *gpsn;
2753         Object *ob = CTX_data_active_object(C);
2754
2755         bGPDframe *gpf_a = NULL;
2756         bGPDstroke *stroke_a = NULL;
2757         bGPDstroke *stroke_b = NULL;
2758         bGPDstroke *new_stroke = NULL;
2759
2760         const int type = RNA_enum_get(op->ptr, "type");
2761         const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
2762
2763         /* sanity checks */
2764         if (ELEM(NULL, gpd))
2765                 return OPERATOR_CANCELLED;
2766
2767         if (activegpl->flag & GP_LAYER_LOCKED)
2768                 return OPERATOR_CANCELLED;
2769
2770         BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
2771
2772
2773         /* read all selected strokes */
2774         bool first = false;
2775         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2776         {
2777                 bGPDframe *gpf = gpl->actframe;
2778                 if (gpf == NULL)
2779                         continue;
2780
2781                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
2782                         gpsn = gps->next;
2783                         if (gps->flag & GP_STROKE_SELECT) {
2784                                 /* skip strokes that are invalid for current view */
2785                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
2786                                         continue;
2787                                 }
2788                                 /* check if the color is editable */
2789                                 if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
2790                                         continue;
2791                                 }
2792
2793                                 /* to join strokes, cyclic must be disabled */
2794                                 gps->flag &= ~GP_STROKE_CYCLIC;
2795
2796                                 /* saves first frame and stroke */
2797                                 if (!first) {
2798                                         first = true;
2799                                         gpf_a = gpf;
2800                                         stroke_a = gps;
2801                                 }
2802                                 else {
2803                                         stroke_b = gps;
2804
2805                                         /* create a new stroke if was not created before (only created if something to join) */
2806                                         if (new_stroke == NULL) {
2807                                                 new_stroke = MEM_dupallocN(stroke_a);
2808                                                 new_stroke->points = MEM_dupallocN(stroke_a->points);
2809                                                 if (stroke_a->dvert != NULL) {
2810                                                         new_stroke->dvert = MEM_dupallocN(stroke_a->dvert);
2811                                                         BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke);
2812                                                 }
2813                                                 new_stroke->triangles = NULL;
2814                                                 new_stroke->tot_triangles = 0;
2815                                                 new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
2816
2817                                                 /* if new, set current color */
2818                                                 if (type == GP_STROKE_JOINCOPY) {
2819                                                         new_stroke->mat_nr = stroke_a->mat_nr;
2820                                                 }
2821                                         }
2822
2823                                         /* join new_stroke and stroke B. New stroke will contain all the previous data */
2824                                         gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
2825
2826                                         /* if join only, delete old strokes */
2827                                         if (type == GP_STROKE_JOIN) {
2828                                                 if (stroke_a) {
2829                                                         BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
2830                                                         BLI_remlink(&gpf->strokes, stroke_a);
2831                                                         BKE_gpencil_free_stroke(stroke_a);
2832                                                         stroke_a = NULL;
2833                                                 }
2834                                                 if (stroke_b) {
2835                                                         BLI_remlink(&gpf->strokes, stroke_b);
2836                                                         BKE_gpencil_free_stroke(stroke_b);
2837                                                         stroke_b = NULL;
2838                                                 }
2839                                         }
2840                                 }
2841                         }
2842                 }
2843         }
2844         CTX_DATA_END;
2845
2846         /* add new stroke if was not added before */
2847         if (type == GP_STROKE_JOINCOPY) {
2848                 if (new_stroke) {
2849                         /* Add a new frame if needed */
2850                         if (activegpl->actframe == NULL)
2851                                 activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
2852
2853                         BLI_addtail(&activegpl->actframe->strokes, new_stroke);
2854                 }
2855         }
2856
2857         /* notifiers */
2858         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2859         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2860
2861         return OPERATOR_FINISHED;
2862 }
2863
2864 void GPENCIL_OT_stroke_join(wmOperatorType *ot)
2865 {
2866         static const EnumPropertyItem join_type[] = {
2867                 {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
2868                 {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
2869                 {0, NULL, 0, NULL, NULL}
2870         };
2871
2872         /* identifiers */
2873         ot->name = "Join Strokes";
2874         ot->idname = "GPENCIL_OT_stroke_join";
2875         ot->description = "Join selected strokes (optionally as new stroke)";
2876
2877         /* api callbacks */
2878         ot->exec = gp_stroke_join_exec;
2879         ot->poll = gp_active_layer_poll;
2880
2881         /* flags */
2882         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2883
2884         /* properties */
2885         ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
2886         RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
2887 }
2888
2889 /* ******************* Stroke flip ************************** */
2890
2891 static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
2892 {
2893         bGPdata *gpd = ED_gpencil_data_get_active(C);
2894         Object *ob = CTX_data_active_object(C);
2895
2896         /* sanity checks */
2897         if (ELEM(NULL, gpd))
2898                 return OPERATOR_CANCELLED;
2899
2900         /* read all selected strokes */
2901         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2902         {
2903                 bGPDframe *gpf = gpl->actframe;
2904                 if (gpf == NULL)
2905                         continue;
2906
2907                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2908                         if (gps->flag & GP_STROKE_SELECT) {
2909                                 /* skip strokes that are invalid for current view */
2910                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
2911                                         continue;
2912                                 }
2913                                 /* check if the color is editable */
2914                                 if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
2915                                         continue;
2916                                 }
2917
2918                                 /* flip stroke */
2919                                 gpencil_flip_stroke(gps);
2920                         }
2921                 }
2922         }
2923         CTX_DATA_END;
2924
2925         /* notifiers */
2926         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2927         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2928
2929         return OPERATOR_FINISHED;
2930 }
2931
2932 void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
2933 {
2934         /* identifiers */
2935         ot->name = "Flip Stroke";
2936         ot->idname = "GPENCIL_OT_stroke_flip";
2937         ot->description = "Change direction of the points of the selected strokes";
2938
2939         /* api callbacks */
2940         ot->exec = gp_stroke_flip_exec;
2941         ot->poll = gp_active_layer_poll;
2942
2943         /* flags */
2944         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2945 }
2946
2947 /* ***************** Reproject Strokes ********************** */
2948
2949 typedef enum eGP_ReprojectModes {
2950         /* Axis */
2951         GP_REPROJECT_FRONT = 0,
2952         GP_REPROJECT_SIDE,
2953         GP_REPROJECT_TOP,
2954         /* On same plane, parallel to viewplane */