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