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