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