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