Blender  V3.3
gpencil_geom.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. */
3 
8 #include <cmath>
9 #include <cstddef>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cstring>
13 
14 #include "CLG_log.h"
15 
16 #include "MEM_guardedalloc.h"
17 
18 #include "BLI_array_utils.h"
19 #include "BLI_blenlib.h"
20 #include "BLI_ghash.h"
21 #include "BLI_hash.h"
22 #include "BLI_heap.h"
23 #include "BLI_math_vec_types.hh"
24 #include "BLI_math_vector.h"
25 #include "BLI_polyfill_2d.h"
26 #include "BLI_span.hh"
27 
29 #include "DNA_gpencil_types.h"
30 #include "DNA_material_types.h"
31 #include "DNA_mesh_types.h"
32 #include "DNA_meshdata_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_screen_types.h"
35 
36 #include "BLT_translation.h"
37 
38 #include "BKE_context.h"
39 #include "BKE_deform.h"
40 #include "BKE_gpencil.h"
41 #include "BKE_gpencil_curve.h"
42 #include "BKE_gpencil_geom.h"
43 #include "BKE_main.h"
44 #include "BKE_material.h"
45 #include "BKE_mesh.h"
46 #include "BKE_object.h"
47 
48 #include "DEG_depsgraph_query.h"
49 
50 using blender::float3;
51 using blender::Span;
52 
53 /* -------------------------------------------------------------------- */
58  const bool use_select,
59  float r_min[3],
60  float r_max[3])
61 {
62  if (gps == nullptr) {
63  return false;
64  }
65 
66  bool changed = false;
67  if (use_select) {
68  for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
69  if (pt.flag & GP_SPOINT_SELECT) {
70  minmax_v3v3_v3(r_min, r_max, &pt.x);
71  changed = true;
72  }
73  }
74  }
75  else {
76  for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
77  minmax_v3v3_v3(r_min, r_max, &pt.x);
78  changed = true;
79  }
80  }
81 
82  return changed;
83 }
84 
85 bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3])
86 {
87  bool changed = false;
88 
89  INIT_MINMAX(r_min, r_max);
90 
91  if (gpd == nullptr) {
92  return changed;
93  }
94 
95  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
96  bGPDframe *gpf = gpl->actframe;
97 
98  if (gpf != nullptr) {
99  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
100  changed |= BKE_gpencil_stroke_minmax(gps, false, r_min, r_max);
101  }
102  }
103  }
104 
105  return changed;
106 }
107 
108 void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
109 {
110  float3 min;
111  float3 max;
113 
114  const float3 tot = min + max;
115  mul_v3_v3fl(r_centroid, tot, 0.5f);
116 }
117 
119 {
121  BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max);
122 }
123 
128 static void boundbox_gpencil(Object *ob)
129 {
130  if (ob->runtime.bb == nullptr) {
131  ob->runtime.bb = MEM_cnew<BoundBox>("GPencil boundbox");
132  }
133 
134  BoundBox *bb = ob->runtime.bb;
135  bGPdata *gpd = (bGPdata *)ob->data;
136 
137  float3 min;
138  float3 max;
139  if (!BKE_gpencil_data_minmax(gpd, min, max)) {
140  min = float3(-1);
141  max = float3(1);
142  }
143 
145 
146  bb->flag &= ~BOUNDBOX_DIRTY;
147 }
148 
150 {
151  if (ELEM(nullptr, ob, ob->data)) {
152  return nullptr;
153  }
154 
155  bGPdata *gpd = (bGPdata *)ob->data;
156  if ((ob->runtime.bb) && ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) {
157  return ob->runtime.bb;
158  }
159 
160  boundbox_gpencil(ob);
161 
162  Object *ob_orig = (Object *)DEG_get_original_id(&ob->id);
163  /* Update orig object's boundbox with re-computed evaluated values. This function can be
164  * called with the evaluated object and need update the original object bound box data
165  * to keep both values synchronized. */
166  if (!ELEM(ob_orig, nullptr, ob)) {
167  if (ob_orig->runtime.bb == nullptr) {
168  ob_orig->runtime.bb = MEM_cnew<BoundBox>("GPencil boundbox");
169  }
170  for (int i = 0; i < 8; i++) {
171  copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]);
172  }
173  }
174 
175  return ob->runtime.bb;
176 }
177 
180 /* -------------------------------------------------------------------- */
184 static int stroke_march_next_point(const bGPDstroke *gps,
185  const int index_next_pt,
186  const float *current,
187  const float dist,
188  float *result,
189  float *pressure,
190  float *strength,
191  float *vert_color,
192  float *uv_fac,
193  float *uv_fill,
194  float *uv_rot,
195  float *ratio_result,
196  int *index_from,
197  int *index_to)
198 {
199  float remaining_till_next = 0.0f;
200  float remaining_march = dist;
201  float step_start[3];
202  float point[3];
203  int next_point_index = index_next_pt;
204  bGPDspoint *pt = nullptr;
205 
206  if (next_point_index == gps->totpoints) {
207  next_point_index = 0;
208  }
209 
210  copy_v3_v3(step_start, current);
211  pt = &gps->points[next_point_index];
212  copy_v3_v3(point, &pt->x);
213  remaining_till_next = len_v3v3(point, step_start);
214 
215  while (remaining_till_next < remaining_march && next_point_index) {
216  remaining_march -= remaining_till_next;
217  pt = &gps->points[next_point_index];
218  if (pt->flag & GP_SPOINT_TEMP_TAG) {
219  pt = &gps->points[next_point_index];
220  copy_v3_v3(result, &pt->x);
221  *pressure = gps->points[next_point_index].pressure;
222  *strength = gps->points[next_point_index].strength;
223  memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
224 
225  *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
226  *index_to = next_point_index;
227  *ratio_result = 1.0f;
228  next_point_index++;
229  return next_point_index == 0 ? gps->totpoints : next_point_index;
230  }
231  next_point_index++;
232  copy_v3_v3(point, &pt->x);
233  copy_v3_v3(step_start, point);
234  if (!(next_point_index < gps->totpoints)) {
235  if (gps->flag & GP_STROKE_CYCLIC) {
236  next_point_index = 0;
237  }
238  else {
239  next_point_index = gps->totpoints - 1;
240  remaining_till_next = 0;
241  break;
242  }
243  }
244  pt = &gps->points[next_point_index];
245  copy_v3_v3(point, &pt->x);
246  remaining_till_next = len_v3v3(point, step_start);
247  }
248  if (remaining_till_next < remaining_march) {
249  pt = &gps->points[next_point_index];
250  copy_v3_v3(result, &pt->x);
251  *pressure = gps->points[next_point_index].pressure;
252  *strength = gps->points[next_point_index].strength;
253  memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
254 
255  *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
256  *index_to = next_point_index;
257  *ratio_result = 1.0f;
258 
259  return 0;
260  }
261 
262  *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
263  *index_to = next_point_index;
264 
265  float ratio = remaining_march / remaining_till_next;
266  interp_v3_v3v3(result, step_start, point, ratio);
267  *ratio_result = ratio;
268  float d1 = len_v3v3(result, &gps->points[*index_from].x);
269  float d2 = len_v3v3(result, &gps->points[next_point_index].x);
270  float vratio = d1 / (d1 + d2);
271 
272  *pressure = interpf(
273  gps->points[next_point_index].pressure, gps->points[*index_from].pressure, vratio);
274  *strength = interpf(
275  gps->points[next_point_index].strength, gps->points[*index_from].strength, vratio);
276  *uv_fac = interpf(gps->points[next_point_index].uv_fac, gps->points[*index_from].uv_fac, vratio);
277  *uv_rot = interpf(gps->points[next_point_index].uv_rot, gps->points[*index_from].uv_rot, vratio);
279  uv_fill, gps->points[*index_from].uv_fill, gps->points[next_point_index].uv_fill, vratio);
280  interp_v4_v4v4(vert_color,
281  gps->points[*index_from].vert_color,
282  gps->points[next_point_index].vert_color,
283  vratio);
284 
285  return next_point_index == 0 ? gps->totpoints : next_point_index;
286 }
287 
289  const int index_next_pt,
290  const float *current,
291  const float dist,
292  const float sharp_threshold,
293  float *result)
294 {
295  float remaining_till_next = 0.0f;
296  float remaining_march = dist;
297  float step_start[3];
298  float point[3];
299  int next_point_index = index_next_pt;
300  bGPDspoint *pt = nullptr;
301 
302  if (next_point_index == gps->totpoints) {
303  next_point_index = 0;
304  }
305 
306  copy_v3_v3(step_start, current);
307  pt = &gps->points[next_point_index];
308  copy_v3_v3(point, &pt->x);
309  remaining_till_next = len_v3v3(point, step_start);
310 
311  while (remaining_till_next < remaining_march && next_point_index) {
312  remaining_march -= remaining_till_next;
313  pt = &gps->points[next_point_index];
314  if (next_point_index < gps->totpoints - 1 &&
315  angle_v3v3v3(&gps->points[next_point_index - 1].x,
316  &gps->points[next_point_index].x,
317  &gps->points[next_point_index + 1].x) < sharp_threshold) {
318  copy_v3_v3(result, &pt->x);
319  pt->flag |= GP_SPOINT_TEMP_TAG;
320  next_point_index++;
321  return next_point_index == 0 ? gps->totpoints : next_point_index;
322  }
323  next_point_index++;
324  copy_v3_v3(point, &pt->x);
325  copy_v3_v3(step_start, point);
326  if (!(next_point_index < gps->totpoints)) {
327  if (gps->flag & GP_STROKE_CYCLIC) {
328  next_point_index = 0;
329  }
330  else {
331  next_point_index = gps->totpoints - 1;
332  remaining_till_next = 0;
333  break;
334  }
335  }
336  pt = &gps->points[next_point_index];
337  copy_v3_v3(point, &pt->x);
338  remaining_till_next = len_v3v3(point, step_start);
339  }
340  if (remaining_till_next < remaining_march) {
341  pt = &gps->points[next_point_index];
342  copy_v3_v3(result, &pt->x);
343  /* Stroke marching only terminates here. */
344  return 0;
345  }
346 
347  float ratio = remaining_march / remaining_till_next;
348  interp_v3_v3v3(result, step_start, point, ratio);
349  return next_point_index == 0 ? gps->totpoints : next_point_index;
350 }
351 
352 static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold)
353 {
354  int point_count = 0;
355  float point[3];
356  int next_point_index = 1;
357  bGPDspoint *pt = nullptr;
358 
359  pt = &gps->points[0];
360  copy_v3_v3(point, &pt->x);
361  point_count++;
362 
363  /* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */
364  for (int i = 0; i < gps->totpoints; i++) {
365  gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG);
366  }
367 
368  while ((next_point_index = stroke_march_next_point_no_interp(
369  gps, next_point_index, point, dist, sharp_threshold, point)) > -1) {
370  point_count++;
371  if (next_point_index == 0) {
372  break; /* last point finished */
373  }
374  }
375  return point_count;
376 }
377 
379  int count,
380  ListBase *result,
381  int *totweight)
382 {
383  LinkData *ld;
384  MDeformVert *dv;
385  MDeformWeight *dw;
386  int i, j;
387  int tw = 0;
388  for (i = 0; i < count; i++) {
389  dv = &dv_list[i];
390 
391  /* find def_nr in list, if not exist, then create one */
392  for (j = 0; j < dv->totweight; j++) {
393  bool found = false;
394  dw = &dv->dw[j];
395  for (ld = (LinkData *)result->first; ld; ld = ld->next) {
396  if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
397  found = true;
398  break;
399  }
400  }
401  if (!found) {
402  ld = MEM_cnew<LinkData>("def_nr_item");
403  ld->data = POINTER_FROM_INT(dw->def_nr);
404  BLI_addtail(result, ld);
405  tw++;
406  }
407  }
408  }
409 
410  *totweight = tw;
411 }
412 
413 static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
414 {
415  int i, j;
416  LinkData *ld;
417  MDeformVert *dst = (MDeformVert *)MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
418 
419  for (i = 0; i < count; i++) {
420  dst[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * totweight,
421  "new_deformWeight");
422  dst[i].totweight = totweight;
423  j = 0;
424  /* re-assign deform groups */
425  for (ld = (LinkData *)def_nr_list->first; ld; ld = ld->next) {
426  dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
427  j++;
428  }
429  }
430 
431  return dst;
432 }
433 
435  bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
436 {
437  const MDeformVert *vl = &gps->dvert[index_from];
438  const MDeformVert *vr = &gps->dvert[index_to];
439 
440  for (int i = 0; i < vert->totweight; i++) {
441  float wl = BKE_defvert_find_weight(vl, vert->dw[i].def_nr);
442  float wr = BKE_defvert_find_weight(vr, vert->dw[i].def_nr);
443  vert->dw[i].weight = interpf(wr, wl, ratio);
444  }
445 }
446 
448  bGPDstroke *gps,
449  const float dist,
450  const bool select,
451  const float sharp_threshold)
452 {
453  bGPDspoint *pt = gps->points;
454  bGPDspoint *pt1 = nullptr;
455  bGPDspoint *pt2 = nullptr;
456  LinkData *ld;
457  ListBase def_nr_list = {nullptr};
458 
459  if (gps->totpoints < 2 || dist < FLT_EPSILON) {
460  return false;
461  }
462  /* TODO: Implement feature point preservation. */
463  int count = stroke_march_count(gps, dist, sharp_threshold);
464  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
465  if (is_cyclic) {
466  count--;
467  }
468 
469  bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count,
470  "gp_stroke_points_sampled");
471  MDeformVert *new_dv = nullptr;
472 
473  int result_totweight;
474 
475  if (gps->dvert != nullptr) {
476  stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
477  new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
478  }
479 
480  int next_point_index = 1;
481  int i = 0;
482  float pressure, strength, ratio_result;
483  float uv_fac, uv_rot, uv_fill[2];
484  float vert_color[4];
485  int index_from, index_to;
486  float last_coord[3];
487 
488  /* 1st point is always at the start */
489  pt1 = &gps->points[0];
490  copy_v3_v3(last_coord, &pt1->x);
491  pt2 = &new_pt[i];
492  copy_v3_v3(&pt2->x, last_coord);
493  new_pt[i].pressure = pt[0].pressure;
494  new_pt[i].strength = pt[0].strength;
495  copy_v3_v3(&pt2->x, last_coord);
496  new_pt[i].pressure = pt[0].pressure;
497  new_pt[i].strength = pt[0].strength;
498  new_pt[i].uv_fac = pt[0].uv_fac;
499  new_pt[i].uv_rot = pt[0].uv_rot;
500  copy_v2_v2(new_pt[i].uv_fill, pt[0].uv_fill);
501  copy_v4_v4(new_pt[i].vert_color, pt[0].vert_color);
502  if (select) {
503  new_pt[i].flag |= GP_SPOINT_SELECT;
504  }
505  i++;
506 
507  if (new_dv) {
508  stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
509  }
510 
511  /* The rest. */
512  while ((next_point_index = stroke_march_next_point(gps,
513  next_point_index,
514  last_coord,
515  dist,
516  last_coord,
517  &pressure,
518  &strength,
519  vert_color,
520  &uv_fac,
521  uv_fill,
522  &uv_rot,
523  &ratio_result,
524  &index_from,
525  &index_to)) > -1) {
526  if (is_cyclic && next_point_index == 0) {
527  break; /* last point finished */
528  }
529  pt2 = &new_pt[i];
530  copy_v3_v3(&pt2->x, last_coord);
531  new_pt[i].pressure = pressure;
532  new_pt[i].strength = strength;
533  new_pt[i].uv_fac = uv_fac;
534  new_pt[i].uv_rot = uv_rot;
535  copy_v2_v2(new_pt[i].uv_fill, uv_fill);
536 
537  memcpy(new_pt[i].vert_color, vert_color, sizeof(float[4]));
538  if (select) {
539  new_pt[i].flag |= GP_SPOINT_SELECT;
540  }
541 
542  if (new_dv) {
543  stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
544  }
545 
546  i++;
547  if (next_point_index == 0) {
548  break; /* last point finished */
549  }
550  }
551 
552  gps->points = new_pt;
553  /* Free original vertex list. */
554  MEM_freeN(pt);
555 
556  if (new_dv) {
557  /* Free original weight data. */
559  MEM_freeN(gps->dvert);
560  while ((ld = (LinkData *)BLI_pophead(&def_nr_list))) {
561  MEM_freeN(ld);
562  }
563 
564  gps->dvert = new_dv;
565  }
566 
567  BLI_assert(i == count);
568  gps->totpoints = i;
569 
570  /* Calc geometry data. */
572 
573  return true;
574 }
575 
583  const int count_before,
584  const int count_after)
585 {
586  bGPDspoint *pts = gps->points;
587 
588  BLI_assert(count_before >= 0);
589  BLI_assert(count_after >= 0);
590  if (!count_before && !count_after) {
591  return false;
592  }
593 
594  const int new_count = count_before + count_after + gps->totpoints;
595 
596  bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__);
597 
598  for (int i = 0; i < count_before; i++) {
599  new_pts[i] = blender::dna::shallow_copy(pts[0]);
600  }
601  memcpy(static_cast<void *>(&new_pts[count_before]), pts, sizeof(bGPDspoint) * gps->totpoints);
602  for (int i = new_count - count_after; i < new_count; i++) {
603  new_pts[i] = blender::dna::shallow_copy(pts[gps->totpoints - 1]);
604  }
605 
606  if (gps->dvert) {
607  MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__);
608 
609  for (int i = 0; i < new_count; i++) {
610  MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)];
611  int inew = i;
612  new_dv[inew].flag = dv->flag;
613  new_dv[inew].totweight = dv->totweight;
614  new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
615  __func__);
616  memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight);
617  }
619  MEM_freeN(gps->dvert);
620  gps->dvert = new_dv;
621  }
622 
623  MEM_freeN(gps->points);
624  gps->points = new_pts;
625  gps->totpoints = new_count;
626 
627  return true;
628 }
629 
631  const float dist,
632  const float overshoot_fac,
633  const short mode,
634  const bool follow_curvature,
635  const int extra_point_count,
636  const float segment_influence,
637  const float max_angle,
638  const bool invert_curvature)
639 {
640 #define BOTH 0
641 #define START 1
642 #define END 2
643 
644  const bool do_start = ELEM(mode, BOTH, START);
645  const bool do_end = ELEM(mode, BOTH, END);
646  float used_percent_length = overshoot_fac;
647  CLAMP(used_percent_length, 1e-4f, 1.0f);
648  if (!isfinite(used_percent_length)) {
649  /* #used_percent_length must always be finite, otherwise a segfault occurs.
650  * Since this function should never segfault, set #used_percent_length to a safe fallback. */
651  /* NOTE: This fallback is used if gps->totpoints == 2, see MOD_gpencillength.c */
652  used_percent_length = 0.1f;
653  }
654 
655  if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) {
656  return false;
657  }
658 
659  /* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */
660  if (!follow_curvature || gps->totpoints <= 2) {
661  /* Not following curvature, just straight line. */
662  /* NOTE: #overshoot_point_param can not be zero. */
663  float overshoot_point_param = used_percent_length * (gps->totpoints - 1);
664  float result[3];
665 
666  if (do_start) {
667  int index1 = floor(overshoot_point_param);
668  int index2 = ceil(overshoot_point_param);
670  &gps->points[index1].x,
671  &gps->points[index2].x,
672  fmodf(overshoot_point_param, 1.0f));
673  sub_v3_v3(result, &gps->points[0].x);
674  if (UNLIKELY(is_zero_v3(result))) {
675  sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x);
676  }
677  madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result));
678  }
679 
680  if (do_end) {
681  int index1 = gps->totpoints - 1 - floor(overshoot_point_param);
682  int index2 = gps->totpoints - 1 - ceil(overshoot_point_param);
684  &gps->points[index1].x,
685  &gps->points[index2].x,
686  fmodf(overshoot_point_param, 1.0f));
687  sub_v3_v3(result, &gps->points[gps->totpoints - 1].x);
688  if (UNLIKELY(is_zero_v3(result))) {
689  sub_v3_v3v3(
690  result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x);
691  }
692  madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result));
693  }
694  return true;
695  }
696 
697  /* Curvature calculation. */
698 
699  /* First allocate the new stroke size. */
700  const int first_old_index = do_start ? extra_point_count : 0;
701  const int last_old_index = gps->totpoints - 1 + first_old_index;
702  const int orig_totpoints = gps->totpoints;
703  BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0);
704 
705  /* The fractional amount of points to query when calculating the average curvature of the
706  * strokes. */
707  const float overshoot_parameter = used_percent_length * (orig_totpoints - 2);
708  int overshoot_pointcount = ceil(overshoot_parameter);
709  CLAMP(overshoot_pointcount, 1, orig_totpoints - 2);
710 
711  /* Do for both sides without code duplication. */
712  float no[3], vec1[3], vec2[3], total_angle[3];
713  for (int k = 0; k < 2; k++) {
714  if ((k == 0 && !do_start) || (k == 1 && !do_end)) {
715  continue;
716  }
717 
718  const int start_i = k == 0 ? first_old_index :
719  last_old_index; // first_old_index, last_old_index
720  const int dir_i = 1 - k * 2; // 1, -1
721 
722  sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x);
723  zero_v3(total_angle);
724  float segment_length = normalize_v3(vec1);
725  float overshoot_length = 0.0f;
726 
727  /* Accumulate rotation angle and length. */
728  int j = 0;
729  for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) {
730  /* Don't fully add last segment to get continuity in overshoot_fac. */
731  float fac = fmin(overshoot_parameter - j, 1.0f);
732 
733  /* Read segments. */
734  copy_v3_v3(vec2, vec1);
735  sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x);
736  const float len = normalize_v3(vec1);
737  float angle = angle_normalized_v3v3(vec1, vec2) * fac;
738 
739  /* Add half of both adjacent legs of the current angle. */
740  const float added_len = (segment_length + len) * 0.5f * fac;
741  overshoot_length += added_len;
742  segment_length = len;
743 
744  if (angle > max_angle) {
745  continue;
746  }
747  if (angle > M_PI * 0.995f) {
748  continue;
749  }
750 
751  angle *= powf(added_len, segment_influence);
752 
753  cross_v3_v3v3(no, vec1, vec2);
755  add_v3_v3(total_angle, no);
756  }
757 
758  if (UNLIKELY(overshoot_length == 0.0f)) {
759  /* Don't do a proper extension if the used points are all in the same position. */
760  continue;
761  }
762 
763  sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x);
764  /* In general curvature = 1/radius. For the case without the
765  * weights introduced by #segment_influence, the calculation is:
766  * `curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length` */
767  float curvature = normalize_v3(total_angle) / overshoot_length;
768  /* Compensate for the weights powf(added_len, segment_influence). */
769  curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence);
770  if (invert_curvature) {
771  curvature = -curvature;
772  }
773  const float angle_step = curvature * dist / extra_point_count;
774  float step_length = dist / extra_point_count;
775  if (fabsf(angle_step) > FLT_EPSILON) {
776  /* Make a direct step length from the assigned arc step length. */
777  step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f);
778  }
779  else {
780  zero_v3(total_angle);
781  }
782  const float prev_length = normalize_v3_length(vec1, step_length);
783 
784  /* Build rotation matrix here to get best performance. */
785  float rot[3][3];
786  float q[4];
787  axis_angle_to_quat(q, total_angle, angle_step);
788  quat_to_mat3(rot, q);
789 
790  /* Rotate the starting direction to account for change in edge lengths. */
792  total_angle,
793  fmaxf(0.0f, 1.0f - fabs(segment_influence)) *
794  (curvature * prev_length - angle_step) / 2.0f);
795  mul_qt_v3(q, vec1);
796 
797  /* Now iteratively accumulate the segments with a rotating added direction. */
798  for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) {
799  mul_v3_m3v3(vec1, rot, vec1);
800  add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x);
801  }
802  }
803  return true;
804 }
805 
808 /* -------------------------------------------------------------------- */
812 bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to)
813 {
814  bGPDspoint *pt = gps->points, *new_pt;
815  MDeformVert *dv, *new_dv;
816 
817  const int new_count = index_to - index_from + 1;
818 
819  if (new_count >= gps->totpoints) {
820  return false;
821  }
822 
823  if (new_count == 1) {
824  if (gps->dvert) {
826  MEM_freeN(gps->dvert);
827  }
828  MEM_freeN(gps->points);
829  gps->points = nullptr;
830  gps->dvert = nullptr;
831  gps->totpoints = 0;
832  return false;
833  }
834 
835  new_pt = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
836  memcpy(static_cast<void *>(new_pt), &pt[index_from], sizeof(bGPDspoint) * new_count);
837 
838  if (gps->dvert) {
839  new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
840  "gp_stroke_dverts_trimmed");
841  for (int i = 0; i < new_count; i++) {
842  dv = &gps->dvert[i + index_from];
843  new_dv[i].flag = dv->flag;
844  new_dv[i].totweight = dv->totweight;
845  new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
846  "gp_stroke_dverts_dw_trimmed");
847  for (int j = 0; j < dv->totweight; j++) {
848  new_dv[i].dw[j].weight = dv->dw[j].weight;
849  new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
850  }
851  }
853  MEM_freeN(gps->dvert);
854  gps->dvert = new_dv;
855  }
856 
857  MEM_freeN(gps->points);
858  gps->points = new_pt;
859  gps->totpoints = new_count;
860 
861  return true;
862 }
863 
866 /* -------------------------------------------------------------------- */
871  bGPDframe *gpf,
872  bGPDstroke *gps,
873  const int before_index,
874  bGPDstroke **remaining_gps)
875 {
876  bGPDstroke *new_gps;
877  bGPDspoint *pt = gps->points, *new_pt;
878  MDeformVert *dv, *new_dv;
879 
880  if (before_index >= gps->totpoints || before_index == 0) {
881  return false;
882  }
883 
884  const int new_count = gps->totpoints - before_index;
885  const int old_count = before_index;
886 
887  /* Handle remaining segments first. */
888 
890  gpf, gps, gps->mat_nr, new_count, gps->thickness);
891 
892  new_pt = new_gps->points; /* Allocated from above. */
893  memcpy(static_cast<void *>(new_pt), &pt[before_index], sizeof(bGPDspoint) * new_count);
894 
895  if (gps->dvert) {
896  new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
897  "gp_stroke_dverts_remaining(MDeformVert)");
898  for (int i = 0; i < new_count; i++) {
899  dv = &gps->dvert[i + before_index];
900  new_dv[i].flag = dv->flag;
901  new_dv[i].totweight = dv->totweight;
902  new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
903  "gp_stroke_dverts_dw_remaining(MDeformWeight)");
904  for (int j = 0; j < dv->totweight; j++) {
905  new_dv[i].dw[j].weight = dv->dw[j].weight;
906  new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
907  }
908  }
909  new_gps->dvert = new_dv;
910  }
911 
912  (*remaining_gps) = new_gps;
913 
914  /* Trim the original stroke into a shorter one.
915  * Keep the end point. */
916 
917  BKE_gpencil_stroke_trim_points(gps, 0, old_count);
919  return true;
920 }
921 
924 /* -------------------------------------------------------------------- */
928 bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
929 {
930 #define START 1
931 #define END 2
932 
933  bGPDspoint *pt = gps->points, *second_last;
934  int i;
935 
936  if (gps->totpoints < 2) {
937  if (gps->totpoints == 1) {
938  second_last = &pt[1];
939  if (len_v3v3(&second_last->x, &pt->x) < dist) {
941  return true;
942  }
943  }
944 
945  return false;
946  }
947 
948  second_last = &pt[gps->totpoints - 2];
949 
950  float len;
951  float len1, cut_len1;
952  float len2, cut_len2;
953  len1 = len2 = cut_len1 = cut_len2 = 0.0f;
954 
955  int index_start = 0;
956  int index_end = 0;
957  if (mode == START) {
958  i = 0;
959  index_end = gps->totpoints - 1;
960  while (len1 < dist && gps->totpoints > i + 1) {
961  len = len_v3v3(&pt[i].x, &pt[i + 1].x);
962  len1 += len;
963  cut_len1 = len1 - dist;
964  i++;
965  }
966  index_start = i - 1;
967  interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len);
968  }
969 
970  if (mode == END) {
971  index_start = 0;
972  i = 2;
973  while (len2 < dist && gps->totpoints >= i) {
974  second_last = &pt[gps->totpoints - i];
975  len = len_v3v3(&second_last[1].x, &second_last->x);
976  len2 += len;
977  cut_len2 = len2 - dist;
978  i++;
979  }
980  index_end = gps->totpoints - i + 2;
981  interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len);
982  }
983 
984  if (index_end <= index_start) {
985  index_start = index_end = 0; /* empty stroke */
986  }
987 
988  if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) {
989  index_start = index_end = 0; /* no length left to cut */
990  }
991 
992  BKE_gpencil_stroke_trim_points(gps, index_start, index_end);
993 
994  if (gps->totpoints == 0) {
995  return false;
996  }
997 
998  return true;
999 }
1000 
1003 /* -------------------------------------------------------------------- */
1008  int point_index,
1009  float influence,
1010  int iterations,
1011  const bool smooth_caps,
1012  const bool keep_shape,
1013  bGPDstroke *r_gps)
1014 {
1015  /* If nothing to do, return early */
1016  if (gps->totpoints <= 2 || iterations <= 0) {
1017  return false;
1018  }
1019 
1020  /* Overview of the algorithm here and in the following smooth functions:
1021  * The smooth functions return the new attribute in question for a single point.
1022  * The result is stored in r_gps->points[point_index], while the data is read from gps.
1023  * To get a correct result, duplicate the stroke point data and read from the copy,
1024  * while writing to the real stroke. Not doing that will result in acceptable, but
1025  * asymmetric results.
1026  * This algorithm works as long as all points are being smoothed. If there is
1027  * points that should not get smoothed, use the old repeat smooth pattern with
1028  * the parameter "iterations" set to 1 or 2. (2 matches the old algorithm).
1029  */
1030 
1031  const bGPDspoint *pt = &gps->points[point_index];
1032  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
1033  /* If smooth_caps is false, the caps will not be translated by smoothing. */
1034  if (!smooth_caps && !is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
1035  copy_v3_v3(&r_gps->points[point_index].x, &pt->x);
1036  return true;
1037  }
1038 
1039  /* This function uses a binomial kernel, which is the discrete version of gaussian blur.
1040  * The weight for a vertex at the relative index point_index is
1041  * w = nCr(n, j + n/2) / 2^n = (n/1 * (n-1)/2 * ... * (n-j-n/2)/(j+n/2)) / 2^n
1042  * All weights together sum up to 1
1043  * This is equivalent to doing multiple iterations of averaging neighbors,
1044  * where n = iterations * 2 and -n/2 <= j <= n/2
1045  *
1046  * Now the problem is that nCr(n, j + n/2) is very hard to compute for n > 500, since even
1047  * double precision isn't sufficient. A very good robust approximation for n > 20 is
1048  * nCr(n, j + n/2) / 2^n = sqrt(2/(pi*n)) * exp(-2*j*j/n)
1049  *
1050  * There is one more problem left: The old smooth algorithm was doing a more aggressive
1051  * smooth. To solve that problem, choose a different n/2, which does not match the range and
1052  * normalize the weights on finish. This may cause some artifacts at low values.
1053  *
1054  * keep_shape is a new option to stop the stroke from severely deforming.
1055  * It uses different partially negative weights.
1056  * w = 2 * (nCr(n, j + n/2) / 2^n) - (nCr(3*n, j + n) / 2^(3*n))
1057  * ~ 2 * sqrt(2/(pi*n)) * exp(-2*j*j/n) - sqrt(2/(pi*3*n)) * exp(-2*j*j/(3*n))
1058  * All weights still sum up to 1.
1059  * Note these weights only work because the averaging is done in relative coordinates.
1060  */
1061  float sco[3] = {0.0f, 0.0f, 0.0f};
1062  float tmp[3];
1063  const int n_half = keep_shape ? (iterations * iterations) / 8 + iterations :
1064  (iterations * iterations) / 4 + 2 * iterations + 12;
1065  double w = keep_shape ? 2.0 : 1.0;
1066  double w2 = keep_shape ?
1067  (1.0 / M_SQRT3) * exp((2 * iterations * iterations) / (double)(n_half * 3)) :
1068  0.0;
1069  double total_w = 0.0;
1070  for (int step = iterations; step > 0; step--) {
1071  int before = point_index - step;
1072  int after = point_index + step;
1073  float w_before = (float)(w - w2);
1074  float w_after = (float)(w - w2);
1075 
1076  if (is_cyclic) {
1077  before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
1078  after = after % gps->totpoints;
1079  }
1080  else {
1081  if (before < 0) {
1082  if (!smooth_caps) {
1083  w_before *= -before / (float)point_index;
1084  }
1085  before = 0;
1086  }
1087  if (after > gps->totpoints - 1) {
1088  if (!smooth_caps) {
1089  w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - point_index);
1090  }
1091  after = gps->totpoints - 1;
1092  }
1093  }
1094 
1095  /* Add both these points in relative coordinates to the weighted average sum. */
1096  sub_v3_v3v3(tmp, &gps->points[before].x, &pt->x);
1097  madd_v3_v3fl(sco, tmp, w_before);
1098  sub_v3_v3v3(tmp, &gps->points[after].x, &pt->x);
1099  madd_v3_v3fl(sco, tmp, w_after);
1100 
1101  total_w += w_before;
1102  total_w += w_after;
1103 
1104  w *= (n_half + step) / (double)(n_half + 1 - step);
1105  w2 *= (n_half * 3 + step) / (double)(n_half * 3 + 1 - step);
1106  }
1107  total_w += w - w2;
1108  /* The accumulated weight total_w should be
1109  * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
1110  * here, but sometimes not quite. */
1111  mul_v3_fl(sco, (float)(1.0 / total_w));
1112  /* Shift back to global coordinates. */
1113  add_v3_v3(sco, &pt->x);
1114 
1115  /* Based on influence factor, blend between original and optimal smoothed coordinate. */
1116  interp_v3_v3v3(&r_gps->points[point_index].x, &pt->x, sco, influence);
1117 
1118  return true;
1119 }
1120 
1123 /* -------------------------------------------------------------------- */
1128  bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
1129 {
1130  /* If nothing to do, return early */
1131  if (gps->totpoints <= 2 || iterations <= 0) {
1132  return false;
1133  }
1134 
1135  /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
1136 
1137  const bGPDspoint *pt = &gps->points[point_index];
1138  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
1139  float strength = 0.0f;
1140  const int n_half = (iterations * iterations) / 4 + iterations;
1141  double w = 1.0;
1142  double total_w = 0.0;
1143  for (int step = iterations; step > 0; step--) {
1144  int before = point_index - step;
1145  int after = point_index + step;
1146  float w_before = (float)w;
1147  float w_after = (float)w;
1148 
1149  if (is_cyclic) {
1150  before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
1151  after = after % gps->totpoints;
1152  }
1153  else {
1154  CLAMP_MIN(before, 0);
1155  CLAMP_MAX(after, gps->totpoints - 1);
1156  }
1157 
1158  /* Add both these points in relative coordinates to the weighted average sum. */
1159  strength += w_before * (gps->points[before].strength - pt->strength);
1160  strength += w_after * (gps->points[after].strength - pt->strength);
1161 
1162  total_w += w_before;
1163  total_w += w_after;
1164 
1165  w *= (n_half + step) / (double)(n_half + 1 - step);
1166  }
1167  total_w += w;
1168  /* The accumulated weight total_w should be
1169  * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
1170  * here, but sometimes not quite. */
1171  strength /= total_w;
1172 
1173  /* Based on influence factor, blend between original and optimal smoothed value. */
1174  r_gps->points[point_index].strength = pt->strength + strength * influence;
1175 
1176  return true;
1177 }
1178 
1181 /* -------------------------------------------------------------------- */
1186  bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
1187 {
1188  /* If nothing to do, return early */
1189  if (gps->totpoints <= 2 || iterations <= 0) {
1190  return false;
1191  }
1192 
1193  /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
1194 
1195  const bGPDspoint *pt = &gps->points[point_index];
1196  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
1197  float pressure = 0.0f;
1198  const int n_half = (iterations * iterations) / 4 + iterations;
1199  double w = 1.0;
1200  double total_w = 0.0;
1201  for (int step = iterations; step > 0; step--) {
1202  int before = point_index - step;
1203  int after = point_index + step;
1204  float w_before = (float)w;
1205  float w_after = (float)w;
1206 
1207  if (is_cyclic) {
1208  before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
1209  after = after % gps->totpoints;
1210  }
1211  else {
1212  CLAMP_MIN(before, 0);
1213  CLAMP_MAX(after, gps->totpoints - 1);
1214  }
1215 
1216  /* Add both these points in relative coordinates to the weighted average sum. */
1217  pressure += w_before * (gps->points[before].pressure - pt->pressure);
1218  pressure += w_after * (gps->points[after].pressure - pt->pressure);
1219 
1220  total_w += w_before;
1221  total_w += w_after;
1222 
1223  w *= (n_half + step) / (double)(n_half + 1 - step);
1224  }
1225  total_w += w;
1226  /* The accumulated weight total_w should be
1227  * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
1228  * here, but sometimes not quite. */
1229  pressure /= total_w;
1230 
1231  /* Based on influence factor, blend between original and optimal smoothed value. */
1232  r_gps->points[point_index].pressure = pt->pressure + pressure * influence;
1233 
1234  return true;
1235 }
1236 
1239 /* -------------------------------------------------------------------- */
1244  int point_index,
1245  float influence,
1246  int iterations,
1247  struct bGPDstroke *r_gps)
1248 {
1249  /* If nothing to do, return early */
1250  if (gps->totpoints <= 2 || iterations <= 0) {
1251  return false;
1252  }
1253 
1254  /* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
1255 
1256  const bGPDspoint *pt = &gps->points[point_index];
1257  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
1258 
1259  /* If don't change the caps. */
1260  if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
1261  r_gps->points[point_index].uv_rot = pt->uv_rot;
1262  r_gps->points[point_index].uv_fac = pt->uv_fac;
1263  return true;
1264  }
1265 
1266  float uv_rot = 0.0f;
1267  float uv_fac = 0.0f;
1268  const int n_half = iterations * iterations + iterations;
1269  double w = 1.0;
1270  double total_w = 0.0;
1271  for (int step = iterations; step > 0; step--) {
1272  int before = point_index - step;
1273  int after = point_index + step;
1274  float w_before = (float)w;
1275  float w_after = (float)w;
1276 
1277  if (is_cyclic) {
1278  before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
1279  after = after % gps->totpoints;
1280  }
1281  else {
1282  if (before < 0) {
1283  w_before *= -before / (float)point_index;
1284  before = 0;
1285  }
1286  if (after > gps->totpoints - 1) {
1287  w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - point_index);
1288  after = gps->totpoints - 1;
1289  }
1290  }
1291 
1292  /* Add both these points in relative coordinates to the weighted average sum. */
1293  uv_rot += w_before * (gps->points[before].uv_rot - pt->uv_rot);
1294  uv_rot += w_after * (gps->points[after].uv_rot - pt->uv_rot);
1295  uv_fac += w_before * (gps->points[before].uv_fac - pt->uv_fac);
1296  uv_fac += w_after * (gps->points[after].uv_fac - pt->uv_fac);
1297 
1298  total_w += w_before;
1299  total_w += w_after;
1300 
1301  w *= (n_half + step) / (double)(n_half + 1 - step);
1302  }
1303  total_w += w;
1304  /* The accumulated weight total_w should be
1305  * ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
1306  * here, but sometimes not quite. */
1307  uv_rot /= total_w;
1308  uv_fac /= total_w;
1309 
1310  /* Based on influence factor, blend between original and optimal smoothed value. */
1311  r_gps->points[point_index].uv_rot = pt->uv_rot + uv_rot * influence;
1312  r_gps->points[point_index].uv_fac = pt->uv_fac + uv_fac * influence;
1313 
1314  return true;
1315 }
1316 
1318  const float influence,
1319  const int iterations,
1320  const bool smooth_position,
1321  const bool smooth_strength,
1322  const bool smooth_thickness,
1323  const bool smooth_uv,
1324  const bool keep_shape,
1325  const float *weights)
1326 {
1327  if (influence <= 0 || iterations <= 0) {
1328  return;
1329  }
1330 
1331  /* Make a copy of the point data to avoid directionality of the smooth operation. */
1332  bGPDstroke gps_old = blender::dna::shallow_copy(*gps);
1333  gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
1334 
1335  /* Smooth stroke. */
1336  for (int i = 0; i < gps->totpoints; i++) {
1337  float val = influence;
1338  if (weights != nullptr) {
1339  val *= weights[i];
1340  if (val <= 0.0f) {
1341  continue;
1342  }
1343  }
1344 
1345  /* TODO: Currently the weights only control the influence, but is would be much better if they
1346  * would control the distribution used in smooth, similar to how the ends are handled. */
1347 
1348  /* Perform smoothing. */
1349  if (smooth_position) {
1350  BKE_gpencil_stroke_smooth_point(&gps_old, i, val, iterations, false, keep_shape, gps);
1351  }
1352  if (smooth_strength) {
1353  BKE_gpencil_stroke_smooth_strength(&gps_old, i, val, iterations, gps);
1354  }
1355  if (smooth_thickness) {
1356  BKE_gpencil_stroke_smooth_thickness(&gps_old, i, val, iterations, gps);
1357  }
1358  if (smooth_uv) {
1359  BKE_gpencil_stroke_smooth_uv(&gps_old, i, val, iterations, gps);
1360  }
1361  }
1362 
1363  /* Free the copied points array. */
1364  MEM_freeN(gps_old.points);
1365 }
1366 
1368  int totpoints,
1369  float (*points2d)[2],
1370  int *r_direction)
1371 {
1372  BLI_assert(totpoints >= 2);
1373 
1374  const bGPDspoint *pt0 = &points[0];
1375  const bGPDspoint *pt1 = &points[1];
1376  const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
1377 
1378  float locx[3];
1379  float locy[3];
1380  float loc3[3];
1381  float normal[3];
1382 
1383  /* local X axis (p0 -> p1) */
1384  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
1385 
1386  /* point vector at 3/4 */
1387  float v3[3];
1388  if (totpoints == 2) {
1389  mul_v3_v3fl(v3, &pt3->x, 0.001f);
1390  }
1391  else {
1392  copy_v3_v3(v3, &pt3->x);
1393  }
1394 
1395  sub_v3_v3v3(loc3, v3, &pt0->x);
1396 
1397  /* vector orthogonal to polygon plane */
1398  cross_v3_v3v3(normal, locx, loc3);
1399 
1400  /* local Y axis (cross to normal/x axis) */
1401  cross_v3_v3v3(locy, normal, locx);
1402 
1403  /* Normalize vectors */
1404  normalize_v3(locx);
1405  normalize_v3(locy);
1406 
1407  /* Calculate last point first. */
1408  const bGPDspoint *pt_last = &points[totpoints - 1];
1409  float tmp[3];
1410  sub_v3_v3v3(tmp, &pt_last->x, &pt0->x);
1411 
1412  points2d[totpoints - 1][0] = dot_v3v3(tmp, locx);
1413  points2d[totpoints - 1][1] = dot_v3v3(tmp, locy);
1414 
1415  /* Calculate the scalar cross product of the 2d points. */
1416  float cross = 0.0f;
1417  float *co_curr;
1418  float *co_prev = (float *)&points2d[totpoints - 1];
1419 
1420  /* Get all points in local space */
1421  for (int i = 0; i < totpoints - 1; i++) {
1422  const bGPDspoint *pt = &points[i];
1423  float loc[3];
1424 
1425  /* Get local space using first point as origin */
1426  sub_v3_v3v3(loc, &pt->x, &pt0->x);
1427 
1428  points2d[i][0] = dot_v3v3(loc, locx);
1429  points2d[i][1] = dot_v3v3(loc, locy);
1430 
1431  /* Calculate cross product. */
1432  co_curr = (float *)&points2d[i][0];
1433  cross += (co_curr[0] - co_prev[0]) * (co_curr[1] + co_prev[1]);
1434  co_prev = (float *)&points2d[i][0];
1435  }
1436 
1437  /* Concave (-1), Convex (1) */
1438  *r_direction = (cross >= 0.0f) ? 1 : -1;
1439 }
1440 
1442  int ref_totpoints,
1443  const bGPDspoint *points,
1444  int totpoints,
1445  float (*points2d)[2],
1446  const float scale,
1447  int *r_direction)
1448 {
1449  BLI_assert(totpoints >= 2);
1450 
1451  const bGPDspoint *pt0 = &ref_points[0];
1452  const bGPDspoint *pt1 = &ref_points[1];
1453  const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)];
1454 
1455  float locx[3];
1456  float locy[3];
1457  float loc3[3];
1458  float normal[3];
1459 
1460  /* local X axis (p0 -> p1) */
1461  sub_v3_v3v3(locx, &pt1->x, &pt0->x);
1462 
1463  /* point vector at 3/4 */
1464  float v3[3];
1465  if (totpoints == 2) {
1466  mul_v3_v3fl(v3, &pt3->x, 0.001f);
1467  }
1468  else {
1469  copy_v3_v3(v3, &pt3->x);
1470  }
1471 
1472  sub_v3_v3v3(loc3, v3, &pt0->x);
1473 
1474  /* vector orthogonal to polygon plane */
1475  cross_v3_v3v3(normal, locx, loc3);
1476 
1477  /* local Y axis (cross to normal/x axis) */
1478  cross_v3_v3v3(locy, normal, locx);
1479 
1480  /* Normalize vectors */
1481  normalize_v3(locx);
1482  normalize_v3(locy);
1483 
1484  /* Get all points in local space */
1485  for (int i = 0; i < totpoints; i++) {
1486  const bGPDspoint *pt = &points[i];
1487  float loc[3];
1488  float v1[3];
1489  float vn[3] = {0.0f, 0.0f, 0.0f};
1490 
1491  /* apply scale to extremes of the stroke to get better collision detection
1492  * the scale is divided to get more control in the UI parameter
1493  */
1494  /* first point */
1495  if (i == 0) {
1496  const bGPDspoint *pt_next = &points[i + 1];
1497  sub_v3_v3v3(vn, &pt->x, &pt_next->x);
1498  normalize_v3(vn);
1499  mul_v3_fl(vn, scale / 10.0f);
1500  add_v3_v3v3(v1, &pt->x, vn);
1501  }
1502  /* last point */
1503  else if (i == totpoints - 1) {
1504  const bGPDspoint *pt_prev = &points[i - 1];
1505  sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
1506  normalize_v3(vn);
1507  mul_v3_fl(vn, scale / 10.0f);
1508  add_v3_v3v3(v1, &pt->x, vn);
1509  }
1510  else {
1511  copy_v3_v3(v1, &pt->x);
1512  }
1513 
1514  /* Get local space using first point as origin (ref stroke) */
1515  sub_v3_v3v3(loc, v1, &pt0->x);
1516 
1517  points2d[i][0] = dot_v3v3(loc, locx);
1518  points2d[i][1] = dot_v3v3(loc, locy);
1519  }
1520 
1521  /* Concave (-1), Convex (1), or Auto-detect (0)? */
1522  *r_direction = (int)locy[2];
1523 }
1524 
1525 /* Calc texture coordinates using flat projected points. */
1526 static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
1527  bGPDstroke *gps,
1528  const float minv[2],
1529  const float maxv[2],
1530  float (*r_uv)[2])
1531 {
1532  const float s = sin(gps->uv_rotation);
1533  const float c = cos(gps->uv_rotation);
1534 
1535  /* Calc center for rotation. */
1536  float center[2] = {0.5f, 0.5f};
1537  float d[2];
1538  d[0] = maxv[0] - minv[0];
1539  d[1] = maxv[1] - minv[1];
1540  for (int i = 0; i < gps->totpoints; i++) {
1541  r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0];
1542  r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1];
1543 
1544  /* Apply translation. */
1545  add_v2_v2(r_uv[i], gps->uv_translation);
1546 
1547  /* Apply Rotation. */
1548  r_uv[i][0] -= center[0];
1549  r_uv[i][1] -= center[1];
1550 
1551  float x = r_uv[i][0] * c - r_uv[i][1] * s;
1552  float y = r_uv[i][0] * s + r_uv[i][1] * c;
1553 
1554  r_uv[i][0] = x + center[0];
1555  r_uv[i][1] = y + center[1];
1556 
1557  /* Apply scale. */
1558  if (gps->uv_scale != 0.0f) {
1559  mul_v2_fl(r_uv[i], 1.0f / gps->uv_scale);
1560  }
1561  }
1562 }
1563 
1566 /* -------------------------------------------------------------------- */
1571 {
1572  BLI_assert(gps->totpoints >= 3);
1573 
1574  /* allocate memory for temporary areas */
1575  gps->tot_triangles = gps->totpoints - 2;
1576  uint(*tmp_triangles)[3] = (uint(*)[3])MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles,
1577  "GP Stroke temp triangulation");
1578  float(*points2d)[2] = (float(*)[2])MEM_mallocN(sizeof(*points2d) * gps->totpoints,
1579  "GP Stroke temp 2d points");
1580  float(*uv)[2] = (float(*)[2])MEM_mallocN(sizeof(*uv) * gps->totpoints,
1581  "GP Stroke temp 2d uv data");
1582 
1583  int direction = 0;
1584 
1585  /* convert to 2d and triangulate */
1586  BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
1587  BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles);
1588 
1589  /* calc texture coordinates automatically */
1590  float minv[2];
1591  float maxv[2];
1592  /* first needs bounding box data */
1593  ARRAY_SET_ITEMS(minv, -1.0f, -1.0f);
1594  ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f);
1595 
1596  /* calc uv data */
1597  gpencil_calc_stroke_fill_uv(points2d, gps, minv, maxv, uv);
1598 
1599  /* Save triangulation data. */
1600  if (gps->tot_triangles > 0) {
1601  MEM_SAFE_FREE(gps->triangles);
1602  gps->triangles = (bGPDtriangle *)MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles,
1603  "GP Stroke triangulation");
1604 
1605  for (int i = 0; i < gps->tot_triangles; i++) {
1606  memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3]));
1607  }
1608 
1609  /* Copy UVs to bGPDspoint. */
1610  for (int i = 0; i < gps->totpoints; i++) {
1611  copy_v2_v2(gps->points[i].uv_fill, uv[i]);
1612  }
1613  }
1614  else {
1615  /* No triangles needed - Free anything allocated previously */
1616  if (gps->triangles) {
1617  MEM_freeN(gps->triangles);
1618  }
1619 
1620  gps->triangles = nullptr;
1621  }
1622 
1623  /* clear memory */
1624  MEM_SAFE_FREE(tmp_triangles);
1625  MEM_SAFE_FREE(points2d);
1626  MEM_SAFE_FREE(uv);
1627 }
1628 
1630 {
1631  if (gps == nullptr || gps->totpoints == 0) {
1632  return;
1633  }
1634 
1635  bGPDspoint *pt = gps->points;
1636  float totlen = 0.0f;
1637  pt[0].uv_fac = totlen;
1638  for (int i = 1; i < gps->totpoints; i++) {
1639  totlen += len_v3v3(&pt[i - 1].x, &pt[i].x);
1640  pt[i].uv_fac = totlen;
1641  }
1642 }
1643 
1645 {
1646  if (gps == nullptr) {
1647  return;
1648  }
1649 
1650  if (gps->editcurve != nullptr) {
1651  if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
1652  /* curve geometry was updated: stroke needs recalculation */
1653  if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
1656  gps, gpd->curve_edit_resolution, is_adaptive);
1658  }
1659  }
1660  else {
1661  /* stroke geometry was updated: editcurve needs recalculation */
1663  }
1664  }
1665 
1666  if (gps->totpoints > 2) {
1668  }
1669  else {
1670  gps->tot_triangles = 0;
1671  MEM_SAFE_FREE(gps->triangles);
1672  }
1673 
1674  /* calc uv data along the stroke */
1676 
1677  /* Calc stroke bounding box. */
1679 }
1680 
1681 float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
1682 {
1683  if (!gps->points || gps->totpoints < 2) {
1684  return 0.0f;
1685  }
1686  float *last_pt = &gps->points[0].x;
1687  float total_length = 0.0f;
1688  for (int i = 1; i < gps->totpoints; i++) {
1689  bGPDspoint *pt = &gps->points[i];
1690  if (use_3d) {
1691  total_length += len_v3v3(&pt->x, last_pt);
1692  }
1693  else {
1694  total_length += len_v2v2(&pt->x, last_pt);
1695  }
1696  last_pt = &pt->x;
1697  }
1698  return total_length;
1699 }
1700 
1702  const int start_index,
1703  const int end_index,
1704  bool use_3d)
1705 {
1706  if (!gps->points || gps->totpoints < 2 || end_index <= start_index) {
1707  return 0.0f;
1708  }
1709 
1710  int index = MAX2(start_index, 0) + 1;
1711  int last_index = MIN2(end_index, gps->totpoints - 1) + 1;
1712 
1713  float *last_pt = &gps->points[index - 1].x;
1714  float total_length = 0.0f;
1715  for (int i = index; i < last_index; i++) {
1716  bGPDspoint *pt = &gps->points[i];
1717  if (use_3d) {
1718  total_length += len_v3v3(&pt->x, last_pt);
1719  }
1720  else {
1721  total_length += len_v2v2(&pt->x, last_pt);
1722  }
1723  last_pt = &pt->x;
1724  }
1725  return total_length;
1726 }
1727 
1729 {
1730  if (gps->totpoints < 4) {
1731  return false;
1732  }
1733  bool intersect = false;
1734  int start = 0;
1735  int end = 0;
1736  float point[3];
1737  /* loop segments from start until we have an intersection */
1738  for (int i = 0; i < gps->totpoints - 2; i++) {
1739  start = i;
1740  bGPDspoint *a = &gps->points[start];
1741  bGPDspoint *b = &gps->points[start + 1];
1742  for (int j = start + 2; j < gps->totpoints - 1; j++) {
1743  end = j + 1;
1744  bGPDspoint *c = &gps->points[j];
1745  bGPDspoint *d = &gps->points[end];
1746  float pointb[3];
1747  /* get intersection */
1748  if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) {
1749  if (len_v3(point) > 0.0f) {
1750  float closest[3];
1751  /* check intersection is on both lines */
1752  float lambda = closest_to_line_v3(closest, point, &a->x, &b->x);
1753  if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
1754  continue;
1755  }
1756  lambda = closest_to_line_v3(closest, point, &c->x, &d->x);
1757  if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
1758  continue;
1759  }
1760 
1761  intersect = true;
1762  break;
1763  }
1764  }
1765  }
1766  if (intersect) {
1767  break;
1768  }
1769  }
1770 
1771  /* trim unwanted points */
1772  if (intersect) {
1773 
1774  /* save points */
1775  bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
1776  MDeformVert *old_dvert = nullptr;
1777  MDeformVert *dvert_src = nullptr;
1778 
1779  if (gps->dvert != nullptr) {
1780  old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
1781  }
1782 
1783  /* resize gps */
1784  int newtot = end - start + 1;
1785 
1786  gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
1787  if (gps->dvert != nullptr) {
1788  gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
1789  }
1790 
1791  for (int i = 0; i < newtot; i++) {
1792  int idx = start + i;
1793  bGPDspoint *pt_src = &old_points[idx];
1794  bGPDspoint *pt_new = &gps->points[i];
1795  *pt_new = blender::dna::shallow_copy(*pt_src);
1796  if (gps->dvert != nullptr) {
1797  dvert_src = &old_dvert[idx];
1798  MDeformVert *dvert = &gps->dvert[i];
1799  memcpy(dvert, dvert_src, sizeof(MDeformVert));
1800  if (dvert_src->dw) {
1801  memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
1802  }
1803  }
1804  if (ELEM(idx, start, end)) {
1805  copy_v3_v3(&pt_new->x, point);
1806  }
1807  }
1808 
1809  gps->totpoints = newtot;
1810 
1811  MEM_SAFE_FREE(old_points);
1812  MEM_SAFE_FREE(old_dvert);
1813  }
1814 
1816 
1817  return intersect;
1818 }
1819 
1821 {
1822  bGPDspoint *pt1 = nullptr;
1823  bGPDspoint *pt2 = nullptr;
1824 
1825  /* Only can close a stroke with 3 points or more. */
1826  if (gps->totpoints < 3) {
1827  return false;
1828  }
1829 
1830  /* Calc average distance between points to get same level of sampling. */
1831  float dist_tot = 0.0f;
1832  for (int i = 0; i < gps->totpoints - 1; i++) {
1833  pt1 = &gps->points[i];
1834  pt2 = &gps->points[i + 1];
1835  dist_tot += len_v3v3(&pt1->x, &pt2->x);
1836  }
1837  /* Calc the average distance. */
1838  float dist_avg = dist_tot / (gps->totpoints - 1);
1839 
1840  /* Calc distance between last and first point. */
1841  pt1 = &gps->points[gps->totpoints - 1];
1842  pt2 = &gps->points[0];
1843  float dist_close = len_v3v3(&pt1->x, &pt2->x);
1844 
1845  /* if the distance to close is very small, don't need add points and just enable cyclic. */
1846  if (dist_close <= dist_avg) {
1847  gps->flag |= GP_STROKE_CYCLIC;
1848  return true;
1849  }
1850 
1851  /* Calc number of points required using the average distance. */
1852  int tot_newpoints = MAX2(dist_close / dist_avg, 1);
1853 
1854  /* Resize stroke array. */
1855  int old_tot = gps->totpoints;
1856  gps->totpoints += tot_newpoints;
1857  gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1858  if (gps->dvert != nullptr) {
1859  gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1860  }
1861 
1862  /* Generate new points */
1863  pt1 = &gps->points[old_tot - 1];
1864  pt2 = &gps->points[0];
1865  bGPDspoint *pt = &gps->points[old_tot];
1866  for (int i = 1; i < tot_newpoints + 1; i++, pt++) {
1867  float step = (tot_newpoints > 1) ? ((float)i / (float)tot_newpoints) : 0.99f;
1868  /* Clamp last point to be near, but not on top of first point. */
1869  if ((tot_newpoints > 1) && (i == tot_newpoints)) {
1870  step *= 0.99f;
1871  }
1872 
1873  /* Average point. */
1874  interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step);
1875  pt->pressure = interpf(pt2->pressure, pt1->pressure, step);
1876  pt->strength = interpf(pt2->strength, pt1->strength, step);
1877  pt->flag = 0;
1878  interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
1879  /* Set point as selected. */
1880  if (gps->flag & GP_STROKE_SELECT) {
1881  pt->flag |= GP_SPOINT_SELECT;
1882  }
1883 
1884  /* Set weights. */
1885  if (gps->dvert != nullptr) {
1886  MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
1887  MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
1888  float weight_1 = dw1 ? dw1->weight : 0.0f;
1889 
1890  MDeformVert *dvert2 = &gps->dvert[0];
1891  MDeformWeight *dw2 = BKE_defvert_ensure_index(dvert2, 0);
1892  float weight_2 = dw2 ? dw2->weight : 0.0f;
1893 
1894  MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1];
1895  dvert_final->totweight = 0;
1896  MDeformWeight *dw = BKE_defvert_ensure_index(dvert_final, 0);
1897  if (dvert_final->dw) {
1898  dw->weight = interpf(weight_2, weight_1, step);
1899  }
1900  }
1901  }
1902 
1903  /* Enable cyclic flag. */
1904  gps->flag |= GP_STROKE_CYCLIC;
1905 
1906  return true;
1907 }
1908 
1911 /* -------------------------------------------------------------------- */
1915 void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
1916 {
1917  bGPDspoint *pt;
1918  MDeformVert *dvert = nullptr;
1919  int i;
1920 
1921  int tot = gps->totpoints; /* number of points in new buffer */
1922  /* first pass: count points to remove */
1923  /* Count how many points are selected (i.e. how many to remove) */
1924  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1925  if (pt->flag & tag) {
1926  /* selected point - one of the points to remove */
1927  tot--;
1928  }
1929  }
1930 
1931  /* if no points are left, we simply delete the entire stroke */
1932  if (tot <= 0) {
1933  /* remove the entire stroke */
1934  if (gps->points) {
1935  MEM_freeN(gps->points);
1936  }
1937  if (gps->dvert) {
1939  MEM_freeN(gps->dvert);
1940  }
1941  if (gps->triangles) {
1942  MEM_freeN(gps->triangles);
1943  }
1944  BLI_freelinkN(&gpf->strokes, gps);
1945  }
1946  else {
1947  /* just copy all points to keep into a smaller buffer */
1948  bGPDspoint *new_points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * tot,
1949  "new gp stroke points copy");
1950  bGPDspoint *npt = new_points;
1951 
1952  MDeformVert *new_dvert = nullptr;
1953  MDeformVert *ndvert = nullptr;
1954 
1955  if (gps->dvert != nullptr) {
1956  new_dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * tot,
1957  "new gp stroke weights copy");
1958  ndvert = new_dvert;
1959  }
1960 
1961  (gps->dvert != nullptr) ? dvert = gps->dvert : nullptr;
1962  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1963  if ((pt->flag & tag) == 0) {
1964  *npt = blender::dna::shallow_copy(*pt);
1965  npt++;
1966 
1967  if (gps->dvert != nullptr) {
1968  *ndvert = *dvert;
1969  ndvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
1970  ndvert++;
1971  }
1972  }
1973  if (gps->dvert != nullptr) {
1974  dvert++;
1975  }
1976  }
1977 
1978  /* free the old buffer */
1979  if (gps->points) {
1980  MEM_freeN(gps->points);
1981  }
1982  if (gps->dvert) {
1984  MEM_freeN(gps->dvert);
1985  }
1986 
1987  /* save the new buffer */
1988  gps->points = new_points;
1989  gps->dvert = new_dvert;
1990  gps->totpoints = tot;
1991 
1992  /* triangles cache needs to be recalculated */
1994  }
1995 }
1996 
1999 /* -------------------------------------------------------------------- */
2003 void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
2004 {
2005  if (gps->totpoints < 3) {
2006  zero_v3(r_normal);
2007  return;
2008  }
2009 
2010  bGPDspoint *points = gps->points;
2011  int totpoints = gps->totpoints;
2012 
2013  const bGPDspoint *pt0 = &points[0];
2014  const bGPDspoint *pt1 = &points[1];
2015  const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
2016 
2017  float vec1[3];
2018  float vec2[3];
2019 
2020  /* initial vector (p0 -> p1) */
2021  sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
2022 
2023  /* point vector at 3/4 */
2024  sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
2025 
2026  /* vector orthogonal to polygon plane */
2027  cross_v3_v3v3(r_normal, vec1, vec2);
2028 
2029  /* Normalize vector */
2030  normalize_v3(r_normal);
2031 }
2032 
2035 /* -------------------------------------------------------------------- */
2040 {
2041  bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
2042  int totpoints = gps->totpoints;
2043  char *marked = nullptr;
2044  char work;
2045 
2046  int start = 0;
2047  int end = gps->totpoints - 1;
2048 
2049  marked = (char *)MEM_callocN(totpoints, "GP marked array");
2050  marked[start] = 1;
2051  marked[end] = 1;
2052 
2053  work = 1;
2054  int totmarked = 0;
2055  /* while still reducing */
2056  while (work) {
2057  int ls, le;
2058  work = 0;
2059 
2060  ls = start;
2061  le = start + 1;
2062 
2063  /* while not over interval */
2064  while (ls < end) {
2065  int max_i = 0;
2066  /* divided to get more control */
2067  float max_dist = epsilon / 10.0f;
2068 
2069  /* find the next marked point */
2070  while (marked[le] == 0) {
2071  le++;
2072  }
2073 
2074  for (int i = ls + 1; i < le; i++) {
2075  float point_on_line[3];
2076  float dist;
2077 
2079  point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
2080 
2081  dist = len_v3v3(point_on_line, &old_points[i].x);
2082 
2083  if (dist > max_dist) {
2084  max_dist = dist;
2085  max_i = i;
2086  }
2087  }
2088 
2089  if (max_i != 0) {
2090  work = 1;
2091  marked[max_i] = 1;
2092  totmarked++;
2093  }
2094 
2095  ls = le;
2096  le = ls + 1;
2097  }
2098  }
2099 
2100  /* adding points marked */
2101  MDeformVert *old_dvert = nullptr;
2102  MDeformVert *dvert_src = nullptr;
2103 
2104  if (gps->dvert != nullptr) {
2105  old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
2106  }
2107  /* resize gps */
2108  int j = 0;
2109  for (int i = 0; i < totpoints; i++) {
2110  bGPDspoint *pt_src = &old_points[i];
2111  bGPDspoint *pt = &gps->points[j];
2112 
2113  if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
2114  *pt = blender::dna::shallow_copy(*pt_src);
2115  if (gps->dvert != nullptr) {
2116  dvert_src = &old_dvert[i];
2117  MDeformVert *dvert = &gps->dvert[j];
2118  memcpy(dvert, dvert_src, sizeof(MDeformVert));
2119  if (dvert_src->dw) {
2120  memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
2121  }
2122  }
2123  j++;
2124  }
2125  else {
2126  if (gps->dvert != nullptr) {
2127  dvert_src = &old_dvert[i];
2128  BKE_gpencil_free_point_weights(dvert_src);
2129  }
2130  }
2131  }
2132 
2133  gps->totpoints = j;
2134 
2135  /* Calc geometry data. */
2137 
2138  MEM_SAFE_FREE(old_points);
2139  MEM_SAFE_FREE(old_dvert);
2140  MEM_SAFE_FREE(marked);
2141 }
2142 
2144 {
2145  if (gps->totpoints < 4) {
2146  return;
2147  }
2148 
2149  /* save points */
2150  bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
2151  MDeformVert *old_dvert = nullptr;
2152  MDeformVert *dvert_src = nullptr;
2153 
2154  if (gps->dvert != nullptr) {
2155  old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
2156  }
2157 
2158  /* resize gps */
2159  int newtot = (gps->totpoints - 2) / 2;
2160  if ((gps->totpoints % 2) != 0) {
2161  newtot++;
2162  }
2163  newtot += 2;
2164 
2165  gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
2166  if (gps->dvert != nullptr) {
2167  gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
2168  }
2169 
2170  int j = 0;
2171  for (int i = 0; i < gps->totpoints; i++) {
2172  bGPDspoint *pt_src = &old_points[i];
2173  bGPDspoint *pt = &gps->points[j];
2174 
2175  if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
2176  *pt = blender::dna::shallow_copy(*pt_src);
2177  if (gps->dvert != nullptr) {
2178  dvert_src = &old_dvert[i];
2179  MDeformVert *dvert = &gps->dvert[j];
2180  memcpy(dvert, dvert_src, sizeof(MDeformVert));
2181  if (dvert_src->dw) {
2182  memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
2183  }
2184  }
2185  j++;
2186  }
2187  else {
2188  if (gps->dvert != nullptr) {
2189  dvert_src = &old_dvert[i];
2190  BKE_gpencil_free_point_weights(dvert_src);
2191  }
2192  }
2193  }
2194 
2195  gps->totpoints = j;
2196  /* Calc geometry data. */
2198 
2199  MEM_SAFE_FREE(old_points);
2200  MEM_SAFE_FREE(old_dvert);
2201 }
2202 
2203 void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
2204 {
2205  bGPDspoint *temp_points;
2206  MDeformVert *temp_dverts = nullptr;
2207  MDeformVert *dvert = nullptr;
2208  MDeformVert *dvert_final = nullptr;
2209  MDeformVert *dvert_next = nullptr;
2210  int totnewpoints, oldtotpoints;
2211 
2212  bool cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
2213 
2214  for (int s = 0; s < level; s++) {
2215  totnewpoints = gps->totpoints;
2216  if (!cyclic) {
2217  totnewpoints--;
2218  }
2219  /* duplicate points in a temp area */
2220  temp_points = gps->points;
2221  oldtotpoints = gps->totpoints;
2222 
2223  /* resize the points arrays */
2224  gps->totpoints += totnewpoints;
2225  gps->points = (bGPDspoint *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->points), __func__);
2226  if (gps->dvert != nullptr) {
2227  temp_dverts = gps->dvert;
2228  gps->dvert = (MDeformVert *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->dvert), __func__);
2229  }
2230 
2231  /* move points from last to first to new place */
2232  for (int i = 0; i < oldtotpoints; i++) {
2233  bGPDspoint *pt = &temp_points[i];
2234  bGPDspoint *pt_final = &gps->points[i * 2];
2235 
2236  copy_v3_v3(&pt_final->x, &pt->x);
2237  pt_final->pressure = pt->pressure;
2238  pt_final->strength = pt->strength;
2239  pt_final->uv_rot = pt->uv_rot;
2240  pt_final->uv_fac = pt->uv_fac;
2241  pt_final->time = pt->time;
2242  pt_final->flag = pt->flag;
2243  pt_final->runtime.pt_orig = pt->runtime.pt_orig;
2244  pt_final->runtime.idx_orig = pt->runtime.idx_orig;
2245  copy_v4_v4(pt_final->vert_color, pt->vert_color);
2246  copy_v4_v4(pt_final->uv_fill, pt->uv_fill);
2247 
2248  if (gps->dvert != nullptr) {
2249  dvert = &temp_dverts[i];
2250  dvert_final = &gps->dvert[i * 2];
2251  dvert_final->totweight = dvert->totweight;
2252  dvert_final->dw = dvert->dw;
2253  }
2254  }
2255  /* interpolate mid points */
2256  for (int i = cyclic ? 0 : 1, j = cyclic ? oldtotpoints - 1 : 0; i < oldtotpoints; j = i, i++) {
2257  bGPDspoint *pt = &temp_points[j];
2258  bGPDspoint *next = &temp_points[i];
2259  bGPDspoint *pt_final = &gps->points[j * 2 + 1];
2260 
2261  /* add a half way point */
2262  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
2263  pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
2264  pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
2265  pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f);
2266  pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f);
2267  interp_v4_v4v4(pt_final->uv_fill, pt->uv_fill, next->uv_fill, 0.5f);
2268  CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
2269  pt_final->time = interpf(pt->time, next->time, 0.5f);
2270  pt_final->runtime.pt_orig = nullptr;
2271  pt_final->flag = 0;
2272  interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
2273 
2274  if (gps->dvert != nullptr) {
2275  dvert = &temp_dverts[j];
2276  dvert_next = &temp_dverts[i];
2277  dvert_final = &gps->dvert[j * 2 + 1];
2278 
2279  dvert_final->totweight = dvert->totweight;
2280  dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
2281 
2282  /* interpolate weight values */
2283  for (int d = 0; d < dvert->totweight; d++) {
2284  MDeformWeight *dw_a = &dvert->dw[d];
2285  if (dvert_next->totweight > d) {
2286  MDeformWeight *dw_b = &dvert_next->dw[d];
2287  MDeformWeight *dw_final = &dvert_final->dw[d];
2288  dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
2289  }
2290  }
2291  }
2292  }
2293 
2294  MEM_SAFE_FREE(temp_points);
2295  MEM_SAFE_FREE(temp_dverts);
2296 
2297  /* Move points to smooth stroke (not simple type). */
2298  if (type != GP_SUBDIV_SIMPLE) {
2299  float mid[3];
2300  /* extreme points are not changed */
2301  for (int i = cyclic ? 0 : 2, j = cyclic ? gps->totpoints - 2 : 0; i < gps->totpoints - 2;
2302  j = i, i += 2) {
2303  bGPDspoint *prev = &gps->points[j + 1];
2304  bGPDspoint *pt = &gps->points[i];
2305  bGPDspoint *next = &gps->points[i + 1];
2306 
2307  /* move point */
2308  interp_v3_v3v3(mid, &prev->x, &next->x, 0.5f);
2309  interp_v3_v3v3(&pt->x, mid, &pt->x, 0.5f);
2310  }
2311  }
2312  }
2313 
2314  /* Calc geometry data. */
2316 }
2317 
2320 /* -------------------------------------------------------------------- */
2325  bGPDframe *gpf,
2326  bGPDstroke *gps,
2327  const float threshold,
2328  const bool use_unselected)
2329 {
2330  bGPDspoint *pt = nullptr;
2331  bGPDspoint *pt_next = nullptr;
2332  float tagged = false;
2333  /* Use square distance to speed up loop */
2334  const float th_square = threshold * threshold;
2335  /* Need to have something to merge. */
2336  if (gps->totpoints < 2) {
2337  return;
2338  }
2339  int i = 0;
2340  int step = 1;
2341  while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
2342  pt = &gps->points[i];
2343  if (pt->flag & GP_SPOINT_TAG) {
2344  i++;
2345  step = 1;
2346  continue;
2347  }
2348  pt_next = &gps->points[i + step];
2349  /* Do not recalc tagged points. */
2350  if (pt_next->flag & GP_SPOINT_TAG) {
2351  step++;
2352  continue;
2353  }
2354  /* Check if contiguous points are selected. */
2355  if (!use_unselected) {
2356  if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
2357  i++;
2358  step = 1;
2359  continue;
2360  }
2361  }
2362  float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
2363  if (len_square <= th_square) {
2364  tagged = true;
2365  if (i != gps->totpoints - 1) {
2366  /* Tag second point for delete. */
2367  pt_next->flag |= GP_SPOINT_TAG;
2368  }
2369  else {
2370  pt->flag |= GP_SPOINT_TAG;
2371  }
2372  /* Jump to next pair of points, keeping first point segment equals. */
2373  step++;
2374  }
2375  else {
2376  /* Analyze next point. */
2377  i++;
2378  step = 1;
2379  }
2380  }
2381 
2382  /* Always untag extremes. */
2383  pt = &gps->points[0];
2384  pt->flag &= ~GP_SPOINT_TAG;
2385  pt = &gps->points[gps->totpoints - 1];
2386  pt->flag &= ~GP_SPOINT_TAG;
2387 
2388  /* Dissolve tagged points */
2389  if (tagged) {
2391  }
2392 
2393  /* Calc geometry data. */
2395 }
2396 
2397 struct GpEdge {
2399  /* Coordinates. */
2400  float v1_co[3], v2_co[3];
2401  /* Normals. */
2402  float n1[3], n2[3];
2403  /* Direction of the segment. */
2404  float vec[3];
2405  int flag;
2406 };
2407 
2409  GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
2410 {
2411  int edge = -1;
2412  float last_angle = 999999.0f;
2413  for (int i = 0; i < totedges; i++) {
2414  GpEdge *gped = &gp_edges[i];
2415  if (gped->flag != 0) {
2416  continue;
2417  }
2418  if (reverse) {
2419  if (gped_init->v1 != gped->v2) {
2420  continue;
2421  }
2422  }
2423  else {
2424  if (gped_init->v2 != gped->v1) {
2425  continue;
2426  }
2427  }
2428  /* Look for straight lines. */
2429  float angle = angle_v3v3(gped->vec, gped_init->vec);
2430  if ((angle < threshold) && (angle <= last_angle)) {
2431  edge = i;
2432  last_angle = angle;
2433  }
2434  }
2435 
2436  return edge;
2437 }
2438 
2439 static int gpencil_walk_edge(GHash *v_table,
2440  GpEdge *gp_edges,
2441  int totedges,
2442  uint *stroke_array,
2443  int init_idx,
2444  const float angle,
2445  const bool reverse)
2446 {
2447  GpEdge *gped_init = &gp_edges[init_idx];
2448  int idx = 1;
2449  int edge = 0;
2450  while (edge > -1) {
2451  edge = gpencil_next_edge(gp_edges, totedges, gped_init, angle, reverse);
2452  if (edge > -1) {
2453  GpEdge *gped = &gp_edges[edge];
2454  stroke_array[idx] = edge;
2455  gped->flag = 1;
2456  gped_init = &gp_edges[edge];
2457  idx++;
2458 
2459  /* Avoid following already visited vertices. */
2460  if (reverse) {
2461  if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v1))) {
2462  edge = -1;
2463  }
2464  else {
2465  BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v1), POINTER_FROM_INT(gped->v1));
2466  }
2467  }
2468  else {
2469  if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v2))) {
2470  edge = -1;
2471  }
2472  else {
2473  BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v2), POINTER_FROM_INT(gped->v2));
2474  }
2475  }
2476  }
2477  }
2478 
2479  return idx;
2480 }
2481 
2483  bGPdata *gpd,
2484  bGPDframe *gpf_stroke,
2485  int stroke_mat_index,
2486  const float angle,
2487  const int thickness,
2488  const float offset,
2489  const float matrix[4][4],
2490  const bool use_seams,
2491  const bool use_vgroups)
2492 {
2493  Mesh *me = (Mesh *)ob->data;
2494  if (me->totedge == 0) {
2495  return;
2496  }
2497  const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me);
2498 
2499  /* Arrays for all edge vertices (forward and backward) that form a edge loop.
2500  * This is reused for each edge-loop to create gpencil stroke. */
2501  uint *stroke = (uint *)MEM_mallocN(sizeof(uint) * me->totedge * 2, __func__);
2502  uint *stroke_fw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__);
2503  uint *stroke_bw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__);
2504 
2505  /* Create array with all edges. */
2506  GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
2507  GpEdge *gped = nullptr;
2508  for (int i = 0; i < me->totedge; i++) {
2509  MEdge *ed = &me->medge[i];
2510  gped = &gp_edges[i];
2511  MVert *mv1 = &me->mvert[ed->v1];
2512  copy_v3_v3(gped->n1, vert_normals[ed->v1]);
2513 
2514  gped->v1 = ed->v1;
2515  copy_v3_v3(gped->v1_co, mv1->co);
2516 
2517  MVert *mv2 = &me->mvert[ed->v2];
2518  copy_v3_v3(gped->n2, vert_normals[ed->v2]);
2519  gped->v2 = ed->v2;
2520  copy_v3_v3(gped->v2_co, mv2->co);
2521 
2522  sub_v3_v3v3(gped->vec, mv1->co, mv2->co);
2523 
2524  /* If use seams, mark as done if not a seam. */
2525  if ((use_seams) && ((ed->flag & ME_SEAM) == 0)) {
2526  gped->flag = 1;
2527  }
2528  }
2529 
2530  /* Loop edges to find edgeloops */
2531  bool pending = true;
2532  int e = 0;
2533  while (pending) {
2534  gped = &gp_edges[e];
2535  /* Look first unused edge. */
2536  if (gped->flag != 0) {
2537  e++;
2538  if (e == me->totedge) {
2539  pending = false;
2540  }
2541  continue;
2542  }
2543  /* Add current edge to arrays. */
2544  stroke_fw[0] = e;
2545  stroke_bw[0] = e;
2546  gped->flag = 1;
2547 
2548  /* Hash used to avoid loop over same vertices. */
2549  GHash *v_table = BLI_ghash_int_new(__func__);
2550  /* Look forward edges. */
2551  int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false);
2552  /* Look backward edges. */
2553  int totbw = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_bw, e, angle, true);
2554 
2555  BLI_ghash_free(v_table, nullptr, nullptr);
2556 
2557  /* Join both arrays. */
2558  int array_len = 0;
2559  for (int i = totbw - 1; i > 0; i--) {
2560  stroke[array_len] = stroke_bw[i];
2561  array_len++;
2562  }
2563  for (int i = 0; i < totedges; i++) {
2564  stroke[array_len] = stroke_fw[i];
2565  array_len++;
2566  }
2567 
2568  /* Create Stroke. */
2569  bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
2570  gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
2571 
2572  /* Create dvert data. */
2573  MDeformVert *me_dvert = me->dvert;
2574  if (use_vgroups && me_dvert) {
2575  gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1),
2576  "gp_stroke_dverts");
2577  }
2578 
2579  /* Create first segment. */
2580  float fpt[3];
2581  for (int i = 0; i < array_len + 1; i++) {
2582  int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2;
2583  MVert *mv = &me->mvert[vertex_index];
2584 
2585  /* Add segment. */
2586  bGPDspoint *pt = &gps_stroke->points[i];
2587  copy_v3_v3(fpt, vert_normals[vertex_index]);
2588  mul_v3_v3fl(fpt, fpt, offset);
2589  add_v3_v3v3(&pt->x, mv->co, fpt);
2590  mul_m4_v3(matrix, &pt->x);
2591 
2592  pt->pressure = 1.0f;
2593  pt->strength = 1.0f;
2594 
2595  /* Copy vertex groups from mesh. Assuming they already exist in the same order. */
2596  if (use_vgroups && me_dvert) {
2597  MDeformVert *dv = &gps_stroke->dvert[i];
2598  MDeformVert *src_dv = &me_dvert[vertex_index];
2599  dv->totweight = src_dv->totweight;
2600  dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
2601  "gp_stroke_dverts_dw");
2602  for (int j = 0; j < dv->totweight; j++) {
2603  dv->dw[j].weight = src_dv->dw[j].weight;
2604  dv->dw[j].def_nr = src_dv->dw[j].def_nr;
2605  }
2606  }
2607  }
2608 
2609  BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
2610  }
2611 
2612  /* Free memory. */
2613  MEM_SAFE_FREE(stroke);
2614  MEM_SAFE_FREE(stroke_fw);
2615  MEM_SAFE_FREE(stroke_bw);
2616  MEM_SAFE_FREE(gp_edges);
2617 }
2618 
2619 /* Helper: Add gpencil material using material as base. */
2621  Object *ob_gp,
2622  const char *name,
2623  const float color[4],
2624  const bool use_stroke,
2625  const bool use_fill,
2626  int *r_idx)
2627 {
2628  Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, name, r_idx);
2629  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
2630 
2631  /* Stroke color. */
2632  if (use_stroke) {
2633  ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
2634  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
2635  }
2636  else {
2637  copy_v4_v4(gp_style->stroke_rgba, color);
2638  gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
2639  }
2640 
2641  /* Fill color. */
2642  copy_v4_v4(gp_style->fill_rgba, color);
2643  if (use_fill) {
2644  gp_style->flag |= GP_MATERIAL_FILL_SHOW;
2645  }
2646 
2647  /* Check at least one is enabled. */
2648  if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
2649  ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
2650  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
2651  }
2652 
2653  return mat_gp;
2654 }
2655 
2656 static int gpencil_material_find_index_by_name(Object *ob, const char *name)
2657 {
2658  for (int i = 0; i < ob->totcol; i++) {
2659  Material *ma = BKE_object_material_get(ob, i + 1);
2660  if ((ma != nullptr) && (ma->gp_style != nullptr) && (STREQ(ma->id.name + 2, name))) {
2661  return i;
2662  }
2663  }
2664 
2665  return -1;
2666 }
2667 
2671 static void make_element_name(const char *obname, const char *name, const int maxlen, char *r_name)
2672 {
2673  char str[256];
2674  SNPRINTF(str, "%s_%s", obname, name);
2675 
2676  /* Replace any point by underscore. */
2677  BLI_str_replace_char(str, '.', '_');
2678 
2679  BLI_strncpy_utf8(r_name, str, maxlen);
2680 }
2681 
2684  Scene *scene,
2685  Object *ob_gp,
2686  Object *ob_mesh,
2687  const float angle,
2688  const int thickness,
2689  const float offset,
2690  const float matrix[4][4],
2691  const int frame_offset,
2692  const bool use_seams,
2693  const bool use_faces,
2694  const bool use_vgroups)
2695 {
2696  if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) {
2697  return false;
2698  }
2699 
2700  bGPdata *gpd = (bGPdata *)ob_gp->data;
2701 
2702  /* Use evaluated data to get mesh with all modifiers on top. */
2703  Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, ob_mesh);
2704  const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
2705  const MPoly *mpoly = me_eval->mpoly;
2706  const MLoop *mloop = me_eval->mloop;
2707  int mpoly_len = me_eval->totpoly;
2708  char element_name[200];
2709 
2710  /* Need at least an edge. */
2711  if (me_eval->totedge < 1) {
2712  return false;
2713  }
2714 
2715  /* Create matching vertex groups. */
2718 
2719  const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
2720  /* Lookup existing stroke material on gp object. */
2721  make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name);
2722  int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name);
2723 
2724  if (stroke_mat_index == -1) {
2725  /* Create new default stroke material as there is no existing material. */
2727  bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index);
2728  }
2729 
2730  /* Export faces as filled strokes. */
2731  if (use_faces && mpoly_len > 0) {
2732  /* Read all polygons and create fill for each. */
2733  make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
2734  /* Create Layer and Frame. */
2735  bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
2736  if (gpl_fill == nullptr) {
2737  gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
2738  }
2740  gpl_fill, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW);
2741  int i;
2742  for (i = 0; i < mpoly_len; i++) {
2743  const MPoly *mp = &mpoly[i];
2744 
2745  /* Find material. */
2746  int mat_idx = 0;
2747  Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
2749  ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
2750  mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
2751  if (mat_idx == -1) {
2752  float color[4];
2753  if (ma != nullptr) {
2754  copy_v3_v3(color, &ma->r);
2755  color[3] = 1.0f;
2756  }
2757  else {
2758  copy_v4_v4(color, default_colors[1]);
2759  }
2760  gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
2761  }
2762 
2763  bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
2764  gps_fill->flag |= GP_STROKE_CYCLIC;
2765 
2766  /* Create dvert data. */
2767  MDeformVert *me_dvert = me_eval->dvert;
2768  if (use_vgroups && me_dvert) {
2769  gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * mp->totloop,
2770  "gp_fill_dverts");
2771  }
2772 
2773  /* Add points to strokes. */
2774  for (int j = 0; j < mp->totloop; j++) {
2775  const MLoop *ml = &mloop[mp->loopstart + j];
2776  const MVert *mv = &me_eval->mvert[ml->v];
2777 
2778  bGPDspoint *pt = &gps_fill->points[j];
2779  copy_v3_v3(&pt->x, mv->co);
2780  mul_m4_v3(matrix, &pt->x);
2781  pt->pressure = 1.0f;
2782  pt->strength = 1.0f;
2783 
2784  /* Copy vertex groups from mesh. Assuming they already exist in the same order. */
2785  if (use_vgroups && me_dvert) {
2786  MDeformVert *dv = &gps_fill->dvert[j];
2787  MDeformVert *src_dv = &me_dvert[ml->v];
2788  dv->totweight = src_dv->totweight;
2789  dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
2790  "gp_fill_dverts_dw");
2791  for (int k = 0; k < dv->totweight; k++) {
2792  dv->dw[k].weight = src_dv->dw[k].weight;
2793  dv->dw[k].def_nr = src_dv->dw[k].def_nr;
2794  }
2795  }
2796  }
2797  /* If has only 3 points subdivide. */
2798  if (mp->totloop == 3) {
2800  }
2801 
2802  BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
2803  }
2804  }
2805 
2806  /* Create stroke from edges. */
2807 
2808  /* Create Layer and Frame. */
2809  make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
2810  bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
2811  if (gpl_stroke == nullptr) {
2812  gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
2813  }
2814  bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
2815  gpl_stroke, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW);
2816 
2818  gpd,
2819  gpf_stroke,
2820  stroke_mat_index,
2821  angle,
2822  thickness,
2823  offset,
2824  matrix,
2825  use_seams,
2826  use_vgroups);
2827 
2828  /* Tag for recalculation */
2830 
2831  return true;
2832 }
2833 
2834 void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
2835 {
2836  if (gpd == nullptr) {
2837  return;
2838  }
2839 
2840  const float scalef = mat4_to_scale(mat);
2841  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2842  /* FIXME: For now, we just skip parented layers.
2843  * Otherwise, we have to update each frame to find
2844  * the current parent position/effects.
2845  */
2846  if (gpl->parent) {
2847  continue;
2848  }
2849 
2850  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2851  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2852  bGPDspoint *pt;
2853  int i;
2854 
2855  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2856  mul_m4_v3(mat, &pt->x);
2857  pt->pressure *= scalef;
2858  }
2859 
2860  /* Distortion may mean we need to re-triangulate. */
2862  }
2863  }
2864  }
2865 }
2866 
2868 {
2869  int total_points = 0;
2870 
2871  if (gpd == nullptr) {
2872  return 0;
2873  }
2874 
2875  LISTBASE_FOREACH (const bGPDlayer *, gpl, &gpd->layers) {
2876  /* FIXME: For now, we just skip parented layers.
2877  * Otherwise, we have to update each frame to find
2878  * the current parent position/effects.
2879  */
2880  if (gpl->parent) {
2881  continue;
2882  }
2883 
2884  LISTBASE_FOREACH (const bGPDframe *, gpf, &gpl->frames) {
2885  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2886  total_points += gps->totpoints;
2887  }
2888  }
2889  }
2890  return total_points;
2891 }
2892 
2894 {
2895  if (gpd == nullptr) {
2896  return;
2897  }
2898 
2899  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2900  /* FIXME: For now, we just skip parented layers.
2901  * Otherwise, we have to update each frame to find
2902  * the current parent position/effects.
2903  */
2904  if (gpl->parent) {
2905  continue;
2906  }
2907 
2908  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2909  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2910  bGPDspoint *pt;
2911  int i;
2912 
2913  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2914  copy_v3_v3(elem_data->co, &pt->x);
2915  elem_data->pressure = pt->pressure;
2916  elem_data++;
2917  }
2918  }
2919  }
2920  }
2921 }
2922 
2924 {
2925  if (gpd == nullptr) {
2926  return;
2927  }
2928 
2929  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2930  /* FIXME: For now, we just skip parented layers.
2931  * Otherwise, we have to update each frame to find
2932  * the current parent position/effects.
2933  */
2934  if (gpl->parent) {
2935  continue;
2936  }
2937 
2938  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2939  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2940  bGPDspoint *pt;
2941  int i;
2942 
2943  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2944  copy_v3_v3(&pt->x, elem_data->co);
2945  pt->pressure = elem_data->pressure;
2946  elem_data++;
2947  }
2948 
2949  /* Distortion may mean we need to re-triangulate. */
2951  }
2952  }
2953  }
2954 }
2955 
2957  const GPencilPointCoordinates *elem_data,
2958  const float mat[4][4])
2959 {
2960  if (gpd == nullptr) {
2961  return;
2962  }
2963 
2964  const float scalef = mat4_to_scale(mat);
2965  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2966  /* FIXME: For now, we just skip parented layers.
2967  * Otherwise, we have to update each frame to find
2968  * the current parent position/effects.
2969  */
2970  if (gpl->parent) {
2971  continue;
2972  }
2973 
2974  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2975  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2976  bGPDspoint *pt;
2977  int i;
2978 
2979  for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) {
2980  mul_v3_m4v3(&pt->x, mat, elem_data->co);
2981  pt->pressure = elem_data->pressure * scalef;
2982  elem_data++;
2983  }
2984 
2985  /* Distortion may mean we need to re-triangulate. */
2987  }
2988  }
2989  }
2990 }
2991 
2993 {
2994  BLI_assert(gps->totpoints > 0);
2995 
2996  float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
2997  bGPDspoint *pt = &gps->points[0];
2998  color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z));
2999  color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x));
3000  color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y));
3001  for (int i = 0; i < gps->totpoints; i++) {
3002  pt = &gps->points[i];
3003  copy_v4_v4(pt->vert_color, color);
3004  }
3005 }
3006 
3008 {
3009  /* Reverse points. */
3010  BLI_array_reverse(gps->points, gps->totpoints);
3011 
3012  /* Reverse vertex groups if available. */
3013  if (gps->dvert) {
3014  BLI_array_reverse(gps->dvert, gps->totpoints);
3015  }
3016 }
3017 
3018 /* Temp data for storing information about an "island" of points
3019  * that should be kept when splitting up a stroke. Used in:
3020  * gpencil_stroke_delete_tagged_points()
3021  */
3024  int end_idx;
3025 };
3026 
3028  bGPDframe *gpf,
3029  bGPDstroke *gps_first,
3030  bGPDstroke *gps_last)
3031 {
3032  bGPDspoint *pt = nullptr;
3033  bGPDspoint *pt_final = nullptr;
3034  const int totpoints = gps_first->totpoints + gps_last->totpoints;
3035 
3036  /* create new stroke */
3037  bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false, true);
3038 
3039  join_stroke->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
3040  join_stroke->totpoints = totpoints;
3041  join_stroke->flag &= ~GP_STROKE_CYCLIC;
3042 
3043  /* copy points (last before) */
3044  int e1 = 0;
3045  int e2 = 0;
3046  float delta = 0.0f;
3047 
3048  for (int i = 0; i < totpoints; i++) {
3049  pt_final = &join_stroke->points[i];
3050  if (i < gps_last->totpoints) {
3051  pt = &gps_last->points[e1];
3052  e1++;
3053  }
3054  else {
3055  pt = &gps_first->points[e2];
3056  e2++;
3057  }
3058 
3059  /* copy current point */
3060  copy_v3_v3(&pt_final->x, &pt->x);
3061  pt_final->pressure = pt->pressure;
3062  pt_final->strength = pt->strength;
3063  pt_final->time = delta;
3064  pt_final->flag = pt->flag;
3065  copy_v4_v4(pt_final->vert_color, pt->vert_color);
3066 
3067  /* retiming with fixed time interval (we cannot determine real time) */
3068  delta += 0.01f;
3069  }
3070 
3071  /* Copy over vertex weight data (if available) */
3072  if ((gps_first->dvert != nullptr) || (gps_last->dvert != nullptr)) {
3073  join_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
3074  MDeformVert *dvert_src = nullptr;
3075  MDeformVert *dvert_dst = nullptr;
3076 
3077  /* Copy weights (last before). */
3078  e1 = 0;
3079  e2 = 0;
3080  for (int i = 0; i < totpoints; i++) {
3081  dvert_dst = &join_stroke->dvert[i];
3082  dvert_src = nullptr;
3083  if (i < gps_last->totpoints) {
3084  if (gps_last->dvert) {
3085  dvert_src = &gps_last->dvert[e1];
3086  e1++;
3087  }
3088  }
3089  else {
3090  if (gps_first->dvert) {
3091  dvert_src = &gps_first->dvert[e2];
3092  e2++;
3093  }
3094  }
3095 
3096  if ((dvert_src) && (dvert_src->dw)) {
3097  dvert_dst->dw = (MDeformWeight *)MEM_dupallocN(dvert_src->dw);
3098  }
3099  }
3100  }
3101 
3102  /* add new stroke at head */
3103  BLI_addhead(&gpf->strokes, join_stroke);
3104  /* Calc geometry data. */
3105  BKE_gpencil_stroke_geometry_update(gpd, join_stroke);
3106 
3107  /* remove first stroke */
3108  BLI_remlink(&gpf->strokes, gps_first);
3109  BKE_gpencil_free_stroke(gps_first);
3110 
3111  /* remove last stroke */
3112  BLI_remlink(&gpf->strokes, gps_last);
3113  BKE_gpencil_free_stroke(gps_last);
3114 }
3115 
3117  bGPDframe *gpf,
3118  bGPDstroke *gps,
3119  bGPDstroke *next_stroke,
3120  int tag_flags,
3121  const bool select,
3122  const bool flat_cap,
3123  const int limit)
3124 {
3125  /* The algorithm used here is as follows:
3126  * 1) We firstly identify the number of "islands" of non-tagged points
3127  * which will all end up being in new strokes.
3128  * - In the most extreme case (i.e. every other vert is a 1-vert island),
3129  * we have at most `n / 2` islands
3130  * - Once we start having larger islands than that, the number required
3131  * becomes much less
3132  * 2) Each island gets converted to a new stroke
3133  * If the number of points is <= limit, the stroke is deleted. */
3134 
3136  sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
3137  bool in_island = false;
3138  int num_islands = 0;
3139 
3140  bGPDstroke *new_stroke = nullptr;
3141  bGPDstroke *gps_first = nullptr;
3142  const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC);
3143 
3144  /* First Pass: Identify start/end of islands */
3145  bGPDspoint *pt = gps->points;
3146  for (int i = 0; i < gps->totpoints; i++, pt++) {
3147  if (pt->flag & tag_flags) {
3148  /* selected - stop accumulating to island */
3149  in_island = false;
3150  }
3151  else {
3152  /* unselected - start of a new island? */
3153  int idx;
3154 
3155  if (in_island) {
3156  /* extend existing island */
3157  idx = num_islands - 1;
3158  islands[idx].end_idx = i;
3159  }
3160  else {
3161  /* start of new island */
3162  in_island = true;
3163  num_islands++;
3164 
3165  idx = num_islands - 1;
3166  islands[idx].start_idx = islands[idx].end_idx = i;
3167  }
3168  }
3169  }
3170 
3171  /* Watch out for special case where No islands = All points selected = Delete Stroke only */
3172  if (num_islands) {
3173  /* There are islands, so create a series of new strokes,
3174  * adding them before the "next" stroke. */
3175  int idx;
3176 
3177  /* Create each new stroke... */
3178  for (idx = 0; idx < num_islands; idx++) {
3179  tGPDeleteIsland *island = &islands[idx];
3180  new_stroke = BKE_gpencil_stroke_duplicate(gps, false, true);
3181  if (flat_cap) {
3182  new_stroke->caps[1 - (idx % 2)] = GP_STROKE_CAP_FLAT;
3183  }
3184 
3185  /* if cyclic and first stroke, save to join later */
3186  if ((is_cyclic) && (gps_first == nullptr)) {
3187  gps_first = new_stroke;
3188  }
3189 
3190  new_stroke->flag &= ~GP_STROKE_CYCLIC;
3191 
3192  /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
3193  new_stroke->totpoints = island->end_idx - island->start_idx + 1;
3194 
3195  /* Copy over the relevant point data */
3196  new_stroke->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints,
3197  "gp delete stroke fragment");
3198  memcpy(static_cast<void *>(new_stroke->points),
3199  gps->points + island->start_idx,
3200  sizeof(bGPDspoint) * new_stroke->totpoints);
3201 
3202  /* Copy over vertex weight data (if available) */
3203  if (gps->dvert != nullptr) {
3204  /* Copy over the relevant vertex-weight points */
3205  new_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints,
3206  "gp delete stroke fragment weight");
3207  memcpy(new_stroke->dvert,
3208  gps->dvert + island->start_idx,
3209  sizeof(MDeformVert) * new_stroke->totpoints);
3210 
3211  /* Copy weights */
3212  int e = island->start_idx;
3213  for (int i = 0; i < new_stroke->totpoints; i++) {
3214  MDeformVert *dvert_src = &gps->dvert[e];
3215  MDeformVert *dvert_dst = &new_stroke->dvert[i];
3216  if (dvert_src->dw) {
3217  dvert_dst->dw = (MDeformWeight *)MEM_dupallocN(dvert_src->dw);
3218  }
3219  e++;
3220  }
3221  }
3222  /* Each island corresponds to a new stroke.
3223  * We must adjust the timings of these new strokes:
3224  *
3225  * Each point's timing data is a delta from stroke's inittime, so as we erase some points
3226  * from the start of the stroke, we have to offset this inittime and all remaining points'
3227  * delta values. This way we get a new stroke with exactly the same timing as if user had
3228  * started drawing from the first non-removed point.
3229  */
3230  {
3231  bGPDspoint *pts;
3232  float delta = gps->points[island->start_idx].time;
3233  int j;
3234 
3235  new_stroke->inittime += (double)delta;
3236 
3237  pts = new_stroke->points;
3238  for (j = 0; j < new_stroke->totpoints; j++, pts++) {
3239  pts->time -= delta;
3240  /* set flag for select again later */
3241  if (select == true) {
3242  pts->flag &= ~GP_SPOINT_SELECT;
3243  pts->flag |= GP_SPOINT_TAG;
3244  }
3245  }
3246  }
3247 
3248  /* Add new stroke to the frame or delete if below limit */
3249  if ((limit > 0) && (new_stroke->totpoints <= limit)) {
3250  if (gps_first == new_stroke) {
3251  gps_first = nullptr;
3252  }
3253  BKE_gpencil_free_stroke(new_stroke);
3254  }
3255  else {
3256  /* Calc geometry data. */
3257  BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
3258 
3259  if (next_stroke) {
3260  BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
3261  }
3262  else {
3263  BLI_addtail(&gpf->strokes, new_stroke);
3264  }
3265  }
3266  }
3267  /* if cyclic, need to join last stroke with first stroke */
3268  if ((is_cyclic) && (gps_first != nullptr) && (gps_first != new_stroke)) {
3269  gpencil_stroke_join_islands(gpd, gpf, gps_first, new_stroke);
3270  }
3271  }
3272 
3273  /* free islands */
3274  MEM_freeN(islands);
3275 
3276  /* Delete the old stroke */
3277  BLI_remlink(&gpf->strokes, gps);
3279 
3280  return new_stroke;
3281 }
3282 
3284  bGPDframe *gpf,
3285  bGPDstroke *gps,
3286  bGPDstroke *next_stroke,
3287  bGPDcurve *gpc,
3288  int tag_flags)
3289 {
3290  if (gpc == nullptr) {
3291  return;
3292  }
3293  const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
3294  const int idx_last = gpc->tot_curve_points - 1;
3295  bGPDstroke *gps_first = nullptr;
3296  bGPDstroke *gps_last = nullptr;
3297 
3298  int idx_start = 0;
3299  int idx_end = 0;
3300  bool prev_selected = gpc->curve_points[0].flag & tag_flags;
3301  for (int i = 1; i < gpc->tot_curve_points; i++) {
3302  bool selected = gpc->curve_points[i].flag & tag_flags;
3303  if (prev_selected == true && selected == false) {
3304  idx_start = i;
3305  }
3306  /* Island ends if the current point is selected or if we reached the end of the stroke */
3307  if ((prev_selected == false && selected == true) || (selected == false && i == idx_last)) {
3308 
3309  idx_end = selected ? i - 1 : i;
3310  int island_length = idx_end - idx_start + 1;
3311 
3312  /* If an island has only a single curve point, there is no curve segment, so skip island */
3313  if (island_length == 1) {
3314  if (is_cyclic) {
3315  if (idx_start > 0 && idx_end < idx_last) {
3316  prev_selected = selected;
3317  continue;
3318  }
3319  }
3320  else {
3321  prev_selected = selected;
3322  continue;
3323  }
3324  }
3325 
3326  bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false);
3327  new_stroke->points = nullptr;
3328  new_stroke->flag &= ~GP_STROKE_CYCLIC;
3329  new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length);
3330 
3331  if (gps_first == nullptr) {
3332  gps_first = new_stroke;
3333  }
3334 
3335  bGPDcurve *new_gpc = new_stroke->editcurve;
3336  memcpy(new_gpc->curve_points,
3337  gpc->curve_points + idx_start,
3338  sizeof(bGPDcurve_point) * island_length);
3339 
3341  new_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
3342 
3343  /* Calc geometry data. */
3344  BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
3345 
3346  if (next_stroke) {
3347  BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
3348  }
3349  else {
3350  BLI_addtail(&gpf->strokes, new_stroke);
3351  }
3352 
3353  gps_last = new_stroke;
3354  }
3355  prev_selected = selected;
3356  }
3357 
3358  /* join first and last stroke if cyclic */
3359  if (is_cyclic && gps_first != nullptr && gps_last != nullptr && gps_first != gps_last) {
3360  bGPDcurve *gpc_first = gps_first->editcurve;
3361  bGPDcurve *gpc_last = gps_last->editcurve;
3362  int first_tot_points = gpc_first->tot_curve_points;
3363  int old_tot_points = gpc_last->tot_curve_points;
3364 
3365  gpc_last->tot_curve_points = first_tot_points + old_tot_points;
3367  gpc_last->curve_points, sizeof(bGPDcurve_point) * gpc_last->tot_curve_points);
3368  /* copy data from first to last */
3369  memcpy(gpc_last->curve_points + old_tot_points,
3370  gpc_first->curve_points,
3371  sizeof(bGPDcurve_point) * first_tot_points);
3372 
3374  gps_last->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
3375 
3376  /* Calc geometry data. */
3377  BKE_gpencil_stroke_geometry_update(gpd, gps_last);
3378 
3379  /* remove first one */
3380  BLI_remlink(&gpf->strokes, gps_first);
3381  BKE_gpencil_free_stroke(gps_first);
3382  }
3383 
3384  /* Delete the old stroke */
3385  BLI_remlink(&gpf->strokes, gps);
3387 }
3388 
3389 /* Helper: copy point between strokes */
3391  MDeformVert *dvert,
3392  bGPDspoint *point,
3393  const float delta[3],
3394  float pressure,
3395  float strength,
3396  float deltatime)
3397 {
3398  bGPDspoint *newpoint;
3399 
3400  gps->points = (bGPDspoint *)MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
3401  if (gps->dvert != nullptr) {
3402  gps->dvert = (MDeformVert *)MEM_reallocN(gps->dvert,
3403  sizeof(MDeformVert) * (gps->totpoints + 1));
3404  }
3405  else {
3406  /* If destination has weight add weight to origin. */
3407  if (dvert != nullptr) {
3408  gps->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1),
3409  __func__);
3410  }
3411  }
3412 
3413  gps->totpoints++;
3414  newpoint = &gps->points[gps->totpoints - 1];
3415 
3416  newpoint->x = point->x * delta[0];
3417  newpoint->y = point->y * delta[1];
3418  newpoint->z = point->z * delta[2];
3419  newpoint->flag = point->flag;
3420  newpoint->pressure = pressure;
3421  newpoint->strength = strength;
3422  newpoint->time = point->time + deltatime;
3423  copy_v4_v4(newpoint->vert_color, point->vert_color);
3424 
3425  if (gps->dvert != nullptr) {
3426  MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
3427 
3428  if (dvert != nullptr) {
3429  newdvert->totweight = dvert->totweight;
3430  newdvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
3431  }
3432  else {
3433  newdvert->totweight = 0;
3434  newdvert->dw = nullptr;
3435  }
3436  }
3437 }
3438 
3440  bGPDstroke *gps_b,
3441  const bool leave_gaps,
3442  const bool fit_thickness,
3443  const bool smooth)
3444 {
3445  bGPDspoint point;
3446  bGPDspoint *pt;
3447  int i;
3448  const float delta[3] = {1.0f, 1.0f, 1.0f};
3449  float deltatime = 0.0f;
3450 
3451  /* sanity checks */
3452  if (ELEM(nullptr, gps_a, gps_b)) {
3453  return;
3454  }
3455 
3456  if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) {
3457  return;
3458  }
3459 
3460  /* define start and end points of each stroke */
3461  float start_a[3], start_b[3], end_a[3], end_b[3];
3462  pt = &gps_a->points[0];
3463  copy_v3_v3(start_a, &pt->x);
3464 
3465  pt = &gps_a->points[gps_a->totpoints - 1];
3466  copy_v3_v3(end_a, &pt->x);
3467 
3468  pt = &gps_b->points[0];
3469  copy_v3_v3(start_b, &pt->x);
3470 
3471  pt = &gps_b->points[gps_b->totpoints - 1];
3472  copy_v3_v3(end_b, &pt->x);
3473 
3474  /* Check if need flip strokes. */
3475  float dist = len_squared_v3v3(end_a, start_b);
3476  bool flip_a = false;
3477  bool flip_b = false;
3478  float lowest = dist;
3479 
3480  dist = len_squared_v3v3(end_a, end_b);
3481  if (dist < lowest) {
3482  lowest = dist;
3483  flip_a = false;
3484  flip_b = true;
3485  }
3486 
3487  dist = len_squared_v3v3(start_a, start_b);
3488  if (dist < lowest) {
3489  lowest = dist;
3490  flip_a = true;
3491  flip_b = false;
3492  }
3493 
3494  dist = len_squared_v3v3(start_a, end_b);
3495  if (dist < lowest) {
3496  lowest = dist;
3497  flip_a = true;
3498  flip_b = true;
3499  }
3500 
3501  if (flip_a) {
3502  BKE_gpencil_stroke_flip(gps_a);
3503  }
3504  if (flip_b) {
3505  BKE_gpencil_stroke_flip(gps_b);
3506  }
3507 
3508  /* don't visibly link the first and last points? */
3509  if (leave_gaps) {
3510  /* 1st: add one tail point to start invisible area */
3511  point = blender::dna::shallow_copy(gps_a->points[gps_a->totpoints - 1]);
3512  deltatime = point.time;
3513 
3514  gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, 0.0f);
3515 
3516  /* 2nd: add one head point to finish invisible area */
3517  point = blender::dna::shallow_copy(gps_b->points[0]);
3518  gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, deltatime);
3519  }
3520 
3521  /* Ratio to apply in the points to keep the same thickness in the joined stroke using the
3522  * destination stroke thickness. */
3523  const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ?
3524  (float)gps_b->thickness / (float)gps_a->thickness :
3525  1.0f;
3526 
3527  /* 3rd: add all points */
3528  const int totpoints_a = gps_a->totpoints;
3529  for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
3530  MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : nullptr;
3532  gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime);
3533  }
3534  /* Smooth the join to avoid hard thickness changes. */
3535  if (smooth) {
3536  const int sample_points = 8;
3537  /* Get the segment to smooth using n points on each side of the join. */
3538  int start = MAX2(0, totpoints_a - sample_points);
3539  int end = MIN2(gps_a->totpoints - 1, start + (sample_points * 2));
3540  const int len = (end - start);
3541  float step = 1.0f / ((len / 2) + 1);
3542 
3543  /* Calc the average pressure. */
3544  float avg_pressure = 0.0f;
3545  for (i = start; i < end; i++) {
3546  pt = &gps_a->points[i];
3547  avg_pressure += pt->pressure;
3548  }
3549  avg_pressure = avg_pressure / len;
3550 
3551  /* Smooth segment thickness and position. */
3552  float ratio = step;
3553  for (i = start; i < end; i++) {
3554  pt = &gps_a->points[i];
3555  pt->pressure += (avg_pressure - pt->pressure) * ratio;
3556  BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f, 2, false, true, gps_a);
3557 
3558  ratio += step;
3559  /* In the center, reverse the ratio. */
3560  if (ratio > 1.0f) {
3561  ratio = ratio - step - step;
3562  step *= -1.0f;
3563  }
3564  }
3565  }
3566 }
3567 
3569  bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const bool tail)
3570 {
3571  GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64);
3572  BKE_gpencil_frame_selected_hash(gpd, frame_list);
3573 
3574  GHashIterator gh_iter;
3575  GHASH_ITER (gh_iter, frame_list) {
3576  int cfra = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter));
3577 
3578  if (gpf->framenum != cfra) {
3579  bGPDframe *gpf_new = BKE_gpencil_layer_frame_find(gpl, cfra);
3580  if (gpf_new == nullptr) {
3581  gpf_new = BKE_gpencil_frame_addnew(gpl, cfra);
3582  }
3583 
3584  if (gpf_new == nullptr) {
3585  continue;
3586  }
3587 
3588  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
3589  if (gps_new == nullptr) {
3590  continue;
3591  }
3592 
3593  if (tail) {
3594  BLI_addhead(&gpf_new->strokes, gps_new);
3595  }
3596  else {
3597  BLI_addtail(&gpf_new->strokes, gps_new);
3598  }
3599  }
3600  }
3601 
3602  /* Free hash table. */
3603  BLI_ghash_free(frame_list, nullptr, nullptr);
3604 }
3605 
3608 /* -------------------------------------------------------------------- */
3614  float x, y, z;
3616  float vertex_color[4];
3619 };
3620 
3621 struct tSampleEdge {
3622  float length_sq;
3625 };
3626 
3627 /* Helper: creates a tSamplePoint from a bGPDspoint and (optionally) a MDeformVert. */
3629 {
3630  tSamplePoint *new_pt = MEM_cnew<tSamplePoint>(__func__);
3631  copy_v3_v3(&new_pt->x, &pt->x);
3632  new_pt->pressure = pt->pressure;
3633  new_pt->strength = pt->strength;
3634  new_pt->time = pt->time;
3635  copy_v4_v4((float *)&new_pt->vertex_color, (float *)&pt->vert_color);
3636  if (dvert != nullptr) {
3637  new_pt->totweight = dvert->totweight;
3638  new_pt->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__);
3639  for (uint i = 0; i < new_pt->totweight; ++i) {
3640  MDeformWeight *dw = &new_pt->dw[i];
3641  MDeformWeight *dw_from = &dvert->dw[i];
3642  dw->def_nr = dw_from->def_nr;
3643  dw->weight = dw_from->weight;
3644  }
3645  }
3646  return new_pt;
3647 }
3648 
3649 /* Helper: creates a tSampleEdge from two tSamplePoints. Also calculates the length (squared) of
3650  * the edge. */
3652 {
3653  tSampleEdge *new_edge = MEM_cnew<tSampleEdge>(__func__);
3654  new_edge->from = from;
3655  new_edge->to = to;
3656  new_edge->length_sq = len_squared_v3v3(&from->x, &to->x);
3657  return new_edge;
3658 }
3659 
3661  bGPDstroke *gps,
3662  const uint32_t target_number,
3663  const bool select)
3664 {
3665  /* Stroke needs at least two points and strictly less points than the target number. */
3666  if (gps == nullptr || gps->totpoints < 2 || gps->totpoints >= target_number) {
3667  return;
3668  }
3669 
3670  const int totpoints = gps->totpoints;
3671  const bool has_dverts = (gps->dvert != nullptr);
3672  const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
3673 
3674  ListBase points = {nullptr, nullptr};
3675  Heap *edges = BLI_heap_new();
3676 
3677  /* Add all points into list. */
3678  for (uint32_t i = 0; i < totpoints; ++i) {
3679  bGPDspoint *pt = &gps->points[i];
3680  MDeformVert *dvert = has_dverts ? &gps->dvert[i] : nullptr;
3682  BLI_addtail(&points, sp);
3683  }
3684 
3685  /* Iterate over edges and insert them into the heap. */
3686  for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != nullptr; pt = pt->next) {
3687  tSampleEdge *se = new_sample_edge_from_sample_points(pt->prev, pt);
3688  /* BLI_heap is a min-heap, but we need the largest key to be at the top, so we take the
3689  * negative of the squared length. */
3690  BLI_heap_insert(edges, -(se->length_sq), se);
3691  }
3692 
3693  if (is_cyclic) {
3694  tSamplePoint *sp_first = (tSamplePoint *)points.first;
3695  tSamplePoint *sp_last = (tSamplePoint *)points.last;
3696  tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first);
3697  BLI_heap_insert(edges, -(se->length_sq), se);
3698  }
3699 
3700  int num_points_needed = target_number - totpoints;
3701  BLI_assert(num_points_needed > 0);
3702 
3703  while (num_points_needed > 0) {
3704  tSampleEdge *se = (tSampleEdge *)BLI_heap_pop_min(edges);
3705  tSamplePoint *sp = se->from;
3706  tSamplePoint *sp_next = se->to;
3707 
3708  /* Subdivide the edge. */
3709  tSamplePoint *new_sp = MEM_cnew<tSamplePoint>(__func__);
3710  interp_v3_v3v3(&new_sp->x, &sp->x, &sp_next->x, 0.5f);
3711  new_sp->pressure = interpf(sp->pressure, sp_next->pressure, 0.5f);
3712  new_sp->strength = interpf(sp->strength, sp_next->strength, 0.5f);
3713  new_sp->time = interpf(sp->time, sp_next->time, 0.5f);
3714  interp_v4_v4v4((float *)&new_sp->vertex_color,
3715  (float *)&sp->vertex_color,
3716  (float *)&sp_next->vertex_color,
3717  0.5f);
3718  if (sp->dw && sp_next->dw) {
3719  new_sp->totweight = MIN2(sp->totweight, sp_next->totweight);
3720  new_sp->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight,
3721  __func__);
3722  for (uint32_t i = 0; i < new_sp->totweight; ++i) {
3723  MDeformWeight *dw = &new_sp->dw[i];
3724  MDeformWeight *dw_from = &sp->dw[i];
3725  MDeformWeight *dw_to = &sp_next->dw[i];
3726  dw->def_nr = dw_from->def_nr;
3727  dw->weight = interpf(dw_from->weight, dw_to->weight, 0.5f);
3728  }
3729  }
3730  BLI_insertlinkafter(&points, sp, new_sp);
3731 
3732  tSampleEdge *se_prev = new_sample_edge_from_sample_points(sp, new_sp);
3733  tSampleEdge *se_next = new_sample_edge_from_sample_points(new_sp, sp_next);
3734  BLI_heap_insert(edges, -(se_prev->length_sq), se_prev);
3735  BLI_heap_insert(edges, -(se_next->length_sq), se_next);
3736 
3737  MEM_freeN(se);
3738  num_points_needed--;
3739  }
3740 
3741  /* Edges are no longer needed. Heap is freed. */
3743 
3744  gps->totpoints = target_number;
3745  gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
3746  if (has_dverts) {
3747  gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
3748  }
3749 
3750  /* Convert list back to stroke point array. */
3751  tSamplePoint *sp = (tSamplePoint *)points.first;
3752  for (uint32_t i = 0; i < gps->totpoints && sp; ++i, sp = sp->next) {
3753  bGPDspoint *pt = &gps->points[i];
3754  MDeformVert *dvert = &gps->dvert[i];
3755 
3756  copy_v3_v3(&pt->x, &sp->x);
3757  pt->pressure = sp->pressure;
3758  pt->strength = sp->strength;
3759  pt->time = sp->time;
3760  copy_v4_v4((float *)&pt->vert_color, (float *)&sp->vertex_color);
3761 
3762  if (sp->dw) {
3763  dvert->totweight = sp->totweight;
3764  dvert->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__);
3765  for (uint32_t j = 0; j < dvert->totweight; ++j) {
3766  MDeformWeight *dw = &dvert->dw[j];
3767  MDeformWeight *dw_from = &sp->dw[j];
3768  dw->def_nr = dw_from->def_nr;
3769  dw->weight = dw_from->weight;
3770  }
3771  }
3772  if (select) {
3773  pt->flag |= GP_SPOINT_SELECT;
3774  }
3775  }
3776 
3777  if (select) {
3778  gps->flag |= GP_STROKE_SELECT;
3780  }
3781 
3782  /* Free the sample points. Important to use the mutable loop here because we are erasing the list
3783  * elements. */
3784  LISTBASE_FOREACH_MUTABLE (tSamplePoint *, temp, &points) {
3785  if (temp->dw != nullptr) {
3786  MEM_freeN(temp->dw);
3787  }
3788  MEM_SAFE_FREE(temp);
3789  }
3790 
3791  /* Update the geometry of the stroke. */
3793 }
3794 
3796  bGPDstroke *gps,
3797  const float diff_mat[4][4])
3798 {
3799  for (int i = 0; i < gps->totpoints; i++) {
3800  bGPDspoint *pt = &gps->points[i];
3801  /* Point to parent space. */
3802  mul_v3_m4v3(&pt->x, diff_mat, &pt->x);
3803  /* point to view space */
3804  mul_m4_v3(rv3d->viewmat, &pt->x);
3805  }
3806 }
3807 
3809  bGPDstroke *gps,
3810  const float diff_mat[4][4])
3811 {
3812  float inverse_diff_mat[4][4];
3813  invert_m4_m4(inverse_diff_mat, diff_mat);
3814 
3815  for (int i = 0; i < gps->totpoints; i++) {
3816  bGPDspoint *pt = &gps->points[i];
3817  mul_v3_m4v3(&pt->x, rv3d->viewinv, &pt->x);
3818  mul_m4_v3(inverse_diff_mat, &pt->x);
3819  }
3820 }
3821 
3824 /* -------------------------------------------------------------------- */
3830  float x, y, z;
3831 };
3832 
3833 static tPerimeterPoint *new_perimeter_point(const float pt[3])
3834 {
3835  tPerimeterPoint *new_pt = MEM_cnew<tPerimeterPoint>(__func__);
3836  copy_v3_v3(&new_pt->x, pt);
3837  return new_pt;
3838 }
3839 
3842  tPerimeterPoint *to,
3843  float center_pt[3],
3844  int subdivisions,
3845  bool clockwise)
3846 {
3847  float vec_from[2];
3848  float vec_to[2];
3849  sub_v2_v2v2(vec_from, &from->x, center_pt);
3850  sub_v2_v2v2(vec_to, &to->x, center_pt);
3851  if (is_zero_v2(vec_from) || is_zero_v2(vec_to)) {
3852  return 0;
3853  }
3854 
3855  float dot = dot_v2v2(vec_from, vec_to);
3856  float det = cross_v2v2(vec_from, vec_to);
3857  float angle = clockwise ? M_PI - atan2f(-det, -dot) : atan2f(-det, -dot) + M_PI;
3858 
3859  /* Number of points is 2^(n+1) + 1 on half a circle (n=subdivisions)
3860  * so we multiply by (angle / pi) to get the right amount of
3861  * points to insert. */
3862  int num_points = (int)(((1 << (subdivisions + 1)) - 1) * (angle / M_PI));
3863  if (num_points > 0) {
3864  float angle_incr = angle / (float)num_points;
3865 
3866  float vec_p[3];
3867  float vec_t[3];
3868  float tmp_angle;
3869  tPerimeterPoint *last_point;
3870  if (clockwise) {
3871  last_point = to;
3872  copy_v2_v2(vec_t, vec_to);
3873  }
3874  else {
3875  last_point = from;
3876  copy_v2_v2(vec_t, vec_from);
3877  }
3878 
3879  for (int i = 0; i < num_points - 1; i++) {
3880  tmp_angle = (i + 1) * angle_incr;
3881 
3882  rotate_v2_v2fl(vec_p, vec_t, tmp_angle);
3883  add_v2_v2(vec_p, center_pt);
3884  vec_p[2] = center_pt[2];
3885 
3886  tPerimeterPoint *new_point = new_perimeter_point(vec_p);
3887  if (clockwise) {
3888  BLI_insertlinkbefore(list, last_point, new_point);
3889  }
3890  else {
3891  BLI_insertlinkafter(list, last_point, new_point);
3892  }
3893 
3894  last_point = new_point;
3895  }
3896 
3897  return num_points - 1;
3898  }
3899 
3900  return 0;
3901 }
3902 
3905  tPerimeterPoint *to,
3906  int subdivisions)
3907 {
3908  int num_points = (1 << (subdivisions + 1)) + 1;
3909  float center_pt[3];
3910  interp_v3_v3v3(center_pt, &from->x, &to->x, 0.5f);
3911 
3912  float vec_center[2];
3913  sub_v2_v2v2(vec_center, &from->x, center_pt);
3914  if (is_zero_v2(vec_center)) {
3915  return 0;
3916  }
3917 
3918  float vec_p[3];
3919  float angle_incr = M_PI / ((float)num_points - 1);
3920 
3921  tPerimeterPoint *last_point = from;
3922  for (int i = 1; i < num_points; i++) {
3923  float angle = i * angle_incr;
3924 
3925  /* Rotate vector around point to get perimeter points. */
3926  rotate_v2_v2fl(vec_p, vec_center, angle);
3927  add_v2_v2(vec_p, center_pt);
3928  vec_p[2] = center_pt[2];
3929 
3930  tPerimeterPoint *new_point = new_perimeter_point(vec_p);
3931  BLI_insertlinkafter(list, last_point, new_point);
3932 
3933  last_point = new_point;
3934  }
3935 
3936  return num_points - 1;
3937 }
3938 
3939 static int generate_perimeter_cap(const float point[4],
3940  const float other_point[4],
3941  float radius,
3942  ListBase *list,
3943  int subdivisions,
3944  short cap_type)
3945 {
3946  float cap_vec[2];
3947  sub_v2_v2v2(cap_vec, other_point, point);
3948  normalize_v2(cap_vec);
3949 
3950  float cap_nvec[2];
3951  if (is_zero_v2(cap_vec)) {
3952  cap_nvec[0] = 0;
3953  cap_nvec[1] = radius;
3954  }
3955  else {
3956  cap_nvec[0] = -cap_vec[1];
3957  cap_nvec[1] = cap_vec[0];
3958  mul_v2_fl(cap_nvec, radius);
3959  }
3960  float cap_nvec_inv[2];
3961  negate_v2_v2(cap_nvec_inv, cap_nvec);
3962 
3963  float vec_perimeter[3];
3964  copy_v3_v3(vec_perimeter, point);
3965  add_v2_v2(vec_perimeter, cap_nvec);
3966 
3967  float vec_perimeter_inv[3];
3968  copy_v3_v3(vec_perimeter_inv, point);
3969  add_v2_v2(vec_perimeter_inv, cap_nvec_inv);
3970 
3971  tPerimeterPoint *p_pt = new_perimeter_point(vec_perimeter);
3972  tPerimeterPoint *p_pt_inv = new_perimeter_point(vec_perimeter_inv);
3973 
3974  BLI_addtail(list, p_pt);
3975  BLI_addtail(list, p_pt_inv);
3976 
3977  int num_points = 0;
3978  if (cap_type == GP_STROKE_CAP_ROUND) {
3979  num_points += generate_semi_circle_from_point_to_point(list, p_pt, p_pt_inv, subdivisions);
3980  }
3981 
3982  return num_points + 2;
3983 }
3984 
3991  const bGPDlayer *gpl,
3992  const bGPDstroke *gps,
3993  int subdivisions,
3994  int *r_num_perimeter_points)
3995 {
3996  /* sanity check */
3997  if (gps->totpoints < 1) {
3998  return nullptr;
3999  }
4000 
4001  float defaultpixsize = 1000.0f / gpd->pixfactor;
4002  float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
4003 
4004  ListBase *perimeter_right_side = MEM_cnew<ListBase>(__func__);
4005  ListBase *perimeter_left_side = MEM_cnew<ListBase>(__func__);
4006  int num_perimeter_points = 0;
4007 
4008  bGPDspoint *first = &gps->points[0];
4009  bGPDspoint *last = &gps->points[gps->totpoints - 1];
4010 
4011  float first_radius = stroke_radius * first->pressure;
4012  float last_radius = stroke_radius * last->pressure;
4013 
4014  bGPDspoint *first_next;
4015  bGPDspoint *last_prev;
4016  if (gps->totpoints > 1) {
4017  first_next = &gps->points[1];
4018  last_prev = &gps->points[gps->totpoints - 2];
4019  }
4020  else {
4021  first_next = first;
4022  last_prev = last;
4023  }
4024 
4025  float first_pt[3];
4026  float last_pt[3];
4027  float first_next_pt[3];
4028  float last_prev_pt[3];
4029  copy_v3_v3(first_pt, &first->x);
4030  copy_v3_v3(last_pt, &last->x);
4031  copy_v3_v3(first_next_pt, &first_next->x);
4032  copy_v3_v3(last_prev_pt, &last_prev->x);
4033 
4034  /* Edge-case if single point. */
4035  if (gps->totpoints == 1) {
4036  first_next_pt[0] += 1.0f;
4037  last_prev_pt[0] -= 1.0f;
4038  }
4039 
4040  /* Generate points for start cap. */
4041  num_perimeter_points += generate_perimeter_cap(
4042  first_pt, first_next_pt, first_radius, perimeter_right_side, subdivisions, gps->caps[0]);
4043 
4044  /* Generate perimeter points. */
4045  float curr_pt[3], next_pt[3], prev_pt[3];
4046  float vec_next[2], vec_prev[2];
4047  float nvec_next[2], nvec_prev[2];
4048  float nvec_next_pt[3], nvec_prev_pt[3];
4049  float vec_tangent[2];
4050 
4051  float vec_miter_left[2], vec_miter_right[2];
4052  float miter_left_pt[3], miter_right_pt[3];
4053 
4054  for (int i = 1; i < gps->totpoints - 1; i++) {
4055  bGPDspoint *curr = &gps->points[i];
4056  bGPDspoint *prev = &gps->points[i - 1];
4057  bGPDspoint *next = &gps->points[i + 1];
4058  float radius = stroke_radius * curr->pressure;
4059 
4060  copy_v3_v3(curr_pt, &curr->x);
4061  copy_v3_v3(next_pt, &next->x);
4062  copy_v3_v3(prev_pt, &prev->x);
4063 
4064  sub_v2_v2v2(vec_prev, curr_pt, prev_pt);
4065  sub_v2_v2v2(vec_next, next_pt, curr_pt);
4066  float prev_length = len_v2(vec_prev);
4067  float next_length = len_v2(vec_next);
4068 
4069  if (normalize_v2(vec_prev) == 0.0f) {
4070  vec_prev[0] = 1.0f;
4071  vec_prev[1] = 0.0f;
4072  }
4073  if (normalize_v2(vec_next) == 0.0f) {
4074  vec_next[0] = 1.0f;
4075  vec_next[1] = 0.0f;
4076  }
4077 
4078  nvec_prev[0] = -vec_prev[1];
4079  nvec_prev[1] = vec_prev[0];
4080 
4081  nvec_next[0] = -vec_next[1];
4082  nvec_next[1] = vec_next[0];
4083 
4084  add_v2_v2v2(vec_tangent, vec_prev, vec_next);
4085  if (normalize_v2(vec_tangent) == 0.0f) {
4086  copy_v2_v2(vec_tangent, nvec_prev);
4087  }
4088 
4089  vec_miter_left[0] = -vec_tangent[1];
4090  vec_miter_left[1] = vec_tangent[0];
4091 
4092  /* calculate miter length */
4093  float an1 = dot_v2v2(vec_miter_left, nvec_prev);
4094  if (an1 == 0.0f) {
4095  an1 = 1.0f;
4096  }
4097  float miter_length = radius / an1;
4098  if (miter_length <= 0.0f) {
4099  miter_length = 0.01f;
4100  }
4101 
4102  normalize_v2_length(vec_miter_left, miter_length);
4103 
4104  copy_v2_v2(vec_miter_right, vec_miter_left);
4105  negate_v2(vec_miter_right);
4106 
4107  float angle = dot_v2v2(vec_next, nvec_prev);
4108  /* Add two points if angle is close to being straight. */
4109  if (fabsf(angle) < 0.0001f) {
4110  normalize_v2_length(nvec_prev, radius);
4111  normalize_v2_length(nvec_next, radius);
4112 
4113  copy_v3_v3(nvec_prev_pt, curr_pt);
4114  add_v2_v2(nvec_prev_pt, nvec_prev);
4115 
4116  copy_v3_v3(nvec_next_pt, curr_pt);
4117  negate_v2(nvec_next);
4118  add_v2_v2(nvec_next_pt, nvec_next);
4119 
4120  tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
4121  tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
4122 
4123  BLI_addtail(perimeter_left_side, normal_prev);
4124  BLI_addtail(perimeter_right_side, normal_next);
4125  num_perimeter_points += 2;
4126  }
4127  else {
4128  /* bend to the left */
4129  if (angle < 0.0f) {
4130  normalize_v2_length(nvec_prev, radius);
4131  normalize_v2_length(nvec_next, radius);
4132 
4133  copy_v3_v3(nvec_prev_pt, curr_pt);
4134  add_v2_v2(nvec_prev_pt, nvec_prev);
4135 
4136  copy_v3_v3(nvec_next_pt, curr_pt);
4137  add_v2_v2(nvec_next_pt, nvec_next);
4138 
4139  tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
4140  tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
4141 
4142  BLI_addtail(perimeter_left_side, normal_prev);
4143  BLI_addtail(perimeter_left_side, normal_next);
4144  num_perimeter_points += 2;
4145 
4146  num_perimeter_points += generate_arc_from_point_to_point(
4147  perimeter_left_side, normal_prev, normal_next, curr_pt, subdivisions, true);
4148 
4149  if (miter_length < prev_length && miter_length < next_length) {
4150  copy_v3_v3(miter_right_pt, curr_pt);
4151  add_v2_v2(miter_right_pt, vec_miter_right);
4152  }
4153  else {
4154  copy_v3_v3(miter_right_pt, curr_pt);
4155  negate_v2(nvec_next);
4156  add_v2_v2(miter_right_pt, nvec_next);
4157  }
4158 
4159  tPerimeterPoint *miter_right = new_perimeter_point(miter_right_pt);
4160  BLI_addtail(perimeter_right_side, miter_right);
4161  num_perimeter_points++;
4162  }
4163  /* bend to the right */
4164  else {
4165  normalize_v2_length(nvec_prev, -radius);
4166  normalize_v2_length(nvec_next, -radius);
4167 
4168  copy_v3_v3(nvec_prev_pt, curr_pt);
4169  add_v2_v2(nvec_prev_pt, nvec_prev);
4170 
4171  copy_v3_v3(nvec_next_pt, curr_pt);
4172  add_v2_v2(nvec_next_pt, nvec_next);
4173 
4174  tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
4175  tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
4176 
4177  BLI_addtail(perimeter_right_side, normal_prev);
4178  BLI_addtail(perimeter_right_side, normal_next);
4179  num_perimeter_points += 2;
4180 
4181  num_perimeter_points += generate_arc_from_point_to_point(
4182  perimeter_right_side, normal_prev, normal_next, curr_pt, subdivisions, false);
4183 
4184  if (miter_length < prev_length && miter_length < next_length) {
4185  copy_v3_v3(miter_left_pt, curr_pt);
4186  add_v2_v2(miter_left_pt, vec_miter_left);
4187  }
4188  else {
4189  copy_v3_v3(miter_left_pt, curr_pt);
4190  negate_v2(nvec_prev);
4191  add_v2_v2(miter_left_pt, nvec_prev);
4192  }
4193 
4194  tPerimeterPoint *miter_left = new_perimeter_point(miter_left_pt);
4195  BLI_addtail(perimeter_left_side, miter_left);
4196  num_perimeter_points++;
4197  }
4198  }
4199  }
4200 
4201  /* generate points for end cap */
4202  num_perimeter_points += generate_perimeter_cap(
4203  last_pt, last_prev_pt, last_radius, perimeter_right_side, subdivisions, gps->caps[1]);
4204 
4205  /* merge both sides to one list */
4206  BLI_listbase_reverse(perimeter_right_side);
4207  BLI_movelisttolist(perimeter_left_side,
4208  perimeter_right_side); // perimeter_left_side contains entire list
4209  ListBase *perimeter_list = perimeter_left_side;
4210 
4211  /* close by creating a point close to the first (make a small gap) */
4212  float close_pt[3];
4213  tPerimeterPoint *close_first = (tPerimeterPoint *)perimeter_list->first;
4214  tPerimeterPoint *close_last = (tPerimeterPoint *)perimeter_list->last;
4215  interp_v3_v3v3(close_pt, &close_last->x, &close_first->x, 0.99f);
4216 
4217  if (compare_v3v3(close_pt, &close_first->x, FLT_EPSILON) == false) {
4218  tPerimeterPoint *close_p_pt = new_perimeter_point(close_pt);
4219  BLI_addtail(perimeter_list, close_p_pt);
4220  num_perimeter_points++;
4221  }
4222 
4223  /* free temp data */
4224  BLI_freelistN(perimeter_right_side);
4225  MEM_freeN(perimeter_right_side);
4226 
4227  *r_num_perimeter_points = num_perimeter_points;
4228  return perimeter_list;
4229 }
4230 
4232  bGPdata *gpd,
4233  const bGPDlayer *gpl,
4234  bGPDstroke *gps,
4235  const int subdivisions,
4236  const float diff_mat[4][4])
4237 {
4238  if (gps->totpoints == 0) {
4239  return nullptr;
4240  }
4241  /* Duplicate only points and fill data. Weight and Curve are not needed. */
4242  bGPDstroke *gps_temp = (bGPDstroke *)MEM_dupallocN(gps);
4243  gps_temp->prev = gps_temp->next = nullptr;
4244  gps_temp->triangles = (bGPDtriangle *)MEM_dupallocN(gps->triangles);
4245  gps_temp->points = (bGPDspoint *)MEM_dupallocN(gps->points);
4246  gps_temp->dvert = nullptr;
4247  gps_temp->editcurve = nullptr;
4248 
4249  const bool cyclic = ((gps_temp->flag & GP_STROKE_CYCLIC) != 0);
4250 
4251  /* If Cyclic, add a new point. */
4252  if (cyclic && (gps_temp->totpoints > 1)) {
4253  gps_temp->totpoints++;
4254  gps_temp->points = (bGPDspoint *)MEM_recallocN(
4255  gps_temp->points, sizeof(*gps_temp->points) * gps_temp->totpoints);
4256  bGPDspoint *pt_src = &gps_temp->points[0];
4257  bGPDspoint *pt_dst = &gps_temp->points[gps_temp->totpoints - 1];
4258  copy_v3_v3(&pt_dst->x, &pt_src->x);
4259  pt_dst->pressure = pt_src->pressure;
4260  pt_dst->strength = pt_src->strength;
4261  pt_dst->uv_fac = 1.0f;
4262  pt_dst->uv_rot = 0;
4263  }
4264 
4265  BKE_gpencil_stroke_to_view_space(rv3d, gps_temp, diff_mat);
4266  int num_perimeter_points = 0;
4267  ListBase *perimeter_points = gpencil_stroke_perimeter_ex(
4268  gpd, gpl, gps_temp, subdivisions, &num_perimeter_points);
4269 
4270  if (num_perimeter_points == 0) {
4271  return nullptr;
4272  }
4273 
4274  /* Create new stroke. */
4275  bGPDstroke *perimeter_stroke = BKE_gpencil_stroke_new(gps_temp->mat_nr, num_perimeter_points, 1);
4276 
4277  int i = 0;
4278  LISTBASE_FOREACH_INDEX (tPerimeterPoint *, curr, perimeter_points, i) {
4279  bGPDspoint *pt = &perimeter_stroke->points[i];
4280 
4281  copy_v3_v3(&pt->x, &curr->x);
4282  pt->pressure = 0.0f;
4283  pt->strength = 1.0f;
4284 
4285  pt->flag |= GP_SPOINT_SELECT;
4286  }
4287 
4288  BKE_gpencil_stroke_from_view_space(rv3d, perimeter_stroke, diff_mat);
4289 
4290  /* Free temp data. */
4291  BLI_freelistN(perimeter_points);
4292  MEM_freeN(perimeter_points);
4293 
4294  /* Triangles cache needs to be recalculated. */
4295  BKE_gpencil_stroke_geometry_update(gpd, perimeter_stroke);
4296 
4297  perimeter_stroke->flag |= GP_STROKE_SELECT | GP_STROKE_CYCLIC;
4298 
4299  BKE_gpencil_free_stroke(gps_temp);
4300 
4301  return perimeter_stroke;
4302 }
4303 
4305 {
4306 
4307  if (gps->totpoints == 1) {
4308  return gps->points[0].pressure;
4309  }
4310 
4311  float tot = 0.0f;
4312  for (int i = 0; i < gps->totpoints; i++) {
4313  const bGPDspoint *pt = &gps->points[i];
4314  tot += pt->pressure;
4315  }
4316 
4317  return tot / (float)gps->totpoints;
4318 }
4319 
4321 {
4322  if (gps->totpoints == 1) {
4323  return true;
4324  }
4325 
4326  const float first_pressure = gps->points[0].pressure;
4327  for (int i = 0; i < gps->totpoints; i++) {
4328  const bGPDspoint *pt = &gps->points[i];
4329  if (pt->pressure != first_pressure) {
4330  return false;
4331  }
4332  }
4333 
4334  return true;
4335 }
4336 
typedef float(TangentPoint)[2]
support for deformation groups and hooks.
void BKE_defgroup_copy_list(struct ListBase *outbase, const struct ListBase *inbase)
struct MDeformWeight * BKE_defvert_ensure_index(struct MDeformVert *dv, int defgroup)
Definition: deform.c:748
float BKE_defvert_find_weight(const struct MDeformVert *dvert, int defgroup)
Definition: deform.c:704
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1155
struct bGPDframe * BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:514
struct Material * BKE_gpencil_object_material_new(struct Main *bmain, struct Object *ob, const char *name, int *r_index)
Definition: gpencil.c:1734
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
Definition: gpencil.c:855
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps)
Definition: gpencil.c:361
void BKE_gpencil_free_point_weights(struct MDeformVert *dvert)
Definition: gpencil.c:353
struct bGPDcurve * BKE_gpencil_stroke_editcurve_new(int tot_curve_points)
Definition: gpencil.c:821
struct bGPDstroke * BKE_gpencil_stroke_add_existing_style(struct bGPDframe *gpf, struct bGPDstroke *existing, int mat_idx, int totpoints, short thickness)
Definition: gpencil.c:810
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive, bool add_to_header)
Definition: gpencil.c:621
struct bGPDstroke * BKE_gpencil_stroke_add(struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, bool insert_at_head)
Definition: gpencil.c:792
struct bGPDstroke * BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
Definition: gpencil.c:756
struct bGPDlayer * BKE_gpencil_layer_named_get(struct bGPdata *gpd, const char *name)
Definition: gpencil.c:1419
struct bGPDframe * BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:1216
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:391
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
#define GPENCIL_STRENGTH_MIN
Definition: BKE_gpencil.h:324
int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char *name_prefix)
Definition: gpencil.c:2855
void BKE_gpencil_frame_selected_hash(struct bGPdata *gpd, struct GHash *r_list)
Definition: gpencil.c:2869
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:341
void BKE_gpencil_stroke_update_geometry_from_editcurve(struct bGPDstroke *gps, uint resolution, 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:687
const float(* BKE_mesh_vertex_normals_ensure(const struct Mesh *mesh))[3]
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.cc:3645
struct Mesh * BKE_object_get_evaluated_mesh(const struct Object *object)
Generic array manipulation API.
#define BLI_array_reverse(arr, arr_len)
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:298
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:822
GHash * BLI_ghash_int_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:321
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:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition: BLI_hash.h:94
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition: BLI_hash.h:53
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition: BLI_heap.c:202
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition: BLI_heap.c:301
void(* HeapFreeFP)(void *ptr)
Definition: BLI_heap.h:21
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition: BLI_heap.c:245
Heap * BLI_heap_new(void) ATTR_WARN_UNUSED_RESULT
Definition: BLI_heap.c:197
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:221
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:239
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
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:466
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:340
void void void void void void BLI_listbase_reverse(struct ListBase *lb) ATTR_NONNULL(1)
Definition: listbase.c:797
#define M_SQRT3
Definition: BLI_math_base.h:35
MINLINE float interpf(float a, float b, float t)
#define M_PI
Definition: BLI_math_base.h:20
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:2935
float 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:379
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:3176
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
float mat4_to_scale(const float M[4][4])
Definition: math_matrix.c:2185
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
Definition: math_matrix.c:897
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void mul_qt_v3(const float q[4], float r[3])
Definition: math_rotation.c:59
void quat_to_mat3(float mat[3][3], const float q[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE bool compare_v3v3(const float a[3], const float b[3], float limit) ATTR_WARN_UNUSED_RESULT
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:385
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:867
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float normalize_v3(float r[3])
MINLINE void sub_v3_v3(float r[3], const float a[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 mul_v2_fl(float r[2], float f)
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition: math_vector.c:14
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])
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], float t)
Definition: math_vector.c:38
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
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])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
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 cross_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3_length(float r[3], float unit_scale)
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
void rotate_v2_v2fl(float r[2], const float p[2], float angle)
Definition: math_vector.c:765
MINLINE bool is_zero_v2(const float a[2]) 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 float normalize_v2_length(float r[2], float unit_scale)
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:361
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float normalize_v2(float r[2])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:445
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void BLI_polyfill_calc(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3])
Definition: polyfill_2d.c:875
void BLI_str_replace_char(char *str, char src, char dst) ATTR_NONNULL()
Definition: string.c:503
#define SNPRINTF(dst, format,...)
Definition: BLI_string.h:485
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(1
unsigned int uint
Definition: BLI_sys_types.h:67
#define CLAMP_MAX(a, c)
#define INIT_MINMAX(min, max)
#define ARRAY_SET_ITEMS(...)
#define CLAMPIS(a, b, c)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define MAX2(a, b)
#define UNLIKELY(x)
#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:10
typedef double(DMatrix)[4][4]
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
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:834
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
@ 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_SPOINT_TEMP_TAG
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
@ ME_SEAM
@ OB_GPENCIL
@ BOUNDBOX_DIRTY
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 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 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 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
float float3[3]
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: avxb.h:154
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
__forceinline BoundBox intersect(const BoundBox &a, const BoundBox &b)
Definition: boundbox.h:179
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
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)
#define powf(x, y)
Definition: cuda/compat.h:103
StackEntry * from
Scene scene
const Depsgraph * depsgraph
int len
Definition: draw_manager.c:108
static bool is_cyclic(const Nurb *nu)
#define rot(x, k)
#define str(s)
void BKE_gpencil_stroke_flip(bGPDstroke *gps)
static tPerimeterPoint * new_perimeter_point(const float pt[3])
static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold)
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)
void BKE_gpencil_stroke_merge_distance(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const float threshold, const bool use_unselected)
#define END
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)
#define START
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_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, const bool use_vgroups)
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, const float sharp_threshold)
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)
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.cc:57
static int generate_arc_from_point_to_point(ListBase *list, tPerimeterPoint *from, tPerimeterPoint *to, float center_pt[3], int subdivisions, bool clockwise)
void BKE_gpencil_stroke_smooth(bGPDstroke *gps, const float influence, const int iterations, const bool smooth_position, const bool smooth_strength, const bool smooth_thickness, const bool smooth_uv, const bool keep_shape, const float *weights)
static void boundbox_gpencil(Object *ob)
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.cc:85
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_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
static MDeformVert * stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
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])
bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
static int gpencil_material_find_index_by_name(Object *ob, const char *name)
bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float overshoot_fac, const short mode, const bool follow_curvature, const int extra_point_count, const float segment_influence, const float max_angle, const bool invert_curvature)
bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
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)
static int stroke_march_next_point_no_interp(const bGPDstroke *gps, const int index_next_pt, const float *current, const float dist, const float sharp_threshold, float *result)
void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
#define BOTH
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)
void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
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)
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
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_join(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps, const bool fit_thickness, const bool smooth)
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)
void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3])
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 *uv_fac, float *uv_fill, float *uv_rot, float *ratio_result, int *index_from, int *index_to)
static tSampleEdge * new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to)
int BKE_gpencil_stroke_point_count(const bGPdata *gpd)
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps, const int start_index, const int end_index, bool use_3d)
bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int point_index, float influence, int iterations, const bool smooth_caps, const bool keep_shape, bGPDstroke *r_gps)
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])
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, const bool use_vgroups)
void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
bool BKE_gpencil_stroke_close(bGPDstroke *gps)
BoundBox * BKE_gpencil_boundbox_get(Object *ob)
void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates *elem_data)
static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, const int count_before, const int count_after)
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)
static void stroke_defvert_create_nr_list(MDeformVert *dv_list, int count, ListBase *result, int *totweight)
IconTextureDrawCall normal
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float threshold
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
ccl_device_inline float2 fabs(const float2 &a)
Definition: math_float2.h:222
ccl_device_inline float3 exp(float3 v)
Definition: math_float3.h:392
ccl_device_inline float3 ceil(const float3 &a)
Definition: math_float3.h:363
static ulong * next
#define atan2f(x, y)
Definition: metal/compat.h:227
#define fmaxf(x, y)
Definition: metal/compat.h:228
#define fminf(x, y)
Definition: metal/compat.h:229
#define fmodf(x, y)
Definition: metal/compat.h:230
#define fabsf(x)
Definition: metal/compat.h:219
bool isfinite(uchar)
Definition: scene/image.cpp:31
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
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
vec_base< T, 3 > cross(const vec_base< T, 3 > &a, const vec_base< T, 3 > &b)
T floor(const T &a)
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
static MEdge new_edge(const int v1, const int v2)
bool is_adaptive(CpuPatchTable *patch_table)
Definition: eval_output.cc:24
static double epsilon
vec_base< float, 3 > float3
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
smooth(Type::FLOAT, "mask_weight")
#define min(a, b)
Definition: sort.c:35
unsigned int uint32_t
Definition: stdint.h:80
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:43
char name[66]
Definition: DNA_ID.h:378
void * data
Definition: DNA_listBase.h:26
struct LinkData * next
Definition: DNA_listBase.h:25
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
struct MDeformWeight * dw
unsigned int def_nr
unsigned int v1
unsigned int v2
unsigned int v
short mat_nr
float co[3]
Definition: BKE_main.h:121
struct MaterialGPencilStyle * gp_style
struct MEdge * medge
struct MVert * mvert
struct MDeformVert * dvert
int totedge
ListBase vertex_group_names
struct MLoop * mloop
int totpoly
int vertex_group_active_index
struct MPoly * mpoly
struct BoundBox * bb
Object_Runtime runtime
void * data
float viewmat[4][4]
float viewinv[4][4]
struct RenderData r
bGPDcurve_point * curve_points
ListBase strokes
struct bGPDspoint * pt_orig
float uv_fill[2]
bGPDspoint_Runtime runtime
float vert_color[4]
struct bGPDstroke * prev
bGPDspoint * points
float uv_translation[2]
bGPDtriangle * triangles
float boundbox_max[3]
float boundbox_min[3]
struct bGPDcurve * editcurve
struct MDeformVert * dvert
struct bGPDstroke * next
unsigned int verts[3]
ListBase vertex_group_names
int curve_edit_resolution
ListBase layers
int vertex_group_active_index
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