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