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