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