19663b66f928de9a52c8faf0e20277b7185d6177
[blender.git] / source / blender / editors / gpencil / gpencil_convert.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
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung
22  *                 Bastien Montagne
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  *
26  * Operator for converting Grease Pencil data to geometry
27  */
28
29 /** \file blender/editors/gpencil/gpencil_convert.c
30  *  \ingroup edgpencil
31  */
32
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stddef.h>
38 #include <math.h>
39
40 #include "MEM_guardedalloc.h"
41
42 #include "BLI_math.h"
43 #include "BLI_blenlib.h"
44 #include "BLI_rand.h"
45 #include "BLI_utildefines.h"
46
47 #include "BLT_translation.h"
48
49 #include "DNA_anim_types.h"
50 #include "DNA_curve_types.h"
51 #include "DNA_object_types.h"
52 #include "DNA_node_types.h"
53 #include "DNA_scene_types.h"
54 #include "DNA_screen_types.h"
55 #include "DNA_space_types.h"
56 #include "DNA_view3d_types.h"
57 #include "DNA_gpencil_types.h"
58 #include "DNA_workspace_types.h"
59
60 #include "BKE_collection.h"
61 #include "BKE_context.h"
62 #include "BKE_curve.h"
63 #include "BKE_fcurve.h"
64 #include "BKE_global.h"
65 #include "BKE_gpencil.h"
66 #include "BKE_layer.h"
67 #include "BKE_library.h"
68 #include "BKE_object.h"
69 #include "BKE_report.h"
70 #include "BKE_scene.h"
71 #include "BKE_screen.h"
72 #include "BKE_tracking.h"
73
74 #include "DEG_depsgraph.h"
75
76 #include "UI_interface.h"
77
78 #include "WM_api.h"
79 #include "WM_types.h"
80
81 #include "RNA_access.h"
82 #include "RNA_define.h"
83
84 #include "UI_resources.h"
85 #include "UI_view2d.h"
86
87 #include "ED_gpencil.h"
88 #include "ED_view3d.h"
89 #include "ED_clip.h"
90 #include "ED_keyframing.h"
91
92 #include "gpencil_intern.h"
93
94 /* ************************************************ */
95 /* Grease Pencil to Data Operator */
96
97 /* defines for possible modes */
98 enum {
99         GP_STROKECONVERT_PATH = 1,
100         GP_STROKECONVERT_CURVE,
101         GP_STROKECONVERT_POLY,
102 };
103
104 /* Defines for possible timing modes */
105 enum {
106         GP_STROKECONVERT_TIMING_NONE = 1,
107         GP_STROKECONVERT_TIMING_LINEAR = 2,
108         GP_STROKECONVERT_TIMING_FULL = 3,
109         GP_STROKECONVERT_TIMING_CUSTOMGAP = 4,
110 };
111
112 /* RNA enum define */
113 static const EnumPropertyItem prop_gpencil_convertmodes[] = {
114         {GP_STROKECONVERT_PATH, "PATH", ICON_CURVE_PATH, "Path", "Animation path"},
115         {GP_STROKECONVERT_CURVE, "CURVE", ICON_CURVE_BEZCURVE, "Bezier Curve", "Smooth Bezier curve"},
116         {GP_STROKECONVERT_POLY, "POLY", ICON_MESH_DATA, "Polygon Curve", "Bezier curve with straight-line segments (vector handles)"},
117         {0, NULL, 0, NULL, NULL}
118 };
119
120 static const EnumPropertyItem prop_gpencil_convert_timingmodes_restricted[] = {
121         {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"},
122         {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"},
123         {0, NULL, 0, NULL, NULL},
124 };
125
126 static const EnumPropertyItem prop_gpencil_convert_timingmodes[] = {
127         {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"},
128         {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"},
129         {GP_STROKECONVERT_TIMING_FULL, "FULL", 0, "Original", "Use the original timing, gaps included"},
130         {GP_STROKECONVERT_TIMING_CUSTOMGAP, "CUSTOMGAP", 0, "Custom Gaps",
131                                             "Use the original timing, but with custom gap lengths (in frames)"},
132         {0, NULL, 0, NULL, NULL},
133 };
134
135 static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop),
136                                                   bool *UNUSED(r_free))
137 {
138         if (RNA_boolean_get(ptr, "use_timing_data")) {
139                 return prop_gpencil_convert_timingmodes;
140         }
141         return prop_gpencil_convert_timingmodes_restricted;
142 }
143
144 /* --- */
145
146 /* convert the coordinates from the given stroke point into 3d-coordinates
147  *      - assumes that the active space is the 3D-View
148  */
149 static void gp_strokepoint_convertcoords(
150         bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt,
151         float p3d[3], const rctf *subrect)
152 {
153         Scene *scene = CTX_data_scene(C);
154         View3D *v3d = CTX_wm_view3d(C);
155         ARegion *ar = CTX_wm_region(C);
156         bGPDspoint mypt, *pt;
157
158         float diff_mat[4][4];
159         pt = &mypt;
160
161         /* calculate difference matrix if parent object */
162         if (gpl->parent == NULL) {
163                 copy_v3_v3(&pt->x, &source_pt->x);
164         }
165         else {
166                 /* apply parent transform */
167                 float fpt[3];
168                 ED_gpencil_parent_location(gpl, diff_mat);
169                 mul_v3_m4v3(fpt, diff_mat, &source_pt->x);
170                 copy_v3_v3(&pt->x, fpt);
171         }
172
173
174         if (gps->flag & GP_STROKE_3DSPACE) {
175                 /* directly use 3d-coordinates */
176                 copy_v3_v3(p3d, &pt->x);
177         }
178         else {
179                 const float *fp = ED_view3d_cursor3d_get(scene, v3d);
180                 float mvalf[2];
181                 
182                 /* get screen coordinate */
183                 if (gps->flag & GP_STROKE_2DSPACE) {
184                         View2D *v2d = &ar->v2d;
185                         UI_view2d_view_to_region_fl(v2d, pt->x, pt->y, &mvalf[0], &mvalf[1]);
186                 }
187                 else {
188                         if (subrect) {
189                                 mvalf[0] = (((float)pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin;
190                                 mvalf[1] = (((float)pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin;
191                         }
192                         else {
193                                 mvalf[0] = (float)pt->x / 100.0f * ar->winx;
194                                 mvalf[1] = (float)pt->y / 100.0f * ar->winy;
195                         }
196                 }
197                 
198                 ED_view3d_win_to_3d(v3d, ar, fp, mvalf, p3d);
199         }
200 }
201
202 /* --- */
203
204 /* temp struct for gp_stroke_path_animation() */
205 typedef struct tGpTimingData {
206         /* Data set from operator settings */
207         int mode;
208         int frame_range; /* Number of frames evaluated for path animation */
209         int start_frame, end_frame;
210         bool realtime; /* Will overwrite end_frame in case of Original or CustomGap timing... */
211         float gap_duration, gap_randomness; /* To be used with CustomGap mode*/
212         int seed;
213         
214         /* Data set from points, used to compute final timing FCurve */
215         int num_points, cur_point;
216         
217         /* Distances */
218         float *dists;
219         float tot_dist;
220         
221         /* Times */
222         float *times; /* Note: Gap times will be negative! */
223         float tot_time, gap_tot_time;
224         double inittime;
225         
226         /* Only used during creation of dists & times lists. */
227         float offset_time;
228 } tGpTimingData;
229
230 /* Init point buffers for timing data.
231  * Note this assumes we only grow those arrays!
232  */
233 static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr)
234 {
235         float *tmp;
236         
237         BLI_assert(nbr > gtd->num_points);
238         
239         /* distances */
240         tmp = gtd->dists;
241         gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__);
242         if (tmp) {
243                 memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points);
244                 MEM_freeN(tmp);
245         }
246         
247         /* times */
248         tmp = gtd->times;
249         gtd->times = MEM_callocN(sizeof(float) * nbr, __func__);
250         if (tmp) {
251                 memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points);
252                 MEM_freeN(tmp);
253         }
254         
255         gtd->num_points = nbr;
256 }
257
258 /* add stroke point to timing buffers */
259 static void gp_timing_data_add_point(tGpTimingData *gtd, const double stroke_inittime, const float time,
260                                      const float delta_dist)
261 {
262         float delta_time = 0.0f;
263         const int cur_point = gtd->cur_point;
264         
265         if (!cur_point) {
266                 /* Special case, first point, if time is not 0.0f we have to compensate! */
267                 gtd->offset_time = -time;
268                 gtd->times[cur_point] = 0.0f;
269         }
270         else if (time < 0.0f) {
271                 /* This is a gap, negative value! */
272                 gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time);
273                 delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1];
274                 
275                 gtd->gap_tot_time += delta_time;
276         }
277         else {
278                 gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time);
279                 delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]);
280         }
281         
282         gtd->tot_time += delta_time;
283         gtd->tot_dist += delta_dist;
284         gtd->dists[cur_point] = gtd->tot_dist;
285         
286         gtd->cur_point++;
287 }
288
289 /* In frames! Binary search for FCurve keys have a threshold of 0.01, so we can't set
290  * arbitrarily close points - this is esp. important with NoGaps mode!
291  */
292 #define MIN_TIME_DELTA 0.02f
293
294 /* Loop over next points to find the end of the stroke, and compute */
295 static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, RNG *rng, const int idx, const int nbr_gaps,
296                                      int *nbr_done_gaps, const float tot_gaps_time, const float delta_time,
297                                      float *next_delta_time)
298 {
299         int j;
300         
301         for (j = idx + 1; j < gtd->num_points; j++) {
302                 if (gtd->times[j] < 0) {
303                         gtd->times[j] = -gtd->times[j];
304                         if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
305                                 /* In this mode, gap time between this stroke and the next should be 0 currently...
306                                  * So we have to compute its final duration!
307                                  */
308                                 if (gtd->gap_randomness > 0.0f) {
309                                         /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range,
310                                          * and which sum to exactly tot_gaps_time...
311                                          */
312                                         int rem_gaps = nbr_gaps - (*nbr_done_gaps);
313                                         if (rem_gaps < 2) {
314                                                 /* Last gap, just give remaining time! */
315                                                 *next_delta_time = tot_gaps_time;
316                                         }
317                                         else {
318                                                 float delta, min, max;
319                                                 
320                                                 /* This code ensures that if the first gaps have been shorter than average gap_duration,
321                                                  * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa!
322                                                  */
323                                                 delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps));
324                                                 
325                                                 /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */
326                                                 min = -gtd->gap_randomness - delta;
327                                                 CLAMP(min, -gtd->gap_randomness, 0.0f);
328                                                 
329                                                 /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */
330                                                 max = gtd->gap_randomness - delta;
331                                                 CLAMP(max, 0.0f, gtd->gap_randomness);
332                                                 *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min;
333                                         }
334                                 }
335                                 else {
336                                         *next_delta_time += gtd->gap_duration;
337                                 }
338                         }
339                         (*nbr_done_gaps)++;
340                         break;
341                 }
342         }
343         
344         return j - 1;
345 }
346
347 static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, RNG *rng, int *nbr_gaps, float *tot_gaps_time)
348 {
349         int i;
350         float delta_time = 0.0f;
351         
352         for (i = 0; i < gtd->num_points; i++) {
353                 if (gtd->times[i] < 0 && i) {
354                         (*nbr_gaps)++;
355                         gtd->times[i] = -gtd->times[i] - delta_time;
356                         delta_time += gtd->times[i] - gtd->times[i - 1];
357                         gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */
358                 }
359                 else {
360                         gtd->times[i] -= delta_time;
361                 }
362         }
363         gtd->tot_time -= delta_time;
364         
365         *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration;
366         gtd->tot_time += *tot_gaps_time;
367         if (G.debug & G_DEBUG) {
368                 printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *tot_gaps_time, *nbr_gaps);
369         }
370         if (gtd->gap_randomness > 0.0f) {
371                 BLI_rng_srandom(rng, gtd->seed);
372         }
373 }
374
375 static void gp_stroke_path_animation_add_keyframes(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu,
376                                                    Curve *cu, tGpTimingData *gtd, RNG *rng, const float time_range,
377                                                    const int nbr_gaps, const float tot_gaps_time)
378 {
379         /* Use actual recorded timing! */
380         const float time_start = (float)gtd->start_frame;
381         
382         float last_valid_time = 0.0f;
383         int end_stroke_idx = -1, start_stroke_idx = 0;
384         float end_stroke_time = 0.0f;
385         
386         /* CustomGaps specific */
387         float delta_time = 0.0f, next_delta_time = 0.0f;
388         int nbr_done_gaps = 0;
389         
390         int i;
391         float cfra;
392         
393         /* This is a bit tricky, as:
394          * - We can't add arbitrarily close points on FCurve (in time).
395          * - We *must* have all "caps" points of all strokes in FCurve, as much as possible!
396          */
397         for (i = 0; i < gtd->num_points; i++) {
398                 /* If new stroke... */
399                 if (i > end_stroke_idx) {
400                         start_stroke_idx = i;
401                         delta_time = next_delta_time;
402                         /* find end of that new stroke */
403                         end_stroke_idx = gp_find_end_of_stroke_idx(gtd, rng, i, nbr_gaps, &nbr_done_gaps,
404                                                                    tot_gaps_time, delta_time, &next_delta_time);
405                         /* This one should *never* be negative! */
406                         end_stroke_time = time_start + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range);
407                 }
408                 
409                 /* Simple proportional stuff... */
410                 cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen;
411                 cfra = time_start + ((gtd->times[i] + delta_time) / gtd->tot_time * time_range);
412                 
413                 /* And now, the checks about timing... */
414                 if (i == start_stroke_idx) {
415                         /* If first point of a stroke, be sure it's enough ahead of last valid keyframe, and
416                          * that the end point of the stroke is far enough!
417                          * In case it is not, we keep the end point...
418                          * Note that with CustomGaps mode, this is here we set the actual gap timing!
419                          */
420                         if ((end_stroke_time - last_valid_time) > MIN_TIME_DELTA * 2) {
421                                 if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
422                                         cfra = last_valid_time + MIN_TIME_DELTA;
423                                 }
424                                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
425                                 last_valid_time = cfra;
426                         }
427                         else if (G.debug & G_DEBUG) {
428                                 printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx);
429                         }
430                 }
431                 else if (i == end_stroke_idx) {
432                         /* Always try to insert end point of a curve (should be safe enough, anyway...) */
433                         if ((cfra - last_valid_time) < MIN_TIME_DELTA) {
434                                 cfra = last_valid_time + MIN_TIME_DELTA;
435                         }
436                         insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
437                         last_valid_time = cfra;
438                 }
439                 else {
440                         /* Else ("middle" point), we only insert it if it's far enough from last keyframe,
441                          * and also far enough from (not yet added!) end_stroke keyframe!
442                          */
443                         if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) {
444                                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_BREAKDOWN, INSERTKEY_FAST);
445                                 last_valid_time = cfra;
446                         }
447                         else if (G.debug & G_DEBUG) {
448                                 printf("\t Skipping \"middle\" point %d, too close from last added point or end point %d\n",
449                                        i, end_stroke_idx);
450                         }
451                 }
452         }
453 }
454
455 static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu, tGpTimingData *gtd)
456 {
457         Scene *scene = CTX_data_scene(C);
458         bAction *act;
459         FCurve *fcu;
460         PointerRNA ptr;
461         PropertyRNA *prop = NULL;
462         int nbr_gaps = 0, i;
463         
464         if (gtd->mode == GP_STROKECONVERT_TIMING_NONE)
465                 return;
466         
467         /* gap_duration and gap_randomness are in frames, but we need seconds!!! */
468         gtd->gap_duration = FRA2TIME(gtd->gap_duration);
469         gtd->gap_randomness = FRA2TIME(gtd->gap_randomness);
470         
471         /* Enable path! */
472         cu->flag |= CU_PATH;
473         cu->pathlen = gtd->frame_range;
474         
475         /* Get RNA pointer to read/write path time values */
476         RNA_id_pointer_create((ID *)cu, &ptr);
477         prop = RNA_struct_find_property(&ptr, "eval_time");
478         
479         /* Ensure we have an F-Curve to add keyframes to */
480         act = verify_adt_action((ID *)cu, true);
481         fcu = verify_fcurve(act, NULL, &ptr, "eval_time", 0, true);
482         
483         if (G.debug & G_DEBUG) {
484                 printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time);
485                 for (i = 0; i < gtd->num_points; i++) {
486                         printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]);
487                 }
488         }
489         
490         if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) {
491                 float cfra;
492                 
493                 /* Linear extrapolation! */
494                 fcu->extend = FCURVE_EXTRAPOLATE_LINEAR;
495                 
496                 cu->ctime = 0.0f;
497                 cfra = (float)gtd->start_frame;
498                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
499                 
500                 cu->ctime = cu->pathlen;
501                 if (gtd->realtime) {
502                         cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */
503                 }
504                 else {
505                         cfra = (float)gtd->end_frame;
506                 }
507                 insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST);
508         }
509         else {
510                 /* Use actual recorded timing! */
511                 RNG *rng = BLI_rng_new(0);
512                 float time_range;
513                 
514                 /* CustomGaps specific */
515                 float tot_gaps_time = 0.0f;
516                 
517                 /* Pre-process gaps, in case we don't want to keep their original timing */
518                 if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
519                         gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time);
520                 }
521                 
522                 if (gtd->realtime) {
523                         time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */
524                 }
525                 else {
526                         time_range = (float)(gtd->end_frame - gtd->start_frame);
527                 }
528                 
529                 if (G.debug & G_DEBUG) {
530                         printf("GP Stroke Path Conversion: Starting keying!\n");
531                 }
532                 
533                 gp_stroke_path_animation_add_keyframes(reports, ptr, prop, fcu, cu, gtd, rng, time_range,
534                                                        nbr_gaps, tot_gaps_time);
535                 
536                 BLI_rng_free(rng);
537         }
538         
539         /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */
540         calchandles_fcurve(fcu);
541         
542         if (G.debug & G_DEBUG) {
543                 printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time);
544                 for (i = 0; i < gtd->num_points; i++) {
545                         printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]);
546                 }
547                 printf("\n\n");
548         }
549         
550         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
551         
552         /* send updates */
553         DEG_id_tag_update(&cu->id, 0);
554 }
555
556 #undef MIN_TIME_DELTA
557
558 #define GAP_DFAC 0.01f
559 #define WIDTH_CORR_FAC 0.1f
560 #define BEZT_HANDLE_FAC 0.3f
561
562 /* convert stroke to 3d path */
563
564 /* helper */
565 static void gp_stroke_to_path_add_point(tGpTimingData *gtd, BPoint *bp, const float p[3], const float prev_p[3],
566                                         const bool do_gtd, const double inittime, const float time,
567                                         const float width, const float rad_fac, float minmax_weights[2])
568 {
569         copy_v3_v3(bp->vec, p);
570         bp->vec[3] = 1.0f;
571         
572         /* set settings */
573         bp->f1 = SELECT;
574         bp->radius = width * rad_fac;
575         bp->weight = width;
576         CLAMP(bp->weight, 0.0f, 1.0f);
577         if (bp->weight < minmax_weights[0]) {
578                 minmax_weights[0] = bp->weight;
579         }
580         else if (bp->weight > minmax_weights[1]) {
581                 minmax_weights[1] = bp->weight;
582         }
583         
584         /* Update timing data */
585         if (do_gtd) {
586                 gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
587         }
588 }
589
590 static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu,
591                               float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point,
592                               const bool add_end_point, tGpTimingData *gtd)
593 {
594         bGPDspoint *pt;
595         Nurb *nu = (curnu) ? *curnu : NULL;
596         BPoint *bp, *prev_bp = NULL;
597         const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE);
598         const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0);
599         int i, old_nbp = 0;
600         
601         /* create new 'nurb' or extend current one within the curve */
602         if (nu) {
603                 old_nbp = nu->pntsu;
604
605                 /* If stitch, the first point of this stroke is already present in current nu.
606                  * Else, we have to add two additional points to make the zero-radius link between strokes.
607                  */
608                 BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points);
609         }
610         else {
611                 nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
612                 
613                 nu->pntsu = gps->totpoints + add_start_end_points;
614                 nu->pntsv = 1;
615                 nu->orderu = 2; /* point-to-point! */
616                 nu->type = CU_NURBS;
617                 nu->flagu = CU_NURB_ENDPOINT;
618                 nu->resolu = cu->resolu;
619                 nu->resolv = cu->resolv;
620                 nu->knotsu = NULL;
621                 
622                 nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints");
623                 
624                 stitch = false; /* Security! */
625         }
626         
627         if (do_gtd) {
628                 gp_timing_data_set_nbr(gtd, nu->pntsu);
629         }
630         
631         /* If needed, make the link between both strokes with two zero-radius additional points */
632         /* About "zero-radius" point interpolations:
633          * - If we have at least two points in current curve (most common case), we linearly extrapolate
634          *   the last segment to get the first point (p1) position and timing.
635          * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point
636          *   with the first point of the current stroke.
637          * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated
638          * if it exists, else (if the stroke is a single point), linear interpolation with last curve point...
639          */
640         if (curnu && !stitch && old_nbp) {
641                 float p1[3], p2[3], p[3], next_p[3];
642                 float dt1 = 0.0f, dt2 = 0.0f;
643                 
644                 BLI_assert(gps->prev != NULL);
645                 
646                 prev_bp = NULL;
647                 if ((old_nbp > 1) && (gps->prev->totpoints > 1)) {
648                         /* Only use last curve segment if previous stroke was not a single-point one! */
649                         prev_bp = &nu->bp[old_nbp - 2];
650                 }
651                 bp = &nu->bp[old_nbp - 1];
652                 
653                 /* First point */
654                 gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
655                 if (prev_bp) {
656                         interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC);
657                         if (do_gtd) {
658                                 const int idx = gps->prev->totpoints - 1;
659                                 dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC);
660                         }
661                 }
662                 else {
663                         interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC);
664                         if (do_gtd) {
665                                 dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC);
666                         }
667                 }
668                 bp++;
669                 gp_stroke_to_path_add_point(gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1,
670                                             0.0f, rad_fac, minmax_weights);
671                 
672                 /* Second point */
673                 /* Note dt2 is always negative, which marks the gap. */
674                 if (gps->totpoints > 1) {
675                         gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
676                         interp_v3_v3v3(p2, p, next_p, -GAP_DFAC);
677                         if (do_gtd) {
678                                 dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
679                         }
680                 }
681                 else {
682                         interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC);
683                         if (do_gtd) {
684                                 dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC);
685                         }
686                 }
687                 bp++;
688                 gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights);
689                 
690                 old_nbp += 2;
691         }
692         else if (add_start_point) {
693                 float p[3], next_p[3];
694                 float dt = 0.0f;
695                 
696                 gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
697                 if (gps->totpoints > 1) {
698                         gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
699                         interp_v3_v3v3(p, p, next_p, -GAP_DFAC);
700                         if (do_gtd) {
701                                 dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
702                         }
703                 }
704                 else {
705                         p[0] -= GAP_DFAC;  /* Rather arbitrary... */
706                         dt = -GAP_DFAC;  /* Rather arbitrary too! */
707                 }
708                 bp = &nu->bp[old_nbp];
709                 /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value)
710                  * would not work (it would be *before* gtd->inittime, which is not supported currently).
711                  */
712                 gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
713                 
714                 old_nbp++;
715         }
716         
717         if (old_nbp) {
718                 prev_bp = &nu->bp[old_nbp - 1];
719         }
720         
721         /* add points */
722         for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp];
723              i < gps->totpoints;
724              i++, pt++, bp++)
725         {
726                 float p[3];
727                 float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC;
728                 
729                 /* get coordinates to add at */
730                 gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect);
731                 
732                 gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time,
733                                             width, rad_fac, minmax_weights);
734                 
735                 prev_bp = bp;
736         }
737
738         if (add_end_point) {
739                 float p[3];
740                 float dt = 0.0f;
741                 
742                 if (gps->totpoints > 1) {
743                         interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC);
744                         if (do_gtd) {
745                                 const int idx = gps->totpoints - 1;
746                                 dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC);
747                         }
748                 }
749                 else {
750                         copy_v3_v3(p, prev_bp->vec);
751                         p[0] += GAP_DFAC;  /* Rather arbitrary... */
752                         dt = GAP_DFAC;  /* Rather arbitrary too! */
753                 }
754                 /* Note bp has already been incremented in main loop above, so it points to the right place. */
755                 gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
756         }
757         
758         /* add nurb to curve */
759         if (!curnu || !*curnu) {
760                 BLI_addtail(&cu->nurb, nu);
761         }
762         if (curnu) {
763                 *curnu = nu;
764         }
765         
766         BKE_nurb_knot_calc_u(nu);
767 }
768
769 /* convert stroke to 3d bezier */
770
771 /* helper */
772 static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, BezTriple *bezt,
773                                           const float p[3], const float h1[3], const float h2[3], const float prev_p[3],
774                                           const bool do_gtd, const double inittime, const float time,
775                                           const float width, const float rad_fac, float minmax_weights[2])
776 {
777         copy_v3_v3(bezt->vec[0], h1);
778         copy_v3_v3(bezt->vec[1], p);
779         copy_v3_v3(bezt->vec[2], h2);
780         
781         /* set settings */
782         bezt->h1 = bezt->h2 = HD_FREE;
783         bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
784         bezt->radius = width * rad_fac;
785         bezt->weight = width;
786         CLAMP(bezt->weight, 0.0f, 1.0f);
787         if (bezt->weight < minmax_weights[0]) {
788                 minmax_weights[0] = bezt->weight;
789         }
790         else if (bezt->weight > minmax_weights[1]) {
791                 minmax_weights[1] = bezt->weight;
792         }
793         
794         /* Update timing data */
795         if (do_gtd) {
796                 gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p));
797         }
798 }
799
800 static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu,
801                                 float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point,
802                                 const bool add_end_point, tGpTimingData *gtd)
803 {
804         bGPDspoint *pt;
805         Nurb *nu = (curnu) ? *curnu : NULL;
806         BezTriple *bezt, *prev_bezt = NULL;
807         int i, tot, old_nbezt = 0;
808         const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0);
809         float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3];
810         const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE);
811         
812         /* create new 'nurb' or extend current one within the curve */
813         if (nu) {
814                 old_nbezt = nu->pntsu;
815                 /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke,
816                  * so no need to add it.
817                  * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes.
818                  */
819                 BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points);
820         }
821         else {
822                 nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
823                 
824                 nu->pntsu = gps->totpoints + add_start_end_points;
825                 nu->resolu = 12;
826                 nu->resolv = 12;
827                 nu->type = CU_BEZIER;
828                 nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts");
829                 
830                 stitch = false; /* Security! */
831         }
832         
833         if (do_gtd) {
834                 gp_timing_data_set_nbr(gtd, nu->pntsu);
835         }
836         
837         tot = gps->totpoints;
838         
839         /* get initial coordinates */
840         pt = gps->points;
841         if (tot) {
842                 gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
843                 if (tot > 1) {
844                         gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
845                 }
846                 if (stitch && tot > 2) {
847                         gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
848                 }
849         }
850         
851         /* If needed, make the link between both strokes with two zero-radius additional points */
852         if (curnu && old_nbezt) {
853                 BLI_assert(gps->prev != NULL);
854                 
855                 /* Update last point's second handle */
856                 if (stitch) {
857                         bezt = &nu->bezt[old_nbezt - 1];
858                         interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC);
859                         copy_v3_v3(bezt->vec[2], h2);
860                         pt++;
861                 }
862                 
863                 /* Create "link points" */
864                 /* About "zero-radius" point interpolations:
865                  * - If we have at least two points in current curve (most common case), we linearly extrapolate
866                  *   the last segment to get the first point (p1) position and timing.
867                  * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point
868                  *   with the first point of the current stroke.
869                  * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated
870                  * if it exists, else (if the stroke is a single point), linear interpolation with last curve point...
871                  */
872                 else {
873                         float p1[3], p2[3];
874                         float dt1 = 0.0f, dt2 = 0.0f;
875                         
876                         prev_bezt = NULL;
877                         if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) {
878                                 /* Only use last curve segment if previous stroke was not a single-point one! */
879                                 prev_bezt = &nu->bezt[old_nbezt - 2];
880                         }
881                         bezt = &nu->bezt[old_nbezt - 1];
882                         
883                         /* First point */
884                         if (prev_bezt) {
885                                 interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC);
886                                 if (do_gtd) {
887                                         const int idx = gps->prev->totpoints - 1;
888                                         dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC);
889                                 }
890                         }
891                         else {
892                                 interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC);
893                                 if (do_gtd) {
894                                         dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC);
895                                 }
896                         }
897                         
898                         /* Second point */
899                         /* Note dt2 is always negative, which marks the gap. */
900                         if (tot > 1) {
901                                 interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC);
902                                 if (do_gtd) {
903                                         dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
904                                 }
905                         }
906                         else {
907                                 interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC);
908                                 if (do_gtd) {
909                                         dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC);
910                                 }
911                         }
912                         
913                         /* Second handle of last point of previous stroke. */
914                         interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC);
915                         copy_v3_v3(bezt->vec[2], h2);
916                         
917                         /* First point */
918                         interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC);
919                         interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC);
920                         bezt++;
921                         gp_stroke_to_bezier_add_point(gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1,
922                                                       0.0f, rad_fac, minmax_weights);
923                         
924                         /* Second point */
925                         interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC);
926                         interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC);
927                         bezt++;
928                         gp_stroke_to_bezier_add_point(gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2,
929                                                       0.0f, rad_fac, minmax_weights);
930                         
931                         old_nbezt += 2;
932                         copy_v3_v3(p3d_prev, p2);
933                 }
934         }
935         else if (add_start_point) {
936                 float p[3];
937                 float dt = 0.0f;
938                 
939                 if (gps->totpoints > 1) {
940                         interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC);
941                         if (do_gtd) {
942                                 dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
943                         }
944                 }
945                 else {
946                         copy_v3_v3(p, p3d_cur);
947                         p[0] -= GAP_DFAC;  /* Rather arbitrary... */
948                         dt = -GAP_DFAC;  /* Rather arbitrary too! */
949                 }
950                 interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC);
951                 interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC);
952                 bezt = &nu->bezt[old_nbezt];
953                 gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt,
954                                               0.0f, rad_fac, minmax_weights);
955                 
956                 old_nbezt++;
957                 copy_v3_v3(p3d_prev, p);
958         }
959         
960         if (old_nbezt) {
961                 prev_bezt = &nu->bezt[old_nbezt - 1];
962         }
963         
964         /* add points */
965         for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) {
966                 float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC;
967                 
968                 if (i || old_nbezt) {
969                         interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC);
970                 }
971                 else {
972                         interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC);
973                 }
974                 
975                 if (i < tot - 1) {
976                         interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC);
977                 }
978                 else {
979                         interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC);
980                 }
981                 
982                 gp_stroke_to_bezier_add_point(gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur,
983                                               do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights);
984                 
985                 /* shift coord vects */
986                 copy_v3_v3(p3d_prev, p3d_cur);
987                 copy_v3_v3(p3d_cur, p3d_next);
988                 
989                 if (i + 2 < tot) {
990                         gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
991                 }
992                 
993                 prev_bezt = bezt;
994         }
995
996         if (add_end_point) {
997                 float p[3];
998                 float dt = 0.0f;
999                 
1000                 if (gps->totpoints > 1) {
1001                         interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC);
1002                         if (do_gtd) {
1003                                 const int idx = gps->totpoints - 1;
1004                                 dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC);
1005                         }
1006                 }
1007                 else {
1008                         copy_v3_v3(p, prev_bezt->vec[1]);
1009                         p[0] += GAP_DFAC;  /* Rather arbitrary... */
1010                         dt = GAP_DFAC;  /* Rather arbitrary too! */
1011                 }
1012                 
1013                 /* Second handle of last point of this stroke. */
1014                 interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC);
1015                 copy_v3_v3(prev_bezt->vec[2], h2);
1016                 
1017                 /* The end point */
1018                 interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC);
1019                 interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC);
1020                 /* Note bezt has already been incremented in main loop above, so it points to the right place. */
1021                 gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt,
1022                                               0.0f, rad_fac, minmax_weights);
1023         }
1024         
1025         /* must calculate handles or else we crash */
1026         BKE_nurb_handles_calc(nu);
1027         
1028         if (!curnu || !*curnu) {
1029                 BLI_addtail(&cu->nurb, nu);
1030         }
1031         if (curnu) {
1032                 *curnu = nu;
1033         }
1034 }
1035
1036 #undef GAP_DFAC
1037 #undef WIDTH_CORR_FAC
1038 #undef BEZT_HANDLE_FAC
1039
1040 static void gp_stroke_finalize_curve_endpoints(Curve *cu)
1041 {
1042         /* start */
1043         Nurb *nu = cu->nurb.first;
1044         int i = 0;
1045         if (nu->bezt) {
1046                 BezTriple *bezt = nu->bezt;
1047                 if (bezt) {
1048                         bezt[i].weight = bezt[i].radius = 0.0f;
1049                 }
1050         }
1051         else if (nu->bp) {
1052                 BPoint *bp = nu->bp;
1053                 if (bp) {
1054                         bp[i].weight = bp[i].radius = 0.0f;
1055                 }
1056         }
1057         
1058         /* end */
1059         nu = cu->nurb.last;
1060         i = nu->pntsu - 1;
1061         if (nu->bezt) {
1062                 BezTriple *bezt = nu->bezt;
1063                 if (bezt) {
1064                         bezt[i].weight = bezt[i].radius = 0.0f;
1065                 }
1066         }
1067         else if (nu->bp) {
1068                 BPoint *bp = nu->bp;
1069                 if (bp) {
1070                         bp[i].weight = bp[i].radius = 0.0f;
1071                 }
1072         }
1073 }
1074
1075 static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2])
1076 {
1077         Nurb *nu;
1078         const float delta = minmax_weights[0];
1079         float fac;
1080         int i;
1081         
1082         /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */
1083         if (IS_EQF(delta, minmax_weights[1]))
1084                 fac = 1.0f;
1085         else
1086                 fac = 1.0f / (minmax_weights[1] - delta);
1087         
1088         for (nu = cu->nurb.first; nu; nu = nu->next) {
1089                 if (nu->bezt) {
1090                         BezTriple *bezt = nu->bezt;
1091                         for (i = 0; i < nu->pntsu; i++, bezt++) {
1092                                 bezt->weight = (bezt->weight - delta) * fac;
1093                         }
1094                 }
1095                 else if (nu->bp) {
1096                         BPoint *bp = nu->bp;
1097                         for (i = 0; i < nu->pntsu; i++, bp++) {
1098                                 bp->weight = (bp->weight - delta) * fac;
1099                         }
1100                 }
1101         }
1102 }
1103
1104 static int gp_camera_view_subrect(bContext *C, rctf *subrect)
1105 {
1106         View3D *v3d = CTX_wm_view3d(C);
1107         ARegion *ar = CTX_wm_region(C);
1108         
1109         if (v3d) {
1110                 RegionView3D *rv3d = ar->regiondata;
1111                 
1112                 /* for camera view set the subrect */
1113                 if (rv3d->persp == RV3D_CAMOB) {
1114                         Scene *scene = CTX_data_scene(C);
1115                         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1116                         ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, subrect, true); /* no shift */
1117                         return 1;
1118                 }
1119         }
1120         
1121         return 0;
1122 }
1123
1124 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
1125 static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bGPDlayer *gpl, const int mode,
1126                               const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd)
1127 {
1128         struct Main *bmain = CTX_data_main(C);
1129         Scene *scene = CTX_data_scene(C);
1130         ViewLayer *view_layer = CTX_data_view_layer(C);
1131         SceneCollection *sc = CTX_data_scene_collection(C);
1132         bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0);
1133         bGPDstroke *gps, *prev_gps = NULL;
1134         Object *ob;
1135         Curve *cu;
1136         Nurb *nu = NULL;
1137         Base *base_new = NULL;
1138         float minmax_weights[2] = {1.0f, 0.0f};
1139         
1140         /* camera framing */
1141         rctf subrect, *subrect_ptr = NULL;
1142         
1143         /* error checking */
1144         if (ELEM(NULL, gpd, gpl, gpf))
1145                 return;
1146         
1147         /* only convert if there are any strokes on this layer's frame to convert */
1148         if (BLI_listbase_is_empty(&gpf->strokes))
1149                 return;
1150         
1151         /* initialize camera framing */
1152         if (gp_camera_view_subrect(C, &subrect)) {
1153                 subrect_ptr = &subrect;
1154         }
1155         
1156         /* init the curve object (remove rotation and get curve data from it)
1157          *      - must clear transforms set on object, as those skew our results
1158          */
1159         ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info);
1160         cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE);
1161         BKE_collection_object_add(&scene->id, sc, ob);
1162         base_new = BKE_view_layer_base_find(view_layer, ob);
1163         
1164         cu->flag |= CU_3D;
1165         
1166         gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime;
1167         
1168         /* add points to curve */
1169         for (gps = gpf->strokes.first; gps; gps = gps->next) {
1170                 const bool add_start_point = (link_strokes && !(prev_gps));
1171                 const bool add_end_point = (link_strokes && !(gps->next));
1172                 
1173                 /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, and stitch them to previous one. */
1174                 bool stitch = false;
1175                 if (prev_gps) {
1176                         bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1];
1177                         bGPDspoint *pt2 = &gps->points[0];
1178                         
1179                         if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) {
1180                                 stitch = true;
1181                         }
1182                 }
1183                 
1184                 /* Decide whether we connect this stroke to previous one */
1185                 if (!(stitch || link_strokes)) {
1186                         nu = NULL;
1187                 }
1188                 
1189                 switch (mode) {
1190                         case GP_STROKECONVERT_PATH:
1191                                 gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch,
1192                                                   add_start_point, add_end_point, gtd);
1193                                 break;
1194                         case GP_STROKECONVERT_CURVE:
1195                         case GP_STROKECONVERT_POLY:  /* convert after */
1196                                 gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch,
1197                                                     add_start_point, add_end_point, gtd);
1198                                 break;
1199                         default:
1200                                 BLI_assert(!"invalid mode");
1201                                 break;
1202                 }
1203                 prev_gps = gps;
1204         }
1205         
1206         /* If link_strokes, be sure first and last points have a zero weight/size! */
1207         if (link_strokes) {
1208                 gp_stroke_finalize_curve_endpoints(cu);
1209         }
1210         
1211         /* Update curve's weights, if needed */
1212         if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) {
1213                 gp_stroke_norm_curve_weights(cu, minmax_weights);
1214         }
1215         
1216         /* Create the path animation, if needed */
1217         gp_stroke_path_animation(C, reports, cu, gtd);
1218         
1219         if (mode == GP_STROKECONVERT_POLY) {
1220                 for (nu = cu->nurb.first; nu; nu = nu->next) {
1221                         BKE_nurb_type_convert(nu, CU_POLY, false);
1222                 }
1223         }
1224         
1225         /* set the layer and select */
1226         base_new->flag |= SELECT;
1227         BKE_scene_object_base_flag_sync_from_base(base_new);
1228 }
1229
1230 /* --- */
1231
1232 /* Check a GP layer has valid timing data! Else, most timing options are hidden in the operator.
1233  * op may be NULL.
1234  */
1235 static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op)
1236 {
1237         Scene *scene = CTX_data_scene(C);
1238         bGPDframe *gpf = NULL;
1239         bGPDstroke *gps = NULL;
1240         bGPDspoint *pt;
1241         double base_time, cur_time, prev_time = -1.0;
1242         int i;
1243         bool valid = true;
1244         
1245         if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first))
1246                 return false;
1247         
1248         do {
1249                 base_time = cur_time = gps->inittime;
1250                 if (cur_time <= prev_time) {
1251                         valid = false;
1252                         break;
1253                 }
1254                 
1255                 prev_time = cur_time;
1256                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1257                         cur_time = base_time + (double)pt->time;
1258                         /* First point of a stroke should have the same time as stroke's inittime,
1259                          * so it's the only case where equality is allowed!
1260                          */
1261                         if ((i && cur_time <= prev_time) || (cur_time < prev_time)) {
1262                                 valid = false;
1263                                 break;
1264                         }
1265                         prev_time = cur_time;
1266                 }
1267                 
1268                 if (!valid) {
1269                         break;
1270                 }
1271         } while ((gps = gps->next));
1272         
1273         if (op) {
1274                 RNA_boolean_set(op->ptr, "use_timing_data", valid);
1275         }
1276         return valid;
1277 }
1278
1279 /* Check end_frame is always > start frame! */
1280 static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UNUSED(scene), struct PointerRNA *ptr)
1281 {
1282         int start_frame = RNA_int_get(ptr, "start_frame");
1283         int end_frame = RNA_int_get(ptr, "end_frame");
1284         
1285         if (end_frame <= start_frame) {
1286                 RNA_int_set(ptr, "end_frame", start_frame + 1);
1287         }
1288 }
1289
1290 static int gp_convert_poll(bContext *C)
1291 {
1292         bGPdata *gpd = ED_gpencil_data_get_active(C);
1293         bGPDlayer *gpl = NULL;
1294         bGPDframe *gpf = NULL;
1295         ScrArea *sa = CTX_wm_area(C);
1296         Scene *scene = CTX_data_scene(C);
1297         ViewLayer *view_layer = CTX_data_view_layer(C);
1298         
1299         /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!),
1300          * and if we are not in edit mode!
1301          */
1302         return ((sa && sa->spacetype == SPACE_VIEW3D) &&
1303                 (gpl = BKE_gpencil_layer_getactive(gpd)) &&
1304                 (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) &&
1305                 (gpf->strokes.first) &&
1306                 (OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL));
1307 }
1308
1309 static int gp_convert_layer_exec(bContext *C, wmOperator *op)
1310 {
1311         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data");
1312         bGPdata *gpd = ED_gpencil_data_get_active(C);
1313         bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
1314         Scene *scene = CTX_data_scene(C);
1315         const int mode = RNA_enum_get(op->ptr, "type");
1316         const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights");
1317         const float rad_fac = RNA_float_get(op->ptr, "radius_multiplier");
1318         const bool link_strokes = RNA_boolean_get(op->ptr, "use_link_strokes");
1319         bool valid_timing;
1320         tGpTimingData gtd;
1321         
1322         /* check if there's data to work with */
1323         if (gpd == NULL) {
1324                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on");
1325                 return OPERATOR_CANCELLED;
1326         }
1327         
1328         if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) {
1329                 BKE_report(op->reports, RPT_WARNING,
1330                            "Current Grease Pencil strokes have no valid timing data, most timing options will be hidden!");
1331         }
1332         valid_timing = RNA_property_boolean_get(op->ptr, prop);
1333         
1334         gtd.mode = RNA_enum_get(op->ptr, "timing_mode");
1335         /* Check for illegal timing mode! */
1336         if (!valid_timing && !ELEM(gtd.mode, GP_STROKECONVERT_TIMING_NONE, GP_STROKECONVERT_TIMING_LINEAR)) {
1337                 gtd.mode = GP_STROKECONVERT_TIMING_LINEAR;
1338                 RNA_enum_set(op->ptr, "timing_mode", gtd.mode);
1339         }
1340         if (!link_strokes) {
1341                 gtd.mode = GP_STROKECONVERT_TIMING_NONE;
1342         }
1343         
1344         /* grab all relevant settings */
1345         gtd.frame_range = RNA_int_get(op->ptr, "frame_range");
1346         gtd.start_frame = RNA_int_get(op->ptr, "start_frame");
1347         gtd.realtime = valid_timing ? RNA_boolean_get(op->ptr, "use_realtime") : false;
1348         gtd.end_frame = RNA_int_get(op->ptr, "end_frame");
1349         gtd.gap_duration = RNA_float_get(op->ptr, "gap_duration");
1350         gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness");
1351         gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration);
1352         gtd.seed = RNA_int_get(op->ptr, "seed");
1353         gtd.num_points = gtd.cur_point = 0;
1354         gtd.dists = gtd.times = NULL;
1355         gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f;
1356         gtd.inittime = 0.0;
1357         gtd.offset_time = 0.0f;
1358         
1359         /* perform conversion */
1360         gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, &gtd);
1361         
1362         /* free temp memory */
1363         if (gtd.dists) {
1364                 MEM_freeN(gtd.dists);
1365                 gtd.dists = NULL;
1366         }
1367         if (gtd.times) {
1368                 MEM_freeN(gtd.times);
1369                 gtd.times = NULL;
1370         }
1371         
1372         /* notifiers */
1373         WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
1374         WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
1375         
1376         /* done */
1377         return OPERATOR_FINISHED;
1378 }
1379
1380 static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop)
1381 {
1382         const char *prop_id = RNA_property_identifier(prop);
1383         const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes");
1384         int timing_mode = RNA_enum_get(ptr, "timing_mode");
1385         bool realtime = RNA_boolean_get(ptr, "use_realtime");
1386         float gap_duration = RNA_float_get(ptr, "gap_duration");
1387         float gap_randomness = RNA_float_get(ptr, "gap_randomness");
1388         const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data");
1389         
1390         /* Always show those props */
1391         if (STREQ(prop_id, "type") ||
1392             STREQ(prop_id, "use_normalize_weights") ||
1393             STREQ(prop_id, "radius_multiplier") ||
1394             STREQ(prop_id, "use_link_strokes"))
1395         {
1396                 return true;
1397         }
1398         
1399         /* Never show this prop */
1400         if (STREQ(prop_id, "use_timing_data"))
1401                 return false;
1402         
1403         if (link_strokes) {
1404                 /* Only show when link_stroke is true */
1405                 if (STREQ(prop_id, "timing_mode"))
1406                         return true;
1407                 
1408                 if (timing_mode != GP_STROKECONVERT_TIMING_NONE) {
1409                         /* Only show when link_stroke is true and stroke timing is enabled */
1410                         if (STREQ(prop_id, "frame_range") ||
1411                             STREQ(prop_id, "start_frame"))
1412                         {
1413                                 return true;
1414                         }
1415                         
1416                         /* Only show if we have valid timing data! */
1417                         if (valid_timing && STREQ(prop_id, "use_realtime"))
1418                                 return true;
1419                         
1420                         /* Only show if realtime or valid_timing is false! */
1421                         if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame"))
1422                                 return true;
1423                         
1424                         if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) {
1425                                 /* Only show for custom gaps! */
1426                                 if (STREQ(prop_id, "gap_duration"))
1427                                         return true;
1428                                 
1429                                 /* Only show randomness for non-null custom gaps! */
1430                                 if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f))
1431                                         return true;
1432                                 
1433                                 /* Only show seed for randomize action! */
1434                                 if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f))
1435                                         return true;
1436                         }
1437                 }
1438         }
1439         
1440         /* Else, hidden! */
1441         return false;
1442 }
1443
1444 static void gp_convert_ui(bContext *C, wmOperator *op)
1445 {
1446         uiLayout *layout = op->layout;
1447         wmWindowManager *wm = CTX_wm_manager(C);
1448         PointerRNA ptr;
1449         
1450         RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
1451         
1452         /* Main auto-draw call */
1453         uiDefAutoButsRNA(layout, &ptr, gp_convert_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false);
1454 }
1455
1456 void GPENCIL_OT_convert(wmOperatorType *ot)
1457 {
1458         PropertyRNA *prop;
1459         
1460         /* identifiers */
1461         ot->name = "Convert Grease Pencil";
1462         ot->idname = "GPENCIL_OT_convert";
1463         ot->description = "Convert the active Grease Pencil layer to a new Curve Object";
1464         
1465         /* callbacks */
1466         ot->invoke = WM_menu_invoke;
1467         ot->exec = gp_convert_layer_exec;
1468         ot->poll = gp_convert_poll;
1469         ot->ui = gp_convert_ui;
1470         
1471         /* flags */
1472         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1473         
1474         /* properties */
1475         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "Which type of curve to convert to");
1476         
1477         RNA_def_boolean(ot->srna, "use_normalize_weights", true, "Normalize Weight",
1478                         "Normalize weight (set from stroke width)");
1479         RNA_def_float(ot->srna, "radius_multiplier", 1.0f, 0.0f, 1000.0f, "Radius Fac",
1480                       "Multiplier for the points' radii (set from stroke width)", 0.0f, 10.0f);
1481         RNA_def_boolean(ot->srna, "use_link_strokes", true, "Link Strokes",
1482                         "Whether to link strokes with zero-radius sections of curves");
1483         
1484         prop = RNA_def_enum(ot->srna, "timing_mode", prop_gpencil_convert_timingmodes, GP_STROKECONVERT_TIMING_FULL,
1485                             "Timing Mode", "How to use timing data stored in strokes");
1486         RNA_def_enum_funcs(prop, rna_GPConvert_mode_items);
1487         
1488         RNA_def_int(ot->srna, "frame_range", 100, 1, 10000, "Frame Range",
1489                     "The duration of evaluation of the path control curve", 1, 1000);
1490         RNA_def_int(ot->srna, "start_frame", 1, 1, 100000, "Start Frame",
1491                     "The start frame of the path control curve", 1, 100000);
1492         RNA_def_boolean(ot->srna, "use_realtime", false, "Realtime",
1493                         "Whether the path control curve reproduces the drawing in realtime, starting from Start Frame");
1494         prop = RNA_def_int(ot->srna, "end_frame", 250, 1, 100000, "End Frame",
1495                            "The end frame of the path control curve (if Realtime is not set)", 1, 100000);
1496         RNA_def_property_update_runtime(prop, gp_convert_set_end_frame);
1497         
1498         RNA_def_float(ot->srna, "gap_duration", 0.0f, 0.0f, 10000.0f, "Gap Duration",
1499                       "Custom Gap mode: (Average) length of gaps, in frames "
1500                       "(Note: Realtime value, will be scaled if Realtime is not set)", 0.0f, 1000.0f);
1501         RNA_def_float(ot->srna, "gap_randomness", 0.0f, 0.0f, 10000.0f, "Gap Randomness",
1502                       "Custom Gap mode: Number of frames that gap lengths can vary", 0.0f, 1000.0f);
1503         RNA_def_int(ot->srna, "seed", 0, 0, 1000, "Random Seed",
1504                     "Custom Gap mode: Random generator seed", 0, 100);
1505         
1506         /* Note: Internal use, this one will always be hidden by UI code... */
1507         prop = RNA_def_boolean(ot->srna, "use_timing_data", false, "Has Valid Timing",
1508                                "Whether the converted Grease Pencil layer has valid timing data (internal use)");
1509         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1510 }
1511
1512 /* ************************************************ */