Blender  V2.93
gpencil_geom.c
Go to the documentation of this file.
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 
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_heap.h"
38 #include "BLI_math_vector.h"
39 #include "BLI_polyfill_2d.h"
40 
41 #include "BLT_translation.h"
42 
44 #include "DNA_gpencil_types.h"
45 #include "DNA_material_types.h"
46 #include "DNA_mesh_types.h"
47 #include "DNA_meshdata_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 
51 #include "BLT_translation.h"
52 
53 #include "BKE_context.h"
54 #include "BKE_deform.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_gpencil_curve.h"
57 #include "BKE_gpencil_geom.h"
58 #include "BKE_main.h"
59 #include "BKE_material.h"
60 #include "BKE_object.h"
61 
62 #include "DEG_depsgraph_query.h"
63 
64 /* GP Object - Boundbox Support */
74  const bool use_select,
75  float r_min[3],
76  float r_max[3])
77 {
78  const bGPDspoint *pt;
79  int i;
80  bool changed = false;
81 
82  if (ELEM(NULL, gps, r_min, r_max)) {
83  return false;
84  }
85 
86  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
87  if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {
88  minmax_v3v3_v3(r_min, r_max, &pt->x);
89  changed = true;
90  }
91  }
92  return changed;
93 }
94 
102 bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
103 {
104  bool changed = false;
105 
106  INIT_MINMAX(r_min, r_max);
107 
108  if (gpd == NULL) {
109  return changed;
110  }
111 
112  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
113  bGPDframe *gpf = gpl->actframe;
114 
115  if (gpf != NULL) {
116  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
117  changed |= BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
118  }
119  }
120  }
121 
122  return changed;
123 }
124 
130 void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
131 {
132  float min[3], max[3], tot[3];
133 
135 
136  add_v3_v3v3(tot, min, max);
137  mul_v3_v3fl(r_centroid, tot, 0.5f);
138 }
139 
145 {
147  BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max);
148 }
149 
154 static void boundbox_gpencil(Object *ob)
155 {
156  BoundBox *bb;
157  bGPdata *gpd;
158  float min[3], max[3];
159 
160  if (ob->runtime.bb == NULL) {
161  ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
162  }
163 
164  bb = ob->runtime.bb;
165  gpd = ob->data;
166 
167  if (!BKE_gpencil_data_minmax(gpd, min, max)) {
168  min[0] = min[1] = min[2] = -1.0f;
169  max[0] = max[1] = max[2] = 1.0f;
170  }
171 
173 
174  bb->flag &= ~BOUNDBOX_DIRTY;
175 }
176 
183 {
184  if (ELEM(NULL, ob, ob->data)) {
185  return NULL;
186  }
187 
188  bGPdata *gpd = (bGPdata *)ob->data;
189  if ((ob->runtime.bb) && ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) {
190  return ob->runtime.bb;
191  }
192 
193  boundbox_gpencil(ob);
194 
195  Object *ob_orig = (Object *)DEG_get_original_id(&ob->id);
196  /* Update orig object's boundbox with re-computed evaluated values. This function can be
197  * called with the evaluated object and need update the original object bound box data
198  * to keep both values synchronized. */
199  if (!ELEM(ob_orig, NULL, ob)) {
200  if (ob_orig->runtime.bb == NULL) {
201  ob_orig->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
202  }
203  for (int i = 0; i < 8; i++) {
204  copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]);
205  }
206  }
207 
208  return ob->runtime.bb;
209 }
210 
211 /* ************************************************** */
212 
213 static int stroke_march_next_point(const bGPDstroke *gps,
214  const int index_next_pt,
215  const float *current,
216  const float dist,
217  float *result,
218  float *pressure,
219  float *strength,
220  float *vert_color,
221  float *ratio_result,
222  int *index_from,
223  int *index_to)
224 {
225  float remaining_till_next = 0.0f;
226  float remaining_march = dist;
227  float step_start[3];
228  float point[3];
229  int next_point_index = index_next_pt;
230  bGPDspoint *pt = NULL;
231 
232  if (!(next_point_index < gps->totpoints)) {
233  return -1;
234  }
235 
236  copy_v3_v3(step_start, current);
237  pt = &gps->points[next_point_index];
238  copy_v3_v3(point, &pt->x);
239  remaining_till_next = len_v3v3(point, step_start);
240 
241  while (remaining_till_next < remaining_march) {
242  remaining_march -= remaining_till_next;
243  pt = &gps->points[next_point_index];
244  copy_v3_v3(point, &pt->x);
245  copy_v3_v3(step_start, point);
246  next_point_index++;
247  if (!(next_point_index < gps->totpoints)) {
248  next_point_index = gps->totpoints - 1;
249  break;
250  }
251  pt = &gps->points[next_point_index];
252  copy_v3_v3(point, &pt->x);
253  remaining_till_next = len_v3v3(point, step_start);
254  }
255  if (remaining_till_next < remaining_march) {
256  pt = &gps->points[next_point_index];
257  copy_v3_v3(result, &pt->x);
258  *pressure = gps->points[next_point_index].pressure;
259  *strength = gps->points[next_point_index].strength;
260  memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
261 
262  *index_from = next_point_index - 1;
263  *index_to = next_point_index;
264  *ratio_result = 1.0f;
265 
266  return 0;
267  }
268 
269  float ratio = remaining_march / remaining_till_next;
270  interp_v3_v3v3(result, step_start, point, ratio);
271  *pressure = interpf(
272  gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
273  *strength = interpf(
274  gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
275  interp_v4_v4v4(vert_color,
276  gps->points[next_point_index - 1].vert_color,
277  gps->points[next_point_index].vert_color,
278  ratio);
279 
280  *index_from = next_point_index - 1;
281  *index_to = next_point_index;
282  *ratio_result = ratio;
283 
284  return next_point_index;
285 }
286 
288  const int index_next_pt,
289  const float *current,
290  const float dist,
291  float *result)
292 {
293  float remaining_till_next = 0.0f;
294  float remaining_march = dist;
295  float step_start[3];
296  float point[3];
297  int next_point_index = index_next_pt;
298  bGPDspoint *pt = NULL;
299 
300  if (!(next_point_index < gps->totpoints)) {
301  return -1;
302  }
303 
304  copy_v3_v3(step_start, current);
305  pt = &gps->points[next_point_index];
306  copy_v3_v3(point, &pt->x);
307  remaining_till_next = len_v3v3(point, step_start);
308 
309  while (remaining_till_next < remaining_march) {
310  remaining_march -= remaining_till_next;
311  pt = &gps->points[next_point_index];
312  copy_v3_v3(point, &pt->x);
313  copy_v3_v3(step_start, point);
314  next_point_index++;
315  if (!(next_point_index < gps->totpoints)) {
316  next_point_index = gps->totpoints - 1;
317  break;
318  }
319  pt = &gps->points[next_point_index];
320  copy_v3_v3(point, &pt->x);
321  remaining_till_next = len_v3v3(point, step_start);
322  }
323  if (remaining_till_next < remaining_march) {
324  pt = &gps->points[next_point_index];
325  copy_v3_v3(result, &pt->x);
326  return 0;
327  }
328 
329  float ratio = remaining_march / remaining_till_next;
330  interp_v3_v3v3(result, step_start, point, ratio);
331  return next_point_index;
332 }
333 
334 static int stroke_march_count(const bGPDstroke *gps, const float dist)
335 {
336  int point_count = 0;
337  float point[3];
338  int next_point_index = 1;
339  bGPDspoint *pt = NULL;
340 
341  pt = &gps->points[0];
342  copy_v3_v3(point, &pt->x);
343  point_count++;
344 
345  while ((next_point_index = stroke_march_next_point_no_interp(
346  gps, next_point_index, point, dist, point)) > -1) {
347  point_count++;
348  if (next_point_index == 0) {
349  break; /* last point finished */
350  }
351  }
352  return point_count;
353 }
354 
356  int count,
357  ListBase *result,
358  int *totweight)
359 {
360  LinkData *ld;
361  MDeformVert *dv;
362  MDeformWeight *dw;
363  int i, j;
364  int tw = 0;
365  for (i = 0; i < count; i++) {
366  dv = &dv_list[i];
367 
368  /* find def_nr in list, if not exist, then create one */
369  for (j = 0; j < dv->totweight; j++) {
370  bool found = false;
371  dw = &dv->dw[j];
372  for (ld = result->first; ld; ld = ld->next) {
373  if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
374  found = true;
375  break;
376  }
377  }
378  if (!found) {
379  ld = MEM_callocN(sizeof(LinkData), "def_nr_item");
380  ld->data = POINTER_FROM_INT(dw->def_nr);
381  BLI_addtail(result, ld);
382  tw++;
383  }
384  }
385  }
386 
387  *totweight = tw;
388 }
389 
390 static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
391 {
392  int i, j;
393  LinkData *ld;
394  MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
395 
396  for (i = 0; i < count; i++) {
397  dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight");
398  dst[i].totweight = totweight;
399  j = 0;
400  /* re-assign deform groups */
401  for (ld = def_nr_list->first; ld; ld = ld->next) {
402  dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
403  j++;
404  }
405  }
406 
407  return dst;
408 }
409 
411  bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
412 {
413  const MDeformVert *vl = &gps->dvert[index_from];
414  const MDeformVert *vr = &gps->dvert[index_to];
415 
416  for (int i = 0; i < vert->totweight; i++) {
417  float wl = BKE_defvert_find_weight(vl, vert->dw[i].def_nr);
418  float wr = BKE_defvert_find_weight(vr, vert->dw[i].def_nr);
419  vert->dw[i].weight = interpf(wr, wl, ratio);
420  }
421 }
422 
429 bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
430 {
431  bGPDspoint *pt = gps->points;
432  bGPDspoint *pt1 = NULL;
433  bGPDspoint *pt2 = NULL;
434  LinkData *ld;
435  ListBase def_nr_list = {0};
436 
437  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
438  return false;
439  }
440  /* TODO: Implement feature point preservation. */
441  int count = stroke_march_count(gps, dist);
442 
443  bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled");
444  MDeformVert *new_dv = NULL;
445 
446  int result_totweight;
447 
448  if (gps->dvert != NULL) {
449  stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
450  new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
451  }
452 
453  int next_point_index = 1;
454  int i = 0;
455  float pressure, strength, ratio_result;
456  float vert_color[4];
457  int index_from, index_to;
458  float last_coord[3];
459 
460  /* 1st point is always at the start */
461  pt1 = &gps->points[0];
462  copy_v3_v3(last_coord, &pt1->x);
463  pt2 = &new_pt[i];
464  copy_v3_v3(&pt2->x, last_coord);
465  new_pt[i].pressure = pt[0].pressure;
466  new_pt[i].strength = pt[0].strength;
467  memcpy(new_pt[i].vert_color, pt[0].vert_color, sizeof(float[4]));
468  if (select) {
469  new_pt[i].flag |= GP_SPOINT_SELECT;
470  }
471  i++;
472 
473  if (new_dv) {
474  stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
475  }
476 
477  /* The rest. */
478  while ((next_point_index = stroke_march_next_point(gps,
479  next_point_index,
480  last_coord,
481  dist,
482  last_coord,
483  &pressure,
484  &strength,
485  vert_color,
486  &ratio_result,
487  &index_from,
488  &index_to)) > -1) {
489  pt2 = &new_pt[i];
490  copy_v3_v3(&pt2->x, last_coord);
491  new_pt[i].pressure = pressure;
492  new_pt[i].strength = strength;
493  memcpy(new_pt[i].vert_color, vert_color, sizeof(float[4]));
494  if (select) {
495  new_pt[i].flag |= GP_SPOINT_SELECT;
496  }
497 
498  if (new_dv) {
499  stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
500  }
501 
502  i++;
503  if (next_point_index == 0) {
504  break; /* last point finished */
505  }
506  }
507 
508  gps->points = new_pt;
509  /* Free original vertex list. */
510  MEM_freeN(pt);
511 
512  if (new_dv) {
513  /* Free original weight data. */
515  MEM_freeN(gps->dvert);
516  while ((ld = BLI_pophead(&def_nr_list))) {
517  MEM_freeN(ld);
518  }
519 
520  gps->dvert = new_dv;
521  }
522 
523  gps->totpoints = i;
524 
525  /* Calc geometry data. */
527 
528  return true;
529 }
530 
537 bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
538 {
539  bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
540  float threshold = (tip_length == 0 ? 0.001f : tip_length);
541 
542  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
543  return false;
544  }
545 
546  last_pt = &pt[gps->totpoints - 1];
547  second_last = &pt[gps->totpoints - 2];
548  next_pt = &pt[1];
549 
550  float len1 = 0.0f;
551  float len2 = 0.0f;
552 
553  int i = 1;
554  while (len1 < threshold && gps->totpoints > i) {
555  next_pt = &pt[i];
556  len1 = len_v3v3(&next_pt->x, &pt->x);
557  i++;
558  }
559 
560  i = 2;
561  while (len2 < threshold && gps->totpoints >= i) {
562  second_last = &pt[gps->totpoints - i];
563  len2 = len_v3v3(&last_pt->x, &second_last->x);
564  i++;
565  }
566 
567  float extend1 = (len1 + dist) / len1;
568  float extend2 = (len2 + dist) / len2;
569 
570  float result1[3], result2[3];
571 
572  interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
573  interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
574 
575  copy_v3_v3(&pt->x, result1);
576  copy_v3_v3(&last_pt->x, result2);
577 
578  return true;
579 }
580 
587 bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to)
588 {
589  bGPDspoint *pt = gps->points, *new_pt;
590  MDeformVert *dv, *new_dv;
591 
592  const int new_count = index_to - index_from + 1;
593 
594  if (new_count >= gps->totpoints) {
595  return false;
596  }
597 
598  if (new_count == 1) {
600  MEM_freeN(gps->points);
601  gps->points = NULL;
602  gps->dvert = NULL;
603  gps->totpoints = 0;
604  return false;
605  }
606 
607  new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
608 
609  for (int i = 0; i < new_count; i++) {
610  memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
611  }
612 
613  if (gps->dvert) {
614  new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed");
615  for (int i = 0; i < new_count; i++) {
616  dv = &gps->dvert[i + index_from];
617  new_dv[i].flag = dv->flag;
618  new_dv[i].totweight = dv->totweight;
619  new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
620  "gp_stroke_dverts_dw_trimmed");
621  for (int j = 0; j < dv->totweight; j++) {
622  new_dv[i].dw[j].weight = dv->dw[j].weight;
623  new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
624  }
625  }
626  MEM_freeN(gps->dvert);
627  gps->dvert = new_dv;
628  }
629 
630  MEM_freeN(gps->points);
631  gps->points = new_pt;
632  gps->totpoints = new_count;
633 
634  return true;
635 }
636 
647  bGPDframe *gpf,
648  bGPDstroke *gps,
649  const int before_index,
650  bGPDstroke **remaining_gps)
651 {
652  bGPDstroke *new_gps;
653  bGPDspoint *pt = gps->points, *new_pt;
654  MDeformVert *dv, *new_dv;
655 
656  if (before_index >= gps->totpoints || before_index == 0) {
657  return false;
658  }
659 
660  const int new_count = gps->totpoints - before_index;
661  const int old_count = before_index;
662 
663  /* Handle remaining segments first. */
664 
666  gpf, gps, gps->mat_nr, new_count, gps->thickness);
667 
668  new_pt = new_gps->points; /* Allocated from above. */
669 
670  for (int i = 0; i < new_count; i++) {
671  memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
672  }
673 
674  if (gps->dvert) {
675  new_dv = MEM_callocN(sizeof(MDeformVert) * new_count,
676  "gp_stroke_dverts_remaining(MDeformVert)");
677  for (int i = 0; i < new_count; i++) {
678  dv = &gps->dvert[i + before_index];
679  new_dv[i].flag = dv->flag;
680  new_dv[i].totweight = dv->totweight;
681  new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
682  "gp_stroke_dverts_dw_remaining(MDeformWeight)");
683  for (int j = 0; j < dv->totweight; j++) {
684  new_dv[i].dw[j].weight = dv->dw[j].weight;
685  new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
686  }
687  }
688  new_gps->dvert = new_dv;
689  }
690 
691  (*remaining_gps) = new_gps;
692 
693  /* Trim the original stroke into a shorter one.
694  * Keep the end point. */
695 
696  BKE_gpencil_stroke_trim_points(gps, 0, old_count);
698  return true;
699 }
700 
706 bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
707 {
708  bGPDspoint *pt = gps->points, *second_last;
709  int i;
710 
711  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
712  return false;
713  }
714 
715  second_last = &pt[gps->totpoints - 2];
716 
717  float len1, this_len1, cut_len1;
718  float len2, this_len2, cut_len2;
719  int index_start, index_end;
720 
721  len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
722 
723  i = 1;
724  while (len1 < dist && gps->totpoints > i - 1) {
725  this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
726  len1 += this_len1;
727  cut_len1 = len1 - dist;
728  i++;
729  }
730  index_start = i - 2;
731 
732  i = 2;
733  while (len2 < dist && gps->totpoints >= i) {
734  second_last = &pt[gps->totpoints - i];
735  this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
736  len2 += this_len2;
737  cut_len2 = len2 - dist;
738  i++;
739  }
740  index_end = gps->totpoints - i + 2;
741 
742  if (len1 < dist || len2 < dist || index_end <= index_start) {
743  index_start = index_end = 0; /* empty stroke */
744  }
745 
746  if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
747  index_start = index_end = 0; /* no length left to cut */
748  }
749 
750  BKE_gpencil_stroke_trim_points(gps, index_start, index_end);
751 
752  if (gps->totpoints == 0) {
753  return false;
754  }
755 
756  pt = gps->points;
757 
758  float cut1 = cut_len1 / this_len1;
759  float cut2 = cut_len2 / this_len2;
760 
761  float result1[3], result2[3];
762 
763  interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
764  interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
765 
766  copy_v3_v3(&pt[0].x, result1);
767  copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
768 
769  return true;
770 }
771 
778 bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf)
779 {
780  bGPDspoint *pt = &gps->points[i];
781  float sco[3] = {0.0f};
782 
783  /* Do nothing if not enough points to smooth out */
784  if (gps->totpoints <= 2) {
785  return false;
786  }
787 
788  /* Only affect endpoints by a fraction of the normal strength,
789  * to prevent the stroke from shrinking too much
790  */
791  if (ELEM(i, 0, gps->totpoints - 1)) {
792  inf *= 0.1f;
793  }
794 
795  /* Compute smoothed coordinate by taking the ones nearby */
796  /* XXX: This is potentially slow,
797  * and suffers from accumulation error as earlier points are handled before later ones. */
798  {
799  /* XXX: this is hardcoded to look at 2 points on either side of the current one
800  * (i.e. 5 items total). */
801  const int steps = 2;
802  const float average_fac = 1.0f / (float)(steps * 2 + 1);
803  int step;
804 
805  /* add the point itself */
806  madd_v3_v3fl(sco, &pt->x, average_fac);
807 
808  /* n-steps before/after current point */
809  /* XXX: review how the endpoints are treated by this algorithm. */
810  /* XXX: falloff measures should also introduce some weighting variations,
811  * so that further-out points get less weight. */
812  for (step = 1; step <= steps; step++) {
813  bGPDspoint *pt1, *pt2;
814  int before = i - step;
815  int after = i + step;
816 
817  CLAMP_MIN(before, 0);
818  CLAMP_MAX(after, gps->totpoints - 1);
819 
820  pt1 = &gps->points[before];
821  pt2 = &gps->points[after];
822 
823  /* add both these points to the average-sum (s += p[i]/n) */
824  madd_v3_v3fl(sco, &pt1->x, average_fac);
825  madd_v3_v3fl(sco, &pt2->x, average_fac);
826  }
827  }
828 
829  /* Based on influence factor, blend between original and optimal smoothed coordinate */
830  interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
831 
832  return true;
833 }
834 
841 bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
842 {
843  bGPDspoint *ptb = &gps->points[point_index];
844 
845  /* Do nothing if not enough points */
846  if ((gps->totpoints <= 2) || (point_index < 1)) {
847  return false;
848  }
849  /* Only affect endpoints by a fraction of the normal influence */
850  float inf = influence;
851  if (ELEM(point_index, 0, gps->totpoints - 1)) {
852  inf *= 0.01f;
853  }
854  /* Limit max influence to reduce pop effect. */
855  CLAMP_MAX(inf, 0.98f);
856 
857  float total = 0.0f;
858  float max_strength = 0.0f;
859  const int steps = 4;
860  const float average_fac = 1.0f / (float)(steps * 2 + 1);
861  int step;
862 
863  /* add the point itself */
864  total += ptb->strength * average_fac;
865  max_strength = ptb->strength;
866 
867  /* n-steps before/after current point */
868  for (step = 1; step <= steps; step++) {
869  bGPDspoint *pt1, *pt2;
870  int before = point_index - step;
871  int after = point_index + step;
872 
873  CLAMP_MIN(before, 0);
874  CLAMP_MAX(after, gps->totpoints - 1);
875 
876  pt1 = &gps->points[before];
877  pt2 = &gps->points[after];
878 
879  /* add both these points to the average-sum (s += p[i]/n) */
880  total += pt1->strength * average_fac;
881  total += pt2->strength * average_fac;
882  /* Save max value. */
883  if (max_strength < pt1->strength) {
884  max_strength = pt1->strength;
885  }
886  if (max_strength < pt2->strength) {
887  max_strength = pt2->strength;
888  }
889  }
890 
891  /* Based on influence factor, blend between original and optimal smoothed value. */
892  ptb->strength = interpf(ptb->strength, total, inf);
893  /* Clamp to maximum stroke strength to avoid weird results. */
894  CLAMP_MAX(ptb->strength, max_strength);
895 
896  return true;
897 }
898 
905 bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
906 {
907  bGPDspoint *ptb = &gps->points[point_index];
908 
909  /* Do nothing if not enough points */
910  if ((gps->totpoints <= 2) || (point_index < 1)) {
911  return false;
912  }
913  /* Only affect endpoints by a fraction of the normal influence */
914  float inf = influence;
915  if (ELEM(point_index, 0, gps->totpoints - 1)) {
916  inf *= 0.01f;
917  }
918  /* Limit max influence to reduce pop effect. */
919  CLAMP_MAX(inf, 0.98f);
920 
921  float total = 0.0f;
922  float max_pressure = 0.0f;
923  const int steps = 4;
924  const float average_fac = 1.0f / (float)(steps * 2 + 1);
925  int step;
926 
927  /* add the point itself */
928  total += ptb->pressure * average_fac;
929  max_pressure = ptb->pressure;
930 
931  /* n-steps before/after current point */
932  for (step = 1; step <= steps; step++) {
933  bGPDspoint *pt1, *pt2;
934  int before = point_index - step;
935  int after = point_index + step;
936 
937  CLAMP_MIN(before, 0);
938  CLAMP_MAX(after, gps->totpoints - 1);
939 
940  pt1 = &gps->points[before];
941  pt2 = &gps->points[after];
942 
943  /* add both these points to the average-sum (s += p[i]/n) */
944  total += pt1->pressure * average_fac;
945  total += pt2->pressure * average_fac;
946  /* Save max value. */
947  if (max_pressure < pt1->pressure) {
948  max_pressure = pt1->pressure;
949  }
950  if (max_pressure < pt2->pressure) {
951  max_pressure = pt2->pressure;
952  }
953  }
954 
955  /* Based on influence factor, blend between original and optimal smoothed value. */
956  ptb->pressure = interpf(ptb->pressure, total, inf);
957  /* Clamp to maximum stroke thickness to avoid weird results. */
958  CLAMP_MAX(ptb->pressure, max_pressure);
959  return true;
960 }
961 
968 bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
969 {
970  bGPDspoint *ptb = &gps->points[point_index];
971 
972  /* Do nothing if not enough points */
973  if (gps->totpoints <= 2) {
974  return false;
975  }
976 
977  /* Compute theoretical optimal value */
978  bGPDspoint *pta, *ptc;
979  int before = point_index - 1;
980  int after = point_index + 1;
981 
982  CLAMP_MIN(before, 0);
983  CLAMP_MAX(after, gps->totpoints - 1);
984 
985  pta = &gps->points[before];
986  ptc = &gps->points[after];
987 
988  /* the optimal value is the corresponding to the interpolation of the pressure
989  * at the distance of point b
990  */
991  float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
992  /* sometimes the factor can be wrong due stroke geometry, so use middle point */
993  if ((fac < 0.0f) || (fac > 1.0f)) {
994  fac = 0.5f;
995  }
996  float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
997 
998  /* Based on influence factor, blend between original and optimal */
999  ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
1000  CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
1001 
1002  return true;
1003 }
1004 
1014  int totpoints,
1015  float (*points2d)[2],
1016  int *r_direction)
1017 {
1018  BLI_assert(totpoints >= 2);
1019 
1020  const bGPDspoint *pt0 = &points[0];
1021  const bGPDspoint *pt1 = &points[1];
1022  const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
1023 
1024  float locx[3];
1025  float locy[3];
1026  float loc3[3];
1027  float normal[3];
1028 
1029  /* local X axis (p0 -> p1) */
1030  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
1031 
1032  /* point vector at 3/4 */
1033  float v3[3];
1034  if (totpoints == 2) {
1035  mul_v3_v3fl(v3, &pt3->x, 0.001f);
1036  }
1037  else {
1038  copy_v3_v3(v3, &pt3->x);
1039  }
1040 
1041  sub_v3_v3v3(loc3, v3, &pt0->x);
1042 
1043  /* vector orthogonal to polygon plane */
1044  cross_v3_v3v3(normal, locx, loc3);
1045 
1046  /* local Y axis (cross to normal/x axis) */
1047  cross_v3_v3v3(locy, normal, locx);
1048 
1049  /* Normalize vectors */
1050  normalize_v3(locx);
1051  normalize_v3(locy);
1052 
1053  /* Calculcate last point first. */
1054  const bGPDspoint *pt_last = &points[totpoints - 1];
1055  float tmp[3];
1056  sub_v3_v3v3(tmp, &pt_last->x, &pt0->x);
1057 
1058  points2d[totpoints - 1][0] = dot_v3v3(tmp, locx);
1059  points2d[totpoints - 1][1] = dot_v3v3(tmp, locy);
1060 
1061  /* Calculate the scalar cross product of the 2d points. */
1062  float cross = 0.0f;
1063  float *co_curr;
1064  float *co_prev = (float *)&points2d[totpoints - 1];
1065 
1066  /* Get all points in local space */
1067  for (int i = 0; i < totpoints - 1; i++) {
1068  const bGPDspoint *pt = &points[i];
1069  float loc[3];
1070 
1071  /* Get local space using first point as origin */
1072  sub_v3_v3v3(loc, &pt->x, &pt0->x);
1073 
1074  points2d[i][0] = dot_v3v3(loc, locx);
1075  points2d[i][1] = dot_v3v3(loc, locy);
1076 
1077  /* Calculate cross product. */
1078  co_curr = (float *)&points2d[i][0];
1079  cross += (co_curr[0] - co_prev[0]) * (co_curr[1] + co_prev[1]);
1080  co_prev = (float *)&points2d[i][0];
1081  }
1082 
1083  /* Concave (-1), Convex (1) */
1084  *r_direction = (cross >= 0.0f) ? 1 : -1;
1085 }
1086 
1099  int ref_totpoints,
1100  const bGPDspoint *points,
1101  int totpoints,
1102  float (*points2d)[2],
1103  const float scale,
1104  int *r_direction)
1105 {
1106  BLI_assert(totpoints >= 2);
1107 
1108  const bGPDspoint *pt0 = &ref_points[0];
1109  const bGPDspoint *pt1 = &ref_points[1];
1110  const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)];
1111 
1112  float locx[3];
1113  float locy[3];
1114  float loc3[3];
1115  float normal[3];
1116 
1117  /* local X axis (p0 -> p1) */
1118  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
1119 
1120  /* point vector at 3/4 */
1121  float v3[3];
1122  if (totpoints == 2) {
1123  mul_v3_v3fl(v3, &pt3->x, 0.001f);
1124  }
1125  else {
1126  copy_v3_v3(v3, &pt3->x);
1127  }
1128 
1129  sub_v3_v3v3(loc3, v3, &pt0->x);
1130 
1131  /* vector orthogonal to polygon plane */
1132  cross_v3_v3v3(normal, locx, loc3);
1133 
1134  /* local Y axis (cross to normal/x axis) */
1135  cross_v3_v3v3(locy, normal, locx);
1136 
1137  /* Normalize vectors */
1138  normalize_v3(locx);
1139  normalize_v3(locy);
1140 
1141  /* Get all points in local space */
1142  for (int i = 0; i < totpoints; i++) {
1143  const bGPDspoint *pt = &points[i];
1144  float loc[3];
1145  float v1[3];
1146  float vn[3] = {0.0f, 0.0f, 0.0f};
1147 
1148  /* apply scale to extremes of the stroke to get better collision detection
1149  * the scale is divided to get more control in the UI parameter
1150  */
1151  /* first point */
1152  if (i == 0) {
1153  const bGPDspoint *pt_next = &points[i + 1];
1154  sub_v3_v3v3(vn, &pt->x, &pt_next->x);
1155  normalize_v3(vn);
1156  mul_v3_fl(vn, scale / 10.0f);
1157  add_v3_v3v3(v1, &pt->x, vn);
1158  }
1159  /* last point */
1160  else if (i == totpoints - 1) {
1161  const bGPDspoint *pt_prev = &points[i - 1];
1162  sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
1163  normalize_v3(vn);
1164  mul_v3_fl(vn, scale / 10.0f);
1165  add_v3_v3v3(v1, &pt->x, vn);
1166  }
1167  else {
1168  copy_v3_v3(v1, &pt->x);
1169  }
1170 
1171  /* Get local space using first point as origin (ref stroke) */
1172  sub_v3_v3v3(loc, v1, &pt0->x);
1173 
1174  points2d[i][0] = dot_v3v3(loc, locx);
1175  points2d[i][1] = dot_v3v3(loc, locy);
1176  }
1177 
1178  /* Concave (-1), Convex (1), or Auto-detect (0)? */
1179  *r_direction = (int)locy[2];
1180 }
1181 
1182 /* Calc texture coordinates using flat projected points. */
1183 static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
1184  bGPDstroke *gps,
1185  const float minv[2],
1186  const float maxv[2],
1187  float (*r_uv)[2])
1188 {
1189  const float s = sin(gps->uv_rotation);
1190  const float c = cos(gps->uv_rotation);
1191 
1192  /* Calc center for rotation. */
1193  float center[2] = {0.5f, 0.5f};
1194  float d[2];
1195  d[0] = maxv[0] - minv[0];
1196  d[1] = maxv[1] - minv[1];
1197  for (int i = 0; i < gps->totpoints; i++) {
1198  r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
1199  r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
1200 
1201  /* Apply translation. */
1202  add_v2_v2(r_uv[i], gps->uv_translation);
1203 
1204  /* Apply Rotation. */
1205  r_uv[i][0] -= center[0];
1206  r_uv[i][1] -= center[1];
1207 
1208  float x = r_uv[i][0] * c - r_uv[i][1] * s;
1209  float y = r_uv[i][0] * s + r_uv[i][1] * c;
1210 
1211  r_uv[i][0] = x + center[0];
1212  r_uv[i][1] = y + center[1];
1213 
1214  /* Apply scale. */
1215  if (gps->uv_scale != 0.0f) {
1216  mul_v2_fl(r_uv[i], 1.0f / gps->uv_scale);
1217  }
1218  }
1219 }
1220 
1226 {
1227  BLI_assert(gps->totpoints >= 3);
1228 
1229  /* allocate memory for temporary areas */
1230  gps->tot_triangles = gps->totpoints - 2;
1231  uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
1232  "GP Stroke temp triangulation");
1233  float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints,
1234  "GP Stroke temp 2d points");
1235  float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data");
1236 
1237  int direction = 0;
1238 
1239  /* convert to 2d and triangulate */
1240  BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
1241  BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
1242 
1243  /* calc texture coordinates automatically */
1244  float minv[2];
1245  float maxv[2];
1246  /* first needs bounding box data */
1247  ARRAY_SET_ITEMS(minv, -1.0f, -1.0f);
1248  ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f);
1249 
1250  /* calc uv data */
1251  gpencil_calc_stroke_fill_uv(points2d, gps, minv, maxv, uv);
1252 
1253  /* Save triangulation data. */
1254  if (gps->tot_triangles > 0) {
1255  MEM_SAFE_FREE(gps->triangles);
1256  gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
1257  "GP Stroke triangulation");
1258 
1259  for (int i = 0; i < gps->tot_triangles; i++) {
1260  memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
1261  }
1262 
1263  /* Copy UVs to bGPDspoint. */
1264  for (int i = 0; i < gps->totpoints; i++) {
1265  copy_v2_v2(gps->points[i].uv_fill, uv[i]);
1266  }
1267  }
1268  else {
1269  /* No triangles needed - Free anything allocated previously */
1270  if (gps->triangles) {
1271  MEM_freeN(gps->triangles);
1272  }
1273 
1274  gps->triangles = NULL;
1275  }
1276 
1277  /* clear memory */
1278  MEM_SAFE_FREE(tmp_triangles);
1279  MEM_SAFE_FREE(points2d);
1280  MEM_SAFE_FREE(uv);
1281 }
1282 
1288 {
1289  if (gps == NULL || gps->totpoints == 0) {
1290  return;
1291  }
1292 
1293  bGPDspoint *pt = gps->points;
1294  float totlen = 0.0f;
1295  pt[0].uv_fac = totlen;
1296  for (int i = 1; i < gps->totpoints; i++) {
1297  totlen += len_v3v3(&pt[i - 1].x, &pt[i].x);
1298  pt[i].uv_fac = totlen;
1299  }
1300 }
1301 
1308 {
1309  if (gps == NULL) {
1310  return;
1311  }
1312 
1313  if (gps->editcurve != NULL) {
1314  if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
1315  /* curve geometry was updated: stroke needs recalculation */
1316  if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
1317  bool is_adaptive = gpd->flag & GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
1319  gps, gpd->curve_edit_resolution, is_adaptive);
1321  }
1322  }
1323  else {
1324  /* stroke geometry was updated: editcurve needs recalculation */
1326  }
1327  }
1328 
1329  if (gps->totpoints > 2) {
1331  }
1332  else {
1333  gps->tot_triangles = 0;
1334  MEM_SAFE_FREE(gps->triangles);
1335  }
1336 
1337  /* calc uv data along the stroke */
1339 
1340  /* Calc stroke bounding box. */
1342 }
1343 
1350 float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
1351 {
1352  if (!gps->points || gps->totpoints < 2) {
1353  return 0.0f;
1354  }
1355  float *last_pt = &gps->points[0].x;
1356  float total_length = 0.0f;
1357  for (int i = 1; i < gps->totpoints; i++) {
1358  bGPDspoint *pt = &gps->points[i];
1359  if (use_3d) {
1360  total_length += len_v3v3(&pt->x, last_pt);
1361  }
1362  else {
1363  total_length += len_v2v2(&pt->x, last_pt);
1364  }
1365  last_pt = &pt->x;
1366  }
1367  return total_length;
1368 }
1369 
1372  const int start_index,
1373  const int end_index,
1374  bool use_3d)
1375 {
1376  if (!gps->points || gps->totpoints < 2 || end_index <= start_index) {
1377  return 0.0f;
1378  }
1379 
1380  int index = MAX2(start_index, 0) + 1;
1381  int last_index = MIN2(end_index, gps->totpoints - 1) + 1;
1382 
1383  float *last_pt = &gps->points[index - 1].x;
1384  float total_length = 0.0f;
1385  for (int i = index; i < last_index; i++) {
1386  bGPDspoint *pt = &gps->points[i];
1387  if (use_3d) {
1388  total_length += len_v3v3(&pt->x, last_pt);
1389  }
1390  else {
1391  total_length += len_v2v2(&pt->x, last_pt);
1392  }
1393  last_pt = &pt->x;
1394  }
1395  return total_length;
1396 }
1397 
1403 {
1404  if (gps->totpoints < 4) {
1405  return false;
1406  }
1407  bool intersect = false;
1408  int start = 0;
1409  int end = 0;
1410  float point[3];
1411  /* loop segments from start until we have an intersection */
1412  for (int i = 0; i < gps->totpoints - 2; i++) {
1413  start = i;
1414  bGPDspoint *a = &gps->points[start];
1415  bGPDspoint *b = &gps->points[start + 1];
1416  for (int j = start + 2; j < gps->totpoints - 1; j++) {
1417  end = j + 1;
1418  bGPDspoint *c = &gps->points[j];
1419  bGPDspoint *d = &gps->points[end];
1420  float pointb[3];
1421  /* get intersection */
1422  if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) {
1423  if (len_v3(point) > 0.0f) {
1424  float closest[3];
1425  /* check intersection is on both lines */
1426  float lambda = closest_to_line_v3(closest, point, &a->x, &b->x);
1427  if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
1428  continue;
1429  }
1430  lambda = closest_to_line_v3(closest, point, &c->x, &d->x);
1431  if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
1432  continue;
1433  }
1434 
1435  intersect = true;
1436  break;
1437  }
1438  }
1439  }
1440  if (intersect) {
1441  break;
1442  }
1443  }
1444 
1445  /* trim unwanted points */
1446  if (intersect) {
1447 
1448  /* save points */
1449  bGPDspoint *old_points = MEM_dupallocN(gps->points);
1450  MDeformVert *old_dvert = NULL;
1451  MDeformVert *dvert_src = NULL;
1452 
1453  if (gps->dvert != NULL) {
1454  old_dvert = MEM_dupallocN(gps->dvert);
1455  }
1456 
1457  /* resize gps */
1458  int newtot = end - start + 1;
1459 
1460  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
1461  if (gps->dvert != NULL) {
1462  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
1463  }
1464 
1465  for (int i = 0; i < newtot; i++) {
1466  int idx = start + i;
1467  bGPDspoint *pt_src = &old_points[idx];
1468  bGPDspoint *pt_new = &gps->points[i];
1469  memcpy(pt_new, pt_src, sizeof(bGPDspoint));
1470  if (gps->dvert != NULL) {
1471  dvert_src = &old_dvert[idx];
1472  MDeformVert *dvert = &gps->dvert[i];
1473  memcpy(dvert, dvert_src, sizeof(MDeformVert));
1474  if (dvert_src->dw) {
1475  memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1476  }
1477  }
1478  if (ELEM(idx, start, end)) {
1479  copy_v3_v3(&pt_new->x, point);
1480  }
1481  }
1482 
1483  gps->totpoints = newtot;
1484 
1485  MEM_SAFE_FREE(old_points);
1486  MEM_SAFE_FREE(old_dvert);
1487  }
1488 
1490 
1491  return intersect;
1492 }
1493 
1499 {
1500  bGPDspoint *pt1 = NULL;
1501  bGPDspoint *pt2 = NULL;
1502 
1503  /* Only can close a stroke with 3 points or more. */
1504  if (gps->totpoints < 3) {
1505  return false;
1506  }
1507 
1508  /* Calc average distance between points to get same level of sampling. */
1509  float dist_tot = 0.0f;
1510  for (int i = 0; i < gps->totpoints - 1; i++) {
1511  pt1 = &gps->points[i];
1512  pt2 = &gps->points[i + 1];
1513  dist_tot += len_v3v3(&pt1->x, &pt2->x);
1514  }
1515  /* Calc the average distance. */
1516  float dist_avg = dist_tot / (gps->totpoints - 1);
1517 
1518  /* Calc distance between last and first point. */
1519  pt1 = &gps->points[gps->totpoints - 1];
1520  pt2 = &gps->points[0];
1521  float dist_close = len_v3v3(&pt1->x, &pt2->x);
1522 
1523  /* if the distance to close is very small, don't need add points and just enable cyclic. */
1524  if (dist_close <= dist_avg) {
1525  gps->flag |= GP_STROKE_CYCLIC;
1526  return true;
1527  }
1528 
1529  /* Calc number of points required using the average distance. */
1530  int tot_newpoints = MAX2(dist_close / dist_avg, 1);
1531 
1532  /* Resize stroke array. */
1533  int old_tot = gps->totpoints;
1534  gps->totpoints += tot_newpoints;
1535  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1536  if (gps->dvert != NULL) {
1537  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1538  }
1539 
1540  /* Generate new points */
1541  pt1 = &gps->points[old_tot - 1];
1542  pt2 = &gps->points[0];
1543  bGPDspoint *pt = &gps->points[old_tot];
1544  for (int i = 1; i < tot_newpoints + 1; i++, pt++) {
1545  float step = (tot_newpoints > 1) ? ((float)i / (float)tot_newpoints) : 0.99f;
1546  /* Clamp last point to be near, but not on top of first point. */
1547  if ((tot_newpoints > 1) && (i == tot_newpoints)) {
1548  step *= 0.99f;
1549  }
1550 
1551  /* Average point. */
1552  interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step);
1553  pt->pressure = interpf(pt2->pressure, pt1->pressure, step);
1554  pt->strength = interpf(pt2->strength, pt1->strength, step);
1555  pt->flag = 0;
1556  interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
1557 
1558  /* Set weights. */
1559  if (gps->dvert != NULL) {
1560  MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
1561  MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
1562  float weight_1 = dw1 ? dw1->weight : 0.0f;
1563 
1564  MDeformVert *dvert2 = &gps->dvert[0];
1565  MDeformWeight *dw2 = BKE_defvert_ensure_index(dvert2, 0);
1566  float weight_2 = dw2 ? dw2->weight : 0.0f;
1567 
1568  MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1];
1569  dvert_final->totweight = 0;
1570  MDeformWeight *dw = BKE_defvert_ensure_index(dvert_final, 0);
1571  if (dvert_final->dw) {
1572  dw->weight = interpf(weight_2, weight_1, step);
1573  }
1574  }
1575  }
1576 
1577  /* Enable cyclic flag. */
1578  gps->flag |= GP_STROKE_CYCLIC;
1579 
1580  return true;
1581 }
1582 
1590 void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
1591 {
1592  bGPDspoint *pt;
1593  MDeformVert *dvert = NULL;
1594  int i;
1595 
1596  int tot = gps->totpoints; /* number of points in new buffer */
1597  /* first pass: count points to remove */
1598  /* Count how many points are selected (i.e. how many to remove) */
1599  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1600  if (pt->flag & tag) {
1601  /* selected point - one of the points to remove */
1602  tot--;
1603  }
1604  }
1605 
1606  /* if no points are left, we simply delete the entire stroke */
1607  if (tot <= 0) {
1608  /* remove the entire stroke */
1609  if (gps->points) {
1610  MEM_freeN(gps->points);
1611  }
1612  if (gps->dvert) {
1614  MEM_freeN(gps->dvert);
1615  }
1616  if (gps->triangles) {
1617  MEM_freeN(gps->triangles);
1618  }
1619  BLI_freelinkN(&gpf->strokes, gps);
1620  }
1621  else {
1622  /* just copy all points to keep into a smaller buffer */
1623  bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
1624  bGPDspoint *npt = new_points;
1625 
1626  MDeformVert *new_dvert = NULL;
1627  MDeformVert *ndvert = NULL;
1628 
1629  if (gps->dvert != NULL) {
1630  new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
1631  ndvert = new_dvert;
1632  }
1633 
1634  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
1635  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1636  if ((pt->flag & tag) == 0) {
1637  *npt = *pt;
1638  npt++;
1639 
1640  if (gps->dvert != NULL) {
1641  *ndvert = *dvert;
1642  ndvert->dw = MEM_dupallocN(dvert->dw);
1643  ndvert++;
1644  }
1645  }
1646  if (gps->dvert != NULL) {
1647  dvert++;
1648  }
1649  }
1650 
1651  /* free the old buffer */
1652  if (gps->points) {
1653  MEM_freeN(gps->points);
1654  }
1655  if (gps->dvert) {
1657  MEM_freeN(gps->dvert);
1658  }
1659 
1660  /* save the new buffer */
1661  gps->points = new_points;
1662  gps->dvert = new_dvert;
1663  gps->totpoints = tot;
1664 
1665  /* triangles cache needs to be recalculated */
1667  }
1668 }
1669 
1675 void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
1676 {
1677  if (gps->totpoints < 3) {
1678  zero_v3(r_normal);
1679  return;
1680  }
1681 
1682  bGPDspoint *points = gps->points;
1683  int totpoints = gps->totpoints;
1684 
1685  const bGPDspoint *pt0 = &points[0];
1686  const bGPDspoint *pt1 = &points[1];
1687  const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
1688 
1689  float vec1[3];
1690  float vec2[3];
1691 
1692  /* initial vector (p0 -> p1) */
1693  sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
1694 
1695  /* point vector at 3/4 */
1696  sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
1697 
1698  /* vector orthogonal to polygon plane */
1699  cross_v3_v3v3(r_normal, vec1, vec2);
1700 
1701  /* Normalize vector */
1702  normalize_v3(r_normal);
1703 }
1704 
1705 /* Stroke Simplify ------------------------------------- */
1706 
1718 {
1719  bGPDspoint *old_points = MEM_dupallocN(gps->points);
1720  int totpoints = gps->totpoints;
1721  char *marked = NULL;
1722  char work;
1723 
1724  int start = 0;
1725  int end = gps->totpoints - 1;
1726 
1727  marked = MEM_callocN(totpoints, "GP marked array");
1728  marked[start] = 1;
1729  marked[end] = 1;
1730 
1731  work = 1;
1732  int totmarked = 0;
1733  /* while still reducing */
1734  while (work) {
1735  int ls, le;
1736  work = 0;
1737 
1738  ls = start;
1739  le = start + 1;
1740 
1741  /* while not over interval */
1742  while (ls < end) {
1743  int max_i = 0;
1744  /* divided to get more control */
1745  float max_dist = epsilon / 10.0f;
1746 
1747  /* find the next marked point */
1748  while (marked[le] == 0) {
1749  le++;
1750  }
1751 
1752  for (int i = ls + 1; i < le; i++) {
1753  float point_on_line[3];
1754  float dist;
1755 
1757  point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
1758 
1759  dist = len_v3v3(point_on_line, &old_points[i].x);
1760 
1761  if (dist > max_dist) {
1762  max_dist = dist;
1763  max_i = i;
1764  }
1765  }
1766 
1767  if (max_i != 0) {
1768  work = 1;
1769  marked[max_i] = 1;
1770  totmarked++;
1771  }
1772 
1773  ls = le;
1774  le = ls + 1;
1775  }
1776  }
1777 
1778  /* adding points marked */
1779  MDeformVert *old_dvert = NULL;
1780  MDeformVert *dvert_src = NULL;
1781 
1782  if (gps->dvert != NULL) {
1783  old_dvert = MEM_dupallocN(gps->dvert);
1784  }
1785  /* resize gps */
1786  int j = 0;
1787  for (int i = 0; i < totpoints; i++) {
1788  bGPDspoint *pt_src = &old_points[i];
1789  bGPDspoint *pt = &gps->points[j];
1790 
1791  if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
1792  memcpy(pt, pt_src, sizeof(bGPDspoint));
1793  if (gps->dvert != NULL) {
1794  dvert_src = &old_dvert[i];
1795  MDeformVert *dvert = &gps->dvert[j];
1796  memcpy(dvert, dvert_src, sizeof(MDeformVert));
1797  if (dvert_src->dw) {
1798  memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1799  }
1800  }
1801  j++;
1802  }
1803  else {
1804  if (gps->dvert != NULL) {
1805  dvert_src = &old_dvert[i];
1806  BKE_gpencil_free_point_weights(dvert_src);
1807  }
1808  }
1809  }
1810 
1811  gps->totpoints = j;
1812 
1813  /* Calc geometry data. */
1815 
1816  MEM_SAFE_FREE(old_points);
1817  MEM_SAFE_FREE(old_dvert);
1818  MEM_SAFE_FREE(marked);
1819 }
1820 
1827 {
1828  if (gps->totpoints < 5) {
1829  return;
1830  }
1831 
1832  /* save points */
1833  bGPDspoint *old_points = MEM_dupallocN(gps->points);
1834  MDeformVert *old_dvert = NULL;
1835  MDeformVert *dvert_src = NULL;
1836 
1837  if (gps->dvert != NULL) {
1838  old_dvert = MEM_dupallocN(gps->dvert);
1839  }
1840 
1841  /* resize gps */
1842  int newtot = (gps->totpoints - 2) / 2;
1843  if (((gps->totpoints - 2) % 2) > 0) {
1844  newtot++;
1845  }
1846  newtot += 2;
1847 
1848  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
1849  if (gps->dvert != NULL) {
1850  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
1851  }
1852 
1853  int j = 0;
1854  for (int i = 0; i < gps->totpoints; i++) {
1855  bGPDspoint *pt_src = &old_points[i];
1856  bGPDspoint *pt = &gps->points[j];
1857 
1858  if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
1859  memcpy(pt, pt_src, sizeof(bGPDspoint));
1860  if (gps->dvert != NULL) {
1861  dvert_src = &old_dvert[i];
1862  MDeformVert *dvert = &gps->dvert[j];
1863  memcpy(dvert, dvert_src, sizeof(MDeformVert));
1864  if (dvert_src->dw) {
1865  memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1866  }
1867  }
1868  j++;
1869  }
1870  else {
1871  if (gps->dvert != NULL) {
1872  dvert_src = &old_dvert[i];
1873  BKE_gpencil_free_point_weights(dvert_src);
1874  }
1875  }
1876  }
1877 
1878  gps->totpoints = j;
1879  /* Calc geometry data. */
1881 
1882  MEM_SAFE_FREE(old_points);
1883  MEM_SAFE_FREE(old_dvert);
1884 }
1885 
1893 void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
1894 {
1895  bGPDspoint *temp_points;
1896  MDeformVert *temp_dverts = NULL;
1897  MDeformVert *dvert = NULL;
1898  MDeformVert *dvert_final = NULL;
1899  MDeformVert *dvert_next = NULL;
1900  int totnewpoints, oldtotpoints;
1901  int i2;
1902 
1903  for (int s = 0; s < level; s++) {
1904  totnewpoints = gps->totpoints - 1;
1905  /* duplicate points in a temp area */
1906  temp_points = MEM_dupallocN(gps->points);
1907  oldtotpoints = gps->totpoints;
1908 
1909  /* resize the points arrays */
1910  gps->totpoints += totnewpoints;
1911  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1912  if (gps->dvert != NULL) {
1913  temp_dverts = MEM_dupallocN(gps->dvert);
1914  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1915  }
1916 
1917  /* move points from last to first to new place */
1918  i2 = gps->totpoints - 1;
1919  for (int i = oldtotpoints - 1; i > 0; i--) {
1920  bGPDspoint *pt = &temp_points[i];
1921  bGPDspoint *pt_final = &gps->points[i2];
1922 
1923  copy_v3_v3(&pt_final->x, &pt->x);
1924  pt_final->pressure = pt->pressure;
1925  pt_final->strength = pt->strength;
1926  pt_final->time = pt->time;
1927  pt_final->flag = pt->flag;
1928  pt_final->runtime.pt_orig = pt->runtime.pt_orig;
1929  pt_final->runtime.idx_orig = pt->runtime.idx_orig;
1930  copy_v4_v4(pt_final->vert_color, pt->vert_color);
1931 
1932  if (gps->dvert != NULL) {
1933  dvert = &temp_dverts[i];
1934  dvert_final = &gps->dvert[i2];
1935  dvert_final->totweight = dvert->totweight;
1936  dvert_final->dw = dvert->dw;
1937  }
1938  i2 -= 2;
1939  }
1940  /* interpolate mid points */
1941  i2 = 1;
1942  for (int i = 0; i < oldtotpoints - 1; i++) {
1943  bGPDspoint *pt = &temp_points[i];
1944  bGPDspoint *next = &temp_points[i + 1];
1945  bGPDspoint *pt_final = &gps->points[i2];
1946 
1947  /* add a half way point */
1948  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
1949  pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
1950  pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
1951  CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
1952  pt_final->time = interpf(pt->time, next->time, 0.5f);
1953  pt_final->runtime.pt_orig = NULL;
1954  pt_final->flag = 0;
1955  interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
1956 
1957  if (gps->dvert != NULL) {
1958  dvert = &temp_dverts[i];
1959  dvert_next = &temp_dverts[i + 1];
1960  dvert_final = &gps->dvert[i2];
1961 
1962  dvert_final->totweight = dvert->totweight;
1963  dvert_final->dw = MEM_dupallocN(dvert->dw);
1964 
1965  /* interpolate weight values */
1966  for (int d = 0; d < dvert->totweight; d++) {
1967  MDeformWeight *dw_a = &dvert->dw[d];
1968  if (dvert_next->totweight > d) {
1969  MDeformWeight *dw_b = &dvert_next->dw[d];
1970  MDeformWeight *dw_final = &dvert_final->dw[d];
1971  dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
1972  }
1973  }
1974  }
1975 
1976  i2 += 2;
1977  }
1978 
1979  MEM_SAFE_FREE(temp_points);
1980  MEM_SAFE_FREE(temp_dverts);
1981 
1982  /* move points to smooth stroke (not simple type )*/
1983  if (type != GP_SUBDIV_SIMPLE) {
1984  /* duplicate points in a temp area with the new subdivide data */
1985  temp_points = MEM_dupallocN(gps->points);
1986 
1987  /* extreme points are not changed */
1988  for (int i = 0; i < gps->totpoints - 2; i++) {
1989  bGPDspoint *pt = &temp_points[i];
1990  bGPDspoint *next = &temp_points[i + 1];
1991  bGPDspoint *pt_final = &gps->points[i + 1];
1992 
1993  /* move point */
1994  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
1995  }
1996  /* free temp memory */
1997  MEM_SAFE_FREE(temp_points);
1998  }
1999  }
2000 
2001  /* Calc geometry data. */
2003 }
2004 
2005 /* Merge by distance ------------------------------------- */
2006 
2018  bGPDframe *gpf,
2019  bGPDstroke *gps,
2020  const float threshold,
2021  const bool use_unselected)
2022 {
2023  bGPDspoint *pt = NULL;
2024  bGPDspoint *pt_next = NULL;
2025  float tagged = false;
2026  /* Use square distance to speed up loop */
2027  const float th_square = threshold * threshold;
2028  /* Need to have something to merge. */
2029  if (gps->totpoints < 2) {
2030  return;
2031  }
2032  int i = 0;
2033  int step = 1;
2034  while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
2035  pt = &gps->points[i];
2036  if (pt->flag & GP_SPOINT_TAG) {
2037  i++;
2038  step = 1;
2039  continue;
2040  }
2041  pt_next = &gps->points[i + step];
2042  /* Do not recalc tagged points. */
2043  if (pt_next->flag & GP_SPOINT_TAG) {
2044  step++;
2045  continue;
2046  }
2047  /* Check if contiguous points are selected. */
2048  if (!use_unselected) {
2049  if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
2050  i++;
2051  step = 1;
2052  continue;
2053  }
2054  }
2055  float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
2056  if (len_square <= th_square) {
2057  tagged = true;
2058  if (i != gps->totpoints - 1) {
2059  /* Tag second point for delete. */
2060  pt_next->flag |= GP_SPOINT_TAG;
2061  }
2062  else {
2063  pt->flag |= GP_SPOINT_TAG;
2064  }
2065  /* Jump to next pair of points, keeping first point segment equals.*/
2066  step++;
2067  }
2068  else {
2069  /* Analyze next point. */
2070  i++;
2071  step = 1;
2072  }
2073  }
2074 
2075  /* Always untag extremes. */
2076  pt = &gps->points[0];
2077  pt->flag &= ~GP_SPOINT_TAG;
2078  pt = &gps->points[gps->totpoints - 1];
2079  pt->flag &= ~GP_SPOINT_TAG;
2080 
2081  /* Dissolve tagged points */
2082  if (tagged) {
2084  }
2085 
2086  /* Calc geometry data. */
2088 }
2089 
2090 typedef struct GpEdge {
2092  /* Coordinates. */
2093  float v1_co[3], v2_co[3];
2094  /* Normals. */
2095  float n1[3], n2[3];
2096  /* Direction of the segment. */
2097  float vec[3];
2098  int flag;
2100 
2102  GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
2103 {
2104  int edge = -1;
2105  float last_angle = 999999.0f;
2106  for (int i = 0; i < totedges; i++) {
2107  GpEdge *gped = &gp_edges[i];
2108  if (gped->flag != 0) {
2109  continue;
2110  }
2111  if (reverse) {
2112  if (gped_init->v1 != gped->v2) {
2113  continue;
2114  }
2115  }
2116  else {
2117  if (gped_init->v2 != gped->v1) {
2118  continue;
2119  }
2120  }
2121  /* Look for straight lines. */
2122  float angle = angle_v3v3(gped->vec, gped_init->vec);
2123  if ((angle < threshold) && (angle <= last_angle)) {
2124  edge = i;
2125  last_angle = angle;
2126  }
2127  }
2128 
2129  return edge;
2130 }
2131 
2132 static int gpencil_walk_edge(GHash *v_table,
2133  GpEdge *gp_edges,
2134  int totedges,
2135  uint *stroke_array,
2136  int init_idx,
2137  const float angle,
2138  const bool reverse)
2139 {
2140  GpEdge *gped_init = &gp_edges[init_idx];
2141  int idx = 1;
2142  int edge = 0;
2143  while (edge > -1) {
2144  edge = gpencil_next_edge(gp_edges, totedges, gped_init, angle, reverse);
2145  if (edge > -1) {
2146  GpEdge *gped = &gp_edges[edge];
2147  stroke_array[idx] = edge;
2148  gped->flag = 1;
2149  gped_init = &gp_edges[edge];
2150  idx++;
2151 
2152  /* Avoid to follow already visited vertice. */
2153  if (reverse) {
2154  if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v1))) {
2155  edge = -1;
2156  }
2157  else {
2158  BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v1), POINTER_FROM_INT(gped->v1));
2159  }
2160  }
2161  else {
2162  if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v2))) {
2163  edge = -1;
2164  }
2165  else {
2166  BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v2), POINTER_FROM_INT(gped->v2));
2167  }
2168  }
2169  }
2170  }
2171 
2172  return idx;
2173 }
2174 
2176  bGPdata *gpd,
2177  bGPDframe *gpf_stroke,
2178  int stroke_mat_index,
2179  const float angle,
2180  const int thickness,
2181  const float offset,
2182  const float matrix[4][4],
2183  const bool use_seams)
2184 {
2185  Mesh *me = (Mesh *)ob->data;
2186  if (me->totedge == 0) {
2187  return;
2188  }
2189 
2190  /* Arrays for all edge vertices (forward and backward) that form a edge loop.
2191  * This is reused for each edgeloop to create gpencil stroke. */
2192  uint *stroke = MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
2193  uint *stroke_fw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
2194  uint *stroke_bw = MEM_callocN(sizeof(uint) * me->totedge, __func__);
2195 
2196  /* Create array with all edges. */
2197  GpEdge *gp_edges = MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
2198  GpEdge *gped = NULL;
2199  for (int i = 0; i < me->totedge; i++) {
2200  MEdge *ed = &me->medge[i];
2201  gped = &gp_edges[i];
2202  MVert *mv1 = &me->mvert[ed->v1];
2203  normal_short_to_float_v3(gped->n1, mv1->no);
2204 
2205  gped->v1 = ed->v1;
2206  copy_v3_v3(gped->v1_co, mv1->co);
2207 
2208  MVert *mv2 = &me->mvert[ed->v2];
2209  normal_short_to_float_v3(gped->n2, mv2->no);
2210  gped->v2 = ed->v2;
2211  copy_v3_v3(gped->v2_co, mv2->co);
2212 
2213  sub_v3_v3v3(gped->vec, mv1->co, mv2->co);
2214 
2215  /* If use seams, mark as done if not a seam. */
2216  if ((use_seams) && ((ed->flag & ME_SEAM) == 0)) {
2217  gped->flag = 1;
2218  }
2219  }
2220 
2221  /* Loop edges to find edgeloops */
2222  bool pending = true;
2223  int e = 0;
2224  while (pending) {
2225  /* Clear arrays of stroke. */
2226  memset(stroke_fw, 0, sizeof(uint) * me->totedge);
2227  memset(stroke_bw, 0, sizeof(uint) * me->totedge);
2228  memset(stroke, 0, sizeof(uint) * me->totedge * 2);
2229 
2230  gped = &gp_edges[e];
2231  /* Look first unused edge. */
2232  if (gped->flag != 0) {
2233  e++;
2234  if (e == me->totedge) {
2235  pending = false;
2236  }
2237  continue;
2238  }
2239  /* Add current edge to arrays. */
2240  stroke_fw[0] = e;
2241  stroke_bw[0] = e;
2242  gped->flag = 1;
2243 
2244  /* Hash used to avoid loop over same vertice. */
2245  GHash *v_table = BLI_ghash_int_new(__func__);
2246  /* Look forward edges. */
2247  int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false);
2248  /* Look backward edges. */
2249  int totbw = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_bw, e, angle, true);
2250 
2251  BLI_ghash_free(v_table, NULL, NULL);
2252 
2253  /* Join both arrays. */
2254  int array_len = 0;
2255  for (int i = totbw - 1; i > 0; i--) {
2256  stroke[array_len] = stroke_bw[i];
2257  array_len++;
2258  }
2259  for (int i = 0; i < totedges; i++) {
2260  stroke[array_len] = stroke_fw[i];
2261  array_len++;
2262  }
2263 
2264  /* Create Stroke. */
2265  bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
2266  gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
2267 
2268  /* Create first segment. */
2269  float fpt[3];
2270  uint v = stroke[0];
2271  gped = &gp_edges[v];
2272  bGPDspoint *pt = &gps_stroke->points[0];
2273  mul_v3_v3fl(fpt, gped->n1, offset);
2274  add_v3_v3v3(&pt->x, gped->v1_co, fpt);
2275  mul_m4_v3(matrix, &pt->x);
2276 
2277  pt->pressure = 1.0f;
2278  pt->strength = 1.0f;
2279 
2280  pt = &gps_stroke->points[1];
2281  mul_v3_v3fl(fpt, gped->n2, offset);
2282  add_v3_v3v3(&pt->x, gped->v2_co, fpt);
2283  mul_m4_v3(matrix, &pt->x);
2284 
2285  pt->pressure = 1.0f;
2286  pt->strength = 1.0f;
2287 
2288  /* Add next segments. */
2289  for (int i = 1; i < array_len; i++) {
2290  v = stroke[i];
2291  gped = &gp_edges[v];
2292 
2293  pt = &gps_stroke->points[i + 1];
2294  mul_v3_v3fl(fpt, gped->n2, offset);
2295  add_v3_v3v3(&pt->x, gped->v2_co, fpt);
2296  mul_m4_v3(matrix, &pt->x);
2297 
2298  pt->pressure = 1.0f;
2299  pt->strength = 1.0f;
2300  }
2301 
2302  BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
2303  }
2304 
2305  /* Free memory. */
2306  MEM_SAFE_FREE(stroke);
2307  MEM_SAFE_FREE(stroke_fw);
2308  MEM_SAFE_FREE(stroke_bw);
2309  MEM_SAFE_FREE(gp_edges);
2310 }
2311 
2312 /* Helper: Add gpencil material using material as base. */
2314  Object *ob_gp,
2315  const char *name,
2316  const float color[4],
2317  const bool use_stroke,
2318  const bool use_fill,
2319  int *r_idx)
2320 {
2321  Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, name, r_idx);
2322  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
2323 
2324  /* Stroke color. */
2325  if (use_stroke) {
2326  ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
2327  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
2328  }
2329  else {
2330  copy_v4_v4(gp_style->stroke_rgba, color);
2331  gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
2332  }
2333 
2334  /* Fill color. */
2335  copy_v4_v4(gp_style->fill_rgba, color);
2336  if (use_fill) {
2337  gp_style->flag |= GP_MATERIAL_FILL_SHOW;
2338  }
2339 
2340  /* Check at least one is enabled. */
2341  if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
2342  ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
2343  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
2344  }
2345 
2346  return mat_gp;
2347 }
2348 
2349 static int gpencil_material_find_index_by_name(Object *ob, const char *name)
2350 {
2351  for (int i = 0; i < ob->totcol; i++) {
2352  Material *ma = BKE_object_material_get(ob, i + 1);
2353  if ((ma != NULL) && (ma->gp_style != NULL) && (STREQ(ma->id.name + 2, name))) {
2354  return i;
2355  }
2356  }
2357 
2358  return -1;
2359 }
2360 
2364 static void make_element_name(const char *obname, const char *name, const int maxlen, char *r_name)
2365 {
2366  char str[256];
2367  SNPRINTF(str, "%s_%s", obname, name);
2368 
2369  /* Replace any point by underscore. */
2370  BLI_str_replace_char(str, '.', '_');
2371 
2372  BLI_strncpy_utf8(r_name, str, maxlen);
2373 }
2374 
2393  Scene *scene,
2394  Object *ob_gp,
2395  Object *ob_mesh,
2396  const float angle,
2397  const int thickness,
2398  const float offset,
2399  const float matrix[4][4],
2400  const int frame_offset,
2401  const bool use_seams,
2402  const bool use_faces)
2403 {
2404  if (ELEM(NULL, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) {
2405  return false;
2406  }
2407 
2408  bGPdata *gpd = (bGPdata *)ob_gp->data;
2409 
2410  /* Use evaluated data to get mesh with all modifiers on top. */
2411  Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, ob_mesh);
2412  Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
2413  MPoly *mp, *mpoly = me_eval->mpoly;
2414  MLoop *mloop = me_eval->mloop;
2415  int mpoly_len = me_eval->totpoly;
2416  char element_name[200];
2417 
2418  /* Need at least an edge. */
2419  if (me_eval->totvert < 2) {
2420  return false;
2421  }
2422 
2423  const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
2424  /* Create stroke material. */
2425  make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name);
2426  int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name);
2427 
2428  if (stroke_mat_index == -1) {
2430  bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index);
2431  }
2432 
2433  /* Export faces as filled strokes. */
2434  if (use_faces) {
2435 
2436  /* Read all polygons and create fill for each. */
2437  if (mpoly_len > 0) {
2438  make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
2439  /* Create Layer and Frame. */
2440  bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
2441  if (gpl_fill == NULL) {
2442  gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true);
2443  }
2445  gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
2446  int i;
2447  for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
2448  MLoop *ml = &mloop[mp->loopstart];
2449  /* Find material. */
2450  int mat_idx = 0;
2451  Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
2453  ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
2454  mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
2455  if (mat_idx == -1) {
2456  float color[4];
2457  if (ma != NULL) {
2458  copy_v3_v3(color, &ma->r);
2459  color[3] = 1.0f;
2460  }
2461  else {
2462  copy_v4_v4(color, default_colors[1]);
2463  }
2464  gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
2465  }
2466 
2467  bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
2468  gps_fill->flag |= GP_STROKE_CYCLIC;
2469 
2470  /* Add points to strokes. */
2471  for (int j = 0; j < mp->totloop; j++, ml++) {
2472  MVert *mv = &me_eval->mvert[ml->v];
2473  bGPDspoint *pt = &gps_fill->points[j];
2474  copy_v3_v3(&pt->x, mv->co);
2475  mul_m4_v3(matrix, &pt->x);
2476  pt->pressure = 1.0f;
2477  pt->strength = 1.0f;
2478  }
2479  /* If has only 3 points subdivide. */
2480  if (mp->totloop == 3) {
2482  }
2483 
2484  BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
2485  }
2486  }
2487  }
2488 
2489  /* Create stroke from edges. */
2490  make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
2491 
2492  /* Create Layer and Frame. */
2493  bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
2494  if (gpl_stroke == NULL) {
2495  gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true);
2496  }
2497  bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
2498  gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
2499 
2501  ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
2502 
2503  /* Tag for recalculation */
2505 
2506  return true;
2507 }
2508 
2514 void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
2515 {
2516  if (gpd == NULL) {
2517  return;
2518  }
2519 
2520  const float scalef = mat4_to_scale(mat);
2521  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2522  /* FIXME: For now, we just skip parented layers.
2523  * Otherwise, we have to update each frame to find
2524  * the current parent position/effects.
2525  */
2526  if (gpl->parent) {
2527  continue;
2528  }
2529 
2530  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2531  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2532  bGPDspoint *pt;
2533  int i;
2534 
2535  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2536  mul_m4_v3(mat, &pt->x);
2537  pt->pressure *= scalef;
2538  }
2539 
2540  /* Distortion may mean we need to re-triangulate. */
2542  }
2543  }
2544  }
2545 }
2546 
2547 /* Used for "move only origins" in object_data_transform.c */
2549 {
2550  int total_points = 0;
2551 
2552  if (gpd == NULL) {
2553  return 0;
2554  }
2555 
2556  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2557  /* FIXME: For now, we just skip parented layers.
2558  * Otherwise, we have to update each frame to find
2559  * the current parent position/effects.
2560  */
2561  if (gpl->parent) {
2562  continue;
2563  }
2564 
2565  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2566  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2567  total_points += gps->totpoints;
2568  }
2569  }
2570  }
2571  return total_points;
2572 }
2573 
2574 /* Used for "move only origins" in object_data_transform.c */
2576 {
2577  if (gpd == NULL) {
2578  return;
2579  }
2580 
2581  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2582  /* FIXME: For now, we just skip parented layers.
2583  * Otherwise, we have to update each frame to find
2584  * the current parent position/effects.
2585  */
2586  if (gpl->parent) {
2587  continue;
2588  }
2589 
2590  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2591  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2592  bGPDspoint *pt;
2593  int i;
2594 
2595  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2596  copy_v3_v3(elem_data->co, &pt->x);
2597  elem_data->pressure = pt->pressure;
2598  elem_data++;
2599  }
2600  }
2601  }
2602  }
2603 }
2604 
2605 /* Used for "move only origins" in object_data_transform.c */
2607 {
2608  if (gpd == NULL) {
2609  return;
2610  }
2611 
2612  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2613  /* FIXME: For now, we just skip parented layers.
2614  * Otherwise, we have to update each frame to find
2615  * the current parent position/effects.
2616  */
2617  if (gpl->parent) {
2618  continue;
2619  }
2620 
2621  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2622  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2623  bGPDspoint *pt;
2624  int i;
2625 
2626  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2627  copy_v3_v3(&pt->x, elem_data->co);
2628  pt->pressure = elem_data->pressure;
2629  elem_data++;
2630  }
2631 
2632  /* Distortion may mean we need to re-triangulate. */
2634  }
2635  }
2636  }
2637 }
2638 
2639 /* Used for "move only origins" in object_data_transform.c */
2641  const GPencilPointCoordinates *elem_data,
2642  const float mat[4][4])
2643 {
2644  if (gpd == NULL) {
2645  return;
2646  }
2647 
2648  const float scalef = mat4_to_scale(mat);
2649  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2650  /* FIXME: For now, we just skip parented layers.
2651  * Otherwise, we have to update each frame to find
2652  * the current parent position/effects.
2653  */
2654  if (gpl->parent) {
2655  continue;
2656  }
2657 
2658  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2659  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2660  bGPDspoint *pt;
2661  int i;
2662 
2663  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2664  mul_v3_m4v3(&pt->x, mat, elem_data->co);
2665  pt->pressure = elem_data->pressure * scalef;
2666  elem_data++;
2667  }
2668 
2669  /* Distortion may mean we need to re-triangulate. */
2671  }
2672  }
2673  }
2674 }
2675 
2681 {
2682  BLI_assert(gps->totpoints > 0);
2683 
2684  float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
2685  bGPDspoint *pt = &gps->points[0];
2686  color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z));
2687  color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x));
2688  color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y));
2689  for (int i = 0; i < gps->totpoints; i++) {
2690  pt = &gps->points[i];
2691  copy_v4_v4(pt->vert_color, color);
2692  }
2693 }
2694 
2695 /* Flip stroke. */
2697 {
2698  int end = gps->totpoints - 1;
2699 
2700  for (int i = 0; i < gps->totpoints / 2; i++) {
2701  bGPDspoint *point, *point2;
2702  bGPDspoint pt;
2703 
2704  /* save first point */
2705  point = &gps->points[i];
2706  pt.x = point->x;
2707  pt.y = point->y;
2708  pt.z = point->z;
2709  pt.flag = point->flag;
2710  pt.pressure = point->pressure;
2711  pt.strength = point->strength;
2712  pt.time = point->time;
2713  copy_v4_v4(pt.vert_color, point->vert_color);
2714 
2715  /* replace first point with last point */
2716  point2 = &gps->points[end];
2717  point->x = point2->x;
2718  point->y = point2->y;
2719  point->z = point2->z;
2720  point->flag = point2->flag;
2721  point->pressure = point2->pressure;
2722  point->strength = point2->strength;
2723  point->time = point2->time;
2724  copy_v4_v4(point->vert_color, point2->vert_color);
2725 
2726  /* replace last point with first saved before */
2727  point = &gps->points[end];
2728  point->x = pt.x;
2729  point->y = pt.y;
2730  point->z = pt.z;
2731  point->flag = pt.flag;
2732  point->pressure = pt.pressure;
2733  point->strength = pt.strength;
2734  point->time = pt.time;
2735  copy_v4_v4(point->vert_color, pt.vert_color);
2736 
2737  end--;
2738  }
2739 }
2740 
2741 /* Temp data for storing information about an "island" of points
2742  * that should be kept when splitting up a stroke. Used in:
2743  * gpencil_stroke_delete_tagged_points()
2744  */
2745 typedef struct tGPDeleteIsland {
2747  int end_idx;
2749 
2751  bGPDframe *gpf,
2752  bGPDstroke *gps_first,
2753  bGPDstroke *gps_last)
2754 {
2755  bGPDspoint *pt = NULL;
2756  bGPDspoint *pt_final = NULL;
2757  const int totpoints = gps_first->totpoints + gps_last->totpoints;
2758 
2759  /* create new stroke */
2760  bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false, true);
2761 
2762  join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
2763  join_stroke->totpoints = totpoints;
2764  join_stroke->flag &= ~GP_STROKE_CYCLIC;
2765 
2766  /* copy points (last before) */
2767  int e1 = 0;
2768  int e2 = 0;
2769  float delta = 0.0f;
2770 
2771  for (int i = 0; i < totpoints; i++) {
2772  pt_final = &join_stroke->points[i];
2773  if (i < gps_last->totpoints) {
2774  pt = &gps_last->points[e1];
2775  e1++;
2776  }
2777  else {
2778  pt = &gps_first->points[e2];
2779  e2++;
2780  }
2781 
2782  /* copy current point */
2783  copy_v3_v3(&pt_final->x, &pt->x);
2784  pt_final->pressure = pt->pressure;
2785  pt_final->strength = pt->strength;
2786  pt_final->time = delta;
2787  pt_final->flag = pt->flag;
2788  copy_v4_v4(pt_final->vert_color, pt->vert_color);
2789 
2790  /* retiming with fixed time interval (we cannot determine real time) */
2791  delta += 0.01f;
2792  }
2793 
2794  /* Copy over vertex weight data (if available) */
2795  if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) {
2796  join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
2797  MDeformVert *dvert_src = NULL;
2798  MDeformVert *dvert_dst = NULL;
2799 
2800  /* Copy weights (last before)*/
2801  e1 = 0;
2802  e2 = 0;
2803  for (int i = 0; i < totpoints; i++) {
2804  dvert_dst = &join_stroke->dvert[i];
2805  dvert_src = NULL;
2806  if (i < gps_last->totpoints) {
2807  if (gps_last->dvert) {
2808  dvert_src = &gps_last->dvert[e1];
2809  e1++;
2810  }
2811  }
2812  else {
2813  if (gps_first->dvert) {
2814  dvert_src = &gps_first->dvert[e2];
2815  e2++;
2816  }
2817  }
2818 
2819  if ((dvert_src) && (dvert_src->dw)) {
2820  dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
2821  }
2822  }
2823  }
2824 
2825  /* add new stroke at head */
2826  BLI_addhead(&gpf->strokes, join_stroke);
2827  /* Calc geometry data. */
2828  BKE_gpencil_stroke_geometry_update(gpd, join_stroke);
2829 
2830  /* remove first stroke */
2831  BLI_remlink(&gpf->strokes, gps_first);
2832  BKE_gpencil_free_stroke(gps_first);
2833 
2834  /* remove last stroke */
2835  BLI_remlink(&gpf->strokes, gps_last);
2836  BKE_gpencil_free_stroke(gps_last);
2837 }
2838 
2839 /* Split the given stroke into several new strokes, partitioning
2840  * it based on whether the stroke points have a particular flag
2841  * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
2842  *
2843  * The algorithm used here is as follows:
2844  * 1) We firstly identify the number of "islands" of non-tagged points
2845  * which will all end up being in new strokes.
2846  * - In the most extreme case (i.e. every other vert is a 1-vert island),
2847  * we have at most n / 2 islands
2848  * - Once we start having larger islands than that, the number required
2849  * becomes much less
2850  * 2) Each island gets converted to a new stroke
2851  * If the number of points is <= limit, the stroke is deleted
2852  */
2854  bGPDframe *gpf,
2855  bGPDstroke *gps,
2856  bGPDstroke *next_stroke,
2857  int tag_flags,
2858  const bool select,
2859  const bool flat_cap,
2860  const int limit)
2861 {
2862  tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2,
2863  "gp_point_islands");
2864  bool in_island = false;
2865  int num_islands = 0;
2866 
2867  bGPDstroke *new_stroke = NULL;
2868  bGPDstroke *gps_first = NULL;
2869  const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC);
2870 
2871  /* First Pass: Identify start/end of islands */
2872  bGPDspoint *pt = gps->points;
2873  for (int i = 0; i < gps->totpoints; i++, pt++) {
2874  if (pt->flag & tag_flags) {
2875  /* selected - stop accumulating to island */
2876  in_island = false;
2877  }
2878  else {
2879  /* unselected - start of a new island? */
2880  int idx;
2881 
2882  if (in_island) {
2883  /* extend existing island */
2884  idx = num_islands - 1;
2885  islands[idx].end_idx = i;
2886  }
2887  else {
2888  /* start of new island */
2889  in_island = true;
2890  num_islands++;
2891 
2892  idx = num_islands - 1;
2893  islands[idx].start_idx = islands[idx].end_idx = i;
2894  }
2895  }
2896  }
2897 
2898  /* Watch out for special case where No islands = All points selected = Delete Stroke only */
2899  if (num_islands) {
2900  /* There are islands, so create a series of new strokes,
2901  * adding them before the "next" stroke. */
2902  int idx;
2903 
2904  /* Create each new stroke... */
2905  for (idx = 0; idx < num_islands; idx++) {
2906  tGPDeleteIsland *island = &islands[idx];
2907  new_stroke = BKE_gpencil_stroke_duplicate(gps, false, true);
2908  if (flat_cap) {
2909  new_stroke->caps[1 - (idx % 2)] = GP_STROKE_CAP_FLAT;
2910  }
2911 
2912  /* if cyclic and first stroke, save to join later */
2913  if ((is_cyclic) && (gps_first == NULL)) {
2914  gps_first = new_stroke;
2915  }
2916 
2917  new_stroke->flag &= ~GP_STROKE_CYCLIC;
2918 
2919  /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
2920  new_stroke->totpoints = island->end_idx - island->start_idx + 1;
2921 
2922  /* Copy over the relevant point data */
2923  new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints,
2924  "gp delete stroke fragment");
2925  memcpy(new_stroke->points,
2926  gps->points + island->start_idx,
2927  sizeof(bGPDspoint) * new_stroke->totpoints);
2928 
2929  /* Copy over vertex weight data (if available) */
2930  if (gps->dvert != NULL) {
2931  /* Copy over the relevant vertex-weight points */
2932  new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints,
2933  "gp delete stroke fragment weight");
2934  memcpy(new_stroke->dvert,
2935  gps->dvert + island->start_idx,
2936  sizeof(MDeformVert) * new_stroke->totpoints);
2937 
2938  /* Copy weights */
2939  int e = island->start_idx;
2940  for (int i = 0; i < new_stroke->totpoints; i++) {
2941  MDeformVert *dvert_src = &gps->dvert[e];
2942  MDeformVert *dvert_dst = &new_stroke->dvert[i];
2943  if (dvert_src->dw) {
2944  dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
2945  }
2946  e++;
2947  }
2948  }
2949  /* Each island corresponds to a new stroke.
2950  * We must adjust the timings of these new strokes:
2951  *
2952  * Each point's timing data is a delta from stroke's inittime, so as we erase some points
2953  * from the start of the stroke, we have to offset this inittime and all remaining points'
2954  * delta values. This way we get a new stroke with exactly the same timing as if user had
2955  * started drawing from the first non-removed point.
2956  */
2957  {
2958  bGPDspoint *pts;
2959  float delta = gps->points[island->start_idx].time;
2960  int j;
2961 
2962  new_stroke->inittime += (double)delta;
2963 
2964  pts = new_stroke->points;
2965  for (j = 0; j < new_stroke->totpoints; j++, pts++) {
2966  pts->time -= delta;
2967  /* set flag for select again later */
2968  if (select == true) {
2969  pts->flag &= ~GP_SPOINT_SELECT;
2970  pts->flag |= GP_SPOINT_TAG;
2971  }
2972  }
2973  }
2974 
2975  /* Add new stroke to the frame or delete if below limit */
2976  if ((limit > 0) && (new_stroke->totpoints <= limit)) {
2977  BKE_gpencil_free_stroke(new_stroke);
2978  }
2979  else {
2980  /* Calc geometry data. */
2981  BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
2982 
2983  if (next_stroke) {
2984  BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
2985  }
2986  else {
2987  BLI_addtail(&gpf->strokes, new_stroke);
2988  }
2989  }
2990  }
2991  /* if cyclic, need to join last stroke with first stroke */
2992  if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) {
2993  gpencil_stroke_join_islands(gpd, gpf, gps_first, new_stroke);
2994  }
2995  }
2996 
2997  /* free islands */
2998  MEM_freeN(islands);
2999 
3000  /* Delete the old stroke */
3001  BLI_remlink(&gpf->strokes, gps);
3003 
3004  return new_stroke;
3005 }
3006 
3008  bGPDframe *gpf,
3009  bGPDstroke *gps,
3010  bGPDstroke *next_stroke,
3011  bGPDcurve *gpc,
3012  int tag_flags)
3013 {
3014  if (gpc == NULL) {
3015  return;
3016  }
3017  const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
3018  const int idx_last = gpc->tot_curve_points - 1;
3019  bGPDstroke *gps_first = NULL;
3020  bGPDstroke *gps_last = NULL;
3021 
3022  int idx_start = 0;
3023  int idx_end = 0;
3024  bool prev_selected = gpc->curve_points[0].flag & tag_flags;
3025  for (int i = 1; i < gpc->tot_curve_points; i++) {
3026  bool selected = gpc->curve_points[i].flag & tag_flags;
3027  if (prev_selected == true && selected == false) {
3028  idx_start = i;
3029  }
3030  /* Island ends if the current point is selected or if we reached the end of the stroke */
3031  if ((prev_selected == false && selected == true) || (selected == false && i == idx_last)) {
3032 
3033  idx_end = selected ? i - 1 : i;
3034  int island_length = idx_end - idx_start + 1;
3035 
3036  /* If an island has only a single curve point, there is no curve segment, so skip island */
3037  if (island_length == 1) {
3038  if (is_cyclic) {
3039  if (idx_start > 0 && idx_end < idx_last) {
3040  prev_selected = selected;
3041  continue;
3042  }
3043  }
3044  else {
3045  prev_selected = selected;
3046  continue;
3047  }
3048  }
3049 
3050  bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false);
3051  new_stroke->points = NULL;
3052  new_stroke->flag &= ~GP_STROKE_CYCLIC;
3053  new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length);
3054 
3055  if (gps_first == NULL) {
3056  gps_first = new_stroke;
3057  }
3058 
3059  bGPDcurve *new_gpc = new_stroke->editcurve;
3060  memcpy(new_gpc->curve_points,
3061  gpc->curve_points + idx_start,
3062  sizeof(bGPDcurve_point) * island_length);
3063 
3065  new_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
3066 
3067  /* Calc geometry data. */
3068  BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
3069 
3070  if (next_stroke) {
3071  BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
3072  }
3073  else {
3074  BLI_addtail(&gpf->strokes, new_stroke);
3075  }
3076 
3077  gps_last = new_stroke;
3078  }
3079  prev_selected = selected;
3080  }
3081 
3082  /* join first and last stroke if cyclic */
3083  if (is_cyclic && gps_first != NULL && gps_last != NULL && gps_first != gps_last) {
3084  bGPDcurve *gpc_first = gps_first->editcurve;
3085  bGPDcurve *gpc_last = gps_last->editcurve;
3086  int first_tot_points = gpc_first->tot_curve_points;
3087  int old_tot_points = gpc_last->tot_curve_points;
3088 
3089  gpc_last->tot_curve_points = first_tot_points + old_tot_points;
3090  gpc_last->curve_points = MEM_recallocN(gpc_last->curve_points,
3091  sizeof(bGPDcurve_point) * gpc_last->tot_curve_points);
3092  /* copy data from first to last */
3093  memcpy(gpc_last->curve_points + old_tot_points,
3094  gpc_first->curve_points,
3095  sizeof(bGPDcurve_point) * first_tot_points);
3096 
3098  gps_last->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
3099 
3100  /* Calc geometry data. */
3101  BKE_gpencil_stroke_geometry_update(gpd, gps_last);
3102 
3103  /* remove first one */
3104  BLI_remlink(&gpf->strokes, gps_first);
3105  BKE_gpencil_free_stroke(gps_first);
3106  }
3107 
3108  /* Delete the old stroke */
3109  BLI_remlink(&gpf->strokes, gps);
3111 }
3112 
3113 /* Helper: copy point between strokes */
3115  MDeformVert *dvert,
3116  bGPDspoint *point,
3117  const float delta[3],
3118  float pressure,
3119  float strength,
3120  float deltatime)
3121 {
3122  bGPDspoint *newpoint;
3123 
3124  gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
3125  if (gps->dvert != NULL) {
3126  gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1));
3127  }
3128  else {
3129  /* If destination has weight add weight to origin. */
3130  if (dvert != NULL) {
3131  gps->dvert = MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1), __func__);
3132  }
3133  }
3134 
3135  gps->totpoints++;
3136  newpoint = &gps->points[gps->totpoints - 1];
3137 
3138  newpoint->x = point->x * delta[0];
3139  newpoint->y = point->y * delta[1];
3140  newpoint->z = point->z * delta[2];
3141  newpoint->flag = point->flag;
3142  newpoint->pressure = pressure;
3143  newpoint->strength = strength;
3144  newpoint->time = point->time + deltatime;
3145  copy_v4_v4(newpoint->vert_color, point->vert_color);
3146 
3147  if (gps->dvert != NULL) {
3148  MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
3149 
3150  if (dvert != NULL) {
3151  newdvert->totweight = dvert->totweight;
3152  newdvert->dw = MEM_dupallocN(dvert->dw);
3153  }
3154  else {
3155  newdvert->totweight = 0;
3156  newdvert->dw = NULL;
3157  }
3158  }
3159 }
3160 
3161 /* Join two strokes using the shortest distance (reorder stroke if necessary ) */
3163  bGPDstroke *gps_b,
3164  const bool leave_gaps,
3165  const bool fit_thickness)
3166 {
3167  bGPDspoint point;
3168  bGPDspoint *pt;
3169  int i;
3170  const float delta[3] = {1.0f, 1.0f, 1.0f};
3171  float deltatime = 0.0f;
3172 
3173  /* sanity checks */
3174  if (ELEM(NULL, gps_a, gps_b)) {
3175  return;
3176  }
3177 
3178  if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) {
3179  return;
3180  }
3181 
3182  /* define start and end points of each stroke */
3183  float start_a[3], start_b[3], end_a[3], end_b[3];
3184  pt = &gps_a->points[0];
3185  copy_v3_v3(start_a, &pt->x);
3186 
3187  pt = &gps_a->points[gps_a->totpoints - 1];
3188  copy_v3_v3(end_a, &pt->x);
3189 
3190  pt = &gps_b->points[0];
3191  copy_v3_v3(start_b, &pt->x);
3192 
3193  pt = &gps_b->points[gps_b->totpoints - 1];
3194  copy_v3_v3(end_b, &pt->x);
3195 
3196  /* Check if need flip strokes. */
3197  float dist = len_squared_v3v3(end_a, start_b);
3198  bool flip_a = false;
3199  bool flip_b = false;
3200  float lowest = dist;
3201 
3202  dist = len_squared_v3v3(end_a, end_b);
3203  if (dist < lowest) {
3204  lowest = dist;
3205  flip_a = false;
3206  flip_b = true;
3207  }
3208 
3209  dist = len_squared_v3v3(start_a, start_b);
3210  if (dist < lowest) {
3211  lowest = dist;
3212  flip_a = true;
3213  flip_b = false;
3214  }
3215 
3216  dist = len_squared_v3v3(start_a, end_b);
3217  if (dist < lowest) {
3218  lowest = dist;
3219  flip_a = true;
3220  flip_b = true;
3221  }
3222 
3223  if (flip_a) {
3224  BKE_gpencil_stroke_flip(gps_a);
3225  }
3226  if (flip_b) {
3227  BKE_gpencil_stroke_flip(gps_b);
3228  }
3229 
3230  /* don't visibly link the first and last points? */
3231  if (leave_gaps) {
3232  /* 1st: add one tail point to start invisible area */
3233  point = gps_a->points[gps_a->totpoints - 1];
3234  deltatime = point.time;
3235 
3236  gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, 0.0f);
3237 
3238  /* 2nd: add one head point to finish invisible area */
3239  point = gps_b->points[0];
3240  gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, deltatime);
3241  }
3242 
3243  const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ?
3244  (float)gps_b->thickness / (float)gps_a->thickness :
3245  1.0f;
3246 
3247  /* 3rd: add all points */
3248  for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
3249  MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : NULL;
3251  gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime);
3252  }
3253 }
3254 
3255 /* Copy the stroke of the frame to all frames selected (except current). */
3257  bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const bool tail)
3258 {
3259  GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64);
3260  BKE_gpencil_frame_selected_hash(gpd, frame_list);
3261 
3262  GHashIterator gh_iter;
3263  GHASH_ITER (gh_iter, frame_list) {
3264  int cfra = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter));
3265 
3266  if (gpf->framenum != cfra) {
3267  bGPDframe *gpf_new = BKE_gpencil_layer_frame_find(gpl, cfra);
3268  if (gpf_new == NULL) {
3269  gpf_new = BKE_gpencil_frame_addnew(gpl, cfra);
3270  }
3271 
3272  if (gpf_new == NULL) {
3273  continue;
3274  }
3275 
3276  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
3277  if (gps_new == NULL) {
3278  continue;
3279  }
3280 
3281  if (tail) {
3282  BLI_addhead(&gpf_new->strokes, gps_new);
3283  }
3284  else {
3285  BLI_addtail(&gpf_new->strokes, gps_new);
3286  }
3287  }
3288  }
3289 
3290  /* Free hash table. */
3291  BLI_ghash_free(frame_list, NULL, NULL);
3292 }
3293 
3294 /* Stroke Uniform Subdivide ------------------------------------- */
3295 
3296 typedef struct tSamplePoint {
3298  float x, y, z;
3300  float vertex_color[4];
3304 
3305 typedef struct tSampleEdge {
3306  float length_sq;
3310 
3311 /* Helper: creates a tSamplePoint from a bGPDspoint and (optionally) a MDeformVert. */
3313 {
3314  tSamplePoint *new_pt = MEM_callocN(sizeof(tSamplePoint), __func__);
3315  copy_v3_v3(&new_pt->x, &pt->x);
3316  new_pt->pressure = pt->pressure;
3317  new_pt->strength = pt->strength;
3318  new_pt->time = pt->time;
3319  copy_v4_v4((float *)&new_pt->vertex_color, (float *)&pt->vert_color);
3320  if (dvert != NULL) {
3321  new_pt->totweight = dvert->totweight;
3322  new_pt->dw = MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__);
3323  for (uint i = 0; i < new_pt->totweight; ++i) {
3324  MDeformWeight *dw = &new_pt->dw[i];
3325  MDeformWeight *dw_from = &dvert->dw[i];
3326  dw->def_nr = dw_from->def_nr;
3327  dw->weight = dw_from->weight;
3328  }
3329  }
3330  return new_pt;
3331 }
3332 
3333 /* Helper: creates a tSampleEdge from two tSamplePoints. Also calculates the length (squared) of
3334  * the edge. */
3336 {
3337  tSampleEdge *new_edge = MEM_callocN(sizeof(tSampleEdge), __func__);
3338  new_edge->from = from;
3339  new_edge->to = to;
3340  new_edge->length_sq = len_squared_v3v3(&from->x, &to->x);
3341  return new_edge;
3342 }
3343 
3354  bGPDstroke *gps,
3355  const uint32_t target_number,
3356  const bool select)
3357 {
3358  /* Stroke needs at least two points and strictly less points than the target number. */
3359  if (gps == NULL || gps->totpoints < 2 || gps->totpoints >= target_number) {
3360  return;
3361  }
3362 
3363  const int totpoints = gps->totpoints;
3364  const bool has_dverts = (gps->dvert != NULL);
3365  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
3366 
3367  ListBase points = {NULL, NULL};
3368  Heap *edges = BLI_heap_new();
3369 
3370  /* Add all points into list. */
3371  for (uint32_t i = 0; i < totpoints; ++i) {
3372  bGPDspoint *pt = &gps->points[i];
3373  MDeformVert *dvert = has_dverts ? &gps->dvert[i] : NULL;
3375  BLI_addtail(&points, sp);
3376  }
3377 
3378  /* Iterate over edges and insert them into the heap. */
3379  for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != NULL; pt = pt->next) {
3380  tSampleEdge *se = new_sample_edge_from_sample_points(pt->prev, pt);
3381  /* BLI_heap is a min-heap, but we need the largest key to be at the top, so we take the
3382  * negative of the squared length. */
3383  BLI_heap_insert(edges, -(se->length_sq), se);
3384  }
3385 
3386  if (is_cyclic) {
3387  tSamplePoint *sp_first = points.first;
3388  tSamplePoint *sp_last = points.last;
3389  tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first);
3390  BLI_heap_insert(edges, -(se->length_sq), se);
3391  }
3392 
3393  int num_points_needed = target_number - totpoints;
3394  BLI_assert(num_points_needed > 0);
3395 
3396  while (num_points_needed > 0) {
3397  tSampleEdge *se = BLI_heap_pop_min(edges);
3398  tSamplePoint *sp = se->from;
3399  tSamplePoint *sp_next = se->to;
3400 
3401  /* Subdivide the edge. */
3402  tSamplePoint *new_sp = MEM_callocN(sizeof(tSamplePoint), __func__);
3403  interp_v3_v3v3(&new_sp->x, &sp->x, &sp_next->x, 0.5f);
3404  new_sp->pressure = interpf(sp->pressure, sp_next->pressure, 0.5f);
3405  new_sp->strength = interpf(sp->strength, sp_next->strength, 0.5f);
3406  new_sp->time = interpf(sp->time, sp_next->time, 0.5f);
3407  interp_v4_v4v4((float *)&new_sp->vertex_color,
3408  (float *)&sp->vertex_color,
3409  (float *)&sp_next->vertex_color,
3410  0.5f);
3411  if (sp->dw && sp_next->dw) {
3412  new_sp->totweight = MIN2(sp->totweight, sp_next->totweight);
3413  new_sp->dw = MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight, __func__);
3414  for (uint32_t i = 0; i < new_sp->totweight; ++i) {
3415  MDeformWeight *dw = &new_sp->dw[i];
3416  MDeformWeight *dw_from = &sp->dw[i];
3417  MDeformWeight *dw_to = &sp_next->dw[i];
3418  dw->def_nr = dw_from->def_nr;
3419  dw->weight = interpf(dw_from->weight, dw_to->weight, 0.5f);
3420  }
3421  }
3422  BLI_insertlinkafter(&points, sp, new_sp);
3423 
3424  tSampleEdge *se_prev = new_sample_edge_from_sample_points(sp, new_sp);
3425  tSampleEdge *se_next = new_sample_edge_from_sample_points(new_sp, sp_next);
3426  BLI_heap_insert(edges, -(se_prev->length_sq), se_prev);
3427  BLI_heap_insert(edges, -(se_next->length_sq), se_next);
3428 
3429  MEM_freeN(se);
3430  num_points_needed--;
3431  }
3432 
3433  /* Edges are no longer needed. Heap is freed. */
3435 
3436  gps->totpoints = target_number;
3437  gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
3438  if (has_dverts) {
3439  gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
3440  }
3441 
3442  /* Convert list back to stroke point array. */
3443  tSamplePoint *sp = points.first;
3444  for (uint32_t i = 0; i < gps->totpoints && sp; ++i, sp = sp->next) {
3445  bGPDspoint *pt = &gps->points[i];
3446  MDeformVert *dvert = &gps->dvert[i];
3447 
3448  copy_v3_v3(&pt->x, &sp->x);
3449  pt->pressure = sp->pressure;
3450  pt->strength = sp->strength;
3451  pt->time = sp->time;
3452  copy_v4_v4((float *)&pt->vert_color, (float *)&sp->vertex_color);
3453 
3454  if (sp->dw) {
3455  dvert->totweight = sp->totweight;
3456  dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__);
3457  for (uint32_t j = 0; j < dvert->totweight; ++j) {
3458  MDeformWeight *dw = &dvert->dw[j];
3459  MDeformWeight *dw_from = &sp->dw[j];
3460  dw->def_nr = dw_from->def_nr;
3461  dw->weight = dw_from->weight;
3462  }
3463  }
3464  if (select) {
3465  pt->flag |= GP_SPOINT_SELECT;
3466  }
3467  }
3468 
3469  if (select) {
3470  gps->flag |= GP_STROKE_SELECT;
3472  }
3473 
3474  /* Free the sample points. Important to use the mutable loop here because we are erasing the list
3475  * elements. */
3476  LISTBASE_FOREACH_MUTABLE (tSamplePoint *, temp, &points) {
3477  if (temp->dw != NULL) {
3478  MEM_freeN(temp->dw);
3479  }
3480  MEM_SAFE_FREE(temp);
3481  }
3482 
3483  /* Update the geometry of the stroke. */
3485 }
3486 
3494  bGPDstroke *gps,
3495  const float diff_mat[4][4])
3496 {
3497  for (int i = 0; i < gps->totpoints; i++) {
3498  bGPDspoint *pt = &gps->points[i];
3499  /* Point to parent space. */
3500  mul_v3_m4v3(&pt->x, diff_mat, &pt->x);
3501  /* point to view space */
3502  mul_m4_v3(rv3d->viewmat, &pt->x);
3503  }
3504 }
3505 
3513  bGPDstroke *gps,
3514  const float diff_mat[4][4])
3515 {
3516  float inverse_diff_mat[4][4];
3517  invert_m4_m4(inverse_diff_mat, diff_mat);
3518 
3519  for (int i = 0; i < gps->totpoints; i++) {
3520  bGPDspoint *pt = &gps->points[i];
3521  mul_v3_m4v3(&pt->x, rv3d->viewinv, &pt->x);
3522  mul_m4_v3(inverse_diff_mat, &pt->x);
3523  }
3524 }
3525 
3526 /* ----------------------------------------------------------------------------- */
3527 /* Stroke to perimeter */
3528 
3529 typedef struct tPerimeterPoint {
3531  float x, y, z;
3533 
3534 static tPerimeterPoint *new_perimeter_point(const float pt[3])
3535 {
3536  tPerimeterPoint *new_pt = MEM_callocN(sizeof(tPerimeterPoint), __func__);
3537  copy_v3_v3(&new_pt->x, pt);
3538  return new_pt;
3539 }
3540 
3543  tPerimeterPoint *to,
3544  float center_pt[3],
3545  int subdivisions,
3546  bool clockwise)
3547 {
3548  float vec_from[2];
3549  float vec_to[2];
3550  sub_v2_v2v2(vec_from, &from->x, center_pt);
3551  sub_v2_v2v2(vec_to, &to->x, center_pt);
3552  if (is_zero_v2(vec_from) || is_zero_v2(vec_to)) {
3553  return 0;
3554  }
3555 
3556  float dot = dot_v2v2(vec_from, vec_to);
3557  float det = cross_v2v2(vec_from, vec_to);
3558  float angle = clockwise ? M_PI - atan2f(-det, -dot) : atan2f(-det, -dot) + M_PI;
3559 
3560  /* Number of points is 2^(n+1) + 1 on half a circle (n=subdivisions)
3561  * so we multiply by (angle / pi) to get the right amount of
3562  * points to insert. */
3563  int num_points = (int)(((1 << (subdivisions + 1)) - 1) * (angle / M_PI));
3564  if (num_points > 0) {
3565  float angle_incr = angle / (float)num_points;
3566 
3567  float vec_p[3];
3568  float vec_t[3];
3569  float tmp_angle;
3570  tPerimeterPoint *last_point;
3571  if (clockwise) {
3572  last_point = to;
3573  copy_v2_v2(vec_t, vec_to);
3574  }
3575  else {
3576  last_point = from;
3577  copy_v2_v2(vec_t, vec_from);
3578  }
3579 
3580  for (int i = 0; i < num_points - 1; i++) {
3581  tmp_angle = (i + 1) * angle_incr;
3582 
3583  rotate_v2_v2fl(vec_p, vec_t, tmp_angle);
3584  add_v2_v2(vec_p, center_pt);
3585  vec_p[2] = center_pt[2];
3586 
3587  tPerimeterPoint *new_point = new_perimeter_point(vec_p);
3588  if (clockwise) {
3589  BLI_insertlinkbefore(list, last_point, new_point);
3590  }
3591  else {
3592  BLI_insertlinkafter(list, last_point, new_point);
3593  }
3594 
3595  last_point = new_point;
3596  }
3597 
3598  return num_points - 1;
3599  }
3600 
3601  return 0;
3602 }
3603 
3606  tPerimeterPoint *to,
3607  int subdivisions)
3608 {
3609  int num_points = (1 << (subdivisions + 1)) + 1;
3610  float center_pt[3];
3611  interp_v3_v3v3(center_pt, &from->x, &to->x, 0.5f);
3612 
3613  float vec_center[2];
3614  sub_v2_v2v2(vec_center, &from->x, center_pt);
3615  if (is_zero_v2(vec_center)) {
3616  return 0;
3617  }
3618 
3619  float vec_p[3];
3620  float angle_incr = M_PI / ((float)num_points - 1);
3621 
3622  tPerimeterPoint *last_point = from;
3623  for (int i = 1; i < num_points; i++) {
3624  float angle = i * angle_incr;
3625 
3626  /* Rotate vector around point to get perimeter points. */
3627  rotate_v2_v2fl(vec_p, vec_center, angle);
3628  add_v2_v2(vec_p, center_pt);
3629  vec_p[2] = center_pt[2];
3630 
3631  tPerimeterPoint *new_point = new_perimeter_point(vec_p);
3632  BLI_insertlinkafter(list, last_point, new_point);
3633 
3634  last_point = new_point;
3635  }
3636 
3637  return num_points - 1;
3638 }
3639 
3640 static int generate_perimeter_cap(const float point[4],
3641  const float other_point[4],
3642  float radius,
3643  ListBase *list,
3644  int subdivisions,
3645  short cap_type)
3646 {
3647  float cap_vec[2];
3648  sub_v2_v2v2(cap_vec, other_point, point);
3649  normalize_v2(cap_vec);
3650 
3651  float cap_nvec[2];
3652  if (is_zero_v2(cap_vec)) {
3653  cap_nvec[0] = 0;
3654  cap_nvec[1] = radius;
3655  }
3656  else {
3657  cap_nvec[0] = -cap_vec[1];
3658  cap_nvec[1] = cap_vec[0];
3659  mul_v2_fl(cap_nvec, radius);
3660  }
3661  float cap_nvec_inv[2];
3662  negate_v2_v2(cap_nvec_inv, cap_nvec);
3663 
3664  float vec_perimeter[3];
3665  copy_v3_v3(vec_perimeter, point);
3666  add_v2_v2(vec_perimeter, cap_nvec);
3667 
3668  float vec_perimeter_inv[3];
3669  copy_v3_v3(vec_perimeter_inv, point);
3670  add_v2_v2(vec_perimeter_inv, cap_nvec_inv);
3671 
3672  tPerimeterPoint *p_pt = new_perimeter_point(vec_perimeter);
3673  tPerimeterPoint *p_pt_inv = new_perimeter_point(vec_perimeter_inv);
3674 
3675  BLI_addtail(list, p_pt);
3676  BLI_addtail(list, p_pt_inv);
3677 
3678  int num_points = 0;
3679  if (cap_type == GP_STROKE_CAP_ROUND) {
3680  num_points += generate_semi_circle_from_point_to_point(list, p_pt, p_pt_inv, subdivisions);
3681  }
3682 
3683  return num_points + 2;
3684 }
3685 
3692  const bGPDlayer *gpl,
3693  const bGPDstroke *gps,
3694  int subdivisions,
3695  int *r_num_perimeter_points)
3696 {
3697  /* sanity check */
3698  if (gps->totpoints < 1) {
3699  return NULL;
3700  }
3701 
3702  float defaultpixsize = 1000.0f / gpd->pixfactor;
3703  float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
3704 
3705  ListBase *perimeter_right_side = MEM_callocN(sizeof(ListBase), __func__);
3706  ListBase *perimeter_left_side = MEM_callocN(sizeof(ListBase), __func__);
3707  int num_perimeter_points = 0;
3708 
3709  bGPDspoint *first = &gps->points[0];
3710  bGPDspoint *last = &gps->points[gps->totpoints - 1];
3711 
3712  float first_radius = stroke_radius * first->pressure;
3713  float last_radius = stroke_radius * last->pressure;
3714 
3715  bGPDspoint *first_next;
3716  bGPDspoint *last_prev;
3717  if (gps->totpoints > 1) {
3718  first_next = &gps->points[1];
3719  last_prev = &gps->points[gps->totpoints - 2];
3720  }
3721  else {
3722  first_next = first;
3723  last_prev = last;
3724  }
3725 
3726  float first_pt[3];
3727  float last_pt[3];
3728  float first_next_pt[3];
3729  float last_prev_pt[3];
3730  copy_v3_v3(first_pt, &first->x);
3731  copy_v3_v3(last_pt, &last->x);
3732  copy_v3_v3(first_next_pt, &first_next->x);
3733  copy_v3_v3(last_prev_pt, &last_prev->x);
3734 
3735  /* Edge-case if single point. */
3736  if (gps->totpoints == 1) {
3737  first_next_pt[0] += 1.0f;
3738  last_prev_pt[0] -= 1.0f;
3739  }
3740 
3741  /* generate points for start cap */
3742  num_perimeter_points += generate_perimeter_cap(
3743  first_pt, first_next_pt, first_radius, perimeter_right_side, subdivisions, gps->caps[0]);
3744 
3745  /* generate perimeter points */
3746  float curr_pt[3], next_pt[3], prev_pt[3];
3747  float vec_next[2], vec_prev[2];
3748  float nvec_next[2], nvec_prev[2];
3749  float nvec_next_pt[3], nvec_prev_pt[3];
3750  float vec_tangent[2];
3751 
3752  float vec_miter_left[2], vec_miter_right[2];
3753  float miter_left_pt[3], miter_right_pt[3];
3754 
3755  for (int i = 1; i < gps->totpoints - 1; i++) {
3756  bGPDspoint *curr = &gps->points[i];
3757  bGPDspoint *prev = &gps->points[i - 1];
3758  bGPDspoint *next = &gps->points[i + 1];
3759  float radius = stroke_radius * curr->pressure;
3760 
3761  copy_v3_v3(curr_pt, &curr->x);
3762  copy_v3_v3(next_pt, &next->x);
3763  copy_v3_v3(prev_pt, &prev->x);
3764 
3765  sub_v2_v2v2(vec_prev, curr_pt, prev_pt);
3766  sub_v2_v2v2(vec_next, next_pt, curr_pt);
3767  float prev_length = len_v2(vec_prev);
3768  float next_length = len_v2(vec_next);
3769 
3770  if (normalize_v2(vec_prev) == 0.0f) {
3771  vec_prev[0] = 1.0f;
3772  vec_prev[1] = 0.0f;
3773  }
3774  if (normalize_v2(vec_next) == 0.0f) {
3775  vec_next[0] = 1.0f;
3776  vec_next[1] = 0.0f;
3777  }
3778 
3779  nvec_prev[0] = -vec_prev[1];
3780  nvec_prev[1] = vec_prev[0];
3781 
3782  nvec_next[0] = -vec_next[1];
3783  nvec_next[1] = vec_next[0];
3784 
3785  add_v2_v2v2(vec_tangent, vec_prev, vec_next);
3786  if (normalize_v2(vec_tangent) == 0.0f) {
3787  copy_v2_v2(vec_tangent, nvec_prev);
3788  }
3789 
3790  vec_miter_left[0] = -vec_tangent[1];
3791  vec_miter_left[1] = vec_tangent[0];
3792 
3793  /* calculate miter length */
3794  float an1 = dot_v2v2(vec_miter_left, nvec_prev);
3795  if (an1 == 0.0f) {
3796  an1 = 1.0f;
3797  }
3798  float miter_length = radius / an1;
3799  if (miter_length <= 0.0f) {
3800  miter_length = 0.01f;
3801  }
3802 
3803  normalize_v2_length(vec_miter_left, miter_length);
3804 
3805  copy_v2_v2(vec_miter_right, vec_miter_left);
3806  negate_v2(vec_miter_right);
3807 
3808  float angle = dot_v2v2(vec_next, nvec_prev);
3809  /* Add two points if angle is close to being straight. */
3810  if (fabsf(angle) < 0.0001f) {
3811  normalize_v2_length(nvec_prev, radius);
3812  normalize_v2_length(nvec_next, radius);
3813 
3814  copy_v3_v3(nvec_prev_pt, curr_pt);
3815  add_v2_v2(nvec_prev_pt, nvec_prev);
3816 
3817  copy_v3_v3(nvec_next_pt, curr_pt);
3818  negate_v2(nvec_next);
3819  add_v2_v2(nvec_next_pt, nvec_next);
3820 
3821  tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
3822  tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
3823 
3824  BLI_addtail(perimeter_left_side, normal_prev);
3825  BLI_addtail(perimeter_right_side, normal_next);
3826  num_perimeter_points += 2;
3827  }
3828  else {
3829  /* bend to the left */
3830  if (angle < 0.0f) {
3831  normalize_v2_length(nvec_prev, radius);
3832  normalize_v2_length(nvec_next, radius);
3833 
3834  copy_v3_v3(nvec_prev_pt, curr_pt);
3835  add_v2_v2(nvec_prev_pt, nvec_prev);
3836 
3837  copy_v3_v3(nvec_next_pt, curr_pt);
3838  add_v2_v2(nvec_next_pt, nvec_next);
3839 
3840  tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
3841  tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
3842 
3843  BLI_addtail(perimeter_left_side, normal_prev);
3844  BLI_addtail(perimeter_left_side, normal_next);
3845  num_perimeter_points += 2;
3846 
3847  num_perimeter_points += generate_arc_from_point_to_point(
3848  perimeter_left_side, normal_prev, normal_next, curr_pt, subdivisions, true);
3849 
3850  if (miter_length < prev_length && miter_length < next_length) {
3851  copy_v3_v3(miter_right_pt, curr_pt);
3852  add_v2_v2(miter_right_pt, vec_miter_right);
3853  }
3854  else {
3855  copy_v3_v3(miter_right_pt, curr_pt);
3856  negate_v2(nvec_next);
3857  add_v2_v2(miter_right_pt, nvec_next);
3858  }
3859 
3860  tPerimeterPoint *miter_right = new_perimeter_point(miter_right_pt);
3861  BLI_addtail(perimeter_right_side, miter_right);
3862  num_perimeter_points++;
3863  }
3864  /* bend to the right */
3865  else {
3866  normalize_v2_length(nvec_prev, -radius);
3867  normalize_v2_length(nvec_next, -radius);
3868 
3869  copy_v3_v3(nvec_prev_pt, curr_pt);
3870  add_v2_v2(nvec_prev_pt, nvec_prev);
3871 
3872  copy_v3_v3(nvec_next_pt, curr_pt);
3873  add_v2_v2(nvec_next_pt, nvec_next);
3874 
3875  tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
3876  tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
3877 
3878  BLI_addtail(perimeter_right_side, normal_prev);
3879  BLI_addtail(perimeter_right_side, normal_next);
3880  num_perimeter_points += 2;
3881 
3882  num_perimeter_points += generate_arc_from_point_to_point(
3883  perimeter_right_side, normal_prev, normal_next, curr_pt, subdivisions, false);
3884 
3885  if (miter_length < prev_length && miter_length < next_length) {
3886  copy_v3_v3(miter_left_pt, curr_pt);
3887  add_v2_v2(miter_left_pt, vec_miter_left);
3888  }
3889  else {
3890  copy_v3_v3(miter_left_pt, curr_pt);
3891  negate_v2(nvec_prev);
3892  add_v2_v2(miter_left_pt, nvec_prev);
3893  }
3894 
3895  tPerimeterPoint *miter_left = new_perimeter_point(miter_left_pt);
3896  BLI_addtail(perimeter_left_side, miter_left);
3897  num_perimeter_points++;
3898  }
3899  }
3900  }
3901 
3902  /* generate points for end cap */
3903  num_perimeter_points += generate_perimeter_cap(
3904  last_pt, last_prev_pt, last_radius, perimeter_right_side, subdivisions, gps->caps[1]);
3905 
3906  /* merge both sides to one list */
3907  BLI_listbase_reverse(perimeter_right_side);
3908  BLI_movelisttolist(perimeter_left_side,
3909  perimeter_right_side); // perimeter_left_side contains entire list
3910  ListBase *perimeter_list = perimeter_left_side;
3911 
3912  /* close by creating a point close to the first (make a small gap) */
3913  float close_pt[3];
3914  tPerimeterPoint *close_first = (tPerimeterPoint *)perimeter_list->first;
3915  tPerimeterPoint *close_last = (tPerimeterPoint *)perimeter_list->last;
3916  interp_v3_v3v3(close_pt, &close_last->x, &close_first->x, 0.99f);
3917 
3918  if (compare_v3v3(close_pt, &close_first->x, FLT_EPSILON) == false) {
3919  tPerimeterPoint *close_p_pt = new_perimeter_point(close_pt);
3920  BLI_addtail(perimeter_list, close_p_pt);
3921  num_perimeter_points++;
3922  }
3923 
3924  /* free temp data */
3925  BLI_freelistN(perimeter_right_side);
3926  MEM_freeN(perimeter_right_side);
3927 
3928  *r_num_perimeter_points = num_perimeter_points;
3929  return perimeter_list;
3930 }
3931 
3939  bGPdata *gpd,
3940  const bGPDlayer *gpl,
3941  bGPDstroke *gps,
3942  const int subdivisions,
3943  const float diff_mat[4][4])
3944 {
3945  if (gps->totpoints == 0) {
3946  return NULL;
3947  }
3948  bGPDstroke *gps_temp = BKE_gpencil_stroke_duplicate(gps, true, false);
3949  const bool cyclic = ((gps_temp->flag & GP_STROKE_CYCLIC) != 0);
3950 
3951  /* If Cyclic, add a new point. */
3952  if (cyclic && (gps_temp->totpoints > 1)) {
3953  gps_temp->totpoints++;
3954  gps_temp->points = MEM_recallocN(gps_temp->points,
3955  sizeof(*gps_temp->points) * gps_temp->totpoints);
3956  bGPDspoint *pt_src = &gps_temp->points[0];
3957  bGPDspoint *pt_dst = &gps_temp->points[gps_temp->totpoints - 1];
3958  copy_v3_v3(&pt_dst->x, &pt_src->x);
3959  pt_dst->pressure = pt_src->pressure;
3960  pt_dst->strength = pt_src->strength;
3961  pt_dst->uv_fac = 1.0f;
3962  pt_dst->uv_rot = 0;
3963  }
3964 
3965  BKE_gpencil_stroke_to_view_space(rv3d, gps_temp, diff_mat);
3966  int num_perimeter_points = 0;
3967  ListBase *perimeter_points = gpencil_stroke_perimeter_ex(
3968  gpd, gpl, gps_temp, subdivisions, &num_perimeter_points);
3969 
3970  if (num_perimeter_points == 0) {
3971  return NULL;
3972  }
3973 
3974  /* Create new stroke. */
3975  bGPDstroke *perimeter_stroke = BKE_gpencil_stroke_new(gps_temp->mat_nr, num_perimeter_points, 1);
3976 
3977  int i = 0;
3978  LISTBASE_FOREACH_INDEX (tPerimeterPoint *, curr, perimeter_points, i) {
3979  bGPDspoint *pt = &perimeter_stroke->points[i];
3980 
3981  copy_v3_v3(&pt->x, &curr->x);
3982  pt->pressure = 0.0f;
3983  pt->strength = 1.0f;
3984 
3985  pt->flag |= GP_SPOINT_SELECT;
3986  }
3987 
3988  BKE_gpencil_stroke_from_view_space(rv3d, perimeter_stroke, diff_mat);
3989 
3990  /* Free temp data. */
3991  BLI_freelistN(perimeter_points);
3992  MEM_freeN(perimeter_points);
3993 
3994  /* Triangles cache needs to be recalculated. */
3995  BKE_gpencil_stroke_geometry_update(gpd, perimeter_stroke);
3996 
3997  perimeter_stroke->flag |= GP_STROKE_SELECT | GP_STROKE_CYCLIC;
3998 
3999  BKE_gpencil_free_stroke(gps_temp);
4000 
4001  return perimeter_stroke;
4002 }
4003 
4006 {
4007 
4008  if (gps->totpoints == 1) {
4009  return gps->points[0].pressure;
4010  }
4011 
4012  float tot = 0.0f;
4013  for (int i = 0; i < gps->totpoints; i++) {
4014  const bGPDspoint *pt = &gps->points[i];
4015  tot += pt->pressure;
4016  }
4017 
4018  return tot / (float)gps->totpoints;
4019 }
4020 
4023 {
4024  if (gps->totpoints == 1) {
4025  return true;
4026  }
4027 
4028  const float first_pressure = gps->points[0].pressure;
4029  for (int i = 0; i < gps->totpoints; i++) {
4030  const bGPDspoint *pt = &gps->points[i];
4031  if (pt->pressure != first_pressure) {
4032  return false;
4033  }
4034  }
4035 
4036  return true;
4037 }
typedef float(TangentPoint)[2]
support for deformation groups and hooks.
struct MDeformWeight * BKE_defvert_ensure_index(struct MDeformVert *dv, const int defgroup)
Definition: deform.c:688
float BKE_defvert_find_weight(const struct MDeformVert *dvert, const int defgroup)
Definition: deform.c:632
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1203
struct bGPDframe * BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:539
struct bGPDstroke * BKE_gpencil_stroke_add(struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, const bool insert_at_head)
Definition: gpencil.c:871
struct Material * BKE_gpencil_object_material_new(struct Main *bmain, struct Object *ob, const char *name, int *r_index)
Definition: gpencil.c:1873
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps)
Definition: gpencil.c:370
void BKE_gpencil_free_point_weights(struct MDeformVert *dvert)
Definition: gpencil.c:362
struct bGPDstroke * BKE_gpencil_stroke_add_existing_style(struct bGPDframe *gpf, struct bGPDstroke *existing, int mat_idx, int totpoints, short thickness)
Definition: gpencil.c:899
struct bGPDcurve * BKE_gpencil_stroke_editcurve_new(const int tot_curve_points)
Definition: gpencil.c:910
struct bGPDstroke * BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
Definition: gpencil.c:826
struct bGPDlayer * BKE_gpencil_layer_named_get(struct bGPdata *gpd, const char *name)
Definition: gpencil.c:1496
struct bGPDframe * BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:1281
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:401
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1307
#define GPENCIL_STRENGTH_MIN
Definition: BKE_gpencil.h:178
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, const bool dup_points, const bool dup_curve)
Definition: gpencil.c:957
int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char *name_prefix)
Definition: gpencil.c:3031
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive)
Definition: gpencil.c:659
void BKE_gpencil_frame_selected_hash(struct bGPdata *gpd, struct GHash *r_list)
Definition: gpencil.c:3046
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:190
void BKE_gpencil_stroke_update_geometry_from_editcurve(struct bGPDstroke *gps, const uint resolution, const bool is_adaptive)
void BKE_gpencil_editcurve_recalculate_handles(struct bGPDstroke *gps)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
Definition: material.c:697
General operations, lookup, etc. for blender objects.
void BKE_boundbox_init_from_minmax(struct BoundBox *bb, const float min[3], const float max[3])
Definition: object.c:3778
struct Mesh * BKE_object_get_evaluated_mesh(struct Object *object)
Definition: object.c:4459
#define BLI_assert(a)
Definition: BLI_assert.h:58
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:146
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:169
bool BLI_ghash_haskey(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:941
GHash * BLI_ghash_int_new_ex(const char *info, const unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:756
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:1008
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition: BLI_hash.h:108
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition: BLI_hash.h:67
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition: BLI_heap.c:221
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition: BLI_heap.c:338
void(* HeapFreeFP)(void *ptr)
Definition: BLI_heap.h:35
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition: BLI_heap.c:268
Heap * BLI_heap_new(void) ATTR_WARN_UNUSED_RESULT
Definition: BLI_heap.c:216
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:257
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:87
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:281
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:188
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:352
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:180
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:547
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:395
void void void void void void BLI_listbase_reverse(struct ListBase *lb) ATTR_NONNULL(1)
Definition: listbase.c:871
#define M_PI_2
Definition: BLI_math_base.h:41
MINLINE float interpf(float a, float b, float t)
#define M_PI
Definition: BLI_math_base.h:38
int isect_line_line_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float r_i1[3], float r_i2[3])
Definition: math_geom.c:3103
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:3449
float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:3364
void closest_to_line_segment_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:375
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1278
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:732
float mat4_to_scale(const float M[4][4])
Definition: math_matrix.c:2196
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:742
MINLINE void copy_v4_v4(float r[4], const float a[4])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], const float t)
Definition: math_vector.c:49
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:443
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
Definition: math_vector.c:1020
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
void rotate_v2_v2fl(float r[2], const float p[2], const float angle)
Definition: math_vector.c:914
MINLINE bool compare_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float r[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void normal_short_to_float_v3(float r[3], const short n[3])
MINLINE void mul_v2_fl(float r[2], float f)
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], const float t)
Definition: math_vector.c:58
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void negate_v2(float r[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void negate_v2_v2(float r[2], const float a[2])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v2_length(float r[2], const float unit_scale)
MINLINE float cross_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float normalize_v2(float r[2])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void BLI_polyfill_calc(const float(*coords)[2], const unsigned int coords_tot, const int coords_sign, unsigned int(*r_tris)[3])
Definition: polyfill_2d.c:905
#define SNPRINTF(dst, format,...)
Definition: BLI_string.h:165
void BLI_str_replace_char(char *string, char src, char dst) ATTR_NONNULL()
Definition: string.c:532
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string_utf8.c:258
unsigned int uint
Definition: BLI_sys_types.h:83
#define CLAMP_MAX(a, c)
#define INIT_MINMAX(min, max)
#define ARRAY_SET_ITEMS(...)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define MAX2(a, b)
#define ELEM(...)
#define MIN2(a, b)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
int max_i(int a, int b)
Definition: Basic.c:26
typedef double(DMatrix)[4][4]
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
void DEG_id_tag_update(struct ID *id, int flag)
struct ID * DEG_get_original_id(struct ID *id)
struct Object * DEG_get_evaluated_object(const struct Depsgraph *depsgraph, struct Object *object)
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:654
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:611
@ GP_CURVE_NEEDS_STROKE_UPDATE
@ GP_STROKE_CAP_ROUND
@ GP_STROKE_CAP_FLAT
@ GP_STROKE_NEEDS_CURVE_UPDATE
@ GP_STROKE_SELECT
@ GP_STROKE_CYCLIC
@ GP_DATA_CURVE_ADAPTIVE_RESOLUTION
@ GP_DATA_CACHE_IS_DIRTY
#define GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)
@ GP_SPOINT_TAG
@ GP_SPOINT_SELECT
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
@ ME_SEAM
@ BOUNDBOX_DIRTY
@ OB_GPENCIL
#define CFRA
NSNotificationCenter * center
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint vn
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
Group RGB to Bright Vector Camera CLAMP
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
bool closest(btVector3 &v)
StackEntry * from
Scene scene
const Depsgraph * depsgraph
#define str(s)
void BKE_gpencil_stroke_flip(bGPDstroke *gps)
static tPerimeterPoint * new_perimeter_point(const float pt[3])
bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
Definition: gpencil_geom.c:537
float BKE_gpencil_stroke_average_pressure_get(bGPDstroke *gps)
static void gpencil_stroke_join_islands(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last)
void 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)
static void gpencil_calc_stroke_fill_uv(const float(*points2d)[2], bGPDstroke *gps, const float minv[2], const float maxv[2], float(*r_uv)[2])
bGPDstroke * BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags, const bool select, const bool flat_cap, const int limit)
struct tSamplePoint tSamplePoint
void BKE_gpencil_stroke_merge_distance(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const float threshold, const bool use_unselected)
void BKE_gpencil_stroke_copy_to_keyframes(bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const bool tail)
float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
void BKE_gpencil_stroke_join(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps, const bool fit_thickness)
static int generate_semi_circle_from_point_to_point(ListBase *list, tPerimeterPoint *from, tPerimeterPoint *to, int subdivisions)
static void make_element_name(const char *obname, const char *name, const int maxlen, char *r_name)
bool BKE_gpencil_stroke_is_pressure_constant(bGPDstroke *gps)
void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
Definition: gpencil_geom.c:429
static int 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)
Definition: gpencil_geom.c:213
static void gpencil_generate_edgeloops(Object *ob, bGPdata *gpd, 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)
static Material * 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)
bGPDstroke * BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, bGPdata *gpd, const bGPDlayer *gpl, bGPDstroke *gps, const int subdivisions, const float diff_mat[4][4])
bool BKE_gpencil_stroke_split(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const int before_index, bGPDstroke **remaining_gps)
Definition: gpencil_geom.c:646
struct tSampleEdge tSampleEdge
bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
Definition: gpencil_geom.c:706
bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_data)
bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps, const bool use_select, float r_min[3], float r_max[3])
Definition: gpencil_geom.c:73
static int generate_arc_from_point_to_point(ListBase *list, tPerimeterPoint *from, tPerimeterPoint *to, float center_pt[3], int subdivisions, bool clockwise)
bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf)
Definition: gpencil_geom.c:778
static void boundbox_gpencil(Object *ob)
Definition: gpencil_geom.c:154
void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd, const GPencilPointCoordinates *elem_data, const float mat[4][4])
bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
Definition: gpencil_geom.c:102
static int generate_perimeter_cap(const float point[4], const float other_point[4], float radius, ListBase *list, int subdivisions, short cap_type)
bool 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)
static MDeformVert * stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
Definition: gpencil_geom.c:390
static int gpencil_next_edge(GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d, bGPDstroke *gps, const float diff_mat[4][4])
struct GpEdge GpEdge
static int gpencil_material_find_index_by_name(Object *ob, const char *name)
static int gpencil_walk_edge(GHash *v_table, GpEdge *gp_edges, int totedges, uint *stroke_array, int init_idx, const float angle, const bool reverse)
void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, bGPDcurve *gpc, int tag_flags)
bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to)
Definition: gpencil_geom.c:587
struct tPerimeterPoint tPerimeterPoint
void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
Definition: gpencil_geom.c:841
void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
static tSamplePoint * new_sample_point_from_gp_point(const bGPDspoint *pt, const MDeformVert *dvert)
static ListBase * gpencil_stroke_perimeter_ex(const bGPdata *gpd, const bGPDlayer *gpl, const bGPDstroke *gps, int subdivisions, int *r_num_perimeter_points)
void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon)
void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction)
static void gpencil_stroke_copy_point(bGPDstroke *gps, MDeformVert *dvert, bGPDspoint *point, const float delta[3], float pressure, float strength, float deltatime)
void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
static int stroke_march_count(const bGPDstroke *gps, const float dist)
Definition: gpencil_geom.c:334
void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
Definition: gpencil_geom.c:130
static tSampleEdge * new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to)
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps, const int start_index, const int end_index, bool use_3d)
static int stroke_march_next_point_no_interp(const bGPDstroke *gps, const int index_next_pt, const float *current, const float dist, float *result)
Definition: gpencil_geom.c:287
int BKE_gpencil_stroke_point_count(bGPdata *gpd)
void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps)
void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d, bGPDstroke *gps, const float diff_mat[4][4])
struct tGPDeleteIsland tGPDeleteIsland
bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
Definition: gpencil_geom.c:905
bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
Definition: gpencil_geom.c:968
void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
Definition: gpencil_geom.c:144
bool BKE_gpencil_stroke_close(bGPDstroke *gps)
BoundBox * BKE_gpencil_boundbox_get(Object *ob)
Definition: gpencil_geom.c:182
void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates *elem_data)
void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, bGPDstroke *gps, const uint32_t target_number, const bool select)
static void stroke_interpolate_deform_weights(bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
Definition: gpencil_geom.c:410
static void stroke_defvert_create_nr_list(MDeformVert *dv_list, int count, ListBase *result, int *totweight)
Definition: gpencil_geom.c:355
IconTextureDrawCall normal
int count
#define atan2f(x, y)
#define fabsf(x)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:42
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static ulong * next
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
static double epsilon
static const int steps
Definition: sky_nishita.cpp:28
#define min(a, b)
Definition: sort.c:51
unsigned int uint32_t
Definition: stdint.h:83
float vec[8][3]
float v2_co[3]
float v1_co[3]
float vec[3]
float n1[3]
float n2[3]
Definition: BLI_heap.c:57
char name[66]
Definition: DNA_ID.h:283
void * data
Definition: DNA_listBase.h:42
struct LinkData * next
Definition: DNA_listBase.h:41
void * last
Definition: DNA_listBase.h:47
void * first
Definition: DNA_listBase.h:47
struct MDeformWeight * dw
unsigned int def_nr
unsigned int v1
unsigned int v2
unsigned int v
short mat_nr
float co[3]
short no[3]
Definition: BKE_main.h:116
struct MaterialGPencilStyle * gp_style
struct MEdge * medge
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
int totpoly
struct MPoly * mpoly
struct BoundBox * bb
Object_Runtime runtime
void * data
float viewmat[4][4]
float viewinv[4][4]
bGPDcurve_point * curve_points
ListBase strokes
struct bGPDspoint * pt_orig
float uv_fill[2]
bGPDspoint_Runtime runtime
float vert_color[4]
bGPDspoint * points
float uv_translation[2]
bGPDtriangle * triangles
float boundbox_max[3]
float boundbox_min[3]
struct bGPDcurve * editcurve
struct MDeformVert * dvert
unsigned int verts[3]
int curve_edit_resolution
ListBase layers
struct tPerimeterPoint * next
struct tPerimeterPoint * prev
tSamplePoint * from
tSamplePoint * to
struct MDeformWeight * dw
float vertex_color[4]
struct tSamplePoint * next
struct tSamplePoint * prev
float max
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: util_avxb.h:167
__forceinline avxf cross(const avxf &a, const avxf &b)
Definition: util_avxf.h:119
__forceinline BoundBox intersect(const BoundBox &a, const BoundBox &b)