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