Fix T43552: Cannot jump between GP keyframes in dopesheet
[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
26 /** \file blender/editors/gpencil/gpencil_edit.c
27  *  \ingroup edgpencil
28  */
29
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stddef.h>
35 #include <math.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLI_math.h"
40 #include "BLI_blenlib.h"
41 #include "BLI_rand.h"
42 #include "BLI_utildefines.h"
43
44 #include "BLF_translation.h"
45
46 #include "DNA_anim_types.h"
47 #include "DNA_curve_types.h"
48 #include "DNA_object_types.h"
49 #include "DNA_node_types.h"
50 #include "DNA_scene_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_view3d_types.h"
54 #include "DNA_gpencil_types.h"
55
56 #include "BKE_context.h"
57 #include "BKE_curve.h"
58 #include "BKE_depsgraph.h"
59 #include "BKE_fcurve.h"
60 #include "BKE_global.h"
61 #include "BKE_gpencil.h"
62 #include "BKE_library.h"
63 #include "BKE_object.h"
64 #include "BKE_report.h"
65 #include "BKE_scene.h"
66 #include "BKE_screen.h"
67 #include "BKE_tracking.h"
68
69 #include "UI_interface.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73
74 #include "RNA_access.h"
75 #include "RNA_define.h"
76
77 #include "UI_view2d.h"
78
79 #include "ED_gpencil.h"
80 #include "ED_view3d.h"
81 #include "ED_clip.h"
82 #include "ED_keyframing.h"
83
84 #include "gpencil_intern.h"
85
86
87 /* ************************************************ */
88 /* Context Wrangling... */
89
90 /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it,
91  * when context info is not available.
92  */
93 bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr)
94 {
95         /* if there's an active area, check if the particular editor may
96          * have defined any special Grease Pencil context for editing...
97          */
98         if (sa) {
99                 SpaceLink *sl = sa->spacedata.first;
100                 
101                 switch (sa->spacetype) {
102                         case SPACE_VIEW3D: /* 3D-View */
103                         case SPACE_TIME:   /* Timeline - XXX: this is a hack to get it to show GP keyframes for 3D view */
104                         case SPACE_ACTION: /* DepeSheet - XXX: this is a hack to get the keyframe jump operator to take GP Keyframes into account */
105                         {
106                                 BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src,
107                                                          GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT));
108                                 
109                                 if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) {
110                                         /* legacy behaviour for usage with old addons requiring object-linked to objects */
111                                         
112                                         /* just in case no active/selected object... */
113                                         if (ob && (ob->flag & SELECT)) {
114                                                 /* for now, as long as there's an object, default to using that in 3D-View */
115                                                 if (ptr) RNA_id_pointer_create(&ob->id, ptr);
116                                                 return &ob->gpd;
117                                         }
118                                         /* else: defaults to scene... */
119                                 }
120                                 else {
121                                         if (ptr) RNA_id_pointer_create(&scene->id, ptr);
122                                         return &scene->gpd;
123                                 }
124                                 break;
125                         }
126                         case SPACE_NODE: /* Nodes Editor */
127                         {
128                                 SpaceNode *snode = (SpaceNode *)sl;
129                                 
130                                 /* return the GP data for the active node block/node */
131                                 if (snode && snode->nodetree) {
132                                         /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */
133                                         if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr);
134                                         return &snode->nodetree->gpd;
135                                 }
136                                 
137                                 /* even when there is no node-tree, don't allow this to flow to scene */
138                                 return NULL;
139                         }
140                         case SPACE_SEQ: /* Sequencer */
141                         {
142                                 SpaceSeq *sseq = (SpaceSeq *)sl;
143                         
144                                 /* for now, Grease Pencil data is associated with the space (actually preview region only) */
145                                 /* XXX our convention for everything else is to link to data though... */
146                                 if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr);
147                                 return &sseq->gpd;
148                         }
149                         case SPACE_IMAGE: /* Image/UV Editor */
150                         {
151                                 SpaceImage *sima = (SpaceImage *)sl;
152                                 
153                                 /* for now, Grease Pencil data is associated with the space... */
154                                 /* XXX our convention for everything else is to link to data though... */
155                                 if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr);
156                                 return &sima->gpd;
157                         }
158                         case SPACE_CLIP: /* Nodes Editor */
159                         {
160                                 SpaceClip *sc = (SpaceClip *)sl;
161                                 MovieClip *clip = ED_space_clip_get_clip(sc);
162                                 
163                                 if (clip) {
164                                         if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) {
165                                                 MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking);
166                                                 
167                                                 if (!track)
168                                                         return NULL;
169                                                 
170                                                 if (ptr)
171                                                         RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr);
172                                                 
173                                                 return &track->gpd;
174                                         }
175                                         else {
176                                                 if (ptr)
177                                                         RNA_id_pointer_create(&clip->id, ptr);
178                                                 
179                                                 return &clip->gpd;
180                                         }
181                                 }
182                                 break;
183                         }
184                         default: /* unsupported space */
185                                 return NULL;
186                 }
187         }
188         
189         /* just fall back on the scene's GP data */
190         if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
191         return (scene) ? &scene->gpd : NULL;
192 }
193
194 /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
195 bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
196 {
197         ID *screen_id = (ID *)CTX_wm_screen(C);
198         Scene *scene = CTX_data_scene(C);
199         ScrArea *sa = CTX_wm_area(C);
200         Object *ob = CTX_data_active_object(C);
201         
202         return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr);
203 }
204
205 /* -------------------------------------------------------- */
206
207 /* Get the active Grease Pencil datablock, when context is not available */
208 bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob)
209 {
210         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL);
211         return (gpd_ptr) ? *(gpd_ptr) : NULL;
212 }
213
214 /* Get the active Grease Pencil datablock */
215 bGPdata *ED_gpencil_data_get_active(const bContext *C)
216 {
217         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
218         return (gpd_ptr) ? *(gpd_ptr) : NULL;
219 }
220
221 /* -------------------------------------------------------- */
222
223 // XXX: this should be removed... We really shouldn't duplicate logic like this!
224 bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, View3D *v3d)
225 {
226         Base *base = scene->basact;
227         bGPdata *gpd = NULL;
228         /* We have to make sure active object is actually visible and selected, else we must use default scene gpd,
229          * to be consistent with ED_gpencil_data_get_active's behavior.
230          */
231         
232         if (base && TESTBASE(v3d, base)) {
233                 gpd = base->object->gpd;
234         }
235         return gpd ? gpd : scene->gpd;
236 }
237
238 /* ************************************************ */
239 /* Panel Operators */
240
241 /* poll callback for adding data/layers - special */
242 static int gp_add_poll(bContext *C)
243 {
244         /* the base line we have is that we have somewhere to add Grease Pencil data */
245         return ED_gpencil_data_get_pointers(C, NULL) != NULL;
246 }
247
248 /* poll callback for checking if there is an active layer */
249 static int gp_active_layer_poll(bContext *C)
250 {
251         bGPdata *gpd = ED_gpencil_data_get_active(C);
252         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
253         
254         return (gpl != NULL);
255 }
256
257 /* ******************* Add New Data ************************ */
258
259 /* add new datablock - wrapper around API */
260 static int gp_data_add_exec(bContext *C, wmOperator *op)
261 {
262         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
263         
264         if (gpd_ptr == NULL) {
265                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
266                 return OPERATOR_CANCELLED;
267         }
268         else {
269                 /* decrement user count and add new datablock */
270                 bGPdata *gpd = (*gpd_ptr);
271                 
272                 id_us_min(&gpd->id);
273                 *gpd_ptr = gpencil_data_addnew(DATA_("GPencil"));
274         }
275         
276         /* notifiers */
277         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
278         
279         return OPERATOR_FINISHED;
280 }
281
282 void GPENCIL_OT_data_add(wmOperatorType *ot)
283 {
284         /* identifiers */
285         ot->name = "Grease Pencil Add New";
286         ot->idname = "GPENCIL_OT_data_add";
287         ot->description = "Add new Grease Pencil datablock";
288         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
289         
290         /* callbacks */
291         ot->exec = gp_data_add_exec;
292         ot->poll = gp_add_poll;
293 }
294
295 /* ******************* Unlink Data ************************ */
296
297 /* poll callback for adding data/layers - special */
298 static int gp_data_unlink_poll(bContext *C)
299 {
300         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
301         
302         /* if we have access to some active data, make sure there's a datablock before enabling this */
303         return (gpd_ptr && *gpd_ptr);
304 }
305
306
307 /* unlink datablock - wrapper around API */
308 static int gp_data_unlink_exec(bContext *C, wmOperator *op)
309 {
310         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
311         
312         if (gpd_ptr == NULL) {
313                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
314                 return OPERATOR_CANCELLED;
315         }
316         else {
317                 /* just unlink datablock now, decreasing its user count */
318                 bGPdata *gpd = (*gpd_ptr);
319
320                 id_us_min(&gpd->id);
321                 *gpd_ptr = NULL;
322         }
323         
324         /* notifiers */
325         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
326         
327         return OPERATOR_FINISHED;
328 }
329
330 void GPENCIL_OT_data_unlink(wmOperatorType *ot)
331 {
332         /* identifiers */
333         ot->name = "Grease Pencil Unlink";
334         ot->idname = "GPENCIL_OT_data_unlink";
335         ot->description = "Unlink active Grease Pencil datablock";
336         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
337         
338         /* callbacks */
339         ot->exec = gp_data_unlink_exec;
340         ot->poll = gp_data_unlink_poll;
341 }
342
343 /* ******************* Add New Layer ************************ */
344
345 /* add new layer - wrapper around API */
346 static int gp_layer_add_exec(bContext *C, wmOperator *op)
347 {
348         bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
349         
350         /* if there's no existing Grease-Pencil data there, add some */
351         if (gpd_ptr == NULL) {
352                 BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
353                 return OPERATOR_CANCELLED;
354         }
355         if (*gpd_ptr == NULL)
356                 *gpd_ptr = gpencil_data_addnew(DATA_("GPencil"));
357         
358         /* add new layer now */
359         gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), 1);
360         
361         /* notifiers */
362         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
363         
364         return OPERATOR_FINISHED;
365 }
366
367 void GPENCIL_OT_layer_add(wmOperatorType *ot)
368 {
369         /* identifiers */
370         ot->name = "Add New Layer";
371         ot->idname = "GPENCIL_OT_layer_add";
372         ot->description = "Add new Grease Pencil layer for the active Grease Pencil datablock";
373         
374         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
375         
376         /* callbacks */
377         ot->exec = gp_layer_add_exec;
378         ot->poll = gp_add_poll;
379 }
380
381 /* ******************* Remove Active Layer ************************* */
382
383 static int gp_layer_remove_exec(bContext *C, wmOperator *op)
384 {
385         bGPdata *gpd = ED_gpencil_data_get_active(C);
386         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
387         
388         /* sanity checks */
389         if (ELEM(NULL, gpd, gpl))
390                 return OPERATOR_CANCELLED;
391         
392         if (gpl->flag & GP_LAYER_LOCKED) {
393                 BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers");
394                 return OPERATOR_CANCELLED;
395         }
396         
397         /* make the layer before this the new active layer
398          * - use the one after if this is the first
399          * - if this is the only layer, this naturally becomes NULL
400          */
401         if (gpl->prev)
402                 gpencil_layer_setactive(gpd, gpl->prev);
403         else
404                 gpencil_layer_setactive(gpd, gpl->next);
405         
406         /* delete the layer now... */
407         gpencil_layer_delete(gpd, gpl);
408         
409         /* notifiers */
410         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
411         
412         return OPERATOR_FINISHED;
413 }
414
415 void GPENCIL_OT_layer_remove(wmOperatorType *ot)
416 {
417         /* identifiers */
418         ot->name = "Remove Layer";
419         ot->idname = "GPENCIL_OT_layer_remove";
420         ot->description = "Remove active Grease Pencil layer";
421         
422         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
423         
424         /* callbacks */
425         ot->exec = gp_layer_remove_exec;
426         ot->poll = gp_active_layer_poll;
427 }
428
429 /* ******************* Move Layer Up/Down ************************** */
430
431 enum {
432         GP_LAYER_MOVE_UP   = -1,
433         GP_LAYER_MOVE_DOWN = 1
434 };
435
436 static int gp_layer_move_exec(bContext *C, wmOperator *op)
437 {
438         bGPdata *gpd = ED_gpencil_data_get_active(C);
439         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
440         
441         int direction = RNA_enum_get(op->ptr, "type");
442         
443         /* sanity checks */
444         if (ELEM(NULL, gpd, gpl))
445                 return OPERATOR_CANCELLED;
446         
447         /* up or down? */
448         if (direction == GP_LAYER_MOVE_UP) {
449                 /* up */
450                 BLI_remlink(&gpd->layers, gpl);
451                 BLI_insertlinkbefore(&gpd->layers, gpl->prev, gpl);
452         }
453         else {
454                 /* down */
455                 BLI_remlink(&gpd->layers, gpl);
456                 BLI_insertlinkafter(&gpd->layers, gpl->next, gpl);
457         }
458         
459         /* notifiers */
460         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
461         
462         return OPERATOR_FINISHED;
463 }
464
465 void GPENCIL_OT_layer_move(wmOperatorType *ot)
466 {
467         static EnumPropertyItem slot_move[] = {
468                 {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
469                 {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
470                 {0, NULL, 0, NULL, NULL}
471         };
472         
473         /* identifiers */
474         ot->name = "Move Grease Pencil Layer";
475         ot->idname = "GPENCIL_OT_layer_move";
476         ot->description = "Move the active Grease Pencil layer up/down in the list";
477         
478         /* api callbacks */
479         ot->exec = gp_layer_move_exec;
480         ot->poll = gp_active_layer_poll;
481         
482         /* flags */
483         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
484         
485         ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
486 }
487
488 /* ********************* Duplicate Layer ************************** */
489
490 static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op))
491 {
492         bGPdata *gpd = ED_gpencil_data_get_active(C);
493         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
494         bGPDlayer *new_layer;
495         
496         /* sanity checks */
497         if (ELEM(NULL, gpd, gpl))
498                 return OPERATOR_CANCELLED;
499         
500         /* make copy of layer, and add it immediately after the existing layer */
501         new_layer = gpencil_layer_duplicate(gpl);
502         BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
503         
504         /* ensure new layer has a unique name, and is now the active layer */
505         BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info));
506         gpencil_layer_setactive(gpd, new_layer);
507         
508         /* notifiers */
509         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
510         
511         return OPERATOR_FINISHED;
512 }
513
514 void GPENCIL_OT_layer_duplicate(wmOperatorType *ot)
515 {
516         /* identifiers */
517         ot->name = "Duplicate Layer";
518         ot->idname = "GPENCIL_OT_layer_duplicate";
519         ot->description = "Make a copy of the active Grease Pencil layer";
520         
521         /* callbacks */
522         ot->exec = gp_layer_copy_exec;
523         ot->poll = gp_active_layer_poll;
524         
525         /* flags */
526         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
527 }
528
529 /* ************************************************ */
530 /* Stroke Editing Operators */
531
532 /* poll callback for all stroke editing operators */
533 static int gp_stroke_edit_poll(bContext *C)
534 {
535         /* NOTE: this is a bit slower, but is the most accurate... */
536         return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
537 }
538
539 /* ************** Duplicate Selected Strokes **************** */
540
541 /* Make copies of selected point segments in a selected stroke */
542 static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
543 {
544         bGPDspoint *pt;
545         int i;
546         
547         int start_idx = -1;
548         
549         
550         /* Step through the original stroke's points:
551          * - We accumulate selected points (from start_idx to current index)
552          *   and then convert that to a new stroke
553          */
554         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
555                 /* searching for start, are waiting for end? */
556                 if (start_idx == -1) {
557                         /* is this the first selected point for a new island? */
558                         if (pt->flag & GP_SPOINT_SELECT) {
559                                 start_idx = i;
560                         }
561                 }
562                 else {
563                         size_t len = 0;
564                         
565                         /* is this the end of current island yet?
566                          * 1) Point i-1 was the last one that was selected
567                          * 2) Point i is the last in the array
568                          */
569                         if ((pt->flag & GP_SPOINT_SELECT) == 0) {
570                                 len = i - start_idx;
571                         }
572                         else if (i == gps->totpoints - 1) {
573                                 len = i - start_idx + 1;
574                         }
575                         //printf("copying from %d to %d = %d\n", start_idx, i, len);
576                 
577                         /* make copies of the relevant data */
578                         if (len) {
579                                 bGPDstroke *gpsd;
580                                 
581                                 /* make a stupid copy first of the entire stroke (to get the flags too) */
582                                 gpsd = MEM_dupallocN(gps);
583                                 
584                                 /* now, make a new points array, and copy of the relevant parts */
585                                 gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
586                                 memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
587                                 gpsd->totpoints = len;
588                                 
589                                 /* add to temp buffer */
590                                 gpsd->next = gpsd->prev = NULL;
591                                 BLI_addtail(new_strokes, gpsd);
592                                 
593                                 /* cleanup + reset for next */
594                                 start_idx = -1;
595                         }
596                 }
597         }
598 }
599
600 static int gp_duplicate_exec(bContext *C, wmOperator *op)
601 {
602         bGPdata *gpd = ED_gpencil_data_get_active(C);
603         
604         if (gpd == NULL) {
605                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
606                 return OPERATOR_CANCELLED;
607         }
608         
609         /* for each visible (and editable) layer's selected strokes,
610          * copy the strokes into a temporary buffer, then append
611          * once all done
612          */
613         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
614         {
615                 ListBase new_strokes = {NULL, NULL};
616                 bGPDframe *gpf = gpl->actframe;
617                 bGPDstroke *gps;
618                 
619                 if (gpf == NULL)
620                         continue;
621                 
622                 /* make copies of selected strokes, and deselect these once we're done */
623                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
624                         /* skip strokes that are invalid for current view */
625                         if (ED_gpencil_stroke_can_use(C, gps) == false)
626                                 continue;
627                         
628                         if (gps->flag & GP_STROKE_SELECT) {
629                                 if (gps->totpoints == 1) {
630                                         /* Special Case: If there's just a single point in this stroke... */
631                                         bGPDstroke *gpsd;
632                                         
633                                         /* make direct copies of the stroke and its points */
634                                         gpsd = MEM_dupallocN(gps);
635                                         gpsd->points = MEM_dupallocN(gps->points);
636                                         
637                                         /* add to temp buffer */
638                                         gpsd->next = gpsd->prev = NULL;
639                                         BLI_addtail(&new_strokes, gpsd);
640                                 }
641                                 else {
642                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
643                                         gp_duplicate_points(gps, &new_strokes);
644                                 }
645                                 
646                                 /* deselect original stroke, or else the originals get moved too
647                                  * (when using the copy + move macro)
648                                  */
649                                 gps->flag &= ~GP_STROKE_SELECT;
650                         }
651                 }
652                 
653                 /* add all new strokes in temp buffer to the frame (preventing double-copies) */
654                 BLI_movelisttolist(&gpf->strokes, &new_strokes);
655                 BLI_assert(new_strokes.first == NULL);
656         }
657         CTX_DATA_END;
658         
659         /* updates */
660         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
661         
662         return OPERATOR_FINISHED;
663 }
664
665 void GPENCIL_OT_duplicate(wmOperatorType *ot)
666 {
667         /* identifiers */
668         ot->name = "Duplicate Strokes";
669         ot->idname = "GPENCIL_OT_duplicate";
670         ot->description = "Duplicate the selected Grease Pencil strokes";
671         
672         /* callbacks */
673         ot->exec = gp_duplicate_exec;
674         ot->poll = gp_stroke_edit_poll;
675         
676         /* flags */
677         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
678 }
679
680 /* ******************* Copy/Paste Strokes ************************* */
681 /* Grease Pencil stroke data copy/paste buffer:
682  * - The copy operation collects all segments of selected strokes,
683  *   dumping "ready to be copied" copies of the strokes into the buffer.
684  * - The paste operation makes a copy of those elements, and adds them
685  *   to the active layer. This effectively flattens down the strokes
686  *   from several different layers into a single layer.
687  */
688
689 /* list of bGPDstroke instances */
690 static ListBase gp_strokes_copypastebuf = {NULL, NULL};
691
692 /* Free copy/paste buffer data */
693 void ED_gpencil_strokes_copybuf_free(void)
694 {
695         bGPDstroke *gps, *gpsn;
696         
697         for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
698                 gpsn = gps->next;
699                 
700                 MEM_freeN(gps->points);
701                 BLI_freelinkN(&gp_strokes_copypastebuf, gps);
702         }
703         
704         gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
705 }
706
707 /* --------------------- */
708 /* Copy selected strokes */
709
710 static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
711 {
712         bGPdata *gpd = ED_gpencil_data_get_active(C);
713         
714         if (gpd == NULL) {
715                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
716                 return OPERATOR_CANCELLED;
717         }
718         
719         /* clear the buffer first */
720         ED_gpencil_strokes_copybuf_free();
721         
722         /* for each visible (and editable) layer's selected strokes,
723          * copy the strokes into a temporary buffer, then append
724          * once all done
725          */
726         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
727         {
728                 bGPDframe *gpf = gpl->actframe;
729                 bGPDstroke *gps;
730                 
731                 if (gpf == NULL)
732                         continue;
733                 
734                 /* make copies of selected strokes, and deselect these once we're done */
735                 for (gps = gpf->strokes.first; gps; gps = gps->next) {
736                         /* skip strokes that are invalid for current view */
737                         if (ED_gpencil_stroke_can_use(C, gps) == false)
738                                 continue;
739                         
740                         if (gps->flag & GP_STROKE_SELECT) {
741                                 if (gps->totpoints == 1) {
742                                         /* Special Case: If there's just a single point in this stroke... */
743                                         bGPDstroke *gpsd;
744                                         
745                                         /* make direct copies of the stroke and its points */
746                                         gpsd = MEM_dupallocN(gps);
747                                         gpsd->points = MEM_dupallocN(gps->points);
748                                         
749                                         /* add to temp buffer */
750                                         gpsd->next = gpsd->prev = NULL;
751                                         BLI_addtail(&gp_strokes_copypastebuf, gpsd);
752                                 }
753                                 else {
754                                         /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
755                                         gp_duplicate_points(gps, &gp_strokes_copypastebuf);
756                                 }
757                         }
758                 }
759         }
760         CTX_DATA_END;
761         
762         /* done - no updates needed */
763         return OPERATOR_FINISHED;
764 }
765
766 void GPENCIL_OT_copy(wmOperatorType *ot)
767 {
768         /* identifiers */
769         ot->name = "Copy Strokes";
770         ot->idname = "GPENCIL_OT_copy";
771         ot->description = "Copy selected Grease Pencil points and strokes";
772         
773         /* callbacks */
774         ot->exec = gp_strokes_copy_exec;
775         ot->poll = gp_stroke_edit_poll;
776         
777         /* flags */
778         //ot->flag = OPTYPE_REGISTER;
779 }
780
781 /* --------------------- */
782 /* Paste selected strokes */
783
784 static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
785 {
786         Scene *scene = CTX_data_scene(C);
787         bGPdata *gpd = ED_gpencil_data_get_active(C);
788         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
789         bGPDframe *gpf;
790         
791         /* check for various error conditions */
792         if (gpd == NULL) {
793                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
794                 return OPERATOR_CANCELLED;
795         }
796         else if (gp_strokes_copypastebuf.first == NULL) {
797                 BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
798                 return OPERATOR_CANCELLED;
799         }
800         else if (gpl == NULL) {
801                 /* no active layer - let's just create one */
802                 gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), 1);
803         }
804         else if (gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) {
805                 BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
806                 return OPERATOR_CANCELLED;
807         }
808         
809         /* Deselect all strokes first */
810         CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
811         {
812                 bGPDspoint *pt;
813                 int i;
814                 
815                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
816                         pt->flag &= ~GP_SPOINT_SELECT;
817                 }
818                 
819                 gps->flag &= ~GP_STROKE_SELECT;
820         }
821         CTX_DATA_END;
822         
823         /* Ensure we have a frame to draw into
824          * NOTE: Since this is an op which creates strokes,
825          *       we are obliged to add a new frame if one
826          *       doesn't exist already
827          */
828         gpf = gpencil_layer_getframe(gpl, CFRA, true);
829         
830         if (gpf) {
831                 bGPDstroke *gps;
832                 
833                 /* Copy each stroke into the layer */
834                 for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
835                         bGPDstroke *new_stroke = MEM_dupallocN(gps);
836                         
837                         new_stroke->points = MEM_dupallocN(gps->points);
838                         new_stroke->next = new_stroke->prev = NULL;
839                         
840                         BLI_addtail(&gpf->strokes, new_stroke);
841                 }
842         }
843         
844         /* updates */
845         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
846         
847         return OPERATOR_FINISHED;
848 }
849
850 void GPENCIL_OT_paste(wmOperatorType *ot)
851 {
852         /* identifiers */
853         ot->name = "Paste Strokes";
854         ot->idname = "GPENCIL_OT_paste";
855         ot->description = "Paste previously copied strokes into active layer";
856         
857         /* callbacks */
858         ot->exec = gp_strokes_paste_exec;
859         ot->poll = gp_stroke_edit_poll;
860         
861         /* flags */
862         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
863 }
864
865 /* ******************* Delete Active Frame ************************ */
866
867 static int gp_actframe_delete_poll(bContext *C)
868 {
869         bGPdata *gpd = ED_gpencil_data_get_active(C);
870         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
871         
872         /* only if there's an active layer with an active frame */
873         return (gpl && gpl->actframe);
874 }
875
876 /* delete active frame - wrapper around API calls */
877 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
878 {
879         Scene *scene = CTX_data_scene(C);
880         bGPdata *gpd = ED_gpencil_data_get_active(C);
881         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
882         bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
883         
884         /* if there's no existing Grease-Pencil data there, add some */
885         if (gpd == NULL) {
886                 BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
887                 return OPERATOR_CANCELLED;
888         }
889         if (ELEM(NULL, gpl, gpf)) {
890                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
891                 return OPERATOR_CANCELLED;
892         }
893         
894         /* delete it... */
895         gpencil_layer_delframe(gpl, gpf);
896         
897         /* notifiers */
898         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
899         
900         return OPERATOR_FINISHED;
901 }
902
903 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
904 {
905         /* identifiers */
906         ot->name = "Delete Active Frame";
907         ot->idname = "GPENCIL_OT_active_frame_delete";
908         ot->description = "Delete the active frame for the active Grease Pencil datablock";
909         
910         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
911         
912         /* callbacks */
913         ot->exec = gp_actframe_delete_exec;
914         ot->poll = gp_actframe_delete_poll;
915 }
916
917 /* ******************* Delete Operator ************************ */
918
919 typedef enum eGP_DeleteMode {
920         /* delete selected stroke points */
921         GP_DELETEOP_POINTS          = 0,
922         /* delete selected strokes */
923         GP_DELETEOP_STROKES         = 1,
924         /* delete active frame */
925         GP_DELETEOP_FRAME           = 2,
926         /* delete selected stroke points (without splitting stroke) */
927         GP_DELETEOP_POINTS_DISSOLVE = 3,
928 } eGP_DeleteMode;
929
930
931 /* Delete selected strokes */
932 static int gp_delete_selected_strokes(bContext *C)
933 {
934         bool changed = false;
935         
936         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
937         {
938                 bGPDframe *gpf = gpl->actframe;
939                 bGPDstroke *gps, *gpsn;
940                 
941                 if (gpf == NULL)
942                         continue;
943                 
944                 /* simply delete strokes which are selected */
945                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
946                         gpsn = gps->next;
947                         
948                         /* skip strokes that are invalid for current view */
949                         if (ED_gpencil_stroke_can_use(C, gps) == false)
950                                 continue;
951                         
952                         /* free stroke if selected */
953                         if (gps->flag & GP_STROKE_SELECT) {
954                                 /* free stroke memory arrays, then stroke itself */
955                                 if (gps->points) MEM_freeN(gps->points);
956                                 BLI_freelinkN(&gpf->strokes, gps);
957                                 
958                                 changed = true;
959                         }
960                 }
961         }
962         CTX_DATA_END;
963         
964         if (changed) {
965                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
966                 return OPERATOR_FINISHED;
967         }
968         else {
969                 return OPERATOR_CANCELLED;
970         }
971 }
972
973 /* Delete selected points but keep the stroke */
974 static int gp_dissolve_selected_points(bContext *C)
975 {
976         bool changed = false;
977         
978         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
979         {
980                 bGPDframe *gpf = gpl->actframe;
981                 bGPDstroke *gps, *gpsn;
982                 
983                 if (gpf == NULL)
984                         continue;
985                 
986                 /* simply delete points from selected strokes
987                  * NOTE: we may still have to remove the stroke if it ends up having no points!
988                  */
989                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
990                         gpsn = gps->next;
991                         
992                         /* skip strokes that are invalid for current view */
993                         if (ED_gpencil_stroke_can_use(C, gps) == false)
994                                 continue;
995                         
996                         if (gps->flag & GP_STROKE_SELECT) {
997                                 bGPDspoint *pt;
998                                 int i;
999                                 
1000                                 int tot = gps->totpoints; /* number of points in new buffer */
1001                                 
1002                                 /* First Pass: Count how many points are selected (i.e. how many to remove) */
1003                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1004                                         if (pt->flag & GP_SPOINT_SELECT) {
1005                                                 /* selected point - one of the points to remove */
1006                                                 tot--;
1007                                         }
1008                                 }
1009                                 
1010                                 /* if no points are left, we simply delete the entire stroke */
1011                                 if (tot <= 0) {
1012                                         /* remove the entire stroke */
1013                                         MEM_freeN(gps->points);
1014                                         BLI_freelinkN(&gpf->strokes, gps);
1015                                 }
1016                                 else {
1017                                         /* just copy all unselected into a smaller buffer */
1018                                         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
1019                                         bGPDspoint *npt        = new_points;
1020                                         
1021                                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1022                                                 if ((pt->flag & GP_SPOINT_SELECT) == 0) {
1023                                                         *npt = *pt;
1024                                                         npt++;
1025                                                 }
1026                                         }
1027                                         
1028                                         /* free the old buffer */
1029                                         MEM_freeN(gps->points);
1030                                         
1031                                         /* save the new buffer */
1032                                         gps->points = new_points;
1033                                         gps->totpoints = tot;
1034                                         
1035                                         /* deselect the stroke, since none of its selected points will still be selected */
1036                                         gps->flag &= ~GP_STROKE_SELECT;
1037                                 }
1038                                 
1039                                 changed = true;
1040                         }
1041                 }
1042         }
1043         CTX_DATA_END;
1044         
1045         if (changed) {
1046                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1047                 return OPERATOR_FINISHED;
1048         }
1049         else {
1050                 return OPERATOR_CANCELLED;
1051         }
1052 }
1053
1054 /* Split selected strokes into segments, splitting on selected points */
1055 static int gp_delete_selected_points(bContext *C)
1056 {
1057         bool changed = false;
1058         
1059         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1060         {
1061                 bGPDframe *gpf = gpl->actframe;
1062                 bGPDstroke *gps, *gpsn;
1063                 
1064                 if (gpf == NULL)
1065                         continue;
1066                 
1067                 /* simply delete strokes which are selected */
1068                 for (gps = gpf->strokes.first; gps; gps = gpsn) {
1069                         gpsn = gps->next;
1070                         
1071                         /* skip strokes that are invalid for current view */
1072                         if (ED_gpencil_stroke_can_use(C, gps) == false)
1073                                 continue;
1074                         
1075                         
1076                         if (gps->flag & GP_STROKE_SELECT) {
1077                                 bGPDspoint *pt;
1078                                 int i;
1079                                 
1080                                 /* The algorithm used here is as follows:
1081                                  * 1) We firstly identify the number of "islands" of non-selected points
1082                                  *    which will all end up being in new strokes.
1083                                  *    - In the most extreme case (i.e. every other vert is a 1-vert island),
1084                                  *      we have at most n / 2 islands
1085                                  *    - Once we start having larger islands than that, the number required
1086                                  *      becomes much less
1087                                  * 2) Each island gets converted to a new stroke
1088                                  */
1089                                 typedef struct tGPDeleteIsland {
1090                                         int start_idx;
1091                                         int end_idx;
1092                                 } tGPDeleteIsland;
1093                                 
1094                                 tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
1095                                 bool in_island  = false;
1096                                 int num_islands = 0;
1097                                 
1098                                 /* First Pass: Identify start/end of islands */
1099                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1100                                         if (pt->flag & GP_SPOINT_SELECT) {
1101                                                 /* selected - stop accumulating to island */
1102                                                 in_island = false;
1103                                         }
1104                                         else {
1105                                                 /* unselected - start of a new island? */
1106                                                 int idx;
1107                                                 
1108                                                 if (in_island) {
1109                                                         /* extend existing island */
1110                                                         idx = num_islands - 1;
1111                                                         islands[idx].end_idx = i;
1112                                                 }
1113                                                 else {
1114                                                         /* start of new island */
1115                                                         in_island = true;
1116                                                         num_islands++;
1117                                                         
1118                                                         idx = num_islands - 1;
1119                                                         islands[idx].start_idx = islands[idx].end_idx = i;
1120                                                 }
1121                                         }
1122                                 }
1123                                 
1124                                 /* Watch out for special case where No islands = All points selected = Delete Stroke only */
1125                                 if (num_islands) {
1126                                         /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
1127                                         int idx;
1128                                         
1129                                         /* deselect old stroke, since it will be used as template for the new strokes */
1130                                         gps->flag &= ~GP_STROKE_SELECT;
1131                                         
1132                                         /* create each new stroke... */
1133                                         for (idx = 0; idx < num_islands; idx++) {
1134                                                 tGPDeleteIsland *island = &islands[idx];
1135                                                 bGPDstroke *new_stroke  = MEM_dupallocN(gps);
1136                                                 
1137                                                 /* compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
1138                                                 new_stroke->totpoints = island->end_idx - island->start_idx + 1;
1139                                                 new_stroke->points    = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
1140                                                 
1141                                                 /* copy over the relevant points */
1142                                                 memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
1143                                                 
1144                                                 /* add new stroke to the frame */
1145                                                 if (gpsn) {
1146                                                         BLI_insertlinkbefore(&gpf->strokes, gpsn, new_stroke);
1147                                                 }
1148                                                 else {
1149                                                         BLI_addtail(&gpf->strokes, new_stroke);
1150                                                 }
1151                                         }
1152                                 }
1153                                 
1154                                 /* free islands */
1155                                 MEM_freeN(islands);
1156                                 
1157                                 /* Delete the old stroke */
1158                                 MEM_freeN(gps->points);
1159                                 BLI_freelinkN(&gpf->strokes, gps);
1160                                 
1161                                 changed = true;
1162                         }
1163                 }
1164         }
1165         CTX_DATA_END;
1166         
1167         if (changed) {
1168                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1169                 return OPERATOR_FINISHED;
1170         }
1171         else {
1172                 return OPERATOR_CANCELLED;
1173         }
1174 }
1175
1176
1177 static int gp_delete_exec(bContext *C, wmOperator *op)
1178 {
1179         eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
1180         int result = OPERATOR_CANCELLED;
1181         
1182         switch (mode) {
1183                 case GP_DELETEOP_STROKES:       /* selected strokes */
1184                         result = gp_delete_selected_strokes(C);
1185                         break;
1186                 
1187                 case GP_DELETEOP_POINTS:        /* selected points (breaks the stroke into segments) */
1188                         result = gp_delete_selected_points(C);
1189                         break;
1190                 
1191                 case GP_DELETEOP_POINTS_DISSOLVE: /* selected points (without splitting the stroke) */
1192                         result = gp_dissolve_selected_points(C);
1193                         break;
1194                 
1195                 case GP_DELETEOP_FRAME:         /* active frame */
1196                         result = gp_actframe_delete_exec(C, op);
1197                         break;
1198         }
1199         
1200         return result;
1201 }
1202
1203 void GPENCIL_OT_delete(wmOperatorType *ot)
1204 {
1205         static EnumPropertyItem prop_gpencil_delete_types[] = {
1206                 {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
1207                 {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
1208                 {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
1209                 {0, "", 0, NULL, NULL},
1210                 {GP_DELETEOP_POINTS_DISSOLVE, "DISSOLVE_POINTS", 0, "Dissolve Points",
1211                                               "Delete selected points without splitting strokes"},
1212                 {0, NULL, 0, NULL, NULL}
1213         };
1214         
1215         /* identifiers */
1216         ot->name = "Delete...";
1217         ot->idname = "GPENCIL_OT_delete";
1218         ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
1219         
1220         /* callbacks */
1221         ot->invoke = WM_menu_invoke;
1222         ot->exec = gp_delete_exec;
1223         ot->poll = gp_stroke_edit_poll;
1224         
1225         /* flags */
1226         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
1227         
1228         /* props */
1229         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
1230 }
1231
1232 /* ************************************************ */
1233 /* Grease Pencil to Data Operator */
1234
1235 /* defines for possible modes */
1236 enum {
1237         GP_STROKECONVERT_PATH = 1,
1238         GP_STROKECONVERT_CURVE,
1239         GP_STROKECONVERT_POLY,
1240 };
1241
1242 /* Defines for possible timing modes */
1243 enum {
1244         GP_STROKECONVERT_TIMING_NONE = 1,
1245         GP_STROKECONVERT_TIMING_LINEAR = 2,
1246         GP_STROKECONVERT_TIMING_FULL = 3,
1247         GP_STROKECONVERT_TIMING_CUSTOMGAP = 4,
1248 };
1249
1250 /* RNA enum define */
1251 static EnumPropertyItem prop_gpencil_convertmodes[] = {
1252         {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
1253         {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
1254         {GP_STROKECONVERT_POLY, "POLY", 0, "Polygon Curve", ""},
1255         {0, NULL, 0, NULL, NULL}
1256 };
1257
1258 static EnumPropertyItem prop_gpencil_convert_timingmodes_restricted[] = {
1259         {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"},
1260         {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"},
1261         {0, NULL, 0, NULL, NULL},
1262 };
1263
1264 static EnumPropertyItem prop_gpencil_convert_timingmodes[] = {
1265         {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"},
1266         {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"},
1267         {GP_STROKECONVERT_TIMING_FULL, "FULL", 0, "Original", "Use the original timing, gaps included"},
1268         {GP_STROKECONVERT_TIMING_CUSTOMGAP, "CUSTOMGAP", 0, "Custom Gaps",
1269                                             "Use the original timing, but with custom gap lengths (in frames)"},
1270         {0, NULL, 0, NULL, NULL},
1271 };
1272
1273 static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop),
1274                                                   bool *UNUSED(r_free))
1275 {
1276         if (RNA_boolean_get(ptr, "use_timing_data")) {
1277                 return prop_gpencil_convert_timingmodes;
1278         }
1279         return prop_gpencil_convert_timingmodes_restricted;
1280 }
1281
1282 /* --- */
1283
1284 /* convert the coordinates from the given stroke point into 3d-coordinates
1285  *      - assumes that the active space is the 3D-View
1286  */
1287 static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
1288 {
1289         Scene *scene = CTX_data_scene(C);
1290         View3D *v3d = CTX_wm_view3d(C);
1291         ARegion *ar = CTX_wm_region(C);
1292         
1293         if (gps->flag & GP_STROKE_3DSPACE) {
1294                 /* directly use 3d-coordinates */
1295                 copy_v3_v3(p3d, &pt->x);
1296         }
1297         else {
1298                 const float *fp = ED_view3d_cursor3d_get(scene, v3d);
1299                 float mvalf[2];
1300                 
1301                 /* get screen coordinate */
1302                 if (gps->flag & GP_STROKE_2DSPACE) {
1303                         View2D *v2d = &ar->v2d;
1304                         UI_view2d_view_to_region_fl(v2d, pt->x, pt->y, &mvalf[0], &mvalf[1]);
1305                 }
1306                 else {
1307                         if (subrect) {
1308                                 mvalf[0] = (((float)pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin;
1309                                 mvalf[1] = (((float)pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin;
1310                         }
1311                         else {
1312                                 mvalf[0] = (float)pt->x / 100.0f * ar->winx;
1313                                 mvalf[1] = (float)pt->y / 100.0f * ar->winy;
1314                         }
1315                 }
1316                 
1317                 ED_view3d_win_to_3d(ar, fp, mvalf, p3d);
1318         }
1319 }
1320
1321 /* --- */
1322
1323 /* temp struct for gp_stroke_path_animation() */
1324 typedef struct tGpTimingData {
1325         /* Data set from operator settings */
1326         int mode;
1327         int frame_range; /* Number of frames evaluated for path animation */
1328         int start_frame, end_frame;
1329         bool realtime; /* Will overwrite end_frame in case of Original or CustomGap timing... */
1330         float gap_duration, gap_randomness; /* To be used with CustomGap mode*/
1331         int seed;
1332         
1333         /* Data set from points, used to compute final timing FCurve */
1334         int num_points, cur_point;
1335         
1336         /* Distances */
1337         float *dists;
1338         float tot_dist;
1339         
1340         /* Times */
1341         float *times; /* Note: Gap times will be negative! */
1342         float tot_time, gap_tot_time;
1343         double inittime;
1344         
1345         /* Only used during creation of dists & times lists. */
1346         float offset_time;
1347 } tGpTimingData;
1348
1349 /* Init point buffers for timing data.
1350  * Note this assumes we only grow those arrays!
1351  */
1352 static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr)
1353 {
1354         float *tmp;
1355         
1356         BLI_assert(nbr > gtd->num_points);
1357         
1358         /* distances */
1359         tmp = gtd->dists;
1360         gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__);
1361         if (tmp) {
1362                 memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points);
1363                 MEM_freeN(tmp);
1364         }
1365         
1366         /* times */
1367         tmp = gtd->times;
1368         gtd->times = MEM_callocN(sizeof(float) * nbr, __func__);
1369         if (tmp) {
1370                 memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points);
1371                 MEM_freeN(tmp);
1372         }
1373         
1374         gtd->num_points = nbr;
1375 }
1376
1377 /* add stroke point to timing buffers */
1378 static void gp_timing_data_add_point(tGpTimingData *gtd, const double stroke_inittime, const float time,
1379                                      const float delta_dist)
1380 {
1381         float delta_time = 0.0f;
1382         const int cur_point = gtd->cur_point;
1383         
1384         if (!cur_point) {
1385                 /* Special case, first point, if time is not 0.0f we have to compensate! */
1386                 gtd->offset_time = -time;
1387                 gtd->times[cur_point] = 0.0f;
1388         }
1389         else if (time < 0.0f) {
1390                 /* This is a gap, negative value! */
1391                 gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time);
1392                 delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1];
1393                 
1394                 gtd->gap_tot_time += delta_time;
1395         }
1396         else {
1397                 gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time);
1398                 delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]);
1399         }
1400         
1401         gtd->tot_time += delta_time;
1402         gtd->tot_dist += delta_dist;
1403         gtd->dists[cur_point] = gtd->tot_dist;
1404         
1405         gtd->cur_point++;
1406 }
1407
1408 /* In frames! Binary search for FCurve keys have a threshold of 0.01, so we can't set
1409  * arbitrarily close points - this is esp. important with NoGaps mode!
1410  */
1411 #define MIN_TIME_DELTA 0.02f
1412
1413 /* Loop over next points to find the end of the stroke, and compute */
1414 static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, RNG *rng, const int idx, const int nbr_gaps,
1415                                      int *nbr_done_gaps, const float tot_gaps_time, const float delta_time,
1416                                      float *next_delta_time)
1417 {
1418         int j;
1419         
1420         for (j = idx + 1; j < gtd->num_points; j++) {
1421                 if (gtd->times[j] < 0) {
1422                         gtd->times[j] = -gtd->times[j];
1423                         if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
1424                                 /* In this mode, gap time between this stroke and the next should be 0 currently...
1425                                  * So we have to compute its final duration!
1426                                  */
1427                                 if (gtd->gap_randomness > 0.0f) {
1428                                         /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range,
1429                                          * and which sum to exactly tot_gaps_time...
1430                                          */
1431                                         int rem_gaps = nbr_gaps - (*nbr_done_gaps);
1432                                         if (rem_gaps < 2) {
1433                                                 /* Last gap, just give remaining time! */
1434                                                 *next_delta_time = tot_gaps_time;
1435                                         }
1436                                         else {
1437                                                 float delta, min, max;
1438                                                 
1439                                                 /* This code ensures that if the first gaps have been shorter than average gap_duration,
1440                                                  * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa!
1441                                                  */
1442                                                 delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps));
1443                                                 
1444                                                 /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */
1445                                                 min = -gtd->gap_randomness - delta;
1446                                                 CLAMP(min, -gtd->gap_randomness, 0.0f);
1447                                                 
1448                                                 /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */
1449                                                 max = gtd->gap_randomness - delta;
1450                                                 CLAMP(max, 0.0f, gtd->gap_randomness);
1451                                                 *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min;
1452                                         }
1453                                 }
1454                                 else {
1455                                         *next_delta_time += gtd->gap_duration;
1456                                 }
1457                         }
1458                         (*nbr_done_gaps)++;
1459                         break;
1460                 }
1461         }
1462         
1463         return j - 1;
1464 }
1465
1466 static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, RNG *rng, int *nbr_gaps, float *tot_gaps_time)
1467 {
1468         int i;
1469         float delta_time = 0.0f;
1470         
1471         for (i = 0; i < gtd->num_points; i++) {
1472                 if (gtd->times[i] < 0 && i) {
1473                         (*nbr_gaps)++;
1474                         gtd->times[i] = -gtd->times[i] - delta_time;
1475                         delta_time += gtd->times[i] - gtd->times[i - 1];
1476                         gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */
1477                 }
1478                 else {
1479                         gtd->times[i] -= delta_time;
1480                 }
1481         }
1482         gtd->tot_time -= delta_time;
1483         
1484         *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration;
1485         gtd->tot_time += *tot_gaps_time;
1486         if (G.debug & G_DEBUG) {
1487                 printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *tot_gaps_time, *nbr_gaps);
1488         }
1489         if (gtd->gap_randomness > 0.0f) {
1490                 BLI_rng_srandom(rng, gtd->seed);
1491         }
1492 }
1493
1494 static void gp_stroke_path_animation_add_keyframes(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu,
1495                                                    Curve *cu, tGpTimingData *gtd, RNG *rng, const float time_range,
1496                                                    const int nbr_gaps, const float tot_gaps_time)
1497 {
1498         /* Use actual recorded timing! */
1499         const float time_start = (float)gtd->start_frame;
1500         
1501         float last_valid_time = 0.0f;
1502         int end_stroke_idx = -1, start_stroke_idx = 0;
1503         float end_stroke_time = 0.0f;
1504         
1505         /* CustomGaps specific */
1506         float delta_time = 0.0f, next_delta_time = 0.0f;
1507         int nbr_done_gaps = 0;
1508         
1509         int i;
1510         float cfra;
1511         
1512         /* This is a bit tricky, as:
1513          * - We can't add arbitrarily close points on FCurve (in time).
1514          * - We *must* have all "caps" points of all strokes in FCurve, as much as possible!
1515          */
1516         for (i = 0; i < gtd->num_points; i++) {
1517                 /* If new stroke... */
1518                 if (i > end_stroke_idx) {
1519                         start_stroke_idx = i;
1520                         delta_time = next_delta_time;
1521                         /* find end of that new stroke */
1522                         end_stroke_idx = gp_find_end_of_stroke_idx(gtd, rng, i, nbr_gaps, &nbr_done_gaps,
1523                                                                    tot_gaps_time, delta_time, &next_delta_time);
1524                         /* This one should *never* be negative! */
1525                         end_stroke_time = time_start + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range);
1526                 }
1527                 
1528                 /* Simple proportional stuff... */
1529                 cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen;
1530                 cfra = time_start + ((gtd->times[i] + delta_time) / gtd->tot_time * time_range);
1531                 
1532                 /* And now, the checks about timing... */
1533                 if (i == start_stroke_idx) {
1534                         /* If first point of a stroke, be sure it's enough ahead of last valid keyframe, and
1535                          * that the end point of the stroke is far enough!
1536                          * In case it is not, we keep the end point...
1537                          * Note that with CustomGaps mode, this is here we set the actual gap timing!
1538                          */
1539                         if ((end_stroke_time - last_valid_time) > MIN_TIME_DELTA * 2) {
1540                                 if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
1541                                         cfra = last_valid_time + MIN_TIME_DELTA;
1542                                 }
1543                                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST);
1544                                 last_valid_time = cfra;
1545                         }
1546                         else if (G.debug & G_DEBUG) {
1547                                 printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx);
1548                         }
1549                 }
1550                 else if (i == end_stroke_idx) {
1551                         /* Always try to insert end point of a curve (should be safe enough, anyway...) */
1552                         if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
1553                                 cfra = last_valid_time + MIN_TIME_DELTA;
1554                         }
1555                         insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST);
1556                         last_valid_time = cfra;
1557                 }
1558                 else {
1559                         /* Else ("middle" point), we only insert it if it's far enough from last keyframe,
1560                          * and also far enough from (not yet added!) end_stroke keyframe!
1561                          */
1562                         if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) {
1563                                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST);
1564                                 last_valid_time = cfra;
1565                         }
1566                         else if (G.debug & G_DEBUG) {
1567                                 printf("\t Skipping \"middle\" point %d, too close from last added point or end point %d\n",
1568                                        i, end_stroke_idx);
1569                         }
1570                 }
1571         }
1572 }
1573
1574 static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu, tGpTimingData *gtd)
1575 {
1576         Scene *scene = CTX_data_scene(C);
1577         bAction *act;
1578         FCurve *fcu;
1579         PointerRNA ptr;
1580         PropertyRNA *prop = NULL;
1581         int nbr_gaps = 0, i;
1582         
1583         if (gtd->mode == GP_STROKECONVERT_TIMING_NONE)
1584                 return;
1585         
1586         /* gap_duration and gap_randomness are in frames, but we need seconds!!! */
1587         gtd->gap_duration = FRA2TIME(gtd->gap_duration);
1588         gtd->gap_randomness = FRA2TIME(gtd->gap_randomness);
1589         
1590         /* Enable path! */
1591         cu->flag |= CU_PATH;
1592         cu->pathlen = gtd->frame_range;
1593         
1594         /* Get RNA pointer to read/write path time values */
1595         RNA_id_pointer_create((ID *)cu, &ptr);
1596         prop = RNA_struct_find_property(&ptr, "eval_time");
1597         
1598         /* Ensure we have an F-Curve to add keyframes to */
1599         act = verify_adt_action((ID *)cu, true);
1600         fcu = verify_fcurve(act, NULL, &ptr, "eval_time", 0, true);
1601         
1602         if (G.debug & G_DEBUG) {
1603                 printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time);
1604                 for (i = 0; i < gtd->num_points; i++) {
1605                         printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]);
1606                 }
1607         }
1608         
1609         if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) {
1610                 float cfra;
1611                 
1612                 /* Linear extrapolation! */
1613                 fcu->extend = FCURVE_EXTRAPOLATE_LINEAR;
1614                 
1615                 cu->ctime = 0.0f;
1616                 cfra = (float)gtd->start_frame;
1617                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST);
1618                 
1619                 cu->ctime = cu->pathlen;
1620                 if (gtd->realtime) {
1621                         cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */
1622                 }
1623                 else {
1624                         cfra = (float)gtd->end_frame;
1625                 }
1626                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST);
1627         }
1628         else {
1629                 /* Use actual recorded timing! */
1630                 RNG *rng = BLI_rng_new(0);
1631                 float time_range;
1632                 
1633                 /* CustomGaps specific */
1634                 float tot_gaps_time = 0.0f;
1635                 
1636                 /* Pre-process gaps, in case we don't want to keep their original timing */
1637                 if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
1638                         gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time);
1639                 }
1640                 
1641                 if (gtd->realtime) {
1642                         time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */
1643                 }
1644                 else {
1645                         time_range = (float)(gtd->end_frame - gtd->start_frame);
1646                 }
1647                 
1648                 if (G.debug & G_DEBUG) {
1649                         printf("GP Stroke Path Conversion: Starting keying!\n");
1650                 }
1651                 
1652                 gp_stroke_path_animation_add_keyframes(reports, ptr, prop, fcu, cu, gtd, rng, time_range,
1653                                                        nbr_gaps, tot_gaps_time);
1654                 
1655                 BLI_rng_free(rng);
1656         }
1657         
1658         /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */
1659         calchandles_fcurve(fcu);
1660         
1661         if (G.debug & G_DEBUG) {
1662                 printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time);
1663                 for (i = 0; i < gtd->num_points; i++) {
1664                         printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]);
1665                 }
1666                 printf("\n\n");
1667         }
1668         
1669         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1670         
1671         /* send updates */
1672         DAG_id_tag_update(&cu->id, 0);
1673 }
1674
1675 #undef MIN_TIME_DELTA
1676
1677 #define GAP_DFAC 0.01f
1678 #define WIDTH_CORR_FAC 0.1f
1679 #define BEZT_HANDLE_FAC 0.3f
1680
1681 /* convert stroke to 3d path */
1682
1683 /* helper */
1684 static void gp_stroke_to_path_add_point(tGpTimingData *gtd, BPoint *bp, const float p[3], const float prev_p[3],
1685                                         const bool do_gtd, const double inittime, const float time,
1686                                         const float width, const float rad_fac, float minmax_weights[2])
1687 {
1688         copy_v3_v3(bp->vec, p);
1689         bp->vec[3] = 1.0f;
1690         
1691         /* set settings */
1692         bp->f1 = SELECT;
1693         bp->radius = width * rad_fac;
1694         bp->weight = width;
1695         CLAMP(bp->weight, 0.0f, 1.0f);
1696         if (bp->weight < minmax_weights[0]) {
1697                 minmax_weights[0] = bp->weight;
1698         }
1699         else if (bp->weight > minmax_weights[1]) {
1700                 minmax_weights[1] = bp->weight;
1701         }
1702         
1703         /* Update timing data */
1704         if (do_gtd) {
1705                 gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
1706         }
1707 }
1708
1709 static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu,
1710                               float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point,
1711                               const bool add_end_point, tGpTimingData *gtd)
1712 {
1713         bGPDspoint *pt;
1714         Nurb *nu = (curnu) ? *curnu : NULL;
1715         BPoint *bp, *prev_bp = NULL;
1716         const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE);
1717         const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0);
1718         int i, old_nbp = 0;
1719         
1720         /* create new 'nurb' or extend current one within the curve */
1721         if (nu) {
1722                 old_nbp = nu->pntsu;
1723
1724                 /* If stitch, the first point of this stroke is already present in current nu.
1725                  * Else, we have to add two additional points to make the zero-radius link between strokes.
1726                  */
1727                 BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points);
1728         }
1729         else {
1730                 nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
1731                 
1732                 nu->pntsu = gps->totpoints + add_start_end_points;
1733                 nu->pntsv = 1;
1734                 nu->orderu = 2; /* point-to-point! */
1735                 nu->type = CU_NURBS;
1736                 nu->flagu = CU_NURB_ENDPOINT;
1737                 nu->resolu = cu->resolu;
1738                 nu->resolv = cu->resolv;
1739                 nu->knotsu = NULL;
1740                 
1741                 nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints");
1742                 
1743                 stitch = false; /* Security! */
1744         }
1745         
1746         if (do_gtd) {
1747                 gp_timing_data_set_nbr(gtd, nu->pntsu);
1748         }
1749         
1750         /* If needed, make the link between both strokes with two zero-radius additional points */
1751         /* About "zero-radius" point interpolations:
1752          * - If we have at least two points in current curve (most common case), we linearly extrapolate
1753          *   the last segment to get the first point (p1) position and timing.
1754          * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point
1755          *   with the first point of the current stroke.
1756          * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated
1757          * if it exists, else (if the stroke is a single point), linear interpolation with last curve point...
1758          */
1759         if (curnu && !stitch && old_nbp) {
1760                 float p1[3], p2[3], p[3], next_p[3];
1761                 float dt1 = 0.0f, dt2 = 0.0f;
1762                 
1763                 BLI_assert(gps->prev != NULL);
1764                 
1765                 prev_bp = NULL;
1766                 if ((old_nbp > 1) && (gps->prev->totpoints > 1)) {
1767                         /* Only use last curve segment if previous stroke was not a single-point one! */
1768                         prev_bp = &nu->bp[old_nbp - 2];
1769                 }
1770                 bp = &nu->bp[old_nbp - 1];
1771                 
1772                 /* First point */
1773                 gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
1774                 if (prev_bp) {
1775                         interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC);
1776                         if (do_gtd) {
1777                                 const int idx = gps->prev->totpoints - 1;
1778                                 dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC);
1779                         }
1780                 }
1781                 else {
1782                         interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC);
1783                         if (do_gtd) {
1784                                 dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC);
1785                         }
1786                 }
1787                 bp++;
1788                 gp_stroke_to_path_add_point(gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1,
1789                                             0.0f, rad_fac, minmax_weights);
1790                 
1791                 /* Second point */
1792                 /* Note dt2 is always negative, which marks the gap. */
1793                 if (gps->totpoints > 1) {
1794                         gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
1795                         interp_v3_v3v3(p2, p, next_p, -GAP_DFAC);
1796                         if (do_gtd) {
1797                                 dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
1798                         }
1799                 }
1800                 else {
1801                         interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC);
1802                         if (do_gtd) {
1803                                 dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC);
1804                         }
1805                 }
1806                 bp++;
1807                 gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights);
1808                 
1809                 old_nbp += 2;
1810         }
1811         else if (add_start_point) {
1812                 float p[3], next_p[3];
1813                 float dt = 0.0f;
1814                 
1815                 gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
1816                 if (gps->totpoints > 1) {
1817                         gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
1818                         interp_v3_v3v3(p, p, next_p, -GAP_DFAC);
1819                         if (do_gtd) {
1820                                 dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
1821                         }
1822                 }
1823                 else {
1824                         p[0] -= GAP_DFAC;  /* Rather arbitrary... */
1825                         dt = -GAP_DFAC;  /* Rather arbitrary too! */
1826                 }
1827                 bp = &nu->bp[old_nbp];
1828                 /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value)
1829                  * would not work (it would be *before* gtd->inittime, which is not supported currently).
1830                  */
1831                 gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
1832                 
1833                 old_nbp++;
1834         }
1835         
1836         if (old_nbp) {
1837                 prev_bp = &nu->bp[old_nbp - 1];
1838         }
1839         
1840         /* add points */
1841         for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp];
1842              i < gps->totpoints;
1843              i++, pt++, bp++)
1844         {
1845                 float p[3];
1846                 float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
1847                 
1848                 /* get coordinates to add at */
1849                 gp_strokepoint_convertcoords(C, gps, pt, p, subrect);
1850                 
1851                 gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time,
1852                                             width, rad_fac, minmax_weights);
1853                 
1854                 prev_bp = bp;
1855         }
1856
1857         if (add_end_point) {
1858                 float p[3];
1859                 float dt = 0.0f;
1860                 
1861                 if (gps->totpoints > 1) {
1862                         interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC);
1863                         if (do_gtd) {
1864                                 const int idx = gps->totpoints - 1;
1865                                 dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC);
1866                         }
1867                 }
1868                 else {
1869                         copy_v3_v3(p, prev_bp->vec);
1870                         p[0] += GAP_DFAC;  /* Rather arbitrary... */
1871                         dt = GAP_DFAC;  /* Rather arbitrary too! */
1872                 }
1873                 /* Note bp has already been incremented in main loop above, so it points to the right place. */
1874                 gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
1875         }
1876         
1877         /* add nurb to curve */
1878         if (!curnu || !*curnu) {
1879                 BLI_addtail(&cu->nurb, nu);
1880         }
1881         if (curnu) {
1882                 *curnu = nu;
1883         }
1884         
1885         BKE_nurb_knot_calc_u(nu);
1886 }
1887
1888 /* convert stroke to 3d bezier */
1889
1890 /* helper */
1891 static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, BezTriple *bezt,
1892                                           const float p[3], const float h1[3], const float h2[3], const float prev_p[3],
1893                                           const bool do_gtd, const double inittime, const float time,
1894                                           const float width, const float rad_fac, float minmax_weights[2])
1895 {
1896         copy_v3_v3(bezt->vec[0], h1);
1897         copy_v3_v3(bezt->vec[1], p);
1898         copy_v3_v3(bezt->vec[2], h2);
1899         
1900         /* set settings */
1901         bezt->h1 = bezt->h2 = HD_FREE;
1902         bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
1903         bezt->radius = width * rad_fac;
1904         bezt->weight = width;
1905         CLAMP(bezt->weight, 0.0f, 1.0f);
1906         if (bezt->weight < minmax_weights[0]) {
1907                 minmax_weights[0] = bezt->weight;
1908         }
1909         else if (bezt->weight > minmax_weights[1]) {
1910                 minmax_weights[1] = bezt->weight;
1911         }
1912         
1913         /* Update timing data */
1914         if (do_gtd) {
1915                 gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
1916         }
1917 }
1918
1919 static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu,
1920                                 float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point,
1921                                 const bool add_end_point, tGpTimingData *gtd)
1922 {
1923         bGPDspoint *pt;
1924         Nurb *nu = (curnu) ? *curnu : NULL;
1925         BezTriple *bezt, *prev_bezt = NULL;
1926         int i, tot, old_nbezt = 0;
1927         const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0);
1928         float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3];
1929         const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE);
1930         
1931         /* create new 'nurb' or extend current one within the curve */
1932         if (nu) {
1933                 old_nbezt = nu->pntsu;
1934                 /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke,
1935                  * so no need to add it.
1936                  * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes.
1937                  */
1938                 BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points);
1939         }
1940         else {
1941                 nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
1942                 
1943                 nu->pntsu = gps->totpoints + add_start_end_points;
1944                 nu->resolu = 12;
1945                 nu->resolv = 12;
1946                 nu->type = CU_BEZIER;
1947                 nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts");
1948                 
1949                 stitch = false; /* Security! */
1950         }
1951         
1952         if (do_gtd) {
1953                 gp_timing_data_set_nbr(gtd, nu->pntsu);
1954         }
1955         
1956         tot = gps->totpoints;
1957         
1958         /* get initial coordinates */
1959         pt = gps->points;
1960         if (tot) {
1961                 gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
1962                 if (tot > 1) {
1963                         gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
1964                 }
1965                 if (stitch && tot > 2) {
1966                         gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
1967                 }
1968         }
1969         
1970         /* If needed, make the link between both strokes with two zero-radius additional points */
1971         if (curnu && old_nbezt) {
1972                 BLI_assert(gps->prev != NULL);
1973                 
1974                 /* Update last point's second handle */
1975                 if (stitch) {
1976                         bezt = &nu->bezt[old_nbezt - 1];
1977                         interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC);
1978                         copy_v3_v3(bezt->vec[2], h2);
1979                         pt++;
1980                 }
1981                 
1982                 /* Create "link points" */
1983                 /* About "zero-radius" point interpolations:
1984                  * - If we have at least two points in current curve (most common case), we linearly extrapolate
1985                  *   the last segment to get the first point (p1) position and timing.
1986                  * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point
1987                  *   with the first point of the current stroke.
1988                  * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated
1989                  * if it exists, else (if the stroke is a single point), linear interpolation with last curve point...
1990                  */
1991                 else {
1992                         float p1[3], p2[3];
1993                         float dt1 = 0.0f, dt2 = 0.0f;
1994                         
1995                         prev_bezt = NULL;
1996                         if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) {
1997                                 /* Only use last curve segment if previous stroke was not a single-point one! */
1998                                 prev_bezt = &nu->bezt[old_nbezt - 2];
1999                         }
2000                         bezt = &nu->bezt[old_nbezt - 1];
2001                         
2002                         /* First point */
2003                         if (prev_bezt) {
2004                                 interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC);
2005                                 if (do_gtd) {
2006                                         const int idx = gps->prev->totpoints - 1;
2007                                         dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC);
2008                                 }
2009                         }
2010                         else {
2011                                 interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC);
2012                                 if (do_gtd) {
2013                                         dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC);
2014                                 }
2015                         }
2016                         
2017                         /* Second point */
2018                         /* Note dt2 is always negative, which marks the gap. */
2019                         if (tot > 1) {
2020                                 interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC);
2021                                 if (do_gtd) {
2022                                         dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
2023                                 }
2024                         }
2025                         else {
2026                                 interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC);
2027                                 if (do_gtd) {
2028                                         dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC);
2029                                 }
2030                         }
2031                         
2032                         /* Second handle of last point of previous stroke. */
2033                         interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC);
2034                         copy_v3_v3(bezt->vec[2], h2);
2035                         
2036                         /* First point */
2037                         interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC);
2038                         interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC);
2039                         bezt++;
2040                         gp_stroke_to_bezier_add_point(gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1,
2041                                                       0.0f, rad_fac, minmax_weights);
2042                         
2043                         /* Second point */
2044                         interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC);
2045                         interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC);
2046                         bezt++;
2047                         gp_stroke_to_bezier_add_point(gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2,
2048                                                       0.0f, rad_fac, minmax_weights);
2049                         
2050                         old_nbezt += 2;
2051                         copy_v3_v3(p3d_prev, p2);
2052                 }
2053         }
2054         else if (add_start_point) {
2055                 float p[3];
2056                 float dt = 0.0f;
2057                 
2058                 if (gps->totpoints > 1) {
2059                         interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC);
2060                         if (do_gtd) {
2061                                 dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
2062                         }
2063                 }
2064                 else {
2065                         copy_v3_v3(p, p3d_cur);
2066                         p[0] -= GAP_DFAC;  /* Rather arbitrary... */
2067                         dt = -GAP_DFAC;  /* Rather arbitrary too! */
2068                 }
2069                 interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC);
2070                 interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC);
2071                 bezt = &nu->bezt[old_nbezt];
2072                 gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt,
2073                                               0.0f, rad_fac, minmax_weights);
2074                 
2075                 old_nbezt++;
2076                 copy_v3_v3(p3d_prev, p);
2077         }
2078         
2079         if (old_nbezt) {
2080                 prev_bezt = &nu->bezt[old_nbezt - 1];
2081         }
2082         
2083         /* add points */
2084         for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) {
2085                 float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
2086                 
2087                 if (i || old_nbezt) {
2088                         interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC);
2089                 }
2090                 else {
2091                         interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC);
2092                 }
2093                 
2094                 if (i < tot - 1) {
2095                         interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC);
2096                 }
2097                 else {
2098                         interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC);
2099                 }
2100                 
2101                 gp_stroke_to_bezier_add_point(gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur,
2102                                               do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights);
2103                 
2104                 /* shift coord vects */
2105                 copy_v3_v3(p3d_prev, p3d_cur);
2106                 copy_v3_v3(p3d_cur, p3d_next);
2107                 
2108                 if (i + 2 < tot) {
2109                         gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
2110                 }
2111                 
2112                 prev_bezt = bezt;
2113         }
2114
2115         if (add_end_point) {
2116                 float p[3];
2117                 float dt = 0.0f;
2118                 
2119                 if (gps->totpoints > 1) {
2120                         interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC);
2121                         if (do_gtd) {
2122                                 const int idx = gps->totpoints - 1;
2123                                 dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC);
2124                         }
2125                 }
2126                 else {
2127                         copy_v3_v3(p, prev_bezt->vec[1]);
2128                         p[0] += GAP_DFAC;  /* Rather arbitrary... */
2129                         dt = GAP_DFAC;  /* Rather arbitrary too! */
2130                 }
2131                 
2132                 /* Second handle of last point of this stroke. */
2133                 interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC);
2134                 copy_v3_v3(prev_bezt->vec[2], h2);
2135                 
2136                 /* The end point */
2137                 interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC);
2138                 interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC);
2139                 /* Note bezt has already been incremented in main loop above, so it points to the right place. */
2140                 gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt,
2141                                               0.0f, rad_fac, minmax_weights);
2142         }
2143         
2144         /* must calculate handles or else we crash */
2145         BKE_nurb_handles_calc(nu);
2146         
2147         if (!curnu || !*curnu) {
2148                 BLI_addtail(&cu->nurb, nu);
2149         }
2150         if (curnu) {
2151                 *curnu = nu;
2152         }
2153 }
2154
2155 #undef GAP_DFAC
2156 #undef WIDTH_CORR_FAC
2157 #undef BEZT_HANDLE_FAC
2158
2159 static void gp_stroke_finalize_curve_endpoints(Curve *cu)
2160 {
2161         /* start */
2162         Nurb *nu = cu->nurb.first;
2163         int i = 0;
2164         if (nu->bezt) {
2165                 BezTriple *bezt = nu->bezt;
2166                 if (bezt) {
2167                         bezt[i].weight = bezt[i].radius = 0.0f;
2168                 }
2169         }
2170         else if (nu->bp) {
2171                 BPoint *bp = nu->bp;
2172                 if (bp) {
2173                         bp[i].weight = bp[i].radius = 0.0f;
2174                 }
2175         }
2176         
2177         /* end */
2178         nu = cu->nurb.last;
2179         i = nu->pntsu - 1;
2180         if (nu->bezt) {
2181                 BezTriple *bezt = nu->bezt;
2182                 if (bezt) {
2183                         bezt[i].weight = bezt[i].radius = 0.0f;
2184                 }
2185         }
2186         else if (nu->bp) {
2187                 BPoint *bp = nu->bp;
2188                 if (bp) {
2189                         bp[i].weight = bp[i].radius = 0.0f;
2190                 }
2191         }
2192 }
2193
2194 static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2])
2195 {
2196         Nurb *nu;
2197         const float delta = minmax_weights[0];
2198         float fac;
2199         int i;
2200         
2201         /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */
2202         if (IS_EQF(delta, minmax_weights[1]))
2203                 fac = 1.0f;
2204         else
2205                 fac = 1.0f / (minmax_weights[1] - delta);
2206         
2207         for (nu = cu->nurb.first; nu; nu = nu->next) {
2208                 if (nu->bezt) {
2209                         BezTriple *bezt = nu->bezt;
2210                         for (i = 0; i < nu->pntsu; i++, bezt++) {
2211                                 bezt->weight = (bezt->weight - delta) * fac;
2212                         }
2213                 }
2214                 else if (nu->bp) {
2215                         BPoint *bp = nu->bp;
2216                         for (i = 0; i < nu->pntsu; i++, bp++) {
2217                                 bp->weight = (bp->weight - delta) * fac;
2218                         }
2219                 }
2220         }
2221 }
2222
2223 static int gp_camera_view_subrect(bContext *C, rctf *subrect)
2224 {
2225         View3D *v3d = CTX_wm_view3d(C);
2226         ARegion *ar = CTX_wm_region(C);
2227         
2228         if (v3d) {
2229                 RegionView3D *rv3d = ar->regiondata;
2230                 
2231                 /* for camera view set the subrect */
2232                 if (rv3d->persp == RV3D_CAMOB) {
2233                         Scene *scene = CTX_data_scene(C);
2234                         ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, subrect, true); /* no shift */
2235                         return 1;
2236                 }
2237         }
2238         
2239         return 0;
2240 }
2241
2242 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
2243 static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bGPDlayer *gpl, const int mode,
2244                               const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd)
2245 {
2246         struct Main *bmain = CTX_data_main(C);
2247         View3D *v3d = CTX_wm_view3d(C);  /* may be NULL */
2248         Scene *scene = CTX_data_scene(C);
2249         bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
2250         bGPDstroke *gps, *prev_gps = NULL;
2251         Object *ob;
2252         Curve *cu;
2253         Nurb *nu = NULL;
2254         Base *base_orig = BASACT, *base_new = NULL;
2255         float minmax_weights[2] = {1.0f, 0.0f};
2256         
2257         /* camera framing */
2258         rctf subrect, *subrect_ptr = NULL;
2259         
2260         /* error checking */
2261         if (ELEM(NULL, gpd, gpl, gpf))
2262                 return;
2263         
2264         /* only convert if there are any strokes on this layer's frame to convert */
2265         if (BLI_listbase_is_empty(&gpf->strokes))
2266                 return;
2267         
2268         /* initialize camera framing */
2269         if (gp_camera_view_subrect(C, &subrect)) {
2270                 subrect_ptr = &subrect;
2271         }
2272         
2273         /* init the curve object (remove rotation and get curve data from it)
2274          *      - must clear transforms set on object, as those skew our results
2275          */
2276         ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info);
2277         cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE);
2278         base_new = BKE_scene_base_add(scene, ob);
2279         
2280         cu->flag |= CU_3D;
2281         
2282         gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime;
2283         
2284         /* add points to curve */
2285         for (gps = gpf->strokes.first; gps; gps = gps->next) {
2286                 const bool add_start_point = (link_strokes && !(prev_gps));
2287                 const bool add_end_point = (link_strokes && !(gps->next));
2288                 
2289                 /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, and stitch them to previous one. */
2290                 bool stitch = false;
2291                 if (prev_gps) {
2292                         bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1];
2293                         bGPDspoint *pt2 = &gps->points[0];
2294                         
2295                         if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) {
2296                                 stitch = true;
2297                         }
2298                 }
2299                 
2300                 /* Decide whether we connect this stroke to previous one */
2301                 if (!(stitch || link_strokes)) {
2302                         nu = NULL;
2303                 }
2304                 
2305                 switch (mode) {
2306                         case GP_STROKECONVERT_PATH:
2307                                 gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch,
2308                                                   add_start_point, add_end_point, gtd);
2309                                 break;
2310                         case GP_STROKECONVERT_CURVE:
2311                         case GP_STROKECONVERT_POLY:  /* convert after */
2312                                 gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch,
2313                                                     add_start_point, add_end_point, gtd);
2314                                 break;
2315                         default:
2316                                 BLI_assert(!"invalid mode");
2317                                 break;
2318                 }
2319                 prev_gps = gps;
2320         }
2321         
2322         /* If link_strokes, be sure first and last points have a zero weight/size! */
2323         if (link_strokes) {
2324                 gp_stroke_finalize_curve_endpoints(cu);
2325         }
2326         
2327         /* Update curve's weights, if needed */
2328         if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) {
2329                 gp_stroke_norm_curve_weights(cu, minmax_weights);
2330         }
2331         
2332         /* Create the path animation, if needed */
2333         gp_stroke_path_animation(C, reports, cu, gtd);
2334         
2335         if (mode == GP_STROKECONVERT_POLY) {
2336                 for (nu = cu->nurb.first; nu; nu = nu->next) {
2337                         BKE_nurb_type_convert(nu, CU_POLY, false);
2338                 }
2339         }
2340         
2341         /* set the layer and select */
2342         base_new->lay  = ob->lay  = base_orig ? base_orig->lay : BKE_screen_view3d_layer_active(v3d, scene);
2343         base_new->flag = ob->flag = base_new->flag | SELECT;
2344 }
2345
2346 /* --- */
2347
2348 /* Check a GP layer has valid timing data! Else, most timing options are hidden in the operator.
2349  * op may be NULL.
2350  */
2351 static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op)
2352 {
2353         Scene *scene = CTX_data_scene(C);
2354         bGPDframe *gpf = NULL;
2355         bGPDstroke *gps = NULL;
2356         bGPDspoint *pt;
2357         double base_time, cur_time, prev_time = -1.0;
2358         int i;
2359         bool valid = true;
2360         
2361         if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first))
2362                 return false;
2363         
2364         do {
2365                 base_time = cur_time = gps->inittime;
2366                 if (cur_time <= prev_time) {
2367                         valid = false;
2368                         break;
2369                 }
2370                 
2371                 prev_time = cur_time;
2372                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2373                         cur_time = base_time + (double)pt->time;
2374                         /* First point of a stroke should have the same time as stroke's inittime,
2375                          * so it's the only case where equality is allowed!
2376                          */
2377                         if ((i && cur_time <= prev_time) || (cur_time < prev_time)) {
2378                                 valid = false;
2379                                 break;
2380                         }
2381                         prev_time = cur_time;
2382                 }
2383                 
2384                 if (!valid) {
2385                         break;
2386                 }
2387         } while ((gps = gps->next));
2388         
2389         if (op) {
2390                 RNA_boolean_set(op->ptr, "use_timing_data", valid);
2391         }
2392         return valid;
2393 }
2394
2395 /* Check end_frame is always > start frame! */
2396 static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UNUSED(scene), struct PointerRNA *ptr)
2397 {
2398         int start_frame = RNA_int_get(ptr, "start_frame");
2399         int end_frame = RNA_int_get(ptr, "end_frame");
2400         
2401         if (end_frame <= start_frame) {
2402                 RNA_int_set(ptr, "end_frame", start_frame + 1);
2403         }
2404 }
2405
2406 static int gp_convert_poll(bContext *C)
2407 {
2408         bGPdata *gpd = ED_gpencil_data_get_active(C);
2409         bGPDlayer *gpl = NULL;
2410         bGPDframe *gpf = NULL;
2411         ScrArea *sa = CTX_wm_area(C);
2412         Scene *scene = CTX_data_scene(C);
2413         
2414         /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!),
2415          * and if we are not in edit mode!
2416          */
2417         return ((sa && sa->spacetype == SPACE_VIEW3D) &&
2418                 (gpl = gpencil_layer_getactive(gpd)) &&
2419                 (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) &&
2420                 (gpf->strokes.first) &&
2421                 (scene->obedit == NULL));
2422 }
2423
2424 static int gp_convert_layer_exec(bContext *C, wmOperator *op)
2425 {
2426         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data");
2427         bGPdata *gpd = ED_gpencil_data_get_active(C);
2428         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
2429         Scene *scene = CTX_data_scene(C);
2430         const int mode = RNA_enum_get(op->ptr, "type");
2431         const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights");
2432         const float rad_fac = RNA_float_get(op->ptr, "radius_multiplier");
2433         const bool link_strokes = RNA_boolean_get(op->ptr, "use_link_strokes");
2434         bool valid_timing;
2435         tGpTimingData gtd;
2436         
2437         /* check if there's data to work with */
2438         if (gpd == NULL) {
2439                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on");
2440                 return OPERATOR_CANCELLED;
2441         }
2442         
2443         if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) {
2444                 BKE_report(op->reports, RPT_WARNING,
2445                            "Current Grease Pencil strokes have no valid timing data, most timing options will be hidden!");
2446         }
2447         valid_timing = RNA_property_boolean_get(op->ptr, prop);
2448         
2449         gtd.mode = RNA_enum_get(op->ptr, "timing_mode");
2450         /* Check for illegal timing mode! */
2451         if (!valid_timing && !ELEM(gtd.mode, GP_STROKECONVERT_TIMING_NONE, GP_STROKECONVERT_TIMING_LINEAR)) {
2452                 gtd.mode = GP_STROKECONVERT_TIMING_LINEAR;
2453                 RNA_enum_set(op->ptr, "timing_mode", gtd.mode);
2454         }
2455         if (!link_strokes) {
2456                 gtd.mode = GP_STROKECONVERT_TIMING_NONE;
2457         }
2458         
2459         /* grab all relevant settings */
2460         gtd.frame_range = RNA_int_get(op->ptr, "frame_range");
2461         gtd.start_frame = RNA_int_get(op->ptr, "start_frame");
2462         gtd.realtime = valid_timing ? RNA_boolean_get(op->ptr, "use_realtime") : false;
2463         gtd.end_frame = RNA_int_get(op->ptr, "end_frame");
2464         gtd.gap_duration = RNA_float_get(op->ptr, "gap_duration");
2465         gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness");
2466         gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration);
2467         gtd.seed = RNA_int_get(op->ptr, "seed");
2468         gtd.num_points = gtd.cur_point = 0;
2469         gtd.dists = gtd.times = NULL;
2470         gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f;
2471         gtd.inittime = 0.0;
2472         gtd.offset_time = 0.0f;
2473         
2474         /* perform conversion */
2475         gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, &gtd);
2476         
2477         /* free temp memory */
2478         if (gtd.dists) {
2479                 MEM_freeN(gtd.dists);
2480                 gtd.dists = NULL;
2481         }
2482         if (gtd.times) {
2483                 MEM_freeN(gtd.times);
2484                 gtd.times = NULL;
2485         }
2486         
2487         /* notifiers */
2488         WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
2489         WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
2490         
2491         /* done */
2492         return OPERATOR_FINISHED;
2493 }
2494
2495 static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop)
2496 {
2497         const char *prop_id = RNA_property_identifier(prop);
2498         const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes");
2499         int timing_mode = RNA_enum_get(ptr, "timing_mode");
2500         bool realtime = RNA_boolean_get(ptr, "use_realtime");
2501         float gap_duration = RNA_float_get(ptr, "gap_duration");
2502         float gap_randomness = RNA_float_get(ptr, "gap_randomness");
2503         const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data");
2504         
2505         /* Always show those props */
2506         if (STREQ(prop_id, "type") ||
2507             STREQ(prop_id, "use_normalize_weights") ||
2508             STREQ(prop_id, "radius_multiplier") ||
2509             STREQ(prop_id, "use_link_strokes"))
2510         {
2511                 return true;
2512         }
2513         
2514         /* Never show this prop */
2515         if (STREQ(prop_id, "use_timing_data"))
2516                 return false;
2517         
2518         if (link_strokes) {
2519                 /* Only show when link_stroke is true */
2520                 if (STREQ(prop_id, "timing_mode"))
2521                         return true;
2522                 
2523                 if (timing_mode != GP_STROKECONVERT_TIMING_NONE) {
2524                         /* Only show when link_stroke is true and stroke timing is enabled */
2525                         if (STREQ(prop_id, "frame_range") ||
2526                             STREQ(prop_id, "start_frame"))
2527                         {
2528                                 return true;
2529                         }
2530                         
2531                         /* Only show if we have valid timing data! */
2532                         if (valid_timing && STREQ(prop_id, "use_realtime"))
2533                                 return true;
2534                         
2535                         /* Only show if realtime or valid_timing is false! */
2536                         if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame"))
2537                                 return true;
2538                         
2539                         if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
2540                                 /* Only show for custom gaps! */
2541                                 if (STREQ(prop_id, "gap_duration"))
2542                                         return true;
2543                                 
2544                                 /* Only show randomness for non-null custom gaps! */
2545                                 if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f))
2546                                         return true;
2547                                 
2548                                 /* Only show seed for randomize action! */
2549                                 if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f))
2550                                         return true;
2551                         }
2552                 }
2553         }
2554         
2555         /* Else, hidden! */
2556         return false;
2557 }
2558
2559 static void gp_convert_ui(bContext *C, wmOperator *op)
2560 {
2561         uiLayout *layout = op->layout;
2562         wmWindowManager *wm = CTX_wm_manager(C);
2563         PointerRNA ptr;
2564         
2565         RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
2566         
2567         /* Main auto-draw call */
2568         uiDefAutoButsRNA(layout, &ptr, gp_convert_draw_check_prop, '\0');
2569 }
2570
2571 void GPENCIL_OT_convert(wmOperatorType *ot)
2572 {
2573         PropertyRNA *prop;
2574         
2575         /* identifiers */
2576         ot->name = "Convert Grease Pencil";
2577         ot->idname = "GPENCIL_OT_convert";
2578         ot->description = "Convert the active Grease Pencil layer to a new Curve Object";
2579         
2580         /* callbacks */
2581         ot->invoke = WM_menu_invoke;
2582         ot->exec = gp_convert_layer_exec;
2583         ot->poll = gp_convert_poll;
2584         ot->ui = gp_convert_ui;
2585         
2586         /* flags */
2587         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2588         
2589         /* properties */
2590         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "Which type of curve to convert to");
2591         
2592         RNA_def_boolean(ot->srna, "use_normalize_weights", true, "Normalize Weight",
2593                         "Normalize weight (set from stroke width)");
2594         RNA_def_float(ot->srna, "radius_multiplier", 1.0f, 0.0f, 1000.0f, "Radius Fac",
2595                       "Multiplier for the points' radii (set from stroke width)", 0.0f, 10.0f);
2596         RNA_def_boolean(ot->srna, "use_link_strokes", true, "Link Strokes",
2597                         "Whether to link strokes with zero-radius sections of curves");
2598         
2599         prop = RNA_def_enum(ot->srna, "timing_mode", prop_gpencil_convert_timingmodes, GP_STROKECONVERT_TIMING_FULL,
2600                             "Timing Mode", "How to use timing data stored in strokes");
2601         RNA_def_enum_funcs(prop, rna_GPConvert_mode_items);
2602         
2603         RNA_def_int(ot->srna, "frame_range", 100, 1, 10000, "Frame Range",
2604                     "The duration of evaluation of the path control curve", 1, 1000);
2605         RNA_def_int(ot->srna, "start_frame", 1, 1, 100000, "Start Frame",
2606                     "The start frame of the path control curve", 1, 100000);
2607         RNA_def_boolean(ot->srna, "use_realtime", false, "Realtime",
2608                         "Whether the path control curve reproduces the drawing in realtime, starting from Start Frame");
2609         prop = RNA_def_int(ot->srna, "end_frame", 250, 1, 100000, "End Frame",
2610                            "The end frame of the path control curve (if Realtime is not set)", 1, 100000);
2611         RNA_def_property_update_runtime(prop, gp_convert_set_end_frame);
2612         
2613         RNA_def_float(ot->srna, "gap_duration", 0.0f, 0.0f, 10000.0f, "Gap Duration",
2614                       "Custom Gap mode: (Average) length of gaps, in frames "
2615                       "(Note: Realtime value, will be scaled if Realtime is not set)", 0.0f, 1000.0f);
2616         RNA_def_float(ot->srna, "gap_randomness", 0.0f, 0.0f, 10000.0f, "Gap Randomness",
2617                       "Custom Gap mode: Number of frames that gap lengths can vary", 0.0f, 1000.0f);
2618         RNA_def_int(ot->srna, "seed", 0, 0, 1000, "Random Seed",
2619                     "Custom Gap mode: Random generator seed", 0, 100);
2620         
2621         /* Note: Internal use, this one will always be hidden by UI code... */
2622         prop = RNA_def_boolean(ot->srna, "use_timing_data", false, "Has Valid Timing",
2623                                "Whether the converted Grease Pencil layer has valid timing data (internal use)");
2624         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2625 }
2626
2627 /* ************************************************ */