1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008, Blender Foundation
17  * This is a new part of Blender
18  */
19 
20 /** \file
21  * \ingroup bke
22  */
23 
24 #include <math.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "CLG_log.h"
31 
32 #include "MEM_guardedalloc.h"
33 
34 #include "BLI_blenlib.h"
35 #include "BLI_ghash.h"
36 #include "BLI_hash.h"
37 #include "BLI_math_vector.h"
38 #include "BLI_polyfill_2d.h"
39 
40 #include "BLT_translation.h"
41 
42 #include "DNA_gpencil_modifier_types.h"
43 #include "DNA_gpencil_types.h"
44 #include "DNA_mesh_types.h"
45 #include "DNA_meshdata_types.h"
46 #include "DNA_scene_types.h"
47 
48 #include "BKE_deform.h"
49 #include "BKE_gpencil.h"
50 #include "BKE_gpencil_geom.h"
51 #include "BKE_main.h"
52 #include "BKE_material.h"
53 #include "BKE_object.h"
54 
55 #include "DEG_depsgraph_query.h"
56 
57 /* GP Object - Boundbox Support */
58 /**
59  *Get min/max coordinate bounds for single stroke.
60  * \param gps: Grease pencil stroke
61  * \param use_select: Include only selected points
62  * \param r_min: Result minimum coordinates
63  * \param r_max: Result maximum coordinates
64  * \return True if it was possible to calculate
65  */
BKE_gpencil_stroke_minmax(const bGPDstroke * gps,const bool use_select,float r_min[3],float r_max[3])66 bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps,
67                                const bool use_select,
68                                float r_min[3],
69                                float r_max[3])
70 {
71   const bGPDspoint *pt;
72   int i;
73   bool changed = false;
74 
75   if (ELEM(NULL, gps, r_min, r_max)) {
76     return false;
77   }
78 
79   for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
80     if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {
81       minmax_v3v3_v3(r_min, r_max, &pt->x);
82       changed = true;
83     }
84   }
85   return changed;
86 }
87 
88 /**
89  * Get min/max bounds of all strokes in grease pencil data-block.
90  * \param gpd: Grease pencil datablock
91  * \param r_min: Result minimum coordinates
92  * \param r_max: Result maximum coordinates
93  * \return True if it was possible to calculate
94  */
BKE_gpencil_data_minmax(const bGPdata * gpd,float r_min[3],float r_max[3])95 bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
96 {
97   bool changed = false;
98 
99   INIT_MINMAX(r_min, r_max);
100 
101   if (gpd == NULL) {
102     return changed;
103   }
104 
105   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
106     bGPDframe *gpf = gpl->actframe;
107 
108     if (gpf != NULL) {
109       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
110         changed |= BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
111       }
112     }
113   }
114 
115   return changed;
116 }
117 
118 /**
119  * Compute center of bounding box.
120  * \param gpd: Grease pencil data-block
121  * \param r_centroid: Location of the center
122  */
BKE_gpencil_centroid_3d(bGPdata * gpd,float r_centroid[3])123 void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
124 {
125   float min[3], max[3], tot[3];
126 
127   BKE_gpencil_data_minmax(gpd, min, max);
128 
129   add_v3_v3v3(tot, min, max);
130   mul_v3_v3fl(r_centroid, tot, 0.5f);
131 }
132 
133 /**
134  * Compute stroke bounding box.
135  * \param gps: Grease pencil Stroke
136  */
BKE_gpencil_stroke_boundingbox_calc(bGPDstroke * gps)137 void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
138 {
139   INIT_MINMAX(gps->boundbox_min, gps->boundbox_max);
140   BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max);
141 }
142 
143 /**
144  * Create bounding box values.
145  * \param ob: Grease pencil object
146  */
boundbox_gpencil(Object * ob)147 static void boundbox_gpencil(Object *ob)
148 {
149   BoundBox *bb;
150   bGPdata *gpd;
151   float min[3], max[3];
152 
153   if (ob->runtime.bb == NULL) {
154     ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
155   }
156 
157   bb = ob->runtime.bb;
158   gpd = ob->data;
159 
160   if (!BKE_gpencil_data_minmax(gpd, min, max)) {
161     min[0] = min[1] = min[2] = -1.0f;
162     max[0] = max[1] = max[2] = 1.0f;
163   }
164 
165   BKE_boundbox_init_from_minmax(bb, min, max);
166 
167   bb->flag &= ~BOUNDBOX_DIRTY;
168 }
169 
170 /**
171  * Get grease pencil object bounding box.
172  * \param ob: Grease pencil object
173  * \return Bounding box
174  */
BKE_gpencil_boundbox_get(Object * ob)175 BoundBox *BKE_gpencil_boundbox_get(Object *ob)
176 {
177   if (ELEM(NULL, ob, ob->data)) {
178     return NULL;
179   }
180 
181   bGPdata *gpd = (bGPdata *)ob->data;
182   if ((ob->runtime.bb) && ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) {
183     return ob->runtime.bb;
184   }
185 
186   boundbox_gpencil(ob);
187 
188   Object *ob_orig = (Object *)DEG_get_original_id(&ob->id);
189   /* Update orig object's boundbox with re-computed evaluated values. This function can be
190    * called with the evaluated object and need update the original object bound box data
191    * to keep both values synchronized. */
192   if ((ob_orig != NULL) && (ob != ob_orig)) {
193     if (ob_orig->runtime.bb == NULL) {
194       ob_orig->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
195     }
196     for (int i = 0; i < 8; i++) {
197       copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]);
198     }
199   }
200 
201   return ob->runtime.bb;
202 }
203 
204 /* ************************************************** */
205 
stroke_march_next_point(const bGPDstroke * gps,const int index_next_pt,const float * current,const float dist,float * result,float * pressure,float * strength,float * vert_color,float * ratio_result,int * index_from,int * index_to)206 static int stroke_march_next_point(const bGPDstroke *gps,
207                                    const int index_next_pt,
208                                    const float *current,
209                                    const float dist,
210                                    float *result,
211                                    float *pressure,
212                                    float *strength,
213                                    float *vert_color,
214                                    float *ratio_result,
215                                    int *index_from,
216                                    int *index_to)
217 {
218   float remaining_till_next = 0.0f;
219   float remaining_march = dist;
220   float step_start[3];
221   float point[3];
222   int next_point_index = index_next_pt;
223   bGPDspoint *pt = NULL;
224 
225   if (!(next_point_index < gps->totpoints)) {
226     return -1;
227   }
228 
229   copy_v3_v3(step_start, current);
230   pt = &gps->points[next_point_index];
231   copy_v3_v3(point, &pt->x);
232   remaining_till_next = len_v3v3(point, step_start);
233 
234   while (remaining_till_next < remaining_march) {
235     remaining_march -= remaining_till_next;
236     pt = &gps->points[next_point_index];
237     copy_v3_v3(point, &pt->x);
238     copy_v3_v3(step_start, point);
239     next_point_index++;
240     if (!(next_point_index < gps->totpoints)) {
241       next_point_index = gps->totpoints - 1;
242       break;
243     }
244     pt = &gps->points[next_point_index];
245     copy_v3_v3(point, &pt->x);
246     remaining_till_next = len_v3v3(point, step_start);
247   }
248   if (remaining_till_next < remaining_march) {
249     pt = &gps->points[next_point_index];
250     copy_v3_v3(result, &pt->x);
251     *pressure = gps->points[next_point_index].pressure;
252     *strength = gps->points[next_point_index].strength;
253     memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
254 
255     *index_from = next_point_index - 1;
256     *index_to = next_point_index;
257     *ratio_result = 1.0f;
258 
259     return 0;
260   }
261 
262   float ratio = remaining_march / remaining_till_next;
263   interp_v3_v3v3(result, step_start, point, ratio);
264   *pressure = interpf(
265       gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
266   *strength = interpf(
267       gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
268   interp_v4_v4v4(vert_color,
269                  gps->points[next_point_index - 1].vert_color,
270                  gps->points[next_point_index].vert_color,
271                  ratio);
272 
273   *index_from = next_point_index - 1;
274   *index_to = next_point_index;
275   *ratio_result = ratio;
276 
277   return next_point_index;
278 }
279 
stroke_march_next_point_no_interp(const bGPDstroke * gps,const int index_next_pt,const float * current,const float dist,float * result)280 static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
281                                              const int index_next_pt,
282                                              const float *current,
283                                              const float dist,
284                                              float *result)
285 {
286   float remaining_till_next = 0.0f;
287   float remaining_march = dist;
288   float step_start[3];
289   float point[3];
290   int next_point_index = index_next_pt;
291   bGPDspoint *pt = NULL;
292 
293   if (!(next_point_index < gps->totpoints)) {
294     return -1;
295   }
296 
297   copy_v3_v3(step_start, current);
298   pt = &gps->points[next_point_index];
299   copy_v3_v3(point, &pt->x);
300   remaining_till_next = len_v3v3(point, step_start);
301 
302   while (remaining_till_next < remaining_march) {
303     remaining_march -= remaining_till_next;
304     pt = &gps->points[next_point_index];
305     copy_v3_v3(point, &pt->x);
306     copy_v3_v3(step_start, point);
307     next_point_index++;
308     if (!(next_point_index < gps->totpoints)) {
309       next_point_index = gps->totpoints - 1;
310       break;
311     }
312     pt = &gps->points[next_point_index];
313     copy_v3_v3(point, &pt->x);
314     remaining_till_next = len_v3v3(point, step_start);
315   }
316   if (remaining_till_next < remaining_march) {
317     pt = &gps->points[next_point_index];
318     copy_v3_v3(result, &pt->x);
319     return 0;
320   }
321 
322   float ratio = remaining_march / remaining_till_next;
323   interp_v3_v3v3(result, step_start, point, ratio);
324   return next_point_index;
325 }
326 
stroke_march_count(const bGPDstroke * gps,const float dist)327 static int stroke_march_count(const bGPDstroke *gps, const float dist)
328 {
329   int point_count = 0;
330   float point[3];
331   int next_point_index = 1;
332   bGPDspoint *pt = NULL;
333 
334   pt = &gps->points[0];
335   copy_v3_v3(point, &pt->x);
336   point_count++;
337 
338   while ((next_point_index = stroke_march_next_point_no_interp(
339               gps, next_point_index, point, dist, point)) > -1) {
340     point_count++;
341     if (next_point_index == 0) {
342       break; /* last point finished */
343     }
344   }
345   return point_count;
346 }
347 
stroke_defvert_create_nr_list(MDeformVert * dv_list,int count,ListBase * result,int * totweight)348 static void stroke_defvert_create_nr_list(MDeformVert *dv_list,
349                                           int count,
350                                           ListBase *result,
351                                           int *totweight)
352 {
353   LinkData *ld;
354   MDeformVert *dv;
355   MDeformWeight *dw;
356   int i, j;
357   int tw = 0;
358   for (i = 0; i < count; i++) {
359     dv = &dv_list[i];
360 
361     /* find def_nr in list, if not exist, then create one */
362     for (j = 0; j < dv->totweight; j++) {
363       bool found = false;
364       dw = &dv->dw[j];
365       for (ld = result->first; ld; ld = ld->next) {
366         if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
367           found = true;
368           break;
369         }
370       }
371       if (!found) {
372         ld = MEM_callocN(sizeof(LinkData), "def_nr_item");
373         ld->data = POINTER_FROM_INT(dw->def_nr);
374         BLI_addtail(result, ld);
375         tw++;
376       }
377     }
378   }
379 
380   *totweight = tw;
381 }
382 
stroke_defvert_new_count(int count,int totweight,ListBase * def_nr_list)383 static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
384 {
385   int i, j;
386   LinkData *ld;
387   MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
388 
389   for (i = 0; i < count; i++) {
390     dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight");
391     dst[i].totweight = totweight;
392     j = 0;
393     /* re-assign deform groups */
394     for (ld = def_nr_list->first; ld; ld = ld->next) {
395       dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
396       j++;
397     }
398   }
399 
400   return dst;
401 }
402 
stroke_interpolate_deform_weights(bGPDstroke * gps,int index_from,int index_to,float ratio,MDeformVert * vert)403 static void stroke_interpolate_deform_weights(
404     bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
405 {
406   const MDeformVert *vl = &gps->dvert[index_from];
407   const MDeformVert *vr = &gps->dvert[index_to];
408 
409   for (int i = 0; i < vert->totweight; i++) {
410     float wl = BKE_defvert_find_weight(vl, vert->dw[i].def_nr);
411     float wr = BKE_defvert_find_weight(vr, vert->dw[i].def_nr);
412     vert->dw[i].weight = interpf(wr, wl, ratio);
413   }
414 }
415 
416 /**
417  * Resample a stroke
418  * \param gps: Stroke to sample
419  * \param dist: Distance of one segment
420  */
BKE_gpencil_stroke_sample(bGPDstroke * gps,const float dist,const bool select)421 bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool select)
422 {
423   bGPDspoint *pt = gps->points;
424   bGPDspoint *pt1 = NULL;
425   bGPDspoint *pt2 = NULL;
426   LinkData *ld;
427   ListBase def_nr_list = {0};
428 
429   if (gps->totpoints < 2 || dist < FLT_EPSILON) {
430     return false;
431   }
432   /* TODO: Implement feature point preservation. */
433   int count = stroke_march_count(gps, dist);
434 
435   bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled");
436   MDeformVert *new_dv = NULL;
437 
438   int result_totweight;
439 
440   if (gps->dvert != NULL) {
441     stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
442     new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
443   }
444 
445   int next_point_index = 1;
446   int i = 0;
447   float pressure, strength, ratio_result;
448   float vert_color[4];
449   int index_from, index_to;
450   float last_coord[3];
451 
452   /*  1st point is always at the start */
453   pt1 = &gps->points[0];
454   copy_v3_v3(last_coord, &pt1->x);
455   pt2 = &new_pt[i];
456   copy_v3_v3(&pt2->x, last_coord);
457   new_pt[i].pressure = pt[0].pressure;
458   new_pt[i].strength = pt[0].strength;
459   memcpy(new_pt[i].vert_color, pt[0].vert_color, sizeof(float[4]));
460   if (select) {
461     new_pt[i].flag |= GP_SPOINT_SELECT;
462   }
463   i++;
464 
465   if (new_dv) {
466     stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
467   }
468 
469   /* The rest. */
470   while ((next_point_index = stroke_march_next_point(gps,
471                                                      next_point_index,
472                                                      last_coord,
473                                                      dist,
474                                                      last_coord,
475                                                      &pressure,
476                                                      &strength,
477                                                      vert_color,
478                                                      &ratio_result,
479                                                      &index_from,
480                                                      &index_to)) > -1) {
481     pt2 = &new_pt[i];
482     copy_v3_v3(&pt2->x, last_coord);
483     new_pt[i].pressure = pressure;
484     new_pt[i].strength = strength;
485     memcpy(new_pt[i].vert_color, vert_color, sizeof(float[4]));
486     if (select) {
487       new_pt[i].flag |= GP_SPOINT_SELECT;
488     }
489 
490     if (new_dv) {
491       stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
492     }
493 
494     i++;
495     if (next_point_index == 0) {
496       break; /* last point finished */
497     }
498   }
499 
500   gps->points = new_pt;
501   /* Free original vertex list. */
502   MEM_freeN(pt);
503 
504   if (new_dv) {
505     /* Free original weight data. */
506     BKE_gpencil_free_stroke_weights(gps);
507     MEM_freeN(gps->dvert);
508     while ((ld = BLI_pophead(&def_nr_list))) {
509       MEM_freeN(ld);
510     }
511 
512     gps->dvert = new_dv;
513   }
514 
515   gps->totpoints = i;
516 
517   /* Calc geometry data. */
518   BKE_gpencil_stroke_geometry_update(gps);
519 
520   return true;
521 }
522 
523 /**
524  * Backbone stretch similar to Freestyle.
525  * \param gps: Stroke to sample
526  * \param dist: Distance of one segment
527  * \param tip_length: Ignore tip jittering, set zero to use default value.
528  */
BKE_gpencil_stroke_stretch(bGPDstroke * gps,const float dist,const float tip_length)529 bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
530 {
531   bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
532   float threshold = (tip_length == 0 ? 0.001f : tip_length);
533 
534   if (gps->totpoints < 2 || dist < FLT_EPSILON) {
535     return false;
536   }
537 
538   last_pt = &pt[gps->totpoints - 1];
539   second_last = &pt[gps->totpoints - 2];
540   next_pt = &pt[1];
541 
542   float len1 = 0.0f;
543   float len2 = 0.0f;
544 
545   int i = 1;
546   while (len1 < threshold && gps->totpoints > i) {
547     next_pt = &pt[i];
548     len1 = len_v3v3(&next_pt->x, &pt->x);
549     i++;
550   }
551 
552   i = 2;
553   while (len2 < threshold && gps->totpoints >= i) {
554     second_last = &pt[gps->totpoints - i];
555     len2 = len_v3v3(&last_pt->x, &second_last->x);
556     i++;
557   }
558 
559   float extend1 = (len1 + dist) / len1;
560   float extend2 = (len2 + dist) / len2;
561 
562   float result1[3], result2[3];
563 
564   interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
565   interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
566 
567   copy_v3_v3(&pt->x, result1);
568   copy_v3_v3(&last_pt->x, result2);
569 
570   return true;
571 }
572 
573 /**
574  * Trim stroke to needed segments
575  * \param gps: Target stroke
576  * \param index_from: the index of the first point to be used in the trimmed result
577  * \param index_to: the index of the last point to be used in the trimmed result
578  */
BKE_gpencil_stroke_trim_points(bGPDstroke * gps,const int index_from,const int index_to)579 bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to)
580 {
581   bGPDspoint *pt = gps->points, *new_pt;
582   MDeformVert *dv, *new_dv;
583 
584   const int new_count = index_to - index_from + 1;
585 
586   if (new_count >= gps->totpoints) {
587     return false;
588   }
589 
590   if (new_count == 1) {
591     BKE_gpencil_free_stroke_weights(gps);
592     MEM_freeN(gps->points);
593     gps->points = NULL;
594     gps->dvert = NULL;
595     gps->totpoints = 0;
596     return false;
597   }
598 
599   new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
600 
601   for (int i = 0; i < new_count; i++) {
602     memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
603   }
604 
605   if (gps->dvert) {
606     new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed");
607     for (int i = 0; i < new_count; i++) {
608       dv = &gps->dvert[i + index_from];
609       new_dv[i].flag = dv->flag;
610       new_dv[i].totweight = dv->totweight;
611       new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
612                                  "gp_stroke_dverts_dw_trimmed");
613       for (int j = 0; j < dv->totweight; j++) {
614         new_dv[i].dw[j].weight = dv->dw[j].weight;
615         new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
616       }
617     }
618     MEM_freeN(gps->dvert);
619     gps->dvert = new_dv;
620   }
621 
622   MEM_freeN(gps->points);
623   gps->points = new_pt;
624   gps->totpoints = new_count;
625 
626   return true;
627 }
628 
629 /**
630  * Split stroke.
631  * \param gpf: Grease pencil frame
632  * \param gps: Grease pencil original stroke
633  * \param before_index: Position of the point to split
634  * \param remaining_gps: Secondary stroke after split.
635  * \return True if the split was done
636  */
BKE_gpencil_stroke_split(bGPDframe * gpf,bGPDstroke * gps,const int before_index,bGPDstroke ** remaining_gps)637 bool BKE_gpencil_stroke_split(bGPDframe *gpf,
638                               bGPDstroke *gps,
639                               const int before_index,
640                               bGPDstroke **remaining_gps)
641 {
642   bGPDstroke *new_gps;
643   bGPDspoint *pt = gps->points, *new_pt;
644   MDeformVert *dv, *new_dv;
645 
646   if (before_index >= gps->totpoints || before_index == 0) {
647     return false;
648   }
649 
650   const int new_count = gps->totpoints - before_index;
651   const int old_count = before_index;
652 
653   /* Handle remaining segments first. */
654 
655   new_gps = BKE_gpencil_stroke_add_existing_style(
656       gpf, gps, gps->mat_nr, new_count, gps->thickness);
657 
658   new_pt = new_gps->points; /* Allocated from above. */
659 
660   for (int i = 0; i < new_count; i++) {
661     memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
662   }
663 
664   if (gps->dvert) {
665     new_dv = MEM_callocN(sizeof(MDeformVert) * new_count,
666                          "gp_stroke_dverts_remaining(MDeformVert)");
667     for (int i = 0; i < new_count; i++) {
668       dv = &gps->dvert[i + before_index];
669       new_dv[i].flag = dv->flag;
670       new_dv[i].totweight = dv->totweight;
671       new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
672                                  "gp_stroke_dverts_dw_remaining(MDeformWeight)");
673       for (int j = 0; j < dv->totweight; j++) {
674         new_dv[i].dw[j].weight = dv->dw[j].weight;
675         new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
676       }
677     }
678     new_gps->dvert = new_dv;
679   }
680 
681   (*remaining_gps) = new_gps;
682 
683   /* Trim the original stroke into a shorter one.
684    * Keep the end point. */
685 
686   BKE_gpencil_stroke_trim_points(gps, 0, old_count);
687   BKE_gpencil_stroke_geometry_update(gps);
688   return true;
689 }
690 
691 /**
692  * Shrink the stroke by length.
693  * \param gps: Stroke to shrink
694  * \param dist: delta length
695  */
BKE_gpencil_stroke_shrink(bGPDstroke * gps,const float dist)696 bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
697 {
698   bGPDspoint *pt = gps->points, *second_last;
699   int i;
700 
701   if (gps->totpoints < 2 || dist < FLT_EPSILON) {
702     return false;
703   }
704 
705   second_last = &pt[gps->totpoints - 2];
706 
707   float len1, this_len1, cut_len1;
708   float len2, this_len2, cut_len2;
709   int index_start, index_end;
710 
711   len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
712 
713   i = 1;
714   while (len1 < dist && gps->totpoints > i - 1) {
715     this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
716     len1 += this_len1;
717     cut_len1 = len1 - dist;
718     i++;
719   }
720   index_start = i - 2;
721 
722   i = 2;
723   while (len2 < dist && gps->totpoints >= i) {
724     second_last = &pt[gps->totpoints - i];
725     this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
726     len2 += this_len2;
727     cut_len2 = len2 - dist;
728     i++;
729   }
730   index_end = gps->totpoints - i + 2;
731 
732   if (len1 < dist || len2 < dist || index_end <= index_start) {
733     index_start = index_end = 0; /* empty stroke */
734   }
735 
736   if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
737     index_start = index_end = 0; /* no length left to cut */
738   }
739 
740   BKE_gpencil_stroke_trim_points(gps, index_start, index_end);
741 
742   if (gps->totpoints == 0) {
743     return false;
744   }
745 
746   pt = gps->points;
747 
748   float cut1 = cut_len1 / this_len1;
749   float cut2 = cut_len2 / this_len2;
750 
751   float result1[3], result2[3];
752 
753   interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
754   interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
755 
756   copy_v3_v3(&pt[0].x, result1);
757   copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
758 
759   return true;
760 }
761 
762 /**
763  * Apply smooth position to stroke point.
764  * \param gps: Stroke to smooth
765  * \param i: Point index
766  * \param inf: Amount of smoothing to apply
767  */
BKE_gpencil_stroke_smooth(bGPDstroke * gps,int i,float inf)768 bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf)
769 {
770   bGPDspoint *pt = &gps->points[i];
771   float sco[3] = {0.0f};
772 
773   /* Do nothing if not enough points to smooth out */
774   if (gps->totpoints <= 2) {
775     return false;
776   }
777 
778   /* Only affect endpoints by a fraction of the normal strength,
779    * to prevent the stroke from shrinking too much
780    */
781   if ((i == 0) || (i == gps->totpoints - 1)) {
782     inf *= 0.1f;
783   }
784 
785   /* Compute smoothed coordinate by taking the ones nearby */
786   /* XXX: This is potentially slow,
787    *      and suffers from accumulation error as earlier points are handled before later ones. */
788   {
789     /* XXX: this is hardcoded to look at 2 points on either side of the current one
790      * (i.e. 5 items total). */
791     const int steps = 2;
792     const float average_fac = 1.0f / (float)(steps * 2 + 1);
793     int step;
794 
795     /* add the point itself */
796     madd_v3_v3fl(sco, &pt->x, average_fac);
797 
798     /* n-steps before/after current point */
799     /* XXX: review how the endpoints are treated by this algorithm. */
800     /* XXX: falloff measures should also introduce some weighting variations,
801      *      so that further-out points get less weight. */
802     for (step = 1; step <= steps; step++) {
803       bGPDspoint *pt1, *pt2;
804       int before = i - step;
805       int after = i + step;
806 
807       CLAMP_MIN(before, 0);
808       CLAMP_MAX(after, gps->totpoints - 1);
809 
810       pt1 = &gps->points[before];
811       pt2 = &gps->points[after];
812 
813       /* add both these points to the average-sum (s += p[i]/n) */
814       madd_v3_v3fl(sco, &pt1->x, average_fac);
815       madd_v3_v3fl(sco, &pt2->x, average_fac);
816     }
817   }
818 
819   /* Based on influence factor, blend between original and optimal smoothed coordinate */
820   interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
821 
822   return true;
823 }
824 
825 /**
826  * Apply smooth strength to stroke point.
827  * \param gps: Stroke to smooth
828  * \param point_index: Point index
829  * \param influence: Amount of smoothing to apply
830  */
BKE_gpencil_stroke_smooth_strength(bGPDstroke * gps,int point_index,float influence)831 bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
832 {
833   bGPDspoint *ptb = &gps->points[point_index];
834 
835   /* Do nothing if not enough points */
836   if ((gps->totpoints <= 2) || (point_index < 1)) {
837     return false;
838   }
839   /* Only affect endpoints by a fraction of the normal influence */
840   float inf = influence;
841   if ((point_index == 0) || (point_index == gps->totpoints - 1)) {
842     inf *= 0.01f;
843   }
844   /* Limit max influence to reduce pop effect. */
845   CLAMP_MAX(inf, 0.98f);
846 
847   float total = 0.0f;
848   float max_strength = 0.0f;
849   const int steps = 4;
850   const float average_fac = 1.0f / (float)(steps * 2 + 1);
851   int step;
852 
853   /* add the point itself */
854   total += ptb->strength * average_fac;
855   max_strength = ptb->strength;
856 
857   /* n-steps before/after current point */
858   for (step = 1; step <= steps; step++) {
859     bGPDspoint *pt1, *pt2;
860     int before = point_index - step;
861     int after = point_index + step;
862 
863     CLAMP_MIN(before, 0);
864     CLAMP_MAX(after, gps->totpoints - 1);
865 
866     pt1 = &gps->points[before];
867     pt2 = &gps->points[after];
868 
869     /* add both these points to the average-sum (s += p[i]/n) */
870     total += pt1->strength * average_fac;
871     total += pt2->strength * average_fac;
872     /* Save max value. */
873     if (max_strength < pt1->strength) {
874       max_strength = pt1->strength;
875     }
876     if (max_strength < pt2->strength) {
877       max_strength = pt2->strength;
878     }
879   }
880 
881   /* Based on influence factor, blend between original and optimal smoothed value. */
882   ptb->strength = interpf(ptb->strength, total, inf);
883   /* Clamp to maximum stroke strength to avoid weird results. */
884   CLAMP_MAX(ptb->strength, max_strength);
885 
886   return true;
887 }
888 
889 /**
890  * Apply smooth for thickness to stroke point (use pressure).
891  * \param gps: Stroke to smooth
892  * \param point_index: Point index
893  * \param influence: Amount of smoothing to apply
894  */
BKE_gpencil_stroke_smooth_thickness(bGPDstroke * gps,int point_index,float influence)895 bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
896 {
897   bGPDspoint *ptb = &gps->points[point_index];
898 
899   /* Do nothing if not enough points */
900   if ((gps->totpoints <= 2) || (point_index < 1)) {
901     return false;
902   }
903   /* Only affect endpoints by a fraction of the normal influence */
904   float inf = influence;
905   if ((point_index == 0) || (point_index == gps->totpoints - 1)) {
906     inf *= 0.01f;
907   }
908   /* Limit max influence to reduce pop effect. */
909   CLAMP_MAX(inf, 0.98f);
910 
911   float total = 0.0f;
912   float max_pressure = 0.0f;
913   const int steps = 4;
914   const float average_fac = 1.0f / (float)(steps * 2 + 1);
915   int step;
916 
917   /* add the point itself */
918   total += ptb->pressure * average_fac;
919   max_pressure = ptb->pressure;
920 
921   /* n-steps before/after current point */
922   for (step = 1; step <= steps; step++) {
923     bGPDspoint *pt1, *pt2;
924     int before = point_index - step;
925     int after = point_index + step;
926 
927     CLAMP_MIN(before, 0);
928     CLAMP_MAX(after, gps->totpoints - 1);
929 
930     pt1 = &gps->points[before];
931     pt2 = &gps->points[after];
932 
933     /* add both these points to the average-sum (s += p[i]/n) */
934     total += pt1->pressure * average_fac;
935     total += pt2->pressure * average_fac;
936     /* Save max value. */
937     if (max_pressure < pt1->pressure) {
938       max_pressure = pt1->pressure;
939     }
940     if (max_pressure < pt2->pressure) {
941       max_pressure = pt2->pressure;
942     }
943   }
944 
945   /* Based on influence factor, blend between original and optimal smoothed value. */
946   ptb->pressure = interpf(ptb->pressure, total, inf);
947   /* Clamp to maximum stroke thickness to avoid weird results. */
948   CLAMP_MAX(ptb->pressure, max_pressure);
949   return true;
950 }
951 
952 /**
953  * Apply smooth for UV rotation to stroke point (use pressure).
954  * \param gps: Stroke to smooth
955  * \param point_index: Point index
956  * \param influence: Amount of smoothing to apply
957  */
BKE_gpencil_stroke_smooth_uv(bGPDstroke * gps,int point_index,float influence)958 bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
959 {
960   bGPDspoint *ptb = &gps->points[point_index];
961 
962   /* Do nothing if not enough points */
963   if (gps->totpoints <= 2) {
964     return false;
965   }
966 
967   /* Compute theoretical optimal value */
968   bGPDspoint *pta, *ptc;
969   int before = point_index - 1;
970   int after = point_index + 1;
971 
972   CLAMP_MIN(before, 0);
973   CLAMP_MAX(after, gps->totpoints - 1);
974 
975   pta = &gps->points[before];
976   ptc = &gps->points[after];
977 
978   /* the optimal value is the corresponding to the interpolation of the pressure
979    * at the distance of point b
980    */
981   float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
982   /* sometimes the factor can be wrong due stroke geometry, so use middle point */
983   if ((fac < 0.0f) || (fac > 1.0f)) {
984     fac = 0.5f;
985   }
986   float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
987 
988   /* Based on influence factor, blend between original and optimal */
989   ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
990   CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
991 
992   return true;
993 }
994 
995 /**
996  * Get points of stroke always flat to view not affected
997  * by camera view or view position.
998  * \param points: Array of grease pencil points (3D)
999  * \param totpoints: Total of points
1000  * \param points2d: Result array of 2D points
1001  * \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0)
1002  */
BKE_gpencil_stroke_2d_flat(const bGPDspoint * points,int totpoints,float (* points2d)[2],int * r_direction)1003 void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
1004                                 int totpoints,
1005                                 float (*points2d)[2],
1006                                 int *r_direction)
1007 {
1008   BLI_assert(totpoints >= 2);
1009 
1010   const bGPDspoint *pt0 = &points[0];
1011   const bGPDspoint *pt1 = &points[1];
1012   const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
1013 
1014   float locx[3];
1015   float locy[3];
1016   float loc3[3];
1017   float normal[3];
1018 
1019   /* local X axis (p0 -> p1) */
1020   sub_v3_v3v3(locx, &pt1->x, &pt0->x);
1021 
1022   /* point vector at 3/4 */
1023   float v3[3];
1024   if (totpoints == 2) {
1025     mul_v3_v3fl(v3, &pt3->x, 0.001f);
1026   }
1027   else {
1028     copy_v3_v3(v3, &pt3->x);
1029   }
1030 
1031   sub_v3_v3v3(loc3, v3, &pt0->x);
1032 
1033   /* vector orthogonal to polygon plane */
1034   cross_v3_v3v3(normal, locx, loc3);
1035 
1036   /* local Y axis (cross to normal/x axis) */
1037   cross_v3_v3v3(locy, normal, locx);
1038 
1039   /* Normalize vectors */
1040   normalize_v3(locx);
1041   normalize_v3(locy);
1042 
1043   /* Get all points in local space */
1044   for (int i = 0; i < totpoints; i++) {
1045     const bGPDspoint *pt = &points[i];
1046     float loc[3];
1047 
1048     /* Get local space using first point as origin */
1049     sub_v3_v3v3(loc, &pt->x, &pt0->x);
1050 
1051     points2d[i][0] = dot_v3v3(loc, locx);
1052     points2d[i][1] = dot_v3v3(loc, locy);
1053   }
1054 
1055   /* Concave (-1), Convex (1), or Auto-detect (0)? */
1056   *r_direction = (int)locy[2];
1057 }
1058 
1059 /**
1060  * Get points of stroke always flat to view not affected by camera view or view position
1061  * using another stroke as reference.
1062  * \param ref_points: Array of reference points (3D)
1063  * \param ref_totpoints: Total reference points
1064  * \param points: Array of points to flat (3D)
1065  * \param totpoints: Total points
1066  * \param points2d: Result array of 2D points
1067  * \param scale: Scale factor
1068  * \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0)
1069  */
BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint * ref_points,int ref_totpoints,const bGPDspoint * points,int totpoints,float (* points2d)[2],const float scale,int * r_direction)1070 void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
1071                                     int ref_totpoints,
1072                                     const bGPDspoint *points,
1073                                     int totpoints,
1074                                     float (*points2d)[2],
1075                                     const float scale,
1076                                     int *r_direction)
1077 {
1078   BLI_assert(totpoints >= 2);
1079 
1080   const bGPDspoint *pt0 = &ref_points[0];
1081   const bGPDspoint *pt1 = &ref_points[1];
1082   const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)];
1083 
1084   float locx[3];
1085   float locy[3];
1086   float loc3[3];
1087   float normal[3];
1088 
1089   /* local X axis (p0 -> p1) */
1090   sub_v3_v3v3(locx, &pt1->x, &pt0->x);
1091 
1092   /* point vector at 3/4 */
1093   float v3[3];
1094   if (totpoints == 2) {
1095     mul_v3_v3fl(v3, &pt3->x, 0.001f);
1096   }
1097   else {
1098     copy_v3_v3(v3, &pt3->x);
1099   }
1100 
1101   sub_v3_v3v3(loc3, v3, &pt0->x);
1102 
1103   /* vector orthogonal to polygon plane */
1104   cross_v3_v3v3(normal, locx, loc3);
1105 
1106   /* local Y axis (cross to normal/x axis) */
1107   cross_v3_v3v3(locy, normal, locx);
1108 
1109   /* Normalize vectors */
1110   normalize_v3(locx);
1111   normalize_v3(locy);
1112 
1113   /* Get all points in local space */
1114   for (int i = 0; i < totpoints; i++) {
1115     const bGPDspoint *pt = &points[i];
1116     float loc[3];
1117     float v1[3];
1118     float vn[3] = {0.0f, 0.0f, 0.0f};
1119 
1120     /* apply scale to extremes of the stroke to get better collision detection
1121      * the scale is divided to get more control in the UI parameter
1122      */
1123     /* first point */
1124     if (i == 0) {
1125       const bGPDspoint *pt_next = &points[i + 1];
1126       sub_v3_v3v3(vn, &pt->x, &pt_next->x);
1127       normalize_v3(vn);
1128       mul_v3_fl(vn, scale / 10.0f);
1129       add_v3_v3v3(v1, &pt->x, vn);
1130     }
1131     /* last point */
1132     else if (i == totpoints - 1) {
1133       const bGPDspoint *pt_prev = &points[i - 1];
1134       sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
1135       normalize_v3(vn);
1136       mul_v3_fl(vn, scale / 10.0f);
1137       add_v3_v3v3(v1, &pt->x, vn);
1138     }
1139     else {
1140       copy_v3_v3(v1, &pt->x);
1141     }
1142 
1143     /* Get local space using first point as origin (ref stroke) */
1144     sub_v3_v3v3(loc, v1, &pt0->x);
1145 
1146     points2d[i][0] = dot_v3v3(loc, locx);
1147     points2d[i][1] = dot_v3v3(loc, locy);
1148   }
1149 
1150   /* Concave (-1), Convex (1), or Auto-detect (0)? */
1151   *r_direction = (int)locy[2];
1152 }
1153 
1154 /* Calc texture coordinates using flat projected points. */
gpencil_calc_stroke_fill_uv(const float (* points2d)[2],bGPDstroke * gps,const float minv[2],const float maxv[2],float (* r_uv)[2])1155 static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
1156                                         bGPDstroke *gps,
1157                                         const float minv[2],
1158                                         const float maxv[2],
1159                                         float (*r_uv)[2])
1160 {
1161   const float s = sin(gps->uv_rotation);
1162   const float c = cos(gps->uv_rotation);
1163 
1164   /* Calc center for rotation. */
1165   float center[2] = {0.5f, 0.5f};
1166   float d[2];
1167   d[0] = maxv[0] - minv[0];
1168   d[1] = maxv[1] - minv[1];
1169   for (int i = 0; i < gps->totpoints; i++) {
1170     r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
1171     r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
1172 
1173     /* Apply translation. */
1174     add_v2_v2(r_uv[i], gps->uv_translation);
1175 
1176     /* Apply Rotation. */
1177     r_uv[i][0] -= center[0];
1178     r_uv[i][1] -= center[1];
1179 
1180     float x = r_uv[i][0] * c - r_uv[i][1] * s;
1181     float y = r_uv[i][0] * s + r_uv[i][1] * c;
1182 
1183     r_uv[i][0] = x + center[0];
1184     r_uv[i][1] = y + center[1];
1185 
1186     /* Apply scale. */
1187     if (gps->uv_scale != 0.0f) {
1188       mul_v2_fl(r_uv[i], 1.0f / gps->uv_scale);
1189     }
1190   }
1191 }
1192 
1193 /**
1194  * Triangulate stroke to generate data for filling areas.
1195  * \param gps: Grease pencil stroke
1196  */
BKE_gpencil_stroke_fill_triangulate(bGPDstroke * gps)1197 void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
1198 {
1199   BLI_assert(gps->totpoints >= 3);
1200 
1201   /* allocate memory for temporary areas */
1202   gps->tot_triangles = gps->totpoints - 2;
1203   uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
1204                                         "GP Stroke temp triangulation");
1205   float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
1206                                     "GP Stroke temp 2d points");
1207   float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
1208 
1209   int direction = 0;
1210 
1211   /* convert to 2d and triangulate */
1212   BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
1213   BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
1214 
1215   /* calc texture coordinates automatically */
1216   float minv[2];
1217   float maxv[2];
1218   /* first needs bounding box data */
1219   ARRAY_SET_ITEMS(minv, -1.0f, -1.0f);
1220   ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f);
1221 
1222   /* calc uv data */
1223   gpencil_calc_stroke_fill_uv(points2d, gps, minv, maxv, uv);
1224 
1225   /* Save triangulation data. */
1226   if (gps->tot_triangles > 0) {
1227     MEM_SAFE_FREE(gps->triangles);
1228     gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
1229                                  "GP Stroke triangulation");
1230 
1231     for (int i = 0; i < gps->tot_triangles; i++) {
1232       memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
1233     }
1234 
1235     /* Copy UVs to bGPDspoint. */
1236     for (int i = 0; i < gps->totpoints; i++) {
1237       copy_v2_v2(gps->points[i].uv_fill, uv[i]);
1238     }
1239   }
1240   else {
1241     /* No triangles needed - Free anything allocated previously */
1242     if (gps->triangles) {
1243       MEM_freeN(gps->triangles);
1244     }
1245 
1246     gps->triangles = NULL;
1247   }
1248 
1249   /* clear memory */
1250   MEM_SAFE_FREE(tmp_triangles);
1251   MEM_SAFE_FREE(points2d);
1252   MEM_SAFE_FREE(uv);
1253 }
1254 
1255 /**
1256  * Update Stroke UV data.
1257  * \param gps: Grease pencil stroke
1258  */
BKE_gpencil_stroke_uv_update(bGPDstroke * gps)1259 void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
1260 {
1261   if (gps == NULL || gps->totpoints == 0) {
1262     return;
1263   }
1264 
1265   bGPDspoint *pt = gps->points;
1266   float totlen = 0.0f;
1267   pt[0].uv_fac = totlen;
1268   for (int i = 1; i < gps->totpoints; i++) {
1269     totlen += len_v3v3(&pt[i - 1].x, &pt[i].x);
1270     pt[i].uv_fac = totlen;
1271   }
1272 }
1273 
1274 /**
1275  * Recalc all internal geometry data for the stroke
1276  * \param gps: Grease pencil stroke
1277  */
BKE_gpencil_stroke_geometry_update(bGPDstroke * gps)1278 void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
1279 {
1280   if (gps == NULL) {
1281     return;
1282   }
1283 
1284   if (gps->totpoints > 2) {
1285     BKE_gpencil_stroke_fill_triangulate(gps);
1286   }
1287   else {
1288     gps->tot_triangles = 0;
1289     MEM_SAFE_FREE(gps->triangles);
1290   }
1291 
1292   /* calc uv data along the stroke */
1293   BKE_gpencil_stroke_uv_update(gps);
1294 
1295   /* Calc stroke bounding box. */
1296   BKE_gpencil_stroke_boundingbox_calc(gps);
1297 }
1298 
1299 /**
1300  * Calculate grease pencil stroke length.
1301  * \param gps: Grease pencil stroke
1302  * \param use_3d: Set to true to use 3D points
1303  * \return Length of the stroke
1304  */
BKE_gpencil_stroke_length(const bGPDstroke * gps,bool use_3d)1305 float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
1306 {
1307   if (!gps->points || gps->totpoints < 2) {
1308     return 0.0f;
1309   }
1310   float *last_pt = &gps->points[0].x;
1311   float total_length = 0.0f;
1312   for (int i = 1; i < gps->totpoints; i++) {
1313     bGPDspoint *pt = &gps->points[i];
1314     if (use_3d) {
1315       total_length += len_v3v3(&pt->x, last_pt);
1316     }
1317     else {
1318       total_length += len_v2v2(&pt->x, last_pt);
1319     }
1320     last_pt = &pt->x;
1321   }
1322   return total_length;
1323 }
1324 
1325 /**
1326  * Trim stroke to the first intersection or loop.
1327  * \param gps: Stroke data
1328  */
BKE_gpencil_stroke_trim(bGPDstroke * gps)1329 bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
1330 {
1331   if (gps->totpoints < 4) {
1332     return false;
1333   }
1334   bool intersect = false;
1335   int start = 0;
1336   int end = 0;
1337   float point[3];
1338   /* loop segments from start until we have an intersection */
1339   for (int i = 0; i < gps->totpoints - 2; i++) {
1340     start = i;
1341     bGPDspoint *a = &gps->points[start];
1342     bGPDspoint *b = &gps->points[start + 1];
1343     for (int j = start + 2; j < gps->totpoints - 1; j++) {
1344       end = j + 1;
1345       bGPDspoint *c = &gps->points[j];
1346       bGPDspoint *d = &gps->points[end];
1347       float pointb[3];
1348       /* get intersection */
1349       if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) {
1350         if (len_v3(point) > 0.0f) {
1351           float closest[3];
1352           /* check intersection is on both lines */
1353           float lambda = closest_to_line_v3(closest, point, &a->x, &b->x);
1354           if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
1355             continue;
1356           }
1357           lambda = closest_to_line_v3(closest, point, &c->x, &d->x);
1358           if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
1359             continue;
1360           }
1361 
1362           intersect = true;
1363           break;
1364         }
1365       }
1366     }
1367     if (intersect) {
1368       break;
1369     }
1370   }
1371 
1372   /* trim unwanted points */
1373   if (intersect) {
1374 
1375     /* save points */
1376     bGPDspoint *old_points = MEM_dupallocN(gps->points);
1377     MDeformVert *old_dvert = NULL;
1378     MDeformVert *dvert_src = NULL;
1379 
1380     if (gps->dvert != NULL) {
1381       old_dvert = MEM_dupallocN(gps->dvert);
1382     }
1383 
1384     /* resize gps */
1385     int newtot = end - start + 1;
1386 
1387     gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
1388     if (gps->dvert != NULL) {
1389       gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
1390     }
1391 
1392     for (int i = 0; i < newtot; i++) {
1393       int idx = start + i;
1394       bGPDspoint *pt_src = &old_points[idx];
1395       bGPDspoint *pt_new = &gps->points[i];
1396       memcpy(pt_new, pt_src, sizeof(bGPDspoint));
1397       if (gps->dvert != NULL) {
1398         dvert_src = &old_dvert[idx];
1399         MDeformVert *dvert = &gps->dvert[i];
1400         memcpy(dvert, dvert_src, sizeof(MDeformVert));
1401         if (dvert_src->dw) {
1402           memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1403         }
1404       }
1405       if (idx == start || idx == end) {
1406         copy_v3_v3(&pt_new->x, point);
1407       }
1408     }
1409 
1410     gps->totpoints = newtot;
1411 
1412     MEM_SAFE_FREE(old_points);
1413     MEM_SAFE_FREE(old_dvert);
1414   }
1415 
1416   BKE_gpencil_stroke_geometry_update(gps);
1417 
1418   return intersect;
1419 }
1420 
1421 /**
1422  * Close grease pencil stroke.
1423  * \param gps: Stroke to close
1424  */
BKE_gpencil_stroke_close(bGPDstroke * gps)1425 bool BKE_gpencil_stroke_close(bGPDstroke *gps)
1426 {
1427   bGPDspoint *pt1 = NULL;
1428   bGPDspoint *pt2 = NULL;
1429 
1430   /* Only can close a stroke with 3 points or more. */
1431   if (gps->totpoints < 3) {
1432     return false;
1433   }
1434 
1435   /* Calc average distance between points to get same level of sampling. */
1436   float dist_tot = 0.0f;
1437   for (int i = 0; i < gps->totpoints - 1; i++) {
1438     pt1 = &gps->points[i];
1439     pt2 = &gps->points[i + 1];
1440     dist_tot += len_v3v3(&pt1->x, &pt2->x);
1441   }
1442   /* Calc the average distance. */
1443   float dist_avg = dist_tot / (gps->totpoints - 1);
1444 
1445   /* Calc distance between last and first point. */
1446   pt1 = &gps->points[gps->totpoints - 1];
1447   pt2 = &gps->points[0];
1448   float dist_close = len_v3v3(&pt1->x, &pt2->x);
1449 
1450   /* if the distance to close is very small, don't need add points and just enable cyclic. */
1451   if (dist_close <= dist_avg) {
1452     gps->flag |= GP_STROKE_CYCLIC;
1453     return true;
1454   }
1455 
1456   /* Calc number of points required using the average distance. */
1457   int tot_newpoints = MAX2(dist_close / dist_avg, 1);
1458 
1459   /* Resize stroke array. */
1460   int old_tot = gps->totpoints;
1461   gps->totpoints += tot_newpoints;
1462   gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1463   if (gps->dvert != NULL) {
1464     gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1465   }
1466 
1467   /* Generate new points */
1468   pt1 = &gps->points[old_tot - 1];
1469   pt2 = &gps->points[0];
1470   bGPDspoint *pt = &gps->points[old_tot];
1471   for (int i = 1; i < tot_newpoints + 1; i++, pt++) {
1472     float step = (tot_newpoints > 1) ? ((float)i / (float)tot_newpoints) : 0.99f;
1473     /* Clamp last point to be near, but not on top of first point. */
1474     if ((tot_newpoints > 1) && (i == tot_newpoints)) {
1475       step *= 0.99f;
1476     }
1477 
1478     /* Average point. */
1479     interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step);
1480     pt->pressure = interpf(pt2->pressure, pt1->pressure, step);
1481     pt->strength = interpf(pt2->strength, pt1->strength, step);
1482     pt->flag = 0;
1483     interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
1484 
1485     /* Set weights. */
1486     if (gps->dvert != NULL) {
1487       MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
1488       MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
1489       float weight_1 = dw1 ? dw1->weight : 0.0f;
1490 
1491       MDeformVert *dvert2 = &gps->dvert[0];
1492       MDeformWeight *dw2 = BKE_defvert_ensure_index(dvert2, 0);
1493       float weight_2 = dw2 ? dw2->weight : 0.0f;
1494 
1495       MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1];
1496       dvert_final->totweight = 0;
1497       MDeformWeight *dw = BKE_defvert_ensure_index(dvert_final, 0);
1498       if (dvert_final->dw) {
1499         dw->weight = interpf(weight_2, weight_1, step);
1500       }
1501     }
1502   }
1503 
1504   /* Enable cyclic flag. */
1505   gps->flag |= GP_STROKE_CYCLIC;
1506 
1507   return true;
1508 }
1509 
1510 /**
1511  * Dissolve points in stroke.
1512  * \param gpf: Grease pencil frame
1513  * \param gps: Grease pencil stroke
1514  * \param tag: Type of tag for point
1515  */
BKE_gpencil_dissolve_points(bGPDframe * gpf,bGPDstroke * gps,const short tag)1516 void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag)
1517 {
1518   bGPDspoint *pt;
1519   MDeformVert *dvert = NULL;
1520   int i;
1521 
1522   int tot = gps->totpoints; /* number of points in new buffer */
1523   /* first pass: count points to remove */
1524   /* Count how many points are selected (i.e. how many to remove) */
1525   for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1526     if (pt->flag & tag) {
1527       /* selected point - one of the points to remove */
1528       tot--;
1529     }
1530   }
1531 
1532   /* if no points are left, we simply delete the entire stroke */
1533   if (tot <= 0) {
1534     /* remove the entire stroke */
1535     if (gps->points) {
1536       MEM_freeN(gps->points);
1537     }
1538     if (gps->dvert) {
1539       BKE_gpencil_free_stroke_weights(gps);
1540       MEM_freeN(gps->dvert);
1541     }
1542     if (gps->triangles) {
1543       MEM_freeN(gps->triangles);
1544     }
1545     BLI_freelinkN(&gpf->strokes, gps);
1546   }
1547   else {
1548     /* just copy all points to keep into a smaller buffer */
1549     bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
1550     bGPDspoint *npt = new_points;
1551 
1552     MDeformVert *new_dvert = NULL;
1553     MDeformVert *ndvert = NULL;
1554 
1555     if (gps->dvert != NULL) {
1556       new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
1557       ndvert = new_dvert;
1558     }
1559 
1560     (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1561     for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1562       if ((pt->flag & tag) == 0) {
1563         *npt = *pt;
1564         npt++;
1565 
1566         if (gps->dvert != NULL) {
1567           *ndvert = *dvert;
1568           ndvert->dw = MEM_dupallocN(dvert->dw);
1569           ndvert++;
1570         }
1571       }
1572       if (gps->dvert != NULL) {
1573         dvert++;
1574       }
1575     }
1576 
1577     /* free the old buffer */
1578     if (gps->points) {
1579       MEM_freeN(gps->points);
1580     }
1581     if (gps->dvert) {
1582       BKE_gpencil_free_stroke_weights(gps);
1583       MEM_freeN(gps->dvert);
1584     }
1585 
1586     /* save the new buffer */
1587     gps->points = new_points;
1588     gps->dvert = new_dvert;
1589     gps->totpoints = tot;
1590 
1591     /* triangles cache needs to be recalculated */
1592     BKE_gpencil_stroke_geometry_update(gps);
1593   }
1594 }
1595 
1596 /**
1597  * Calculate stroke normals.
1598  * \param gps: Grease pencil stroke
1599  * \param r_normal: Return Normal vector normalized
1600  */
BKE_gpencil_stroke_normal(const bGPDstroke * gps,float r_normal[3])1601 void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
1602 {
1603   if (gps->totpoints < 3) {
1604     zero_v3(r_normal);
1605     return;
1606   }
1607 
1608   bGPDspoint *points = gps->points;
1609   int totpoints = gps->totpoints;
1610 
1611   const bGPDspoint *pt0 = &points[0];
1612   const bGPDspoint *pt1 = &points[1];
1613   const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
1614 
1615   float vec1[3];
1616   float vec2[3];
1617 
1618   /* initial vector (p0 -> p1) */
1619   sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
1620 
1621   /* point vector at 3/4 */
1622   sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
1623 
1624   /* vector orthogonal to polygon plane */
1625   cross_v3_v3v3(r_normal, vec1, vec2);
1626 
1627   /* Normalize vector */
1628   normalize_v3(r_normal);
1629 }
1630 
1631 /* Stroke Simplify ------------------------------------- */
1632 
1633 /** Reduce a series of points to a simplified version, but
1634  * maintains the general shape of the series
1635  *
1636  * Ramer - Douglas - Peucker algorithm
1637  * by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
1638  * \param gps: Grease pencil stroke
1639  * \param epsilon: Epsilon value to define precision of the algorithm
1640  */
BKE_gpencil_stroke_simplify_adaptive(bGPDstroke * gps,float epsilon)1641 void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
1642 {
1643   bGPDspoint *old_points = MEM_dupallocN(gps->points);
1644   int totpoints = gps->totpoints;
1645   char *marked = NULL;
1646   char work;
1647 
1648   int start = 0;
1649   int end = gps->totpoints - 1;
1650 
1651   marked = MEM_callocN(totpoints, "GP marked array");
1652   marked[start] = 1;
1653   marked[end] = 1;
1654 
1655   work = 1;
1656   int totmarked = 0;
1657   /* while still reducing */
1658   while (work) {
1659     int ls, le;
1660     work = 0;
1661 
1662     ls = start;
1663     le = start + 1;
1664 
1665     /* while not over interval */
1666     while (ls < end) {
1667       int max_i = 0;
1668       /* divided to get more control */
1669       float max_dist = epsilon / 10.0f;
1670 
1671       /* find the next marked point */
1672       while (marked[le] == 0) {
1673         le++;
1674       }
1675 
1676       for (int i = ls + 1; i < le; i++) {
1677         float point_on_line[3];
1678         float dist;
1679 
1680         closest_to_line_segment_v3(
1681             point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
1682 
1683         dist = len_v3v3(point_on_line, &old_points[i].x);
1684 
1685         if (dist > max_dist) {
1686           max_dist = dist;
1687           max_i = i;
1688         }
1689       }
1690 
1691       if (max_i != 0) {
1692         work = 1;
1693         marked[max_i] = 1;
1694         totmarked++;
1695       }
1696 
1697       ls = le;
1698       le = ls + 1;
1699     }
1700   }
1701 
1702   /* adding points marked */
1703   MDeformVert *old_dvert = NULL;
1704   MDeformVert *dvert_src = NULL;
1705 
1706   if (gps->dvert != NULL) {
1707     old_dvert = MEM_dupallocN(gps->dvert);
1708   }
1709   /* resize gps */
1710   int j = 0;
1711   for (int i = 0; i < totpoints; i++) {
1712     bGPDspoint *pt_src = &old_points[i];
1713     bGPDspoint *pt = &gps->points[j];
1714 
1715     if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
1716       memcpy(pt, pt_src, sizeof(bGPDspoint));
1717       if (gps->dvert != NULL) {
1718         dvert_src = &old_dvert[i];
1719         MDeformVert *dvert = &gps->dvert[j];
1720         memcpy(dvert, dvert_src, sizeof(MDeformVert));
1721         if (dvert_src->dw) {
1722           memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1723         }
1724       }
1725       j++;
1726     }
1727     else {
1728       if (gps->dvert != NULL) {
1729         dvert_src = &old_dvert[i];
1730         BKE_gpencil_free_point_weights(dvert_src);
1731       }
1732     }
1733   }
1734 
1735   gps->totpoints = j;
1736 
1737   /* Calc geometry data. */
1738   BKE_gpencil_stroke_geometry_update(gps);
1739 
1740   MEM_SAFE_FREE(old_points);
1741   MEM_SAFE_FREE(old_dvert);
1742   MEM_SAFE_FREE(marked);
1743 }
1744 
1745 /**
1746  * Simplify alternate vertex of stroke except extremes.
1747  * \param gps: Grease pencil stroke
1748  */
BKE_gpencil_stroke_simplify_fixed(bGPDstroke * gps)1749 void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
1750 {
1751   if (gps->totpoints < 5) {
1752     return;
1753   }
1754 
1755   /* save points */
1756   bGPDspoint *old_points = MEM_dupallocN(gps->points);
1757   MDeformVert *old_dvert = NULL;
1758   MDeformVert *dvert_src = NULL;
1759 
1760   if (gps->dvert != NULL) {
1761     old_dvert = MEM_dupallocN(gps->dvert);
1762   }
1763 
1764   /* resize gps */
1765   int newtot = (gps->totpoints - 2) / 2;
1766   if (((gps->totpoints - 2) % 2) > 0) {
1767     newtot++;
1768   }
1769   newtot += 2;
1770 
1771   gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
1772   if (gps->dvert != NULL) {
1773     gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
1774   }
1775 
1776   int j = 0;
1777   for (int i = 0; i < gps->totpoints; i++) {
1778     bGPDspoint *pt_src = &old_points[i];
1779     bGPDspoint *pt = &gps->points[j];
1780 
1781     if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
1782       memcpy(pt, pt_src, sizeof(bGPDspoint));
1783       if (gps->dvert != NULL) {
1784         dvert_src = &old_dvert[i];
1785         MDeformVert *dvert = &gps->dvert[j];
1786         memcpy(dvert, dvert_src, sizeof(MDeformVert));
1787         if (dvert_src->dw) {
1788           memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1789         }
1790       }
1791       j++;
1792     }
1793     else {
1794       if (gps->dvert != NULL) {
1795         dvert_src = &old_dvert[i];
1796         BKE_gpencil_free_point_weights(dvert_src);
1797       }
1798     }
1799   }
1800 
1801   gps->totpoints = j;
1802   /* Calc geometry data. */
1803   BKE_gpencil_stroke_geometry_update(gps);
1804 
1805   MEM_SAFE_FREE(old_points);
1806   MEM_SAFE_FREE(old_dvert);
1807 }
1808 
1809 /**
1810  * Subdivide grease pencil stroke.
1811  * \param gps: Grease pencil stroke
1812  * \param level: Level of subdivision
1813  * \param type: Type of subdivision
1814  */
BKE_gpencil_stroke_subdivide(bGPDstroke * gps,int level,int type)1815 void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
1816 {
1817   bGPDspoint *temp_points;
1818   MDeformVert *temp_dverts = NULL;
1819   MDeformVert *dvert = NULL;
1820   MDeformVert *dvert_final = NULL;
1821   MDeformVert *dvert_next = NULL;
1822   int totnewpoints, oldtotpoints;
1823   int i2;
1824 
1825   for (int s = 0; s < level; s++) {
1826     totnewpoints = gps->totpoints - 1;
1827     /* duplicate points in a temp area */
1828     temp_points = MEM_dupallocN(gps->points);
1829     oldtotpoints = gps->totpoints;
1830 
1831     /* resize the points arrays */
1832     gps->totpoints += totnewpoints;
1833     gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1834     if (gps->dvert != NULL) {
1835       temp_dverts = MEM_dupallocN(gps->dvert);
1836       gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1837     }
1838 
1839     /* move points from last to first to new place */
1840     i2 = gps->totpoints - 1;
1841     for (int i = oldtotpoints - 1; i > 0; i--) {
1842       bGPDspoint *pt = &temp_points[i];
1843       bGPDspoint *pt_final = &gps->points[i2];
1844 
1845       copy_v3_v3(&pt_final->x, &pt->x);
1846       pt_final->pressure = pt->pressure;
1847       pt_final->strength = pt->strength;
1848       pt_final->time = pt->time;
1849       pt_final->flag = pt->flag;
1850       pt_final->runtime.pt_orig = pt->runtime.pt_orig;
1851       pt_final->runtime.idx_orig = pt->runtime.idx_orig;
1852       copy_v4_v4(pt_final->vert_color, pt->vert_color);
1853 
1854       if (gps->dvert != NULL) {
1855         dvert = &temp_dverts[i];
1856         dvert_final = &gps->dvert[i2];
1857         dvert_final->totweight = dvert->totweight;
1858         dvert_final->dw = dvert->dw;
1859       }
1860       i2 -= 2;
1861     }
1862     /* interpolate mid points */
1863     i2 = 1;
1864     for (int i = 0; i < oldtotpoints - 1; i++) {
1865       bGPDspoint *pt = &temp_points[i];
1866       bGPDspoint *next = &temp_points[i + 1];
1867       bGPDspoint *pt_final = &gps->points[i2];
1868 
1869       /* add a half way point */
1870       interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
1871       pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
1872       pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
1873       CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
1874       pt_final->time = interpf(pt->time, next->time, 0.5f);
1875       pt_final->runtime.pt_orig = NULL;
1876       pt_final->flag = 0;
1877       interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
1878 
1879       if (gps->dvert != NULL) {
1880         dvert = &temp_dverts[i];
1881         dvert_next = &temp_dverts[i + 1];
1882         dvert_final = &gps->dvert[i2];
1883 
1884         dvert_final->totweight = dvert->totweight;
1885         dvert_final->dw = MEM_dupallocN(dvert->dw);
1886 
1887         /* interpolate weight values */
1888         for (int d = 0; d < dvert->totweight; d++) {
1889           MDeformWeight *dw_a = &dvert->dw[d];
1890           if (dvert_next->totweight > d) {
1891             MDeformWeight *dw_b = &dvert_next->dw[d];
1892             MDeformWeight *dw_final = &dvert_final->dw[d];
1893             dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
1894           }
1895         }
1896       }
1897 
1898       i2 += 2;
1899     }
1900 
1901     MEM_SAFE_FREE(temp_points);
1902     MEM_SAFE_FREE(temp_dverts);
1903 
1904     /* move points to smooth stroke (not simple type )*/
1905     if (type != GP_SUBDIV_SIMPLE) {
1906       /* duplicate points in a temp area with the new subdivide data */
1907       temp_points = MEM_dupallocN(gps->points);
1908 
1909       /* extreme points are not changed */
1910       for (int i = 0; i < gps->totpoints - 2; i++) {
1911         bGPDspoint *pt = &temp_points[i];
1912         bGPDspoint *next = &temp_points[i + 1];
1913         bGPDspoint *pt_final = &gps->points[i + 1];
1914 
1915         /* move point */
1916         interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
1917       }
1918       /* free temp memory */
1919       MEM_SAFE_FREE(temp_points);
1920     }
1921   }
1922 
1923   /* Calc geometry data. */
1924   BKE_gpencil_stroke_geometry_update(gps);
1925 }
1926 
1927 /* Merge by distance ------------------------------------- */
1928 
1929 /**
1930  * Reduce a series of points when the distance is below a threshold.
1931  * Special case for first and last points (both are keeped) for other points,
1932  * the merge point always is at first point.
1933  * \param gpf: Grease Pencil frame
1934  * \param gps: Grease Pencil stroke
1935  * \param threshold: Distance between points
1936  * \param use_unselected: Set to true to analyze all stroke and not only selected points
1937  */
BKE_gpencil_stroke_merge_distance(bGPDframe * gpf,bGPDstroke * gps,const float threshold,const bool use_unselected)1938 void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
1939                                        bGPDstroke *gps,
1940                                        const float threshold,
1941                                        const bool use_unselected)
1942 {
1943   bGPDspoint *pt = NULL;
1944   bGPDspoint *pt_next = NULL;
1945   float tagged = false;
1946   /* Use square distance to speed up loop */
1947   const float th_square = threshold * threshold;
1948   /* Need to have something to merge. */
1949   if (gps->totpoints < 2) {
1950     return;
1951   }
1952   int i = 0;
1953   int step = 1;
1954   while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
1955     pt = &gps->points[i];
1956     if (pt->flag & GP_SPOINT_TAG) {
1957       i++;
1958       step = 1;
1959       continue;
1960     }
1961     pt_next = &gps->points[i + step];
1962     /* Do not recalc tagged points. */
1963     if (pt_next->flag & GP_SPOINT_TAG) {
1964       step++;
1965       continue;
1966     }
1967     /* Check if contiguous points are selected. */
1968     if (!use_unselected) {
1969       if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
1970         i++;
1971         step = 1;
1972         continue;
1973       }
1974     }
1975     float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
1976     if (len_square <= th_square) {
1977       tagged = true;
1978       if (i != gps->totpoints - 1) {
1979         /* Tag second point for delete. */
1980         pt_next->flag |= GP_SPOINT_TAG;
1981       }
1982       else {
1983         pt->flag |= GP_SPOINT_TAG;
1984       }
1985       /* Jump to next pair of points, keeping first point segment equals.*/
1986       step++;
1987     }
1988     else {
1989       /* Analyze next point. */
1990       i++;
1991       step = 1;
1992     }
1993   }
1994 
1995   /* Always untag extremes. */
1996   pt = &gps->points[0];
1997   pt->flag &= ~GP_SPOINT_TAG;
1998   pt = &gps->points[gps->totpoints - 1];
1999   pt->flag &= ~GP_SPOINT_TAG;
2000 
2001   /* Dissolve tagged points */
2002   if (tagged) {
2003     BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG);
2004   }
2005 
2006   /* Calc geometry data. */
2007   BKE_gpencil_stroke_geometry_update(gps);
2008 }
2009 
2010 typedef struct GpEdge {
2011   uint v1, v2;
2012   /* Coordinates. */
2013   float v1_co[3], v2_co[3];
2014   /* Normals. */
2015   float n1[3], n2[3];
2016   /* Direction of the segment. */
2017   float vec[3];
2018   int flag;
2019 } GpEdge;
2020 
gpencil_next_edge(GpEdge * gp_edges,int totedges,GpEdge * gped_init,const float threshold,const bool reverse)2021 static int gpencil_next_edge(
2022     GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
2023 {
2024   int edge = -1;
2025   float last_angle = 999999.0f;
2026   for (int i = 0; i < totedges; i++) {
2027     GpEdge *gped = &gp_edges[i];
2028     if (gped->flag != 0) {
2029       continue;
2030     }
2031     if (reverse) {
2032       if (gped_init->v1 != gped->v2) {
2033         continue;
2034       }
2035     }
2036     else {
2037       if (gped_init->v2 != gped->v1) {
2038         continue;
2039       }
2040     }
2041     /* Look for straight lines. */
2042     float angle = angle_v3v3(gped->vec, gped_init->vec);
2043     if ((angle < threshold) && (angle <= last_angle)) {
2044       edge = i;
2045       last_angle = angle;
2046     }
2047   }
2048 
2049   return edge;
2050 }
2051 
gpencil_walk_edge(GHash * v_table,GpEdge * gp_edges,int totedges,uint * stroke_array,int init_idx,const float angle,const bool reverse)2052 static int gpencil_walk_edge(GHash *v_table,
2053                              GpEdge *gp_edges,
2054                              int totedges,
2055                              uint *stroke_array,
2056                              int init_idx,
2057                              const float angle,
2058                              const bool reverse)
2059 {
2060   GpEdge *gped_init = &gp_edges[init_idx];
2061   int idx = 1;
2062   int edge = 0;
2063   while (edge > -1) {
2064     edge = gpencil_next_edge(gp_edges, totedges, gped_init, angle, reverse);
2065     if (edge > -1) {
2066       GpEdge *gped = &gp_edges[edge];
2067       stroke_array[idx] = edge;
2068       gped->flag = 1;
2069       gped_init = &gp_edges[edge];
2070       idx++;
2071 
2072       /* Avoid to follow already visited vertice. */
2073       if (reverse) {
2074         if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v1))) {
2075           edge = -1;
2076         }
2077         else {
2078           BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v1), POINTER_FROM_INT(gped->v1));
2079         }
2080       }
2081       else {
2082         if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v2))) {
2083           edge = -1;
2084         }
2085         else {
2086           BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v2), POINTER_FROM_INT(gped->v2));
2087         }
2088       }
2089     }
2090   }
2091 
2092   return idx;
2093 }
2094 
gpencil_generate_edgeloops(Object * ob,bGPDframe * gpf_stroke,int stroke_mat_index,const float angle,const int thickness,const float offset,const float matrix[4][4],const bool use_seams)2095 static void gpencil_generate_edgeloops(Object *ob,
2096                                        bGPDframe *gpf_stroke,
2097                                        int stroke_mat_index,
2098                                        const float angle,
2099                                        const int thickness,
2100                                        const float offset,
2101                                        const float matrix[4][4],
2102                                        const bool use_seams)
2103 {
2104   Mesh *me = (Mesh *)ob->data;
2105   if (me->totedge == 0) {
2106     return;
2107   }
2108 
2109   /* Arrays for all edge vertices (forward and backward) that form a edge loop.
2110    * This is reused for each edgeloop to create gpencil stroke. */
2111   uint *stroke = MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
2112   uint *stroke_fw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
2113   uint *stroke_bw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
2114 
2115   /* Create array with all edges. */
2116   GpEdge *gp_edges = MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
2117   GpEdge *gped = NULL;
2118   for (int i = 0; i < me->totedge; i++) {
2119     MEdge *ed = &me->medge[i];
2120     gped = &gp_edges[i];
2121     MVert *mv1 = &me->mvert[ed->v1];
2122     normal_short_to_float_v3(gped->n1, mv1->no);
2123 
2124     gped->v1 = ed->v1;
2125     copy_v3_v3(gped->v1_co, mv1->co);
2126 
2127     MVert *mv2 = &me->mvert[ed->v2];
2128     normal_short_to_float_v3(gped->n2, mv2->no);
2129     gped->v2 = ed->v2;
2130     copy_v3_v3(gped->v2_co, mv2->co);
2131 
2132     sub_v3_v3v3(gped->vec, mv1->co, mv2->co);
2133 
2134     /* If use seams, mark as done if not a seam. */
2135     if ((use_seams) && ((ed->flag & ME_SEAM) == 0)) {
2136       gped->flag = 1;
2137     }
2138   }
2139 
2140   /* Loop edges to find edgeloops */
2141   bool pending = true;
2142   int e = 0;
2143   while (pending) {
2144     /* Clear arrays of stroke. */
2145     memset(stroke_fw, 0, sizeof(uint) * me->totedge);
2146     memset(stroke_bw, 0, sizeof(uint) * me->totedge);
2147     memset(stroke, 0, sizeof(uint) * me->totedge * 2);
2148 
2149     gped = &gp_edges[e];
2150     /* Look first unused edge. */
2151     if (gped->flag != 0) {
2152       e++;
2153       if (e == me->totedge) {
2154         pending = false;
2155       }
2156       continue;
2157     }
2158     /* Add current edge to arrays. */
2159     stroke_fw[0] = e;
2160     stroke_bw[0] = e;
2161     gped->flag = 1;
2162 
2163     /* Hash used to avoid loop over same vertice. */
2164     GHash *v_table = BLI_ghash_int_new(__func__);
2165     /* Look forward edges. */
2166     int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false);
2167     /* Look backward edges. */
2168     int totbw = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_bw, e, angle, true);
2169 
2170     BLI_ghash_free(v_table, NULL, NULL);
2171 
2172     /* Join both arrays. */
2173     int array_len = 0;
2174     for (int i = totbw - 1; i > 0; i--) {
2175       stroke[array_len] = stroke_bw[i];
2176       array_len++;
2177     }
2178     for (int i = 0; i < totedges; i++) {
2179       stroke[array_len] = stroke_fw[i];
2180       array_len++;
2181     }
2182 
2183     /* Create Stroke. */
2184     bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
2185         gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
2186 
2187     /* Create first segment. */
2188     float fpt[3];
2189     uint v = stroke[0];
2190     gped = &gp_edges[v];
2191     bGPDspoint *pt = &gps_stroke->points[0];
2192     mul_v3_v3fl(fpt, gped->n1, offset);
2193     add_v3_v3v3(&pt->x, gped->v1_co, fpt);
2194     mul_m4_v3(matrix, &pt->x);
2195 
2196     pt->pressure = 1.0f;
2197     pt->strength = 1.0f;
2198 
2199     pt = &gps_stroke->points[1];
2200     mul_v3_v3fl(fpt, gped->n2, offset);
2201     add_v3_v3v3(&pt->x, gped->v2_co, fpt);
2202     mul_m4_v3(matrix, &pt->x);
2203 
2204     pt->pressure = 1.0f;
2205     pt->strength = 1.0f;
2206 
2207     /* Add next segments. */
2208     for (int i = 1; i < array_len; i++) {
2209       v = stroke[i];
2210       gped = &gp_edges[v];
2211 
2212       pt = &gps_stroke->points[i + 1];
2213       mul_v3_v3fl(fpt, gped->n2, offset);
2214       add_v3_v3v3(&pt->x, gped->v2_co, fpt);
2215       mul_m4_v3(matrix, &pt->x);
2216 
2217       pt->pressure = 1.0f;
2218       pt->strength = 1.0f;
2219     }
2220 
2221     BKE_gpencil_stroke_geometry_update(gps_stroke);
2222   }
2223 
2224   /* Free memory. */
2225   MEM_SAFE_FREE(stroke);
2226   MEM_SAFE_FREE(stroke_fw);
2227   MEM_SAFE_FREE(stroke_bw);
2228   MEM_SAFE_FREE(gp_edges);
2229 }
2230 
2231 /* Helper: Add gpencil material using material as base. */
gpencil_add_material(Main * bmain,Object * ob_gp,const char * name,const float color[4],const bool use_stroke,const bool use_fill,int * r_idx)2232 static Material *gpencil_add_material(Main *bmain,
2233                                       Object *ob_gp,
2234                                       const char *name,
2235                                       const float color[4],
2236                                       const bool use_stroke,
2237                                       const bool use_fill,
2238                                       int *r_idx)
2239 {
2240   Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, name, r_idx);
2241   MaterialGPencilStyle *gp_style = mat_gp->gp_style;
2242 
2243   /* Stroke color. */
2244   if (use_stroke) {
2245     ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
2246     gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
2247   }
2248   else {
2249     copy_v4_v4(gp_style->stroke_rgba, color);
2250     gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
2251   }
2252 
2253   /* Fill color. */
2254   copy_v4_v4(gp_style->fill_rgba, color);
2255   if (use_fill) {
2256     gp_style->flag |= GP_MATERIAL_FILL_SHOW;
2257   }
2258 
2259   /* Check at least one is enabled. */
2260   if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
2261       ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
2262     gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
2263   }
2264 
2265   return mat_gp;
2266 }
2267 
gpencil_material_find_index_by_name(Object * ob,const char * name)2268 static int gpencil_material_find_index_by_name(Object *ob, const char *name)
2269 {
2270   for (int i = 0; i < ob->totcol; i++) {
2271     Material *ma = BKE_object_material_get(ob, i + 1);
2272     if ((ma != NULL) && (ma->gp_style != NULL) && (STREQ(ma->id.name + 2, name))) {
2273       return i;
2274     }
2275   }
2276 
2277   return -1;
2278 }
2279 
2280 /**
2281  * Create the name with the object name and a suffix.
2282  */
make_element_name(const char * obname,const char * name,const int maxlen,char * r_name)2283 static void make_element_name(const char *obname, const char *name, const int maxlen, char *r_name)
2284 {
2285   char str[256];
2286   SNPRINTF(str, "%s_%s", obname, name);
2287 
2288   /* Replace any point by underscore. */
2289   BLI_str_replace_char(str, '.', '_');
2290 
2291   BLI_strncpy_utf8(r_name, str, maxlen);
2292 }
2293 
2294 /**
2295  * Convert a mesh object to grease pencil stroke.
2296  *
2297  * \param bmain: Main thread pointer.
2298  * \param depsgraph: Original depsgraph.
2299  * \param scene: Original scene.
2300  * \param ob_gp: Grease pencil object to add strokes.
2301  * \param ob_mesh: Mesh to convert.
2302  * \param angle: Limit angle to consider a edgeloop ends.
2303  * \param thickness: Thickness of the strokes.
2304  * \param offset: Offset along the normals.
2305  * \param matrix: Transformation matrix.
2306  * \param frame_offset: Destination frame number offset.
2307  * \param use_seams: Only export seam edges.
2308  * \param use_faces: Export faces as filled strokes.
2309  */
BKE_gpencil_convert_mesh(Main * bmain,Depsgraph * depsgraph,Scene * scene,Object * ob_gp,Object * ob_mesh,const float angle,const int thickness,const float offset,const float matrix[4][4],const int frame_offset,const bool use_seams,const bool use_faces)2310 bool BKE_gpencil_convert_mesh(Main *bmain,
2311                               Depsgraph *depsgraph,
2312                               Scene *scene,
2313                               Object *ob_gp,
2314                               Object *ob_mesh,
2315                               const float angle,
2316                               const int thickness,
2317                               const float offset,
2318                               const float matrix[4][4],
2319                               const int frame_offset,
2320                               const bool use_seams,
2321                               const bool use_faces)
2322 {
2323   if (ELEM(NULL, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
2324     return false;
2325   }
2326 
2327   bGPdata *gpd = (bGPdata *)ob_gp->data;
2328 
2329   /* Use evaluated data to get mesh with all modifiers on top. */
2330   Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, ob_mesh);
2331   Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
2332   MPoly *mp, *mpoly = me_eval->mpoly;
2333   MLoop *mloop = me_eval->mloop;
2334   int mpoly_len = me_eval->totpoly;
2335   char element_name[200];
2336 
2337   /* Need at least an edge. */
2338   if (me_eval->totvert < 2) {
2339     return false;
2340   }
2341 
2342   const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
2343   /* Create stroke material. */
2344   make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name);
2345   int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name);
2346 
2347   if (stroke_mat_index == -1) {
2348     gpencil_add_material(
2349         bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index);
2350   }
2351 
2352   /* Export faces as filled strokes. */
2353   if (use_faces) {
2354 
2355     /* Read all polygons and create fill for each. */
2356     if (mpoly_len > 0) {
2357       make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
2358       /* Create Layer and Frame. */
2359       bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
2360       if (gpl_fill == NULL) {
2361         gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true);
2362       }
2363       bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
2364           gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
2365       int i;
2366       for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
2367         MLoop *ml = &mloop[mp->loopstart];
2368         /* Find material. */
2369         int mat_idx = 0;
2370         Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
2371         make_element_name(
2372             ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
2373         mat_idx = gpencil_material_find_index_by_name(ob_gp, element_name);
2374         if (mat_idx == -1) {
2375           float color[4];
2376           if (ma != NULL) {
2377             copy_v3_v3(color, &ma->r);
2378             color[3] = 1.0f;
2379           }
2380           else {
2381             copy_v4_v4(color, default_colors[1]);
2382           }
2383           gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
2384         }
2385 
2386         bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
2387         gps_fill->flag |= GP_STROKE_CYCLIC;
2388 
2389         /* Add points to strokes. */
2390         for (int j = 0; j < mp->totloop; j++, ml++) {
2391           MVert *mv = &me_eval->mvert[ml->v];
2392           bGPDspoint *pt = &gps_fill->points[j];
2393           copy_v3_v3(&pt->x, mv->co);
2394           mul_m4_v3(matrix, &pt->x);
2395           pt->pressure = 1.0f;
2396           pt->strength = 1.0f;
2397         }
2398         /* If has only 3 points subdivide. */
2399         if (mp->totloop == 3) {
2400           BKE_gpencil_stroke_subdivide(gps_fill, 1, GP_SUBDIV_SIMPLE);
2401         }
2402 
2403         BKE_gpencil_stroke_geometry_update(gps_fill);
2404       }
2405     }
2406   }
2407 
2408   /* Create stroke from edges. */
2409   make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
2410 
2411   /* Create Layer and Frame. */
2412   bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
2413   if (gpl_stroke == NULL) {
2414     gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true);
2415   }
2416   bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
2417       gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
2418 
2419   gpencil_generate_edgeloops(
2420       ob_eval, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
2421 
2422   /* Tag for recalculation */
2423   DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
2424 
2425   return true;
2426 }
2427 
2428 /**
2429  * Apply grease pencil Transforms.
2430  * \param gpd: Grease pencil data-block
2431  * \param mat: Transformation matrix
2432  */
BKE_gpencil_transform(bGPdata * gpd,const float mat[4][4])2433 void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
2434 {
2435   if (gpd == NULL) {
2436     return;
2437   }
2438 
2439   const float scalef = mat4_to_scale(mat);
2440   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2441     /* FIXME: For now, we just skip parented layers.
2442      * Otherwise, we have to update each frame to find
2443      * the current parent position/effects.
2444      */
2445     if (gpl->parent) {
2446       continue;
2447     }
2448 
2449     LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2450       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2451         bGPDspoint *pt;
2452         int i;
2453 
2454         for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2455           mul_m4_v3(mat, &pt->x);
2456           pt->pressure *= scalef;
2457         }
2458 
2459         /* Distortion may mean we need to re-triangulate. */
2460         BKE_gpencil_stroke_geometry_update(gps);
2461       }
2462     }
2463   }
2464 }
2465 
2466 /* Used for "move only origins" in object_data_transform.c */
BKE_gpencil_stroke_point_count(bGPdata * gpd)2467 int BKE_gpencil_stroke_point_count(bGPdata *gpd)
2468 {
2469   int total_points = 0;
2470 
2471   if (gpd == NULL) {
2472     return 0;
2473   }
2474 
2475   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2476     /* FIXME: For now, we just skip parented layers.
2477      * Otherwise, we have to update each frame to find
2478      * the current parent position/effects.
2479      */
2480     if (gpl->parent) {
2481       continue;
2482     }
2483 
2484     LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2485       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2486         total_points += gps->totpoints;
2487       }
2488     }
2489   }
2490   return total_points;
2491 }
2492 
2493 /* Used for "move only origins" in object_data_transform.c */
BKE_gpencil_point_coords_get(bGPdata * gpd,GPencilPointCoordinates * elem_data)2494 void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_data)
2495 {
2496   if (gpd == NULL) {
2497     return;
2498   }
2499 
2500   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2501     /* FIXME: For now, we just skip parented layers.
2502      * Otherwise, we have to update each frame to find
2503      * the current parent position/effects.
2504      */
2505     if (gpl->parent) {
2506       continue;
2507     }
2508 
2509     LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2510       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2511         bGPDspoint *pt;
2512         int i;
2513 
2514         for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2515           copy_v3_v3(elem_data->co, &pt->x);
2516           elem_data->pressure = pt->pressure;
2517           elem_data++;
2518         }
2519       }
2520     }
2521   }
2522 }
2523 
2524 /* Used for "move only origins" in object_data_transform.c */
BKE_gpencil_point_coords_apply(bGPdata * gpd,const GPencilPointCoordinates * elem_data)2525 void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates *elem_data)
2526 {
2527   if (gpd == NULL) {
2528     return;
2529   }
2530 
2531   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2532     /* FIXME: For now, we just skip parented layers.
2533      * Otherwise, we have to update each frame to find
2534      * the current parent position/effects.
2535      */
2536     if (gpl->parent) {
2537       continue;
2538     }
2539 
2540     LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2541       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2542         bGPDspoint *pt;
2543         int i;
2544 
2545         for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2546           copy_v3_v3(&pt->x, elem_data->co);
2547           pt->pressure = elem_data->pressure;
2548           elem_data++;
2549         }
2550 
2551         /* Distortion may mean we need to re-triangulate. */
2552         BKE_gpencil_stroke_geometry_update(gps);
2553       }
2554     }
2555   }
2556 }
2557 
2558 /* Used for "move only origins" in object_data_transform.c */
BKE_gpencil_point_coords_apply_with_mat4(bGPdata * gpd,const GPencilPointCoordinates * elem_data,const float mat[4][4])2559 void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
2560                                               const GPencilPointCoordinates *elem_data,
2561                                               const float mat[4][4])
2562 {
2563   if (gpd == NULL) {
2564     return;
2565   }
2566 
2567   const float scalef = mat4_to_scale(mat);
2568   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2569     /* FIXME: For now, we just skip parented layers.
2570      * Otherwise, we have to update each frame to find
2571      * the current parent position/effects.
2572      */
2573     if (gpl->parent) {
2574       continue;
2575     }
2576 
2577     LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2578       LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2579         bGPDspoint *pt;
2580         int i;
2581 
2582         for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2583           mul_v3_m4v3(&pt->x, mat, elem_data->co);
2584           pt->pressure = elem_data->pressure * scalef;
2585           elem_data++;
2586         }
2587 
2588         /* Distortion may mean we need to re-triangulate. */
2589         BKE_gpencil_stroke_geometry_update(gps);
2590       }
2591     }
2592   }
2593 }
2594 
2595 /**
2596  * Set a random color to stroke using vertex color.
2597  * \param gps: Stroke
2598  */
BKE_gpencil_stroke_set_random_color(bGPDstroke * gps)2599 void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
2600 {
2601   BLI_assert(gps->totpoints > 0);
2602 
2603   float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
2604   bGPDspoint *pt = &gps->points[0];
2605   color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z));
2606   color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x));
2607   color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y));
2608   for (int i = 0; i < gps->totpoints; i++) {
2609     pt = &gps->points[i];
2610     copy_v4_v4(pt->vert_color, color);
2611   }
2612 }
2613 /** \} */
2614