Grease Pencil: Merge GPencil_Editing_Stage3 branch into master
[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
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_math.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44
45 #include "BLT_translation.h"
46
47 #include "DNA_object_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52 #include "DNA_gpencil_types.h"
53
54 #include "BKE_context.h"
55 #include "BKE_global.h"
56 #include "BKE_gpencil.h"
57 #include "BKE_library.h"
58 #include "BKE_report.h"
59 #include "BKE_screen.h"
60
61 #include "UI_interface.h"
62 #include "UI_resources.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "RNA_access.h"
68 #include "RNA_define.h"
69 #include "RNA_enum_types.h"
70
71 #include "UI_view2d.h"
72
73 #include "ED_gpencil.h"
74 #include "ED_object.h"
75 #include "ED_view3d.h"
76
77 #include "gpencil_intern.h"
78
79 /* ************************************************ */
80 /* Stroke Edit Mode Management */
81
82 static int gpencil_editmode_toggle_poll(bContext *C)
83 {
84         return ED_gpencil_data_get_active(C) != NULL;
85 }
86
87 static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
88 {
89         bGPdata *gpd = ED_gpencil_data_get_active(C);
90         
91         if (gpd == NULL)
92                 return OPERATOR_CANCELLED;
93         
94         /* Just toggle editmode flag... */
95         gpd->flag ^= GP_DATA_STROKE_EDITMODE;
96         
97         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
98         WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
99         
100         return OPERATOR_FINISHED;
101 }
102
103 void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
104 {
105         /* identifiers */
106         ot->name = "Strokes Edit Mode Toggle";
107         ot->idname = "GPENCIL_OT_editmode_toggle";
108         ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
109         
110         /* callbacks */
111         ot->exec = gpencil_editmode_toggle_exec;
112         ot->poll = gpencil_editmode_toggle_poll;
113         
114         /* flags */
115         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
116 }
117
118 /* ************************************************ */
119 /* Stroke Editing Operators */
120
121 /* poll callback for all stroke editing operators */
122 static int gp_stroke_edit_poll(bContext *C)
123 {
124         /* NOTE: this is a bit slower, but is the most accurate... */
125         return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
126 }
127
128 /* ************** Duplicate Selected Strokes **************** */
129
130 /* Make copies of selected point segments in a selected stroke */
131 static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
132 {
133         bGPDspoint *pt;
134         int i;
135         
136         int start_idx = -1;
137         
138         
139         /* Step through the original stroke's points:
140          * - We accumulate selected points (from start_idx to current index)
141          *   and then convert that to a new stroke
142          */
143         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
144                 /* searching for start, are waiting for end? */
145                 if (start_idx == -1) {
146                         /* is this the first selected point for a new island? */
147                         if (pt->flag & GP_SPOINT_SELECT) {
148                                 start_idx = i;
149                         }
150                 }
151                 else {
152                         size_t len = 0;
153                         
154                         /* is this the end of current island yet?
155                          * 1) Point i-1 was the last one that was selected
156                          * 2) Point i is the last in the array
157                          */
158                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
159                                 len = i - start_idx;
160                         }
161                         else if (i == gps->totpoints - 1) {
162                                 len = i - start_idx + 1;
163                         }
164                         //printf("copying from %d to %d = %d\n", start_idx, i, len);
165                 
166                         /* make copies of the relevant data */
167                         if (len) {
168                                 bGPDstroke *gpsd;
169                                 
170                                 /* make a stupid copy first of the entire stroke (to get the flags too) */
171                                 gpsd = MEM_dupallocN(gps);
172                                 
173                                 /* now, make a new points array, and copy of the relevant parts */
174                                 gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
175                                 memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
176                                 gpsd->totpoints = len;
177                                 
178                                 /* add to temp buffer */
179                                 gpsd->next = gpsd->prev = NULL;
180                                 BLI_addtail(new_strokes, gpsd);
181                                 
182                                 /* cleanup + reset for next */
183                                 start_idx = -1;
184                         }
185                 }
186         }
187 }
188
189 static int gp_duplicate_exec(bContext *C, wmOperator *op)
190 {
191         bGPdata *gpd = ED_gpencil_data_get_active(C);
192         
193         if (gpd == NULL) {
194                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
195                 return OPERATOR_CANCELLED;
196         }
197         
198         /* for each visible (and editable) layer's selected strokes,
199          * copy the strokes into a temporary buffer, then append
200          * once all done
201          */
202         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
203         {
204                 ListBase new_strokes = {NULL, NULL};
205                 bGPDframe *gpf = gpl->actframe;
206                 bGPDstroke *gps;
207                 
208                 if (gpf == NULL)
209                         continue;
210                 
211                 /* make copies of selected strokes, and deselect these once we're done */
212                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
213                         /* skip strokes that are invalid for current view */
214                         if (ED_gpencil_stroke_can_use(C, gps) == false)
215                                 continue;
216                         
217                         if (gps->flag & GP_STROKE_SELECT) {
218                                 if (gps->totpoints == 1) {
219                                         /* Special Case: If there's just a single point in this stroke... */
220                                         bGPDstroke *gpsd;
221                                         
222                                         /* make direct copies of the stroke and its points */
223                                         gpsd = MEM_dupallocN(gps);
224                                         gpsd->points = MEM_dupallocN(gps->points);
225                                         
226                                         /* add to temp buffer */
227                                         gpsd->next = gpsd->prev = NULL;
228                                         BLI_addtail(&new_strokes, gpsd);
229                                 }
230                                 else {
231                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
232                                         gp_duplicate_points(gps, &new_strokes);
233                                 }
234                                 
235                                 /* deselect original stroke, or else the originals get moved too
236                                  * (when using the copy + move macro)
237                                  */
238                                 gps->flag &= ~GP_STROKE_SELECT;
239                         }
240                 }
241                 
242                 /* add all new strokes in temp buffer to the frame (preventing double-copies) */
243                 BLI_movelisttolist(&gpf->strokes, &new_strokes);
244                 BLI_assert(new_strokes.first == NULL);
245         }
246         CTX_DATA_END;
247         
248         /* updates */
249         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
250         
251         return OPERATOR_FINISHED;
252 }
253
254 void GPENCIL_OT_duplicate(wmOperatorType *ot)
255 {
256         /* identifiers */
257         ot->name = "Duplicate Strokes";
258         ot->idname = "GPENCIL_OT_duplicate";
259         ot->description = "Duplicate the selected Grease Pencil strokes";
260         
261         /* callbacks */
262         ot->exec = gp_duplicate_exec;
263         ot->poll = gp_stroke_edit_poll;
264         
265         /* flags */
266         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
267 }
268
269 /* ******************* Copy/Paste Strokes ************************* */
270 /* Grease Pencil stroke data copy/paste buffer:
271  * - The copy operation collects all segments of selected strokes,
272  *   dumping "ready to be copied" copies of the strokes into the buffer.
273  * - The paste operation makes a copy of those elements, and adds them
274  *   to the active layer. This effectively flattens down the strokes
275  *   from several different layers into a single layer.
276  */
277
278 /* list of bGPDstroke instances */
279 /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
280 ListBase gp_strokes_copypastebuf = {NULL, NULL};
281
282 /* Free copy/paste buffer data */
283 void ED_gpencil_strokes_copybuf_free(void)
284 {
285         bGPDstroke *gps, *gpsn;
286         
287         for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
288                 gpsn = gps->next;
289                 
290                 MEM_freeN(gps->points);
291                 BLI_freelinkN(&gp_strokes_copypastebuf, gps);
292         }
293         
294         BLI_listbase_clear(&gp_strokes_copypastebuf);
295 }
296
297 /* --------------------- */
298 /* Copy selected strokes */
299
300 static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
301 {
302         bGPdata *gpd = ED_gpencil_data_get_active(C);
303         
304         if (gpd == NULL) {
305                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
306                 return OPERATOR_CANCELLED;
307         }
308         
309         /* clear the buffer first */
310         ED_gpencil_strokes_copybuf_free();
311         
312         /* for each visible (and editable) layer's selected strokes,
313          * copy the strokes into a temporary buffer, then append
314          * once all done
315          */
316         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
317         {
318                 bGPDframe *gpf = gpl->actframe;
319                 bGPDstroke *gps;
320                 
321                 if (gpf == NULL)
322                         continue;
323                 
324                 /* make copies of selected strokes, and deselect these once we're done */
325                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
326                         /* skip strokes that are invalid for current view */
327                         if (ED_gpencil_stroke_can_use(C, gps) == false)
328                                 continue;
329                         
330                         if (gps->flag & GP_STROKE_SELECT) {
331                                 if (gps->totpoints == 1) {
332                                         /* Special Case: If there's just a single point in this stroke... */
333                                         bGPDstroke *gpsd;
334                                         
335                                         /* make direct copies of the stroke and its points */
336                                         gpsd = MEM_dupallocN(gps);
337                                         gpsd->points = MEM_dupallocN(gps->points);
338                                         
339                                         /* add to temp buffer */
340                                         gpsd->next = gpsd->prev = NULL;
341                                         BLI_addtail(&gp_strokes_copypastebuf, gpsd);
342                                 }
343                                 else {
344                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
345                                         gp_duplicate_points(gps, &gp_strokes_copypastebuf);
346                                 }
347                         }
348                 }
349         }
350         CTX_DATA_END;
351         
352         /* done - no updates needed */
353         return OPERATOR_FINISHED;
354 }
355
356 void GPENCIL_OT_copy(wmOperatorType *ot)
357 {
358         /* identifiers */
359         ot->name = "Copy Strokes";
360         ot->idname = "GPENCIL_OT_copy";
361         ot->description = "Copy selected Grease Pencil points and strokes";
362         
363         /* callbacks */
364         ot->exec = gp_strokes_copy_exec;
365         ot->poll = gp_stroke_edit_poll;
366         
367         /* flags */
368         //ot->flag = OPTYPE_REGISTER;
369 }
370
371 /* --------------------- */
372 /* Paste selected strokes */
373
374 static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
375 {
376         Scene *scene = CTX_data_scene(C);
377         bGPdata *gpd = ED_gpencil_data_get_active(C);
378         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
379         bGPDframe *gpf;
380         
381         /* check for various error conditions */
382         if (gpd == NULL) {
383                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
384                 return OPERATOR_CANCELLED;
385         }
386         else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
387                 BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
388                 return OPERATOR_CANCELLED;
389         }
390         else if (gpl == NULL) {
391                 /* no active layer - let's just create one */
392                 gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), 1);
393         }
394         else if (gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) {
395                 BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
396                 return OPERATOR_CANCELLED;
397         }
398         else {
399                 /* Check that some of the strokes in the buffer can be used */
400                 bGPDstroke *gps;
401                 bool ok = false;
402                 
403                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
404                         if (ED_gpencil_stroke_can_use(C, gps)) {
405                                 ok = true;
406                                 break;
407                         }
408                 }
409                 
410                 if (ok == false) {
411                         /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
412                          * but should be enough to give users a good idea of what's going on
413                          */
414                         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
415                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
416                         else
417                                 BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
418                                 
419                         return OPERATOR_CANCELLED;
420                 }
421         }
422         
423         /* Deselect all strokes first */
424         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
425         {
426                 bGPDspoint *pt;
427                 int i;
428                 
429                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
430                         pt->flag &= ~GP_SPOINT_SELECT;
431                 }
432                 
433                 gps->flag &= ~GP_STROKE_SELECT;
434         }
435         CTX_DATA_END;
436         
437         /* Ensure we have a frame to draw into
438          * NOTE: Since this is an op which creates strokes,
439          *       we are obliged to add a new frame if one
440          *       doesn't exist already
441          */
442         gpf = gpencil_layer_getframe(gpl, CFRA, true);
443         
444         if (gpf) {
445                 bGPDstroke *gps;
446                 
447                 /* Copy each stroke into the layer */
448                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
449                         if (ED_gpencil_stroke_can_use(C, gps)) {
450                                 bGPDstroke *new_stroke = MEM_dupallocN(gps);
451                                 
452                                 new_stroke->points = MEM_dupallocN(gps->points);
453                                 new_stroke->next = new_stroke->prev = NULL;
454                                 
455                                 BLI_addtail(&gpf->strokes, new_stroke);
456                         }
457                 }
458         }
459         
460         /* updates */
461         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
462         
463         return OPERATOR_FINISHED;
464 }
465
466 void GPENCIL_OT_paste(wmOperatorType *ot)
467 {
468         /* identifiers */
469         ot->name = "Paste Strokes";
470         ot->idname = "GPENCIL_OT_paste";
471         ot->description = "Paste previously copied strokes into active layer";
472         
473         /* callbacks */
474         ot->exec = gp_strokes_paste_exec;
475         ot->poll = gp_stroke_edit_poll;
476         
477         /* flags */
478         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
479 }
480
481 /* ******************* Move To Layer ****************************** */
482
483 static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
484 {
485         uiPopupMenu *pup;
486         uiLayout *layout;
487         
488         /* call the menu, which will call this operator again, hence the canceled */
489         pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
490         layout = UI_popup_menu_layout(pup);
491         uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
492         UI_popup_menu_end(C, pup);
493         
494         return OPERATOR_INTERFACE;
495 }
496
497 // FIXME: allow moving partial strokes
498 static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
499 {
500         bGPdata *gpd = CTX_data_gpencil_data(C);
501         bGPDlayer *target_layer = NULL;
502         ListBase strokes = {NULL, NULL};
503         int layer_num = RNA_enum_get(op->ptr, "layer");
504         
505         /* Get layer or create new one */
506         if (layer_num == -1) {
507                 /* Create layer */
508                 target_layer = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
509         }
510         else {
511                 /* Try to get layer */
512                 target_layer = BLI_findlink(&gpd->layers, layer_num);
513                 
514                 if (target_layer == NULL) {
515                         BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
516                         return OPERATOR_CANCELLED;
517                 }
518         }
519         
520         /* Extract all strokes to move to this layer
521          * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
522          *       getting repeatedly moved
523          */
524         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
525         {
526                 bGPDframe *gpf = gpl->actframe;
527                 bGPDstroke *gps, *gpsn;
528                 
529                 /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
530                 if ((gpl == target_layer) || (gpf == NULL))
531                         continue;
532                 
533                 /* make copies of selected strokes, and deselect these once we're done */
534                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
535                         gpsn = gps->next;
536                         
537                         /* skip strokes that are invalid for current view */
538                         if (ED_gpencil_stroke_can_use(C, gps) == false)
539                                 continue;
540                         
541                         /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
542                         if (gps->flag & GP_STROKE_SELECT) {
543                                 BLI_remlink(&gpf->strokes, gps);
544                                 BLI_addtail(&strokes, gps);
545                         }
546                 }
547         }
548         CTX_DATA_END;
549         
550         /* Paste them all in one go */
551         if (strokes.first) {
552                 Scene *scene = CTX_data_scene(C);
553                 bGPDframe *gpf = gpencil_layer_getframe(target_layer, CFRA, true);
554                 
555                 BLI_movelisttolist(&gpf->strokes, &strokes);
556                 BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
557         }
558         
559         /* updates */
560         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
561         
562         return OPERATOR_FINISHED;
563 }
564
565 void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
566 {
567         /* identifiers */
568         ot->name = "Move Strokes to Layer";
569         ot->idname = "GPENCIL_OT_move_to_layer";
570         ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
571         
572         /* callbacks */
573         ot->invoke = gp_move_to_layer_invoke;
574         ot->exec = gp_move_to_layer_exec;
575         ot->poll = gp_stroke_edit_poll; // XXX?
576         
577         /* flags */
578         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
579         
580         /* gp layer to use (dynamic enum) */
581         ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
582         RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
583 }
584
585 /* ******************* Delete Active Frame ************************ */
586
587 static int gp_actframe_delete_poll(bContext *C)
588 {
589         bGPdata *gpd = ED_gpencil_data_get_active(C);
590         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
591         
592         /* only if there's an active layer with an active frame */
593         return (gpl && gpl->actframe);
594 }
595
596 /* delete active frame - wrapper around API calls */
597 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
598 {
599         Scene *scene = CTX_data_scene(C);
600         bGPdata *gpd = ED_gpencil_data_get_active(C);
601         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
602         bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
603         
604         /* if there's no existing Grease-Pencil data there, add some */
605         if (gpd == NULL) {
606                 BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
607                 return OPERATOR_CANCELLED;
608         }
609         if (ELEM(NULL, gpl, gpf)) {
610                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
611                 return OPERATOR_CANCELLED;
612         }
613         
614         /* delete it... */
615         gpencil_layer_delframe(gpl, gpf);
616         
617         /* notifiers */
618         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
619         
620         return OPERATOR_FINISHED;
621 }
622
623 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
624 {
625         /* identifiers */
626         ot->name = "Delete Active Frame";
627         ot->idname = "GPENCIL_OT_active_frame_delete";
628         ot->description = "Delete the active frame for the active Grease Pencil datablock";
629         
630         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
631         
632         /* callbacks */
633         ot->exec = gp_actframe_delete_exec;
634         ot->poll = gp_actframe_delete_poll;
635 }
636
637 /* ******************* Delete Operator ************************ */
638
639 typedef enum eGP_DeleteMode {
640         /* delete selected stroke points */
641         GP_DELETEOP_POINTS          = 0,
642         /* delete selected strokes */
643         GP_DELETEOP_STROKES         = 1,
644         /* delete active frame */
645         GP_DELETEOP_FRAME           = 2,
646 } eGP_DeleteMode;
647
648 /* ----------------------------------- */
649
650 /* Delete selected strokes */
651 static int gp_delete_selected_strokes(bContext *C)
652 {
653         bool changed = false;
654         
655         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
656         {
657                 bGPDframe *gpf = gpl->actframe;
658                 bGPDstroke *gps, *gpsn;
659                 
660                 if (gpf == NULL)
661                         continue;
662                 
663                 /* simply delete strokes which are selected */
664                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
665                         gpsn = gps->next;
666                         
667                         /* skip strokes that are invalid for current view */
668                         if (ED_gpencil_stroke_can_use(C, gps) == false)
669                                 continue;
670                         
671                         /* free stroke if selected */
672                         if (gps->flag & GP_STROKE_SELECT) {
673                                 /* free stroke memory arrays, then stroke itself */
674                                 if (gps->points) MEM_freeN(gps->points);
675                                 BLI_freelinkN(&gpf->strokes, gps);
676                                 
677                                 changed = true;
678                         }
679                 }
680         }
681         CTX_DATA_END;
682         
683         if (changed) {
684                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
685                 return OPERATOR_FINISHED;
686         }
687         else {
688                 return OPERATOR_CANCELLED;
689         }
690 }
691
692 /* ----------------------------------- */
693
694 /* Delete selected points but keep the stroke */
695 static int gp_dissolve_selected_points(bContext *C)
696 {
697         bool changed = false;
698         
699         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
700         {
701                 bGPDframe *gpf = gpl->actframe;
702                 bGPDstroke *gps, *gpsn;
703                 
704                 if (gpf == NULL)
705                         continue;
706                 
707                 /* simply delete points from selected strokes
708                  * NOTE: we may still have to remove the stroke if it ends up having no points!
709                  */
710                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
711                         gpsn = gps->next;
712                         
713                         /* skip strokes that are invalid for current view */
714                         if (ED_gpencil_stroke_can_use(C, gps) == false)
715                                 continue;
716                         
717                         if (gps->flag & GP_STROKE_SELECT) {
718                                 bGPDspoint *pt;
719                                 int i;
720                                 
721                                 int tot = gps->totpoints; /* number of points in new buffer */
722                                 
723                                 /* First Pass: Count how many points are selected (i.e. how many to remove) */
724                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
725                                         if (pt->flag & GP_SPOINT_SELECT) {
726                                                 /* selected point - one of the points to remove */
727                                                 tot--;
728                                         }
729                                 }
730                                 
731                                 /* if no points are left, we simply delete the entire stroke */
732                                 if (tot <= 0) {
733                                         /* remove the entire stroke */
734                                         MEM_freeN(gps->points);
735                                         BLI_freelinkN(&gpf->strokes, gps);
736                                 }
737                                 else {
738                                         /* just copy all unselected into a smaller buffer */
739                                         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
740                                         bGPDspoint *npt        = new_points;
741                                         
742                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
743                                                 if ((pt->flag & GP_SPOINT_SELECT) == 0) {
744                                                         *npt = *pt;
745                                                         npt++;
746                                                 }
747                                         }
748                                         
749                                         /* free the old buffer */
750                                         MEM_freeN(gps->points);
751                                         
752                                         /* save the new buffer */
753                                         gps->points = new_points;
754                                         gps->totpoints = tot;
755                                         
756                                         /* deselect the stroke, since none of its selected points will still be selected */
757                                         gps->flag &= ~GP_STROKE_SELECT;
758                                 }
759                                 
760                                 changed = true;
761                         }
762                 }
763         }
764         CTX_DATA_END;
765         
766         if (changed) {
767                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
768                 return OPERATOR_FINISHED;
769         }
770         else {
771                 return OPERATOR_CANCELLED;
772         }
773 }
774
775 /* ----------------------------------- */
776
777 /* Temp data for storing information about an "island" of points
778  * that should be kept when splitting up a stroke. Used in:
779  * gp_stroke_delete_tagged_points()
780  */
781 typedef struct tGPDeleteIsland {
782         int start_idx;
783         int end_idx;
784 } tGPDeleteIsland;
785
786
787 /* Split the given stroke into several new strokes, partitioning
788  * it based on whether the stroke points have a particular flag
789  * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
790  *
791  * The algorithm used here is as follows:
792  * 1) We firstly identify the number of "islands" of non-tagged points
793  *    which will all end up being in new strokes.
794  *    - In the most extreme case (i.e. every other vert is a 1-vert island),
795  *      we have at most n / 2 islands
796  *    - Once we start having larger islands than that, the number required
797  *      becomes much less
798  * 2) Each island gets converted to a new stroke
799  */
800 void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags)
801 {
802         tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
803         bool in_island  = false;
804         int num_islands = 0;
805         
806         bGPDspoint *pt;
807         int i;
808         
809         /* First Pass: Identify start/end of islands */
810         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
811                 if (pt->flag & tag_flags) {
812                         /* selected - stop accumulating to island */
813                         in_island = false;
814                 }
815                 else {
816                         /* unselected - start of a new island? */
817                         int idx;
818                         
819                         if (in_island) {
820                                 /* extend existing island */
821                                 idx = num_islands - 1;
822                                 islands[idx].end_idx = i;
823                         }
824                         else {
825                                 /* start of new island */
826                                 in_island = true;
827                                 num_islands++;
828                                 
829                                 idx = num_islands - 1;
830                                 islands[idx].start_idx = islands[idx].end_idx = i;
831                         }
832                 }
833         }
834         
835         /* Watch out for special case where No islands = All points selected = Delete Stroke only */
836         if (num_islands) {
837                 /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
838                 int idx;
839                 
840                 /* Create each new stroke... */
841                 for (idx = 0; idx < num_islands; idx++) {
842                         tGPDeleteIsland *island = &islands[idx];
843                         bGPDstroke *new_stroke  = MEM_dupallocN(gps);
844                         
845                         /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
846                         new_stroke->totpoints = island->end_idx - island->start_idx + 1;
847                         new_stroke->points    = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
848                         
849                         /* Copy over the relevant points */
850                         memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
851                         
852                         
853                         /* Each island corresponds to a new stroke. We must adjust the 
854                          * timings of these new strokes:
855                          *
856                          * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
857                          * the start of the stroke, we have to offset this inittime and all remaing points' delta values.
858                          * This way we get a new stroke with exactly the same timing as if user had started drawing from
859                          * the first non-removed point...
860                          */
861                         {
862                                 bGPDspoint *pts;
863                                 float delta = gps->points[island->start_idx].time;
864                                 int j;
865                                 
866                                 new_stroke->inittime += (double)delta;
867                                 
868                                 pts = new_stroke->points;
869                                 for (j = 0; j < new_stroke->totpoints; j++, pts++) {
870                                         pts->time -= delta;
871                                 }
872                         }
873                         
874                         /* Add new stroke to the frame */
875                         if (next_stroke) {
876                                 BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
877                         }
878                         else {
879                                 BLI_addtail(&gpf->strokes, new_stroke);
880                         }
881                 }
882         }
883         
884         /* free islands */
885         MEM_freeN(islands);
886         
887         /* Delete the old stroke */
888         MEM_freeN(gps->points);
889         BLI_freelinkN(&gpf->strokes, gps);
890 }
891
892
893 /* Split selected strokes into segments, splitting on selected points */
894 static int gp_delete_selected_points(bContext *C)
895 {
896         bool changed = false;
897         
898         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
899         {
900                 bGPDframe *gpf = gpl->actframe;
901                 bGPDstroke *gps, *gpsn;
902                 
903                 if (gpf == NULL)
904                         continue;
905                 
906                 /* simply delete strokes which are selected */
907                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
908                         gpsn = gps->next;
909                         
910                         /* skip strokes that are invalid for current view */
911                         if (ED_gpencil_stroke_can_use(C, gps) == false)
912                                 continue;
913                         
914                         
915                         if (gps->flag & GP_STROKE_SELECT) {
916                                 /* deselect old stroke, since it will be used as template for the new strokes */
917                                 gps->flag &= ~GP_STROKE_SELECT;
918                                 
919                                 /* delete unwanted points by splitting stroke into several smaller ones */
920                                 gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT);
921                                 
922                                 changed = true;
923                         }
924                 }
925         }
926         CTX_DATA_END;
927         
928         if (changed) {
929                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
930                 return OPERATOR_FINISHED;
931         }
932         else {
933                 return OPERATOR_CANCELLED;
934         }
935 }
936
937 /* ----------------------------------- */
938
939 static int gp_delete_exec(bContext *C, wmOperator *op)
940 {
941         eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
942         int result = OPERATOR_CANCELLED;
943         
944         switch (mode) {
945                 case GP_DELETEOP_STROKES:       /* selected strokes */
946                         result = gp_delete_selected_strokes(C);
947                         break;
948                 
949                 case GP_DELETEOP_POINTS:        /* selected points (breaks the stroke into segments) */
950                         result = gp_delete_selected_points(C);
951                         break;
952
953                 case GP_DELETEOP_FRAME:         /* active frame */
954                         result = gp_actframe_delete_exec(C, op);
955                         break;
956         }
957         
958         return result;
959 }
960
961 void GPENCIL_OT_delete(wmOperatorType *ot)
962 {
963         static EnumPropertyItem prop_gpencil_delete_types[] = {
964                 {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
965                 {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
966                 {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
967                 {0, NULL, 0, NULL, NULL}
968         };
969         
970         /* identifiers */
971         ot->name = "Delete...";
972         ot->idname = "GPENCIL_OT_delete";
973         ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
974         
975         /* callbacks */
976         ot->invoke = WM_menu_invoke;
977         ot->exec = gp_delete_exec;
978         ot->poll = gp_stroke_edit_poll;
979         
980         /* flags */
981         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
982         
983         /* props */
984         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
985 }
986
987 static int gp_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
988 {
989         return gp_dissolve_selected_points(C);
990 }
991
992 void GPENCIL_OT_dissolve(wmOperatorType *ot)
993 {
994         /* identifiers */
995         ot->name = "Dissolve";
996         ot->idname = "GPENCIL_OT_dissolve";
997         ot->description = "Delete selected points without splitting strokes";
998
999         /* callbacks */
1000         ot->exec = gp_dissolve_exec;
1001         ot->poll = gp_stroke_edit_poll;
1002
1003         /* flags */
1004         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1005 }
1006
1007 /* ****************** Snapping - Strokes <-> Cursor ************************ */
1008
1009 /* Poll callback for snap operators */
1010 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
1011  *       define a cursor or gridstep which can be used
1012  */
1013 static int gp_snap_poll(bContext *C)
1014 {
1015         bGPdata *gpd = CTX_data_gpencil_data(C);
1016         ScrArea *sa = CTX_wm_area(C);
1017         
1018         return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
1019 }
1020
1021 /* --------------------------------- */
1022
1023 static int gp_snap_to_grid(bContext *C, wmOperator *op)
1024 {
1025         RegionView3D *rv3d = CTX_wm_region_data(C);
1026         float gridf = rv3d->gridview;
1027         
1028         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
1029         {
1030                 bGPDspoint *pt;
1031                 int i;
1032                 
1033                 // TOOD: if entire stroke is selected, offset entire stroke by same amount?
1034                 
1035                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1036                         /* only if point is selected.. */
1037                         if (pt->flag & GP_SPOINT_SELECT) {
1038                                 pt->x = gridf * floorf(0.5f + pt->x / gridf);
1039                                 pt->y = gridf * floorf(0.5f + pt->y / gridf);
1040                                 pt->z = gridf * floorf(0.5f + pt->z / gridf);
1041                         }
1042                 }
1043         }
1044         CTX_DATA_END;
1045         
1046         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1047         return OPERATOR_FINISHED;
1048 }
1049
1050 void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
1051 {
1052         /* identifiers */
1053         ot->name = "Snap Selection to Grid";
1054         ot->idname = "GPENCIL_OT_snap_to_grid";
1055         ot->description = "Snap selected points to the nearest grid points";
1056         
1057         /* callbacks */
1058         ot->exec = gp_snap_to_grid;
1059         ot->poll = gp_snap_poll;
1060         
1061         /* flags */
1062         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1063 }
1064
1065 /* ------------------------------- */
1066
1067 static int gp_snap_to_cursor(bContext *C, wmOperator *op)
1068 {
1069         Scene *scene = CTX_data_scene(C);
1070         View3D *v3d = CTX_wm_view3d(C);
1071         
1072         const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
1073         const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d);
1074         
1075         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
1076         {
1077                 bGPDspoint *pt;
1078                 int i;
1079                 
1080                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
1081                 if ((gps->flag & GP_STROKE_SELECT) == 0)
1082                         continue;
1083                 
1084                 if (use_offset) {
1085                         float offset[3];
1086                         
1087                         /* compute offset from first point of stroke to cursor */
1088                         /* TODO: Allow using midpoint instead? */
1089                         sub_v3_v3v3(offset, cursor_global, &gps->points->x);
1090                         
1091                         /* apply offset to all points in the stroke */
1092                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1093                                 add_v3_v3(&pt->x, offset);
1094                         }
1095                 }
1096                 else {
1097                         /* affect each selected point */
1098                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1099                                 if (pt->flag & GP_SPOINT_SELECT) {
1100                                         copy_v3_v3(&pt->x, cursor_global);
1101                                 }
1102                         }
1103                 }
1104         }
1105         CTX_DATA_END;
1106         
1107         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1108         return OPERATOR_FINISHED;
1109 }
1110
1111 void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
1112 {
1113         /* identifiers */
1114         ot->name = "Snap Selection to Cursor";
1115         ot->idname = "GPENCIL_OT_snap_to_cursor";
1116         ot->description = "Snap selected points/strokes to the cursor";
1117         
1118         /* callbacks */
1119         ot->exec = gp_snap_to_cursor;
1120         ot->poll = gp_snap_poll;
1121         
1122         /* flags */
1123         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1124         
1125         /* props */
1126         ot->prop = RNA_def_boolean(ot->srna, "use_offset", true, "With Offset",
1127                                    "Offset the entire stroke instead of selected points only");
1128 }
1129
1130 /* ------------------------------- */
1131
1132 static int gp_snap_cursor_to_sel(bContext *C, wmOperator *op)
1133 {
1134         Scene *scene = CTX_data_scene(C);
1135         View3D *v3d = CTX_wm_view3d(C);
1136         
1137         float *cursor = ED_view3d_cursor3d_get(scene, v3d);
1138         float centroid[3] = {0.0f};
1139         float min[3], max[3];
1140         size_t count = 0;
1141         
1142         INIT_MINMAX(min, max);
1143         
1144         /* calculate midpoints from selected points */
1145         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
1146         {
1147                 bGPDspoint *pt;
1148                 int i;
1149                 
1150                 /* only continue if this stroke is selected (editable doesn't guarantee this)... */
1151                 if ((gps->flag & GP_STROKE_SELECT) == 0)
1152                         continue;
1153                 
1154                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1155                         if (pt->flag & GP_SPOINT_SELECT) {
1156                                 add_v3_v3(centroid, &pt->x);
1157                                 minmax_v3v3_v3(min, max, &pt->x);
1158                                 count++;
1159                         }
1160                 }
1161         }
1162         CTX_DATA_END;
1163         
1164         if (v3d->around == V3D_AROUND_CENTER_MEAN) {
1165                 mul_v3_fl(centroid, 1.0f / (float)count);
1166                 copy_v3_v3(cursor, centroid);
1167         }
1168         else {
1169                 mid_v3_v3v3(cursor, min, max);
1170         }
1171
1172         
1173         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1174         return OPERATOR_FINISHED;
1175 }
1176
1177 void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
1178 {
1179         /* identifiers */
1180         ot->name = "Snap Cursor to Selected Points";
1181         ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
1182         ot->description = "Snap cursor to center of selected points";
1183         
1184         /* callbacks */
1185         ot->exec = gp_snap_cursor_to_sel;
1186         ot->poll = gp_snap_poll;
1187         
1188         /* flags */
1189         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1190 }
1191
1192
1193 /* ************************************************ */