072f4d0e317cc5b734376a632adaef0f48989237
[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_GPENCIL_EDIT;
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_GPENCIL_PAINT;
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_GPENCIL_PAINT) {
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_GPENCIL_SCULPT;
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_GPENCIL_WEIGHT;
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_CACHES;
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_CACHES;
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                 BLI_ghash_insert(new_colors, key, ma);
875         }
876
877         gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma);
878
879         return new_colors;
880 }
881
882 /* --------------------- */
883 /* Copy selected strokes */
884
885 static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
886 {
887         Main *bmain = CTX_data_main(C);
888         Object *ob = CTX_data_active_object(C);
889         bGPdata *gpd = ED_gpencil_data_get_active(C);
890
891         if (gpd == NULL) {
892                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
893                 return OPERATOR_CANCELLED;
894         }
895
896         if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
897                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
898                 return OPERATOR_CANCELLED;
899         }
900
901         /* clear the buffer first */
902         ED_gpencil_strokes_copybuf_free();
903
904         /* for each visible (and editable) layer's selected strokes,
905          * copy the strokes into a temporary buffer, then append
906          * once all done
907          */
908         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
909         {
910                 bGPDframe *gpf = gpl->actframe;
911                 bGPDstroke *gps;
912
913                 if (gpf == NULL)
914                         continue;
915
916                 /* make copies of selected strokes, and deselect these once we're done */
917                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
918                         /* skip strokes that are invalid for current view */
919                         if (ED_gpencil_stroke_can_use(C, gps) == false)
920                                 continue;
921
922                         if (gps->flag & GP_STROKE_SELECT) {
923                                 if (gps->totpoints == 1) {
924                                         /* Special Case: If there's just a single point in this stroke... */
925                                         bGPDstroke *gpsd;
926
927                                         /* make direct copies of the stroke and its points */
928                                         gpsd = MEM_dupallocN(gps);
929                                         BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */
930                                         gpsd->points = MEM_dupallocN(gps->points);
931                                         if (gps->dvert != NULL) {
932                                                 gpsd->dvert = MEM_dupallocN(gps->dvert);
933                                                 BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
934                                         }
935
936                                         /* triangles cache - will be recalculated on next redraw */
937                                         gpsd->flag |= GP_STROKE_RECALC_CACHES;
938                                         gpsd->tot_triangles = 0;
939                                         gpsd->triangles = NULL;
940
941                                         /* add to temp buffer */
942                                         gpsd->next = gpsd->prev = NULL;
943                                         BLI_addtail(&gp_strokes_copypastebuf, gpsd);
944                                 }
945                                 else {
946                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
947                                         gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
948                                 }
949                         }
950                 }
951         }
952         CTX_DATA_END;
953
954         /* Build up hash of material colors used in these strokes */
955         if (gp_strokes_copypastebuf.first) {
956                 gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
957                 GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain);
958                 for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
959                         if (ED_gpencil_stroke_can_use(C, gps)) {
960                                 char **ma_name_val;
961                                 if (!BLI_ghash_ensure_p(gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
962                                         Material *ma = give_current_material(ob, gps->mat_nr + 1);
963                                         char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
964                                         *ma_name_val = MEM_dupallocN(ma_name);
965                                 }
966                         }
967                 }
968                 gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name);
969         }
970
971         /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
972         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX?
973
974         /* done */
975         return OPERATOR_FINISHED;
976 }
977
978 void GPENCIL_OT_copy(wmOperatorType *ot)
979 {
980         /* identifiers */
981         ot->name = "Copy Strokes";
982         ot->idname = "GPENCIL_OT_copy";
983         ot->description = "Copy selected Grease Pencil points and strokes";
984
985         /* callbacks */
986         ot->exec = gp_strokes_copy_exec;
987         ot->poll = gp_stroke_edit_poll;
988
989         /* flags */
990         //ot->flag = OPTYPE_REGISTER;
991 }
992
993 /* --------------------- */
994 /* Paste selected strokes */
995
996 static bool gp_strokes_paste_poll(bContext *C)
997 {
998         /* 1) Must have GP datablock to paste to
999          *    - We don't need to have an active layer though, as that can easily get added
1000          *    - If the active layer is locked, we can't paste there, but that should prompt a warning instead
1001          * 2) Copy buffer must at least have something (though it may be the wrong sort...)
1002          */
1003         return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
1004 }
1005
1006 typedef enum eGP_PasteMode {
1007         GP_COPY_ONLY = -1,
1008         GP_COPY_MERGE = 1
1009 } eGP_PasteMode;
1010
1011 static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
1012 {
1013         Object *ob = CTX_data_active_object(C);
1014         bGPdata *gpd = ED_gpencil_data_get_active(C);
1015         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
1016         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1017         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1018         bGPDframe *gpf;
1019
1020         eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
1021         GHash *new_colors;
1022
1023         /* check for various error conditions */
1024         if (gpd == NULL) {
1025                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
1026                 return OPERATOR_CANCELLED;
1027         }
1028         else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1029                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1030                 return OPERATOR_CANCELLED;
1031         }
1032         else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
1033                 BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
1034                 return OPERATOR_CANCELLED;
1035         }
1036         else if (gpl == NULL) {
1037                 /* no active layer - let's just create one */
1038                 gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
1039         }
1040         else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
1041                 BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
1042                 return OPERATOR_CANCELLED;
1043         }
1044         else {
1045                 /* Check that some of the strokes in the buffer can be used */
1046                 bGPDstroke *gps;
1047                 bool ok = false;
1048
1049                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
1050                         if (ED_gpencil_stroke_can_use(C, gps)) {
1051                                 ok = true;
1052                                 break;
1053                         }
1054                 }
1055
1056                 if (ok == false) {
1057                         /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
1058                          * but should be enough to give users a good idea of what's going on
1059                          */
1060                         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
1061                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
1062                         else
1063                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
1064
1065                         return OPERATOR_CANCELLED;
1066                 }
1067         }
1068
1069         /* Deselect all strokes first */
1070         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
1071         {
1072                 bGPDspoint *pt;
1073                 int i;
1074
1075                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1076                         pt->flag &= ~GP_SPOINT_SELECT;
1077                 }
1078
1079                 gps->flag &= ~GP_STROKE_SELECT;
1080         }
1081         CTX_DATA_END;
1082
1083         /* Ensure that all the necessary colors exist */
1084         new_colors = gp_copybuf_validate_colormap(C);
1085
1086         /* Copy over the strokes from the buffer (and adjust the colors) */
1087         for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
1088                 if (ED_gpencil_stroke_can_use(C, gps)) {
1089                         /* Need to verify if layer exists */
1090                         if (type != GP_COPY_MERGE) {
1091                                 gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
1092                                 if (gpl == NULL) {
1093                                         /* no layer - use active (only if layer deleted before paste) */
1094                                         gpl = CTX_data_active_gpencil_layer(C);
1095                                 }
1096                         }
1097
1098                         /* Ensure we have a frame to draw into
1099                          * NOTE: Since this is an op which creates strokes,
1100                          *       we are obliged to add a new frame if one
1101                          *       doesn't exist already
1102                          */
1103                         gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
1104                         if (gpf) {
1105                                 /* Create new stroke */
1106                                 bGPDstroke *new_stroke = MEM_dupallocN(gps);
1107                                 new_stroke->runtime.tmp_layerinfo[0] = '\0';
1108
1109                                 new_stroke->points = MEM_dupallocN(gps->points);
1110                                 if (gps->dvert != NULL) {
1111                                         new_stroke->dvert = MEM_dupallocN(gps->dvert);
1112                                         BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
1113                                 }
1114                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1115                                 new_stroke->triangles = NULL;
1116
1117                                 new_stroke->next = new_stroke->prev = NULL;
1118                                 BLI_addtail(&gpf->strokes, new_stroke);
1119
1120                                 /* Remap material */
1121                                 Material *ma = BLI_ghash_lookup(new_colors, &new_stroke->mat_nr);
1122                                 if ((ma) && (BKE_gpencil_get_material_index(ob, ma) > 0)) {
1123                                         gps->mat_nr = BKE_gpencil_get_material_index(ob, ma) - 1;
1124                                         CLAMP_MIN(gps->mat_nr, 0);
1125                                 }
1126                                 else {
1127                                         gps->mat_nr = 0; /* only if the color is not found */
1128                                 }
1129
1130                         }
1131                 }
1132         }
1133
1134         /* free temp data */
1135         BLI_ghash_free(new_colors, NULL, NULL);
1136
1137         /* updates */
1138         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1139         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1140
1141         return OPERATOR_FINISHED;
1142 }
1143
1144 void GPENCIL_OT_paste(wmOperatorType *ot)
1145 {
1146         static const EnumPropertyItem copy_type[] = {
1147                 {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
1148                 {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
1149                 {0, NULL, 0, NULL, NULL}
1150         };
1151
1152         /* identifiers */
1153         ot->name = "Paste Strokes";
1154         ot->idname = "GPENCIL_OT_paste";
1155         ot->description = "Paste previously copied strokes or copy and merge in active layer";
1156
1157         /* callbacks */
1158         ot->exec = gp_strokes_paste_exec;
1159         ot->poll = gp_strokes_paste_poll;
1160
1161         /* flags */
1162         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
1163
1164         /* properties */
1165         ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
1166 }
1167
1168 /* ******************* Move To Layer ****************************** */
1169
1170 static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1171 {
1172         uiPopupMenu *pup;
1173         uiLayout *layout;
1174
1175         /* call the menu, which will call this operator again, hence the canceled */
1176         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
1177         layout = UI_popup_menu_layout(pup);
1178         uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
1179         UI_popup_menu_end(C, pup);
1180
1181         return OPERATOR_INTERFACE;
1182 }
1183
1184 // FIXME: allow moving partial strokes
1185 static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
1186 {
1187         bGPdata *gpd = CTX_data_gpencil_data(C);
1188         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1189         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1190         bGPDlayer *target_layer = NULL;
1191         ListBase strokes = {NULL, NULL};
1192         int layer_num = RNA_enum_get(op->ptr, "layer");
1193
1194         if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1195                 BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1196                 return OPERATOR_CANCELLED;
1197         }
1198
1199         /* Get layer or create new one */
1200         if (layer_num == -1) {
1201                 /* Create layer */
1202                 target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
1203         }
1204         else {
1205                 /* Try to get layer */
1206                 target_layer = BLI_findlink(&gpd->layers, layer_num);
1207
1208                 if (target_layer == NULL) {
1209                         BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
1210                         return OPERATOR_CANCELLED;
1211                 }
1212         }
1213
1214         /* Extract all strokes to move to this layer
1215          * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
1216          *       getting repeatedly moved
1217          */
1218         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1219         {
1220                 bGPDframe *gpf = gpl->actframe;
1221                 bGPDstroke *gps, *gpsn;
1222
1223                 /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
1224                 if ((gpl == target_layer) || (gpf == NULL))
1225                         continue;
1226
1227                 /* make copies of selected strokes, and deselect these once we're done */
1228                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1229                         gpsn = gps->next;
1230
1231                         /* skip strokes that are invalid for current view */
1232                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1233                                 continue;
1234
1235                         /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
1236                         if (gps->flag & GP_STROKE_SELECT) {
1237                                 BLI_remlink(&gpf->strokes, gps);
1238                                 BLI_addtail(&strokes, gps);
1239                         }
1240                 }
1241         }
1242         CTX_DATA_END;
1243
1244         /* Paste them all in one go */
1245         if (strokes.first) {
1246                 bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, GP_GETFRAME_ADD_NEW);
1247
1248                 BLI_movelisttolist(&gpf->strokes, &strokes);
1249                 BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
1250         }
1251
1252         /* updates */
1253         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1254         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1255
1256         return OPERATOR_FINISHED;
1257 }
1258
1259 void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
1260 {
1261         /* identifiers */
1262         ot->name = "Move Strokes to Layer";
1263         ot->idname = "GPENCIL_OT_move_to_layer";
1264         ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
1265
1266         /* callbacks */
1267         ot->invoke = gp_move_to_layer_invoke;
1268         ot->exec = gp_move_to_layer_exec;
1269         ot->poll = gp_stroke_edit_poll; // XXX?
1270
1271         /* flags */
1272         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1273
1274         /* gp layer to use (dynamic enum) */
1275         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
1276         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
1277 }
1278
1279 /* ********************* Add Blank Frame *************************** */
1280
1281 /* Basically the same as the drawing op */
1282 static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C)
1283 {
1284         if (ED_operator_regionactive(C)) {
1285                 /* check if current context can support GPencil data */
1286                 if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
1287                         return 1;
1288                 }
1289                 else {
1290                         CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
1291                 }
1292         }
1293         else {
1294                 CTX_wm_operator_poll_msg_set(C, "Active region not set");
1295         }
1296
1297         return 0;
1298 }
1299
1300 static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
1301 {
1302         bGPdata *gpd = ED_gpencil_data_get_active(C);
1303         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1304         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1305
1306         bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd);
1307
1308         const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
1309
1310         /* Initialise datablock and an active layer if nothing exists yet */
1311         if (ELEM(NULL, gpd, active_gpl)) {
1312                 /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */
1313                 WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL);
1314         }
1315
1316         /* Go through each layer, adding a frame after the active one
1317          * and/or shunting all the others out of the way
1318          */
1319         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1320         {
1321                 if ((all_layers == false) && (gpl != active_gpl)) {
1322                         continue;
1323                 }
1324
1325                 /* 1) Check for an existing frame on the current frame */
1326                 bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval);
1327                 if (gpf) {
1328                         /* Shunt all frames after (and including) the existing one later by 1-frame */
1329                         for (; gpf; gpf = gpf->next) {
1330                                 gpf->framenum += 1;
1331                         }
1332                 }
1333
1334                 /* 2) Now add a new frame, with nothing in it */
1335                 gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
1336         }
1337         CTX_DATA_END;
1338
1339         /* notifiers */
1340         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1341         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1342
1343         return OPERATOR_FINISHED;
1344 }
1345
1346 void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
1347 {
1348         /* identifiers */
1349         ot->name = "Insert Blank Frame";
1350         ot->idname = "GPENCIL_OT_blank_frame_add";
1351         ot->description = "Insert a blank frame on the current frame "
1352                 "(all subsequently existing frames, if any, are shifted right by one frame)";
1353
1354         /* callbacks */
1355         ot->exec = gp_blank_frame_add_exec;
1356         ot->poll = gp_add_poll;
1357
1358         /* properties */
1359         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1360         RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active");
1361 }
1362
1363 /* ******************* Delete Active Frame ************************ */
1364
1365 static bool gp_actframe_delete_poll(bContext *C)
1366 {
1367         bGPdata *gpd = ED_gpencil_data_get_active(C);
1368         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1369
1370         /* only if there's an active layer with an active frame */
1371         return (gpl && gpl->actframe);
1372 }
1373
1374 /* delete active frame - wrapper around API calls */
1375 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
1376 {
1377         bGPdata *gpd = ED_gpencil_data_get_active(C);
1378         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1379
1380         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1381         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1382
1383         bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
1384
1385         /* if there's no existing Grease-Pencil data there, add some */
1386         if (gpd == NULL) {
1387                 BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
1388                 return OPERATOR_CANCELLED;
1389         }
1390         if (ELEM(NULL, gpl, gpf)) {
1391                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
1392                 return OPERATOR_CANCELLED;
1393         }
1394
1395         /* delete it... */
1396         BKE_gpencil_layer_delframe(gpl, gpf);
1397
1398         /* notifiers */
1399         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1400         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1401
1402         return OPERATOR_FINISHED;
1403 }
1404
1405 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
1406 {
1407         /* identifiers */
1408         ot->name = "Delete Active Frame";
1409         ot->idname = "GPENCIL_OT_active_frame_delete";
1410         ot->description = "Delete the active frame for the active Grease Pencil Layer";
1411
1412         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1413
1414         /* callbacks */
1415         ot->exec = gp_actframe_delete_exec;
1416         ot->poll = gp_actframe_delete_poll;
1417 }
1418
1419 /* **************** Delete All Active Frames ****************** */
1420
1421 static bool gp_actframe_delete_all_poll(bContext *C)
1422 {
1423         bGPdata *gpd = ED_gpencil_data_get_active(C);
1424
1425         /* 1) There must be grease pencil data
1426          * 2) Hopefully some of the layers have stuff we can use
1427          */
1428         return (gpd && gpd->layers.first);
1429 }
1430
1431 static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
1432 {
1433         bGPdata *gpd = ED_gpencil_data_get_active(C);
1434         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1435         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1436
1437         bool success = false;
1438
1439         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1440         {
1441                 /* try to get the "active" frame - but only if it actually occurs on this frame */
1442                 bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
1443
1444                 if (gpf == NULL)
1445                         continue;
1446
1447                 /* delete it... */
1448                 BKE_gpencil_layer_delframe(gpl, gpf);
1449
1450                 /* we successfully modified something */
1451                 success = true;
1452         }
1453         CTX_DATA_END;
1454
1455         /* updates */
1456         if (success) {
1457                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1458                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1459                 return OPERATOR_FINISHED;
1460         }
1461         else {
1462                 BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
1463                 return OPERATOR_CANCELLED;
1464         }
1465 }
1466
1467 void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
1468 {
1469         /* identifiers */
1470         ot->name = "Delete All Active Frames";
1471         ot->idname = "GPENCIL_OT_active_frames_delete_all";
1472         ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
1473
1474         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1475
1476         /* callbacks */
1477         ot->exec = gp_actframe_delete_all_exec;
1478         ot->poll = gp_actframe_delete_all_poll;
1479 }
1480
1481 /* ******************* Delete Operator ************************ */
1482
1483 typedef enum eGP_DeleteMode {
1484         /* delete selected stroke points */
1485         GP_DELETEOP_POINTS          = 0,
1486         /* delete selected strokes */
1487         GP_DELETEOP_STROKES         = 1,
1488         /* delete active frame */
1489         GP_DELETEOP_FRAME           = 2,
1490 } eGP_DeleteMode;
1491
1492 typedef enum eGP_DissolveMode {
1493         /* dissolve all selected points */
1494         GP_DISSOLVE_POINTS = 0,
1495         /* dissolve between selected points */
1496         GP_DISSOLVE_BETWEEN = 1,
1497         /* dissolve unselected points */
1498         GP_DISSOLVE_UNSELECT = 2,
1499 } eGP_DissolveMode;
1500
1501 /* ----------------------------------- */
1502
1503 /* Delete selected strokes */
1504 static int gp_delete_selected_strokes(bContext *C)
1505 {
1506         bool changed = false;
1507         bGPdata *gpd = ED_gpencil_data_get_active(C);
1508         const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1509
1510         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1511         {
1512                 bGPDframe *init_gpf = gpl->actframe;
1513                 if (is_multiedit) {
1514                         init_gpf = gpl->frames.first;
1515                 }
1516
1517                 for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1518                         if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1519                                 bGPDstroke *gps, *gpsn;
1520
1521                                 if (gpf == NULL)
1522                                         continue;
1523
1524                                 /* simply delete strokes which are selected */
1525                                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1526                                         gpsn = gps->next;
1527
1528                                         /* skip strokes that are invalid for current view */
1529                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1530                                                 continue;
1531
1532                                         /* free stroke if selected */
1533                                         if (gps->flag & GP_STROKE_SELECT) {
1534                                                 /* free stroke memory arrays, then stroke itself */
1535                                                 if (gps->points) {
1536                                                         MEM_freeN(gps->points);
1537                                                 }
1538                                                 if (gps->dvert) {
1539                                                         BKE_gpencil_free_stroke_weights(gps);
1540                                                         MEM_freeN(gps->dvert);
1541                                                 }
1542                                                 MEM_SAFE_FREE(gps->triangles);
1543                                                 BLI_freelinkN(&gpf->strokes, gps);
1544
1545                                                 changed = true;
1546                                         }
1547                                 }
1548                         }
1549                 }
1550         }
1551         CTX_DATA_END;
1552
1553         if (changed) {
1554                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1555                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1556                 return OPERATOR_FINISHED;
1557         }
1558         else {
1559                 return OPERATOR_CANCELLED;
1560         }
1561 }
1562
1563 /* ----------------------------------- */
1564
1565 /* Delete selected points but keep the stroke */
1566 static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
1567 {
1568         Object *ob = CTX_data_active_object(C);
1569         bGPdata *gpd = ED_gpencil_data_get_active(C);
1570         const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1571         bool changed = false;
1572         int first = 0;
1573         int last = 0;
1574
1575         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1576         {
1577                 bGPDframe *init_gpf = gpl->actframe;
1578                 if (is_multiedit) {
1579                         init_gpf = gpl->frames.first;
1580                 }
1581
1582                 for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1583                         if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1584
1585                                 bGPDstroke *gps, *gpsn;
1586
1587                                 if (gpf == NULL)
1588                                         continue;
1589
1590                                 /* simply delete points from selected strokes
1591                                  * NOTE: we may still have to remove the stroke if it ends up having no points!
1592                                  */
1593                                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1594                                         gpsn = gps->next;
1595
1596                                         /* skip strokes that are invalid for current view */
1597                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1598                                                 continue;
1599                                         /* check if the color is editable */
1600                                         if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false)
1601                                                 continue;
1602
1603                                         /* the stroke must have at least one point selected for any operator */
1604                                         if (gps->flag & GP_STROKE_SELECT) {
1605                                                 bGPDspoint *pt;
1606                                                 MDeformVert *dvert = NULL;
1607                                                 int i;
1608
1609                                                 int tot = gps->totpoints; /* number of points in new buffer */
1610
1611                                                 /* first pass: count points to remove */
1612                                                 switch (mode) {
1613                                                         case GP_DISSOLVE_POINTS:
1614                                                                 /* Count how many points are selected (i.e. how many to remove) */
1615                                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1616                                                                         if (pt->flag & GP_SPOINT_SELECT) {
1617                                                                                 /* selected point - one of the points to remove */
1618                                                                                 tot--;
1619                                                                         }
1620                                                                 }
1621                                                                 break;
1622                                                         case GP_DISSOLVE_BETWEEN:
1623                                                                 /* need to find first and last point selected */
1624                                                                 first = -1;
1625                                                                 last = 0;
1626                                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1627                                                                         if (pt->flag & GP_SPOINT_SELECT) {
1628                                                                                 if (first < 0) {
1629                                                                                         first = i;
1630                                                                                 }
1631                                                                                 last = i;
1632                                                                         }
1633                                                                 }
1634                                                                 /* count unselected points in the range */
1635                                                                 for (i = first, pt = gps->points + first; i < last; i++, pt++) {
1636                                                                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1637                                                                                 tot--;
1638                                                                         }
1639                                                                 }
1640                                                                 break;
1641                                                         case GP_DISSOLVE_UNSELECT:
1642                                                                 /* count number of unselected points */
1643                                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1644                                                                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1645                                                                                 tot--;
1646                                                                         }
1647                                                                 }
1648                                                                 break;
1649                                                         default:
1650                                                                 return false;
1651                                                                 break;
1652                                                 }
1653
1654                                                 /* if no points are left, we simply delete the entire stroke */
1655                                                 if (tot <= 0) {
1656                                                         /* remove the entire stroke */
1657                                                         if (gps->points) {
1658                                                                 MEM_freeN(gps->points);
1659                                                         }
1660                                                         if (gps->dvert) {
1661                                                                 BKE_gpencil_free_stroke_weights(gps);
1662                                                                 MEM_freeN(gps->dvert);
1663                                                         }
1664                                                         if (gps->triangles) {
1665                                                                 MEM_freeN(gps->triangles);
1666                                                         }
1667                                                         BLI_freelinkN(&gpf->strokes, gps);
1668                                                         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1669                                                 }
1670                                                 else {
1671                                                         /* just copy all points to keep into a smaller buffer */
1672                                                         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
1673                                                         bGPDspoint *npt = new_points;
1674
1675                                                         MDeformVert *new_dvert = NULL;
1676                                                         MDeformVert *ndvert = NULL;
1677
1678                                                         if (gps->dvert != NULL) {
1679                                                                 new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
1680                                                                 ndvert = new_dvert;
1681                                                         }
1682
1683                                                         switch (mode) {
1684                                                                 case GP_DISSOLVE_POINTS:
1685                                                                         (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1686                                                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1687                                                                                 if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1688                                                                                         *npt = *pt;
1689                                                                                         npt++;
1690
1691                                                                                         if (gps->dvert != NULL) {
1692                                                                                                 *ndvert = *dvert;
1693                                                                                                 ndvert->dw = MEM_dupallocN(dvert->dw);
1694                                                                                                 ndvert++;
1695                                                                                                 dvert++;
1696                                                                                         }
1697                                                                                 }
1698                                                                         }
1699                                                                         break;
1700                                                                 case GP_DISSOLVE_BETWEEN:
1701                                                                         /* copy first segment */
1702                                                                         (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1703                                                                         for (i = 0, pt = gps->points; i < first; i++, pt++) {
1704                                                                                 *npt = *pt;
1705                                                                                 npt++;
1706
1707                                                                                 if (gps->dvert != NULL) {
1708                                                                                         *ndvert = *dvert;
1709                                                                                         ndvert->dw = MEM_dupallocN(dvert->dw);
1710                                                                                         ndvert++;
1711                                                                                         dvert++;
1712                                                                                 }
1713                                                                         }
1714                                                                         /* copy segment (selected points) */
1715                                                                         (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
1716                                                                         for (i = first, pt = gps->points + first; i < last; i++, pt++) {
1717                                                                                 if (pt->flag & GP_SPOINT_SELECT) {
1718                                                                                         *npt = *pt;
1719                                                                                         npt++;
1720
1721                                                                                         if (gps->dvert != NULL) {
1722                                                                                                 *ndvert = *dvert;
1723                                                                                                 ndvert->dw = MEM_dupallocN(dvert->dw);
1724                                                                                                 ndvert++;
1725                                                                                                 dvert++;
1726                                                                                         }
1727                                                                                 }
1728                                                                         }
1729                                                                         /* copy last segment */
1730                                                                         (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
1731                                                                         for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
1732                                                                                 *npt = *pt;
1733                                                                                 npt++;
1734
1735                                                                                 if (gps->dvert != NULL) {
1736                                                                                         *ndvert = *dvert;
1737                                                                                         ndvert->dw = MEM_dupallocN(dvert->dw);
1738                                                                                         ndvert++;
1739                                                                                         dvert++;
1740                                                                                 }
1741                                                                         }
1742
1743                                                                         break;
1744                                                                 case GP_DISSOLVE_UNSELECT:
1745                                                                         /* copy any selected point */
1746                                                                         (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1747                                                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1748                                                                                 if (pt->flag & GP_SPOINT_SELECT) {
1749                                                                                         *npt = *pt;
1750                                                                                         npt++;
1751
1752                                                                                         if (gps->dvert != NULL) {
1753                                                                                                 *ndvert = *dvert;
1754                                                                                                 ndvert->dw = MEM_dupallocN(dvert->dw);
1755                                                                                                 ndvert++;
1756                                                                                                 dvert++;
1757                                                                                         }
1758                                                                                 }
1759                                                                         }
1760                                                                         break;
1761                                                         }
1762
1763                                                         /* free the old buffer */
1764                                                         if (gps->points) {
1765                                                                 MEM_freeN(gps->points);
1766                                                         }
1767                                                         if (gps->dvert) {
1768                                                                 BKE_gpencil_free_stroke_weights(gps);
1769                                                                 MEM_freeN(gps->dvert);
1770                                                         }
1771
1772                                                         /* save the new buffer */
1773                                                         gps->points = new_points;
1774                                                         gps->dvert = new_dvert;
1775                                                         gps->totpoints = tot;
1776
1777                                                         /* triangles cache needs to be recalculated */
1778                                                         gps->flag |= GP_STROKE_RECALC_CACHES;
1779                                                         gps->tot_triangles = 0;
1780
1781                                                         /* deselect the stroke, since none of its selected points will still be selected */
1782                                                         gps->flag &= ~GP_STROKE_SELECT;
1783                                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1784                                                                 pt->flag &= ~GP_SPOINT_SELECT;
1785                                                         }
1786                                                 }
1787
1788                                                 changed = true;
1789                                         }
1790                                 }
1791                         }
1792                 }
1793         }
1794         CTX_DATA_END;
1795
1796         if (changed) {
1797                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1798                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1799                 return OPERATOR_FINISHED;
1800         }
1801         else {
1802                 return OPERATOR_CANCELLED;
1803         }
1804 }
1805
1806 /* ----------------------------------- */
1807
1808 /* Temp data for storing information about an "island" of points
1809  * that should be kept when splitting up a stroke. Used in:
1810  * gp_stroke_delete_tagged_points()
1811  */
1812 typedef struct tGPDeleteIsland {
1813         int start_idx;
1814         int end_idx;
1815 } tGPDeleteIsland;
1816
1817
1818 /* Split the given stroke into several new strokes, partitioning
1819  * it based on whether the stroke points have a particular flag
1820  * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
1821  *
1822  * The algorithm used here is as follows:
1823  * 1) We firstly identify the number of "islands" of non-tagged points
1824  *    which will all end up being in new strokes.
1825  *    - In the most extreme case (i.e. every other vert is a 1-vert island),
1826  *      we have at most n / 2 islands
1827  *    - Once we start having larger islands than that, the number required
1828  *      becomes much less
1829  * 2) Each island gets converted to a new stroke
1830  */
1831 void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke,
1832         int tag_flags, bool select)
1833 {
1834         tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
1835         bool in_island  = false;
1836         int num_islands = 0;
1837
1838
1839         /* First Pass: Identify start/end of islands */
1840         bGPDspoint *pt = gps->points;
1841         for (int i = 0; i < gps->totpoints; i++, pt++) {
1842                 if (pt->flag & tag_flags) {
1843                         /* selected - stop accumulating to island */
1844                         in_island = false;
1845                 }
1846                 else {
1847                         /* unselected - start of a new island? */
1848                         int idx;
1849
1850                         if (in_island) {
1851                                 /* extend existing island */
1852                                 idx = num_islands - 1;
1853                                 islands[idx].end_idx = i;
1854                         }
1855                         else {
1856                                 /* start of new island */
1857                                 in_island = true;
1858                                 num_islands++;
1859
1860                                 idx = num_islands - 1;
1861                                 islands[idx].start_idx = islands[idx].end_idx = i;
1862                         }
1863                 }
1864         }
1865
1866         /* Watch out for special case where No islands = All points selected = Delete Stroke only */
1867         if (num_islands) {
1868                 /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
1869                 int idx;
1870
1871                 /* Create each new stroke... */
1872                 for (idx = 0; idx < num_islands; idx++) {
1873                         tGPDeleteIsland *island = &islands[idx];
1874                         bGPDstroke *new_stroke = MEM_dupallocN(gps);
1875
1876                         /* initialize triangle memory  - to be calculated on next redraw */
1877                         new_stroke->triangles = NULL;
1878                         new_stroke->flag |= GP_STROKE_RECALC_CACHES;
1879                         new_stroke->tot_triangles = 0;
1880
1881                         /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
1882                         new_stroke->totpoints = island->end_idx - island->start_idx + 1;
1883
1884                         /* Copy over the relevant point data */
1885                         new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
1886                         memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
1887
1888                         /* Copy over vertex weight data (if available) */
1889                         if (new_stroke->dvert != NULL) {
1890                                 /* Copy over the relevant vertex-weight points */
1891                                 new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, "gp delete stroke fragment weight");
1892                                 memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints);
1893
1894                                 /* Copy weights */
1895                                 int e = island->start_idx;
1896                                 for (int i = 0; i < new_stroke->totpoints; i++) {
1897                                         MDeformVert *dvert_dst = &gps->dvert[e];
1898                                         MDeformVert *dvert_src = &new_stroke->dvert[i];
1899                                         dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
1900                                         e++;
1901                                 }
1902                         }
1903
1904                         /* Each island corresponds to a new stroke. We must adjust the
1905                          * timings of these new strokes:
1906                          *
1907                          * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
1908                          * the start of the stroke, we have to offset this inittime and all remaining points' delta values.
1909                          * This way we get a new stroke with exactly the same timing as if user had started drawing from
1910                          * the first non-removed point...
1911                          */
1912                         {
1913                                 bGPDspoint *pts;
1914                                 float delta = gps->points[island->start_idx].time;
1915                                 int j;
1916
1917                                 new_stroke->inittime += (double)delta;
1918
1919                                 pts = new_stroke->points;
1920                                 for (j = 0; j < new_stroke->totpoints; j++, pts++) {
1921                                         pts->time -= delta;
1922                                         /* set flag for select again later */
1923                                         if (select == true) {
1924                                                 pts->flag &= ~GP_SPOINT_SELECT;
1925                                                 pts->flag |= GP_SPOINT_TAG;
1926                                         }
1927                                 }
1928                         }
1929
1930                         /* Add new stroke to the frame */
1931                         if (next_stroke) {
1932                                 BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
1933                         }
1934                         else {
1935                                 BLI_addtail(&gpf->strokes, new_stroke);
1936                         }
1937                 }
1938         }
1939
1940         /* free islands */
1941         MEM_freeN(islands);
1942
1943         /* Delete the old stroke */
1944         if (gps->points) {
1945                 MEM_freeN(gps->points);
1946         }
1947         if (gps->dvert) {
1948                 BKE_gpencil_free_stroke_weights(gps);
1949                 MEM_freeN(gps->dvert);
1950         }
1951         if (gps->triangles) {
1952                 MEM_freeN(gps->triangles);
1953         }
1954         BLI_freelinkN(&gpf->strokes, gps);
1955 }
1956
1957 /* Split selected strokes into segments, splitting on selected points */
1958 static int gp_delete_selected_points(bContext *C)
1959 {
1960         Object *ob = CTX_data_active_object(C);
1961         bGPdata *gpd = ED_gpencil_data_get_active(C);
1962         const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1963         bool changed = false;
1964
1965         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1966         {
1967                 bGPDframe *init_gpf = gpl->actframe;
1968                 if (is_multiedit) {
1969                         init_gpf = gpl->frames.first;
1970                 }
1971
1972                 for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1973                         if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1974                                 bGPDstroke *gps, *gpsn;
1975
1976                                 if (gpf == NULL)
1977                                         continue;
1978
1979                                 /* simply delete strokes which are selected */
1980                                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1981                                         gpsn = gps->next;
1982
1983                                         /* skip strokes that are invalid for current view */
1984                                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1985                                                 continue;
1986                                         /* check if the color is editable */
1987                                         if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false)
1988                                                 continue;
1989
1990
1991                                         if (gps->flag & GP_STROKE_SELECT) {
1992                                                 /* deselect old stroke, since it will be used as template for the new strokes */
1993                                                 gps->flag &= ~GP_STROKE_SELECT;
1994
1995                                                 /* delete unwanted points by splitting stroke into several smaller ones */
1996                                                 gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false);
1997
1998                                                 changed = true;
1999                                         }
2000                                 }
2001                         }
2002                 }
2003         }
2004         CTX_DATA_END;
2005
2006         if (changed) {
2007                 DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2008                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2009                 return OPERATOR_FINISHED;
2010         }
2011         else {
2012                 return OPERATOR_CANCELLED;
2013         }
2014 }
2015
2016 /* simple wrapper to external call */
2017 int gp_delete_selected_point_wrap(bContext *C)
2018 {
2019         return gp_delete_selected_points(C);
2020 }
2021
2022 /* ----------------------------------- */
2023
2024 static int gp_delete_exec(bContext *C, wmOperator *op)
2025 {
2026         eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
2027         int result = OPERATOR_CANCELLED;
2028
2029         switch (mode) {
2030                 case GP_DELETEOP_STROKES:       /* selected strokes */
2031                         result = gp_delete_selected_strokes(C);
2032                         break;
2033
2034                 case GP_DELETEOP_POINTS:        /* selected points (breaks the stroke into segments) */
2035                         result = gp_delete_selected_points(C);
2036                         break;
2037
2038                 case GP_DELETEOP_FRAME:         /* active frame */
2039                         result = gp_actframe_delete_exec(C, op);
2040                         break;
2041         }
2042
2043         return result;
2044 }
2045
2046 void GPENCIL_OT_delete(wmOperatorType *ot)
2047 {
2048         static const EnumPropertyItem prop_gpencil_delete_types[] = {
2049                 {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
2050                 {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
2051                 {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
2052                 {0, NULL, 0, NULL, NULL}
2053         };
2054
2055         /* identifiers */
2056         ot->name = "Delete";
2057         ot->idname = "GPENCIL_OT_delete";
2058         ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
2059
2060         /* callbacks */
2061         ot->invoke = WM_menu_invoke;
2062         ot->exec = gp_delete_exec;
2063         ot->poll = gp_stroke_edit_poll;
2064
2065         /* flags */
2066         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
2067
2068         /* props */
2069         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
2070 }
2071
2072 static int gp_dissolve_exec(bContext *C, wmOperator *op)
2073 {
2074         eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
2075
2076         return gp_dissolve_selected_points(C, mode);
2077 }
2078
2079 void GPENCIL_OT_dissolve(wmOperatorType *ot)
2080 {
2081         static EnumPropertyItem prop_gpencil_dissolve_types[] = {
2082                 {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"},
2083                 {GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points"},
2084                 {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"},
2085                 {0, NULL, 0, NULL, NULL}
2086         };
2087
2088         /* identifiers */
2089         ot->name = "Dissolve";
2090         ot->idname = "GPENCIL_OT_dissolve";
2091         ot->description = "Delete selected points without splitting strokes";
2092
2093         /* callbacks */
2094         ot->invoke = WM_menu_invoke;
2095         ot->exec = gp_dissolve_exec;
2096         ot->poll = gp_stroke_edit_poll;
2097
2098         /* flags */
2099         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
2100
2101         /* props */
2102         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0, "Type", "Method used for disolving Stroke points");
2103 }
2104
2105 /* ****************** Snapping - Strokes <-> Cursor ************************ */
2106
2107 /* Poll callback for snap operators */
2108 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
2109  *       define a cursor or gridstep which can be used
2110  */
2111 static bool gp_snap_poll(bContext *C)
2112 {
2113         bGPdata *gpd = CTX_data_gpencil_data(C);
2114         ScrArea *sa = CTX_wm_area(C);
2115
2116         return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
2117 }
2118
2119 /* --------------------------------- */
2120
2121 static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
2122 {
2123         bGPdata *gpd = ED_gpencil_data_get_active(C);
2124         View3D *v3d = CTX_wm_view3d(C);
2125         Scene *scene = CTX_data_scene(C);
2126         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2127         Object *obact = CTX_data_active_object(C);
2128         const float gridf = ED_view3d_grid_scale(scene, v3d, NULL);
2129
2130         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2131                 /* only editable and visible layers are considered */
2132                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2133                         bGPDframe *gpf = gpl->actframe;
2134                         float diff_mat[4][4];
2135
2136                         /* calculate difference matrix object */
2137                         ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
2138
2139                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2140                                 bGPDspoint *pt;
2141                                 int i;
2142
2143                                 /* skip strokes that are invalid for current view */
2144                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2145                                         continue;
2146                                 /* check if the color is editable */
2147                                 if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false)
2148                                         continue;
2149
2150                                 // TODO: if entire stroke is selected, offset entire stroke by same amount?
2151                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2152                                         /* only if point is selected */
2153                                         if (pt->flag & GP_SPOINT_SELECT) {
2154                                                 /* apply parent transformations */
2155                                                 float fpt[3];
2156                                                 mul_v3_m4v3(fpt, diff_mat, &pt->x);
2157
2158                                                 fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
2159                                                 fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
2160                                                 fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
2161
2162                                                 /* return data */
2163                                                 copy_v3_v3(&pt->x, fpt);
2164                                                 gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
2165                                         }
2166                                 }
2167                         }
2168                 }
2169         }
2170
2171         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2172         DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
2173         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2174         return OPERATOR_FINISHED;
2175 }
2176
2177 void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
2178 {
2179         /* identifiers */
2180         ot->name = "Snap Selection to Grid";
2181         ot->idname = "GPENCIL_OT_snap_to_grid";
2182         ot->description = "Snap selected points to the nearest grid points";
2183
2184         /* callbacks */
2185         ot->exec = gp_snap_to_grid;
2186         ot->poll = gp_snap_poll;
2187
2188         /* flags */
2189         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2190 }
2191
2192 /* ------------------------------- */
2193
2194 static int gp_snap_to_cursor(bContext *C, wmOperator *op)
2195 {
2196         bGPdata *gpd = ED_gpencil_data_get_active(C);
2197
2198         Scene *scene = CTX_data_scene(C);
2199         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2200         Object *obact = CTX_data_active_object(C);
2201
2202         const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
2203         const float *cursor_global = scene->cursor.location;
2204
2205         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2206                 /* only editable and visible layers are considered */
2207                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2208                         bGPDframe *gpf = gpl->actframe;
2209                         float diff_mat[4][4];
2210
2211                         /* calculate difference matrix */
2212                         ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
2213
2214                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2215                                 bGPDspoint *pt;
2216                                 int i;
2217
2218                                 /* skip strokes that are invalid for current view */
2219                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2220                                         continue;
2221                                 /* check if the color is editable */
2222                                 if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false)
2223                                         continue;
2224                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
2225                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
2226                                         continue;
2227
2228                                 if (use_offset) {
2229                                         float offset[3];
2230
2231                                         /* compute offset from first point of stroke to cursor */
2232                                         /* TODO: Allow using midpoint instead? */
2233                                         sub_v3_v3v3(offset, cursor_global, &gps->points->x);
2234
2235                                         /* apply offset to all points in the stroke */
2236                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2237                                                 add_v3_v3(&pt->x, offset);
2238                                         }
2239                                 }
2240                                 else {
2241                                         /* affect each selected point */
2242                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2243                                                 if (pt->flag & GP_SPOINT_SELECT) {
2244                                                         copy_v3_v3(&pt->x, cursor_global);
2245                                                         gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
2246                                                 }
2247                                         }
2248                                 }
2249                         }
2250
2251                 }
2252         }
2253
2254         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2255         DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
2256         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2257         return OPERATOR_FINISHED;
2258 }
2259
2260 void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
2261 {
2262         /* identifiers */
2263         ot->name = "Snap Selection to Cursor";
2264         ot->idname = "GPENCIL_OT_snap_to_cursor";
2265         ot->description = "Snap selected points/strokes to the cursor";
2266
2267         /* callbacks */
2268         ot->exec = gp_snap_to_cursor;
2269         ot->poll = gp_snap_poll;
2270
2271         /* flags */
2272         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2273
2274         /* props */
2275         ot->prop = RNA_def_boolean(ot->srna, "use_offset", true, "With Offset",
2276                 "Offset the entire stroke instead of selected points only");
2277 }
2278
2279 /* ------------------------------- */
2280
2281 static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
2282 {
2283         bGPdata *gpd = ED_gpencil_data_get_active(C);
2284
2285         Scene *scene = CTX_data_scene(C);
2286         View3D *v3d = CTX_wm_view3d(C);
2287         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2288         Object *obact = CTX_data_active_object(C);
2289
2290         float *cursor = scene->cursor.location;
2291         float centroid[3] = {0.0f};
2292         float min[3], max[3];
2293         size_t count = 0;
2294
2295         INIT_MINMAX(min, max);
2296
2297         /* calculate midpoints from selected points */
2298         for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
2299                 /* only editable and visible layers are considered */
2300                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2301                         bGPDframe *gpf = gpl->actframe;
2302                         float diff_mat[4][4];
2303
2304                         /* calculate difference matrix */
2305                         ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
2306
2307                         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2308                                 bGPDspoint *pt;
2309                                 int i;
2310
2311                                 /* skip strokes that are invalid for current view */
2312                                 if (ED_gpencil_stroke_can_use(C, gps) == false)
2313                                         continue;
2314                                 /* check if the color is editable */
2315                                 if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false)
2316                                         continue;
2317                                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
2318                                 if ((gps->flag & GP_STROKE_SELECT) == 0)
2319                                         continue;
2320
2321                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2322                                         if (pt->flag & GP_SPOINT_SELECT) {
2323                                                 /* apply parent transformations */
2324                                                 float fpt[3];
2325                                                 mul_v3_m4v3(fpt, diff_mat, &pt->x);
2326
2327                                                 add_v3_v3(centroid, fpt);
2328                                                 minmax_v3v3_v3(min, max, fpt);
2329
2330                                                 count++;
2331                                         }
2332                                 }
2333
2334                         }
2335                 }
2336         }
2337
2338         if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) {
2339                 mul_v3_fl(centroid, 1.0f / (float)count);
2340                 copy_v3_v3(cursor, centroid);
2341         }
2342         else {
2343                 mid_v3_v3v3(cursor, min, max);
2344         }
2345
2346
2347         DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
2348         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
2349
2350         return OPERATOR_FINISHED;
2351 }
2352
2353 void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
2354 {
2355         /* identifiers */
2356         ot->name = "Snap Cursor to Selected Points";
2357         ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
2358         ot->description = "Snap cursor to center of selected points";
2359
2360         /* callbacks */
2361         ot->exec = gp_snap_cursor_to_sel;
2362         ot->poll = gp_snap_poll;
2363
2364         /* flags */
2365         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2366 }
2367
2368 /* ******************* Apply layer thickness change to strokes ************************** */
2369
2370 static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
2371 {
2372         bGPdata *gpd = ED_gpencil_data_get_active(C);
2373         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
2374
2375         /* sanity checks */
2376         if (ELEM(NULL, gpd, gpl, gpl->frames.first))
2377                 return OPERATOR_CANCELLED;
2378
2379         /* loop all strokes */
2380         for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
2381                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2382                         /* Apply thickness */
2383                         if ((gps->thickness == 0) && (gpl->line_change == 0)) {
2384                                 gps->thickness = gpl->thickness;
2385                         }
2386                         else {
2387                                 gps->thickness = gps->thickness + gpl->line_change;
2388                         }
2389                 }
2390         }
2391         /* clear value */
2392         gpl->thickness = 0.0f;
2393         gpl->line_change = 0;
2394
2395         /* notifiers */
2396         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2397         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2398
2399         return OPERATOR_FINISHED;
2400 }
2401
2402 void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
2403 {
2404         /* identifiers */
2405         ot->name = "Apply Stroke Thickness";
2406         ot->idname = "GPENCIL_OT_stroke_apply_thickness";
2407         ot->description = "Apply the thickness change of the layer to its strokes";
2408
2409         /* api callbacks */
2410         ot->exec = gp_stroke_apply_thickness_exec;
2411         ot->poll = gp_active_layer_poll;
2412 }
2413
2414 /* ******************* Close Strokes ************************** */
2415
2416 enum {
2417         GP_STROKE_CYCLIC_CLOSE = 1,
2418         GP_STROKE_CYCLIC_OPEN = 2,
2419         GP_STROKE_CYCLIC_TOGGLE = 3
2420 };
2421
2422 static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
2423 {
2424         bGPdata *gpd = ED_gpencil_data_get_active(C);
2425         Object *ob = CTX_data_active_object(C);
2426
2427         const int type = RNA_enum_get(op->ptr, "type");
2428
2429         /* sanity checks */
2430         if (ELEM(NULL, gpd))
2431                 return OPERATOR_CANCELLED;
2432
2433         /* loop all selected strokes */
2434         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2435         {
2436                 if (gpl->actframe == NULL)
2437                         continue;
2438
2439                 for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
2440                         MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
2441
2442                         /* skip strokes that are not selected or invalid for current view */
2443                         if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
2444                                 continue;
2445                         /* skip hidden or locked colors */
2446                         if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED))
2447                                 continue;
2448
2449                         switch (type) {
2450                                 case GP_STROKE_CYCLIC_CLOSE:
2451                                         /* Close all (enable) */
2452                                         gps->flag |= GP_STROKE_CYCLIC;
2453                                         break;
2454                                 case GP_STROKE_CYCLIC_OPEN:
2455                                         /* Open all (disable) */
2456                                         gps->flag &= ~GP_STROKE_CYCLIC;
2457                                         break;
2458                                 case GP_STROKE_CYCLIC_TOGGLE:
2459                                         /* Just toggle flag... */
2460                                         gps->flag ^= GP_STROKE_CYCLIC;
2461                                         break;
2462                                 default:
2463                                         BLI_assert(0);
2464                                         break;
2465                         }
2466                 }
2467         }
2468         CTX_DATA_END;
2469
2470         /* notifiers */
2471         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2472         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2473
2474         return OPERATOR_FINISHED;
2475 }
2476
2477 /**
2478  * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
2479  * option to force opened/closed strokes instead of just toggle behavior.
2480  */
2481 void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
2482 {
2483         static const EnumPropertyItem cyclic_type[] = {
2484                 {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
2485                 {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
2486                 {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
2487                 {0, NULL, 0, NULL, NULL}
2488         };
2489
2490         /* identifiers */
2491         ot->name = "Set Cyclical State";
2492         ot->idname = "GPENCIL_OT_stroke_cyclical_set";
2493         ot->description = "Close or open the selected stroke adding an edge from last to first point";
2494
2495         /* api callbacks */
2496         ot->exec = gp_stroke_cyclical_set_exec;
2497         ot->poll = gp_active_layer_poll;
2498
2499         /* flags */
2500         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2501
2502         /* properties */
2503         ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
2504 }
2505
2506 /* ******************* Stroke join ************************** */
2507
2508 /* Helper: flip stroke */
2509 static void gpencil_flip_stroke(bGPDstroke *gps)
2510 {
2511         int end = gps->totpoints - 1;
2512
2513         for (int i = 0; i < gps->totpoints / 2; i++) {
2514                 bGPDspoint *point, *point2;
2515                 bGPDspoint pt;
2516
2517                 /* save first point */
2518                 point = &gps->points[i];
2519                 pt.x = point->x;
2520                 pt.y = point->y;
2521                 pt.z = point->z;
2522                 pt.flag = point->flag;
2523                 pt.pressure = point->pressure;
2524                 pt.strength = point->strength;
2525                 pt.time = point->time;
2526
2527                 /* replace first point with last point */
2528                 point2 = &gps->points[end];
2529                 point->x = point2->x;
2530                 point->y = point2->y;
2531                 point->z = point2->z;
2532                 point->flag = point2->flag;
2533                 point->pressure = point2->pressure;
2534                 point->strength = point2->strength;
2535                 point->time = point2->time;
2536
2537                 /* replace last point with first saved before */
2538                 point = &gps->points[end];
2539                 point->x = pt.x;
2540                 point->y = pt.y;
2541                 point->z = pt.z;
2542                 point->flag = pt.flag;
2543                 point->pressure = pt.pressure;
2544                 point->strength = pt.strength;
2545                 point->time = pt.time;
2546
2547                 end--;
2548         }
2549 }
2550
2551 /* Helper: copy point between strokes */
2552 static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, int idx, float delta[3],
2553         float pressure, float strength, float deltatime)
2554 {
2555         bGPDspoint *newpoint;
2556
2557         gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
2558         if (gps->dvert != NULL) {
2559                 gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1));
2560         }
2561         gps->totpoints++;
2562         newpoint = &gps->points[gps->totpoints - 1];
2563
2564         newpoint->x = point->x * delta[0];
2565         newpoint->y = point->y * delta[1];
2566         newpoint->z = point->z * delta[2];
2567         newpoint->flag = point->flag;
2568         newpoint->pressure = pressure;
2569         newpoint->strength = strength;
2570         newpoint->time = point->time + deltatime;
2571
2572         if (gps->dvert != NULL) {
2573                 MDeformVert *dvert = &gps->dvert[idx];
2574                 MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
2575
2576                 newdvert->totweight = dvert->totweight;
2577                 newdvert->dw = MEM_dupallocN(dvert->dw);
2578         }
2579 }
2580
2581 /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
2582 static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
2583 {
2584         bGPDspoint point;
2585         bGPDspoint *pt;
2586         int i;
2587         float delta[3] = {1.0f, 1.0f, 1.0f};
2588         float deltatime = 0.0f;
2589
2590         /* sanity checks */
2591         if (ELEM(NULL, gps_a, gps_b))
2592                 return;
2593
2594         if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
2595                 return;
2596
2597         /* define start and end points of each stroke */
2598         float sa[3], sb[3], ea[3], eb[3];
2599         pt = &gps_a->points[0];
2600         copy_v3_v3(sa, &pt->x);
2601
2602         pt = &gps_a->points[gps_a->totpoints - 1];
2603         copy_v3_v3(ea, &pt->x);
2604
2605         pt = &gps_b->points[0];
2606         copy_v3_v3(sb, &pt->x);
2607
2608         pt = &gps_b->points[gps_b->totpoints - 1];
2609         copy_v3_v3(eb, &pt->x);
2610
2611         /* review if need flip stroke B */
2612         float ea_sb = len_squared_v3v3(ea, sb);
2613         float ea_eb = len_squared_v3v3(ea, eb);
2614         /* flip if distance to end point is shorter */
2615         if (ea_eb < ea_sb) {
2616                 gpencil_flip_stroke(gps_b);
2617         }
2618
2619         /* don't visibly link the first and last points? */
2620         if (leave_gaps) {
2621                 /* 1st: add one tail point to start invisible area */
2622                 point = gps_a->points[gps_a->totpoints - 1];
2623                 deltatime = point.time;
2624                 gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, delta, 0.0f, 0.0f, 0.0f);
2625
2626                 /* 2nd: add one head point to finish invisible area */
2627                 point = gps_b->points[0];
2628                 gpencil_stroke_copy_point(gps_a, &point, 0, delta, 0.0f, 0.0f, deltatime);
2629         }
2630
2631         /* 3rd: add all points */
2632         for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
2633                 /* check if still room in buffer */
2634                 if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
2635                         gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime);
2636                 }
2637         }
2638 }
2639
2640 static int gp_stroke_join_exec(bContext *C, wmOperator *op)
2641 {
2642         bGPdata *gpd = ED_gpencil_data_get_active(C);
2643         bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
2644         bGPDstroke *gps, *gpsn;
2645         Object *ob = CTX_data_active_object(C);
2646
2647         bGPDframe *gpf_a = NULL;
2648         bGPDstroke *stroke_a = NULL;
2649         bGPDstroke *stroke_b = NULL;
2650         bGPDstroke *new_stroke = NULL;
2651
2652         const int type = RNA_enum_get(op->ptr, "type");
2653         const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
2654
2655         /* sanity checks */
2656         if (ELEM(NULL, gpd))
2657                 return OPERATOR_CANCELLED;
2658
2659         if (activegpl->flag & GP_LAYER_LOCKED)
2660                 return OPERATOR_CANCELLED;
2661
2662         BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
2663
2664
2665         /* read all selected strokes */
2666         bool first = false;
2667         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2668         {
2669                 bGPDframe *gpf = gpl->actframe;
2670                 if (gpf == NULL)
2671                         continue;
2672
2673                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
2674                         gpsn = gps->next;
2675                         if (gps->flag & GP_STROKE_SELECT) {
2676                                 /* skip strokes that are invalid for current view */
2677                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
2678                                         continue;
2679                                 }
2680                                 /* check if the color is editable */
2681                                 if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
2682                                         continue;
2683                                 }
2684
2685                                 /* to join strokes, cyclic must be disabled */
2686                                 gps->flag &= ~GP_STROKE_CYCLIC;
2687
2688                                 /* saves first frame and stroke */
2689                                 if (!first) {
2690                                         first = true;
2691                                         gpf_a = gpf;
2692                                         stroke_a = gps;
2693                                 }
2694                                 else {
2695                                         stroke_b = gps;
2696
2697                                         /* create a new stroke if was not created before (only created if something to join) */
2698                                         if (new_stroke == NULL) {
2699                                                 new_stroke = MEM_dupallocN(stroke_a);
2700                                                 new_stroke->points = MEM_dupallocN(stroke_a->points);
2701                                                 if (stroke_a->dvert != NULL) {
2702                                                         new_stroke->dvert = MEM_dupallocN(stroke_a->dvert);
2703                                                         BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke);
2704                                                 }
2705                                                 new_stroke->triangles = NULL;
2706                                                 new_stroke->tot_triangles = 0;
2707                                                 new_stroke->flag |= GP_STROKE_RECALC_CACHES;
2708
2709                                                 /* if new, set current color */
2710                                                 if (type == GP_STROKE_JOINCOPY) {
2711                                                         new_stroke->mat_nr = stroke_a->mat_nr;
2712                                                 }
2713                                         }
2714
2715                                         /* join new_stroke and stroke B. New stroke will contain all the previous data */
2716                                         gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
2717
2718                                         /* if join only, delete old strokes */
2719                                         if (type == GP_STROKE_JOIN) {
2720                                                 if (stroke_a) {
2721                                                         BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
2722                                                         BLI_remlink(&gpf->strokes, stroke_a);
2723                                                         BKE_gpencil_free_stroke(stroke_a);
2724                                                         stroke_a = NULL;
2725                                                 }
2726                                                 if (stroke_b) {
2727                                                         BLI_remlink(&gpf->strokes, stroke_b);
2728                                                         BKE_gpencil_free_stroke(stroke_b);
2729                                                         stroke_b = NULL;
2730                                                 }
2731                                         }
2732                                 }
2733                         }
2734                 }
2735         }
2736         CTX_DATA_END;
2737
2738         /* add new stroke if was not added before */
2739         if (type == GP_STROKE_JOINCOPY) {
2740                 if (new_stroke) {
2741                         /* Add a new frame if needed */
2742                         if (activegpl->actframe == NULL)
2743                                 activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
2744
2745                         BLI_addtail(&activegpl->actframe->strokes, new_stroke);
2746                 }
2747         }
2748
2749         /* notifiers */
2750         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2751         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2752
2753         return OPERATOR_FINISHED;
2754 }
2755
2756 void GPENCIL_OT_stroke_join(wmOperatorType *ot)
2757 {
2758         static const EnumPropertyItem join_type[] = {
2759                 {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
2760                 {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
2761                 {0, NULL, 0, NULL, NULL}
2762         };
2763
2764         /* identifiers */
2765         ot->name = "Join Strokes";
2766         ot->idname = "GPENCIL_OT_stroke_join";
2767         ot->description = "Join selected strokes (optionally as new stroke)";
2768
2769         /* api callbacks */
2770         ot->exec = gp_stroke_join_exec;
2771         ot->poll = gp_active_layer_poll;
2772
2773         /* flags */
2774         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2775
2776         /* properties */
2777         ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
2778         RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
2779 }
2780
2781 /* ******************* Stroke flip ************************** */
2782
2783 static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
2784 {
2785         bGPdata *gpd = ED_gpencil_data_get_active(C);
2786         Object *ob = CTX_data_active_object(C);
2787
2788         /* sanity checks */
2789         if (ELEM(NULL, gpd))
2790                 return OPERATOR_CANCELLED;
2791
2792         /* read all selected strokes */
2793         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
2794         {
2795                 bGPDframe *gpf = gpl->actframe;
2796                 if (gpf == NULL)
2797                         continue;
2798
2799                 for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
2800                         if (gps->flag & GP_STROKE_SELECT) {
2801                                 /* skip strokes that are invalid for current view */
2802                                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
2803                                         continue;
2804                                 }
2805                                 /* check if the color is editable */
2806                                 if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
2807                                         continue;
2808                                 }
2809
2810                                 /* flip stroke */
2811                                 gpencil_flip_stroke(gps);
2812                         }
2813                 }
2814         }
2815         CTX_DATA_END;
2816
2817         /* notifiers */
2818         DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
2819         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
2820
2821         return OPERATOR_FINISHED;
2822 }
2823
2824 void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
2825 {
2826         /* identifiers */
2827         ot->name = "Flip Stroke";
2828         ot->idname = "GPENCIL_OT_stroke_flip";
2829         ot->description = "Change direction of the points of the selected strokes";
2830
2831         /* api callbacks */
2832         ot->exec = gp_stroke_flip_exec;
2833         ot->poll = gp_active_layer_poll;
2834
2835         /* flags */
2836         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2837 }
2838
2839 /* ***************** Reproject Strokes ********************** */
2840
2841 typedef enum eGP_ReprojectModes {
2842         /* Axis */
2843         GP_REPROJECT_FRONT = 0,
2844         GP_REPROJECT_SIDE,
2845         GP_REPROJECT_TOP,
2846         /* On same plane, parallel to viewplane */
2847         GP_REPROJECT_VIEW,
2848         /* Reprojected on to the scene geometry */
2849         GP_REPROJECT_SURFACE,
2850 } eGP_ReprojectModes;
2851
2852 static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
2853 {
2854         bGPdata *gpd = ED_gpencil_data_get_active(C);
2855         Scene *scene = CTX_data_scene(C);
2856         ToolSettings *ts = CTX_data_tool_settings(C);
2857         Depsgraph *depsgraph = CTX_data_depsgraph(C);
2858         Object *ob = CTX_data_active_object(C);
2859         ARegion *ar = CTX_wm_region(C);
2860         RegionView3D *rv3d = ar->regiondata;
2861
2862         GP_SpaceConversion gsc = {NULL};
2863         eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
2864
2865         float origin[3];
2866
2867         /* init space conversion stuff */
2868         gp_point_conversion_init(C, &gsc);
2869
2870         /* init autodist for geometry projection */
2871         if (mode == GP_REPROJECT_SURFACE) {
2872                 view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar);
2873                 ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0);
2874         }
2875
2876         // TODO: For deforming geometry workflow, create new frames?
2877
2878         /* Go through each editable + selected stroke, adjusting each of its points one by one... */
2879         GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
2880         {
2881                 if (gps->flag & GP_STROKE_SELECT) {
2882                         bGPDspoint *pt;
2883                         int i;
2884                         float inverse_diff_mat[4][4];
2885
2886                         /* Compute inverse matrix for unapplying parenting once instead of doing per-point */
2887                         /* TODO: add this bit to the iteration macro? */
2888                         invert_m4_m4(inverse_diff_mat, gpstroke_iter.diff_mat);
2889
2890                         /* Adjust each point */
2891                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2892                                 float xy[2];
2893
2894                                 /* 3D to Screenspace */
2895                                 /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace
2896                                  *       coordinates, resulting in lost precision, which in turn causes stairstepping
2897                                  *       artifacts in the final points.
2898                                  */
2899                                 bGPDspoint pt2;
2900                                 gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
2901                                 gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
2902
2903                                 /* Project stroke in one axis */
2904                                 if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) {
2905                                         ED_gp_get_drawing_reference(scene, ob, gpl,
2906                                                 ts->gpencil_v3d_align, origin);
2907
2908                                         int axis = 0;
2909                                         switch (mode) {
2910                                                 case GP_REPROJECT_FRONT:
2911                                                 {
2912                                                         axis = 1;
2913                                                         break;
2914                                                 }
2915                                                 case GP_REPROJECT_SIDE:
2916                                                 {
2917                                                         axis = 0;
2918                                                         break;
2919                                                 }
2920                                                 case GP_REPROJECT_TOP:
2921                                                 {
2922                                                         axis = 2;
2923                                                         break;
2924                                                 }
2925                                                 default:
2926                                                 {
2927                                                         axis = 1;
2928                                                         break;
2929                                                 }
2930                                         }
2931
2932                                         ED_gp_project_point_to_plane(ob, rv3d, origin,
2933                                                 axis, &pt2);
2934
2935                                         copy_v3_v3(&pt->x, &pt2.x);
2936
2937                                         /* apply parent again */
2938                                         gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt);
2939          &nbs