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