Blender  V3.3
bmesh_bevel.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
9 #include "MEM_guardedalloc.h"
10 
11 #include "DNA_curveprofile_types.h"
12 #include "DNA_meshdata_types.h"
13 #include "DNA_modifier_types.h"
14 #include "DNA_scene_types.h"
15 
16 #include "BLI_alloca.h"
17 #include "BLI_array.h"
18 #include "BLI_math.h"
19 #include "BLI_memarena.h"
20 #include "BLI_utildefines.h"
21 
22 #include "BKE_curveprofile.h"
23 #include "BKE_customdata.h"
24 #include "BKE_deform.h"
25 #include "BKE_mesh.h"
26 
27 #include "eigen_capi.h"
28 
29 #include "bmesh.h"
30 #include "bmesh_bevel.h" /* own include */
31 
32 #include "./intern/bmesh_private.h"
33 
34 // #define BEVEL_DEBUG_TIME
35 #ifdef BEVEL_DEBUG_TIME
36 # include "PIL_time.h"
37 #endif
38 
39 #define BEVEL_EPSILON_D 1e-6
40 #define BEVEL_EPSILON 1e-6f
41 #define BEVEL_EPSILON_SQ 1e-12f
42 #define BEVEL_EPSILON_BIG 1e-4f
43 #define BEVEL_EPSILON_BIG_SQ 1e-8f
44 #define BEVEL_EPSILON_ANG DEG2RADF(2.0f)
45 #define BEVEL_SMALL_ANG DEG2RADF(10.0f)
47 #define BEVEL_SMALL_ANG_DOT (1.0f - cosf(BEVEL_SMALL_ANG))
49 #define BEVEL_EPSILON_ANG_DOT (1.0f - cosf(BEVEL_EPSILON_ANG))
50 #define BEVEL_MAX_ADJUST_PCT 10.0f
51 #define BEVEL_MAX_AUTO_ADJUST_PCT 300.0f
52 #define BEVEL_MATCH_SPEC_WEIGHT 0.2
53 
54 //#define DEBUG_CUSTOM_PROFILE_CUTOFF
55 /* Happens far too often, uncomment for development. */
56 // #define BEVEL_ASSERT_PROJECT
57 
58 /* for testing */
59 // #pragma GCC diagnostic error "-Wpadded"
60 
61 /* Constructed vertex, sometimes later instantiated as BMVert. */
62 typedef struct NewVert {
64  float co[3];
65  char _pad[4];
67 
68 struct BoundVert;
69 
70 /* Data for one end of an edge involved in a bevel. */
71 typedef struct EdgeHalf {
73  struct EdgeHalf *next, *prev;
81  struct BoundVert *leftv;
83  struct BoundVert *rightv;
87  int seg;
89  float offset_l;
91  float offset_r;
97  bool is_bev;
99  bool is_rev;
101  bool is_seam;
104  char _pad[4];
106 
121 typedef struct Profile {
123  float super_r;
125  float height;
127  float start[3];
129  float middle[3];
131  float end[3];
133  float plane_no[3];
135  float plane_co[3];
137  float proj_dir[3];
139  float *prof_co;
141  float *prof_co_2;
145 #define PRO_SQUARE_R 1e4f
146 #define PRO_CIRCLE_R 2.0f
147 #define PRO_LINE_R 1.0f
148 #define PRO_SQUARE_IN_R 0.0f
149 
155 typedef struct ProfileSpacing {
157  double *xvals;
159  double *yvals;
161  double *xvals_2;
163  double *yvals_2;
165  int seg_2;
167  float fullness;
169 
183 typedef struct MathLayerInfo {
189 
194 typedef struct BoundVert {
196  struct BoundVert *next, *prev;
206  int index;
208  float sinratio;
214  bool any_seam;
216  bool visited;
223  char _pad[3];
225  int seam_len;
229 
231 typedef struct VMesh {
237  int count;
239  int seg;
241  enum {
242  M_NONE, /* No polygon mesh needed. */
243  M_POLY, /* A simple polygon. */
244  M_ADJ, /* "Adjacent edges" mesh pattern. */
245  M_TRI_FAN, /* A simple polygon - fan filled. */
246  M_CUTOFF, /* A triangulated face at the end of each profile. */
248 
249  int _pad;
251 
252 /* Data for a vertex involved in a bevel. */
253 typedef struct BevVert {
259  int selcount;
263  float offset;
265  bool any_seam;
267  bool visited;
269  char _pad[6];
276 
281 typedef enum {
292 } FKind;
293 
295 typedef enum AngleKind {
303 
305 typedef struct BevelParams {
321  float offset;
329  int seg;
331  float profile;
333  float pro_super_r;
343  bool mark_seam;
348  char _pad[1];
352  const struct MDeformVert *dvert;
356  int mat_nr;
366  float spread;
368  float smoothresh;
370 
371 // #pragma GCC diagnostic ignored "-Wpadded"
372 
373 /* Only for debugging, this file shouldn't be in blender repository. */
374 // #include "bevdebug.c"
375 
376 /* Use the unused _BM_ELEM_TAG_ALT flag to flag the 'long' loops (parallel to beveled edge)
377  * of edge-polygons. */
378 #define BM_ELEM_LONG_TAG (1 << 6)
379 
380 /* These flag values will get set on geom we want to return in 'out' slots for edges and verts. */
381 #define EDGE_OUT 4
382 #define VERT_OUT 8
383 
384 /* If we're called from the modifier, tool flags aren't available,
385  * but don't need output geometry. */
386 static void flag_out_edge(BMesh *bm, BMEdge *bme)
387 {
388  if (bm->use_toolflags) {
390  }
391 }
392 
393 static void flag_out_vert(BMesh *bm, BMVert *bmv)
394 {
395  if (bm->use_toolflags) {
397  }
398 }
399 
401 {
402  if (bm->use_toolflags) {
404  }
405 }
406 
407 static void record_face_kind(BevelParams *bp, BMFace *f, FKind fkind)
408 {
409  if (bp->face_hash) {
411  }
412 }
413 
415 {
416  void *val = BLI_ghash_lookup(bp->face_hash, f);
417  return val ? (FKind)POINTER_AS_INT(val) : F_ORIG;
418 }
419 
420 /* Are d1 and d2 parallel or nearly so? */
421 static bool nearly_parallel(const float d1[3], const float d2[3])
422 {
423  float ang = angle_v3v3(d1, d2);
424 
425  return (fabsf(ang) < BEVEL_EPSILON_ANG) || (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG);
426 }
427 
431 static bool nearly_parallel_normalized(const float d1[3], const float d2[3])
432 {
433  BLI_ASSERT_UNIT_V3(d1);
434  BLI_ASSERT_UNIT_V3(d2);
435 
436  const float direction_dot = dot_v3v3(d1, d2);
437  return compare_ff(fabsf(direction_dot), 1.0f, BEVEL_EPSILON_ANG_DOT);
438 }
439 
440 /* Make a new BoundVert of the given kind, inserting it at the end of the circular linked
441  * list with entry point bv->boundstart, and return it. */
442 static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3])
443 {
445 
446  copy_v3_v3(ans->nv.co, co);
447  if (!vm->boundstart) {
448  ans->index = 0;
449  vm->boundstart = ans;
450  ans->next = ans->prev = ans;
451  }
452  else {
453  BoundVert *tail = vm->boundstart->prev;
454  ans->index = tail->index + 1;
455  ans->prev = tail;
456  ans->next = vm->boundstart;
457  tail->next = ans;
458  vm->boundstart->prev = ans;
459  }
460  ans->profile.super_r = PRO_LINE_R;
461  ans->adjchain = NULL;
462  ans->sinratio = 1.0f;
463  ans->visited = false;
464  ans->any_seam = false;
465  ans->is_arc_start = false;
466  ans->is_patch_start = false;
467  ans->is_profile_start = false;
468  vm->count++;
469  return ans;
470 }
471 
472 BLI_INLINE void adjust_bound_vert(BoundVert *bv, const float co[3])
473 {
474  copy_v3_v3(bv->nv.co, co);
475 }
476 
477 /* Mesh verts are indexed (i, j, k) where
478  * i = boundvert index (0 <= i < nv)
479  * j = ring index (0 <= j <= ns2)
480  * k = segment index (0 <= k <= ns)
481  * Not all of these are used, and some will share BMVerts. */
482 static NewVert *mesh_vert(VMesh *vm, int i, int j, int k)
483 {
484  int nj = (vm->seg / 2) + 1;
485  int nk = vm->seg + 1;
486 
487  return &vm->mesh[i * nk * nj + j * nk + k];
488 }
489 
490 static void create_mesh_bmvert(BMesh *bm, VMesh *vm, int i, int j, int k, BMVert *eg)
491 {
492  NewVert *nv = mesh_vert(vm, i, j, k);
493  nv->v = BM_vert_create(bm, nv->co, eg, BM_CREATE_NOP);
495  flag_out_vert(bm, nv->v);
496 }
497 
498 static void copy_mesh_vert(VMesh *vm, int ito, int jto, int kto, int ifrom, int jfrom, int kfrom)
499 {
500  NewVert *nvto = mesh_vert(vm, ito, jto, kto);
501  NewVert *nvfrom = mesh_vert(vm, ifrom, jfrom, kfrom);
502  nvto->v = nvfrom->v;
503  copy_v3_v3(nvto->co, nvfrom->co);
504 }
505 
506 /* Find the EdgeHalf in bv's array that has edge bme. */
508 {
509  for (int i = 0; i < bv->edgecount; i++) {
510  if (bv->edges[i].e == bme) {
511  return &bv->edges[i];
512  }
513  }
514  return NULL;
515 }
516 
517 /* Find the BevVert corresponding to BMVert bmv. */
519 {
520  return BLI_ghash_lookup(bp->vert_hash, bmv);
521 }
522 
529 {
530  BevVert *bvo = find_bevvert(bp, e->is_rev ? e->e->v1 : e->e->v2);
531  if (bvo) {
532  if (r_bvother) {
533  *r_bvother = bvo;
534  }
535  EdgeHalf *eother = find_edge_half(bvo, e->e);
536  BLI_assert(eother != NULL);
537  return eother;
538  }
539  if (r_bvother) {
540  *r_bvother = NULL;
541  }
542  return NULL;
543 }
544 
545 /* Return the next EdgeHalf after from_e that is beveled.
546  * If from_e is NULL, find the first beveled edge. */
547 static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e)
548 {
549  if (from_e == NULL) {
550  from_e = &bv->edges[bv->edgecount - 1];
551  }
552  EdgeHalf *e = from_e;
553  do {
554  if (e->is_bev) {
555  return e;
556  }
557  } while ((e = e->next) != from_e);
558  return NULL;
559 }
560 
561 /* Return the count of edges between e1 and e2 when going around bv CCW. */
563 {
564  int count = 0;
565  EdgeHalf *e = e1;
566 
567  do {
568  if (e == e2) {
569  break;
570  }
571  e = e->next;
572  count++;
573  } while (e != e1);
574  return count;
575 }
576 
577 /* Assume bme1 and bme2 both share some vert. Do they share a face?
578  * If they share a face then there is some loop around bme1 that is in a face
579  * where the next or previous edge in the face must be bme2. */
580 static bool edges_face_connected_at_vert(BMEdge *bme1, BMEdge *bme2)
581 {
582  BMIter iter;
583  BMLoop *l;
584  BM_ITER_ELEM (l, &iter, bme1, BM_LOOPS_OF_EDGE) {
585  if (l->prev->e == bme2 || l->next->e == bme2) {
586  return true;
587  }
588  }
589  return false;
590 }
591 
600 {
601  BMFace *frep;
602 
603  BMFace *frep2 = NULL;
604  if (v->ebev) {
605  frep = v->ebev->fprev;
606  if (v->efirst->fprev != frep) {
607  frep2 = v->efirst->fprev;
608  }
609  }
610  else if (v->efirst) {
611  frep = v->efirst->fprev;
612  if (frep) {
613  if (v->elast->fnext != frep) {
614  frep2 = v->elast->fnext;
615  }
616  else if (v->efirst->fnext != frep) {
617  frep2 = v->efirst->fnext;
618  }
619  else if (v->elast->fprev != frep) {
620  frep2 = v->efirst->fprev;
621  }
622  }
623  else if (v->efirst->fnext) {
624  frep = v->efirst->fnext;
625  if (v->elast->fnext != frep) {
626  frep2 = v->elast->fnext;
627  }
628  }
629  else if (v->elast->fprev) {
630  frep = v->elast->fprev;
631  }
632  }
633  else if (v->prev->elast) {
634  frep = v->prev->elast->fnext;
635  if (v->next->efirst) {
636  if (frep) {
637  frep2 = v->next->efirst->fprev;
638  }
639  else {
640  frep = v->next->efirst->fprev;
641  }
642  }
643  }
644  else {
645  frep = NULL;
646  }
647  if (r_fother) {
648  *r_fother = frep2;
649  }
650  return frep;
651 }
652 
664  BMVert **vert_arr,
665  const int totv,
666  BMFace **face_arr,
667  BMFace *facerep,
668  BMEdge **snap_edge_arr,
669  int mat_nr,
670  bool do_interp)
671 {
672  BMFace *f = BM_face_create_verts(bm, vert_arr, totv, facerep, BM_CREATE_NOP, true);
673 
674  if ((facerep || (face_arr && face_arr[0])) && f) {
675  BM_elem_attrs_copy(bm, bm, facerep ? facerep : face_arr[0], f);
676  if (do_interp) {
677  int i = 0;
678  BMIter iter;
679  BMLoop *l;
680  BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
681  BMFace *interp_f;
682  if (face_arr) {
683  /* Assume loops of created face are in same order as verts. */
684  BLI_assert(l->v == vert_arr[i]);
685  interp_f = face_arr[i];
686  }
687  else {
688  interp_f = facerep;
689  }
690  if (interp_f) {
691  BMEdge *bme = NULL;
692  if (snap_edge_arr) {
693  bme = snap_edge_arr[i];
694  }
695  float save_co[3];
696  if (bme) {
697  copy_v3_v3(save_co, l->v->co);
698  closest_to_line_segment_v3(l->v->co, save_co, bme->v1->co, bme->v2->co);
699  }
700  BM_loop_interp_from_face(bm, l, interp_f, true, true);
701  if (bme) {
702  copy_v3_v3(l->v->co, save_co);
703  }
704  }
705  i++;
706  }
707  }
708  }
709 
710  /* Not essential for bevels own internal logic,
711  * this is done so the operator can select newly created geometry. */
712  if (f) {
714  BMIter iter;
715  BMEdge *bme;
716  BM_ITER_ELEM (bme, &iter, f, BM_EDGES_OF_FACE) {
717  flag_out_edge(bm, bme);
718  }
719  }
720 
721  if (mat_nr >= 0) {
722  f->mat_nr = (short)mat_nr;
723  }
724  return f;
725 }
726 
727 /* Is Loop layer layer_index contiguous across shared vertex of l1 and l2? */
728 static bool contig_ldata_across_loops(BMesh *bm, BMLoop *l1, BMLoop *l2, int layer_index)
729 {
730  const int offset = bm->ldata.layers[layer_index].offset;
731  const int type = bm->ldata.layers[layer_index].type;
732 
733  return CustomData_data_equals(
734  type, (char *)l1->head.data + offset, (char *)l2->head.data + offset);
735 }
736 
737 /* Are all loop layers with have math (e.g., UVs)
738  * contiguous from face f1 to face f2 across edge e?
739  */
741 {
742  if (bm->ldata.totlayer == 0) {
743  return true;
744  }
745 
746  BMLoop *lef1, *lef2;
747  if (!BM_edge_loop_pair(e, &lef1, &lef2)) {
748  return false;
749  }
750  /* If faces are oriented consistently around e,
751  * should now have lef1 and lef2 being f1 and f2 in either order.
752  */
753  if (lef1->f == f2) {
754  SWAP(BMLoop *, lef1, lef2);
755  }
756  if (lef1->f != f1 || lef2->f != f2) {
757  return false;
758  }
759  BMVert *v1 = lef1->v;
760  BMVert *v2 = lef2->v;
761  if (v1 == v2) {
762  return false;
763  }
764  BLI_assert((v1 == e->v1 && v2 == e->v2) || (v1 == e->v2 && v2 == e->v1));
766  BMLoop *lv1f1 = lef1;
767  BMLoop *lv2f1 = lef1->next;
768  BMLoop *lv1f2 = lef2->next;
769  BMLoop *lv2f2 = lef2;
770  BLI_assert(lv1f1->v == v1 && lv1f1->f == f1 && lv2f1->v == v2 && lv2f1->f == f1 &&
771  lv1f2->v == v1 && lv1f2->f == f2 && lv2f2->v == v2 && lv2f2->f == f2);
772  for (int i = 0; i < bm->ldata.totlayer; i++) {
773  if (CustomData_layer_has_math(&bm->ldata, i)) {
774  if (!contig_ldata_across_loops(bm, lv1f1, lv1f2, i) ||
775  !contig_ldata_across_loops(bm, lv2f1, lv2f2, i)) {
776  return false;
777  }
778  }
779  }
780  return true;
781 }
782 
787 static void swap_face_components(int *face_component, int totface, int c1, int c2)
788 {
789  if (c1 == c2) {
790  return; /* Nothing to do. */
791  }
792  for (int f = 0; f < totface; f++) {
793  if (face_component[f] == c1) {
794  face_component[f] = c2;
795  }
796  else if (face_component[f] == c2) {
797  face_component[f] = c1;
798  }
799  }
800 }
801 
802 /*
803  * Set up the fields of bp->math_layer_info.
804  * We always set has_math_layers to the correct value.
805  * Only if there are UV layers and the number of segments is odd,
806  * we need to calculate connected face components in UV space.
807  */
809 {
810  int f;
811  bp->math_layer_info.has_math_layers = false;
813  for (int i = 0; i < bm->ldata.totlayer; i++) {
815  bp->math_layer_info.has_math_layers = true;
816  break;
817  }
818  }
819  if (!bp->math_layer_info.has_math_layers || (bp->seg % 2) == 0) {
820  return;
821  }
822 
825  int totface = bm->totface;
826  int *face_component = BLI_memarena_alloc(bp->mem_arena, sizeof(int) * totface);
827  bp->math_layer_info.face_component = face_component;
828 
829  /* Use an array as a stack. Stack size can't exceed total faces if keep track of what is in
830  * stack. */
831  BMFace **stack = MEM_malloc_arrayN(totface, sizeof(BMFace *), __func__);
832  bool *in_stack = MEM_malloc_arrayN(totface, sizeof(bool), __func__);
833 
834  /* Set all component ids by DFS from faces with unassigned components. */
835  for (f = 0; f < totface; f++) {
836  face_component[f] = -1;
837  in_stack[f] = false;
838  }
839  int current_component = -1;
840  for (f = 0; f < totface; f++) {
841  if (face_component[f] == -1 && !in_stack[f]) {
842  int stack_top = 0;
843  current_component++;
844  BLI_assert(stack_top < totface);
845  stack[stack_top] = BM_face_at_index(bm, f);
846  in_stack[f] = true;
847  while (stack_top >= 0) {
848  BMFace *bmf = stack[stack_top];
849  stack_top--;
850  int bmf_index = BM_elem_index_get(bmf);
851  in_stack[bmf_index] = false;
852  if (face_component[bmf_index] != -1) {
853  continue;
854  }
855  face_component[bmf_index] = current_component;
856  /* Neighbors are faces that share an edge with bmf and
857  * are where contig_ldata_across_edge(...) is true for the
858  * shared edge and two faces.
859  */
860  BMIter eiter;
861  BMEdge *bme;
862  BM_ITER_ELEM (bme, &eiter, bmf, BM_EDGES_OF_FACE) {
863  BMIter fiter;
864  BMFace *bmf_other;
865  BM_ITER_ELEM (bmf_other, &fiter, bme, BM_FACES_OF_EDGE) {
866  if (bmf_other != bmf) {
867  int bmf_other_index = BM_elem_index_get(bmf_other);
868  if (face_component[bmf_other_index] != -1 || in_stack[bmf_other_index]) {
869  continue;
870  }
871  if (contig_ldata_across_edge(bm, bme, bmf, bmf_other)) {
872  stack_top++;
873  BLI_assert(stack_top < totface);
874  stack[stack_top] = bmf_other;
875  in_stack[bmf_other_index] = true;
876  }
877  }
878  }
879  }
880  }
881  }
882  }
883  MEM_freeN(stack);
884  MEM_freeN(in_stack);
885  /* We can usually get more pleasing result if components 0 and 1
886  * are the topmost and bottom-most (in z-coordinate) components,
887  * so adjust component indices to make that so. */
888  if (current_component <= 0) {
889  return; /* Only one component, so no need to do this. */
890  }
891  BMFace *top_face = NULL;
892  float top_face_z = -1e30f;
893  int top_face_component = -1;
894  BMFace *bot_face = NULL;
895  float bot_face_z = 1e30f;
896  int bot_face_component = -1;
897  for (f = 0; f < totface; f++) {
898  float cent[3];
899  BMFace *bmf = BM_face_at_index(bm, f);
900  BM_face_calc_center_bounds(bmf, cent);
901  float fz = cent[2];
902  if (fz > top_face_z) {
903  top_face_z = fz;
904  top_face = bmf;
905  top_face_component = face_component[f];
906  }
907  if (fz < bot_face_z) {
908  bot_face_z = fz;
909  bot_face = bmf;
910  bot_face_component = face_component[f];
911  }
912  }
913  BLI_assert(top_face != NULL && bot_face != NULL);
914  UNUSED_VARS_NDEBUG(top_face, bot_face);
915  swap_face_components(face_component, totface, face_component[0], top_face_component);
916  if (bot_face_component != top_face_component) {
917  if (bot_face_component == 0) {
918  /* It was swapped with old top_face_component. */
919  bot_face_component = top_face_component;
920  }
921  swap_face_components(face_component, totface, face_component[1], bot_face_component);
922  }
923 }
924 
937 static BMFace *choose_rep_face(BevelParams *bp, BMFace **face, int nfaces)
938 {
939 #define VEC_VALUE_LEN 6
940  float(*value_vecs)[VEC_VALUE_LEN] = NULL;
941  int num_viable = 0;
942 
943  value_vecs = BLI_array_alloca(value_vecs, nfaces);
944  bool *still_viable = BLI_array_alloca(still_viable, nfaces);
945  for (int f = 0; f < nfaces; f++) {
946  BMFace *bmf = face[f];
947  if (bmf == NULL) {
948  still_viable[f] = false;
949  continue;
950  }
951  still_viable[f] = true;
952  num_viable++;
953  int bmf_index = BM_elem_index_get(bmf);
954  int value_index = 0;
955  /* First tie-breaker: lower math-layer connected component id. */
956  value_vecs[f][value_index++] = bp->math_layer_info.face_component ?
957  (float)bp->math_layer_info.face_component[bmf_index] :
958  0.0f;
959  /* Next tie-breaker: selected face beats unselected one. */
960  value_vecs[f][value_index++] = BM_elem_flag_test(bmf, BM_ELEM_SELECT) ? 0.0f : 1.0f;
961  /* Next tie-breaker: lower material index. */
962  value_vecs[f][value_index++] = bmf->mat_nr >= 0 ? (float)bmf->mat_nr : 0.0f;
963  /* Next three tie-breakers: z, x, y components of face center. */
964  float cent[3];
965  BM_face_calc_center_bounds(bmf, cent);
966  value_vecs[f][value_index++] = cent[2];
967  value_vecs[f][value_index++] = cent[0];
968  value_vecs[f][value_index++] = cent[1];
969  BLI_assert(value_index == VEC_VALUE_LEN);
970  }
971 
972  /* Look for a face that has a unique minimum value for in a value_index,
973  * trying each value_index in turn until find a unique minimum.
974  */
975  int best_f = -1;
976  for (int value_index = 0; num_viable > 1 && value_index < VEC_VALUE_LEN; value_index++) {
977  for (int f = 0; f < nfaces; f++) {
978  if (!still_viable[f] || f == best_f) {
979  continue;
980  }
981  if (best_f == -1) {
982  best_f = f;
983  continue;
984  }
985  if (value_vecs[f][value_index] < value_vecs[best_f][value_index]) {
986  best_f = f;
987  /* Previous f's are now not viable any more. */
988  for (int i = f - 1; i >= 0; i--) {
989  if (still_viable[i]) {
990  still_viable[i] = false;
991  num_viable--;
992  }
993  }
994  }
995  else if (value_vecs[f][value_index] > value_vecs[best_f][value_index]) {
996  still_viable[f] = false;
997  num_viable--;
998  }
999  }
1000  }
1001  if (best_f == -1) {
1002  best_f = 0;
1003  }
1004  return face[best_f];
1005 #undef VEC_VALUE_LEN
1006 }
1007 
1008 /* Merge (using average) all the UV values for loops of v's faces.
1009  * Caller should ensure that no seams are violated by doing this. */
1010 static void bev_merge_uvs(BMesh *bm, BMVert *v)
1011 {
1012  int num_of_uv_layers = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
1013 
1014  for (int i = 0; i < num_of_uv_layers; i++) {
1015  int cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, i);
1016 
1017  if (cd_loop_uv_offset == -1) {
1018  return;
1019  }
1020 
1021  int n = 0;
1022  float uv[2] = {0.0f, 0.0f};
1023  BMIter iter;
1024  BMLoop *l;
1025  BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
1026  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1027  add_v2_v2(uv, luv->uv);
1028  n++;
1029  }
1030  if (n > 1) {
1031  mul_v2_fl(uv, 1.0f / (float)n);
1032  BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
1033  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1034  copy_v2_v2(luv->uv, uv);
1035  }
1036  }
1037  }
1038 }
1039 
1040 /* Merge (using average) the UV values for two specific loops of v: those for faces containing v,
1041  * and part of faces that share edge bme. */
1042 static void bev_merge_edge_uvs(BMesh *bm, BMEdge *bme, BMVert *v)
1043 {
1044  int num_of_uv_layers = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
1045 
1046  BMLoop *l1 = NULL;
1047  BMLoop *l2 = NULL;
1048  BMIter iter;
1049  BMLoop *l;
1050  BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
1051  if (l->e == bme) {
1052  l1 = l;
1053  }
1054  else if (l->prev->e == bme) {
1055  l2 = l;
1056  }
1057  }
1058  if (l1 == NULL || l2 == NULL) {
1059  return;
1060  }
1061 
1062  for (int i = 0; i < num_of_uv_layers; i++) {
1063  int cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, i);
1064 
1065  if (cd_loop_uv_offset == -1) {
1066  return;
1067  }
1068 
1069  float uv[2] = {0.0f, 0.0f};
1070  MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l1, cd_loop_uv_offset);
1071  add_v2_v2(uv, luv->uv);
1072  luv = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_uv_offset);
1073  add_v2_v2(uv, luv->uv);
1074  mul_v2_fl(uv, 0.5f);
1075  luv = BM_ELEM_CD_GET_VOID_P(l1, cd_loop_uv_offset);
1076  copy_v2_v2(luv->uv, uv);
1077  luv = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_uv_offset);
1078  copy_v2_v2(luv->uv, uv);
1079  }
1080 }
1081 
1082 /* Calculate coordinates of a point a distance d from v on e->e and return it in slideco. */
1083 static void slide_dist(EdgeHalf *e, BMVert *v, float d, float r_slideco[3])
1084 {
1085  float dir[3];
1086  sub_v3_v3v3(dir, v->co, BM_edge_other_vert(e->e, v)->co);
1087  float len = normalize_v3(dir);
1088 
1089  if (d > len) {
1090  d = len - (float)(50.0 * BEVEL_EPSILON_D);
1091  }
1092  copy_v3_v3(r_slideco, v->co);
1093  madd_v3_v3fl(r_slideco, dir, -d);
1094 }
1095 
1096 /* Is co not on the edge e? If not, return the closer end of e in ret_closer_v. */
1097 static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_v)
1098 {
1099  float h[3], u[3];
1100  float *l1 = e->e->v1->co;
1101 
1102  sub_v3_v3v3(u, e->e->v2->co, l1);
1103  sub_v3_v3v3(h, co, l1);
1104  float lenu = normalize_v3(u);
1105  float lambda = dot_v3v3(u, h);
1106  if (lambda <= -BEVEL_EPSILON_BIG * lenu) {
1107  *ret_closer_v = e->e->v1;
1108  return true;
1109  }
1110  if (lambda >= (1.0f + BEVEL_EPSILON_BIG) * lenu) {
1111  *ret_closer_v = e->e->v2;
1112  return true;
1113  }
1114  return false;
1115 }
1116 
1117 /* Return whether the angle is less than, equal to, or larger than 180 degrees. */
1119 {
1120  BMVert *v1 = BM_edge_other_vert(e1->e, v);
1121  BMVert *v2 = BM_edge_other_vert(e2->e, v);
1122  float dir1[3], dir2[3];
1123  sub_v3_v3v3(dir1, v->co, v1->co);
1124  sub_v3_v3v3(dir2, v->co, v2->co);
1125  normalize_v3(dir1);
1126  normalize_v3(dir2);
1127 
1128  /* First check for in-line edges using a simpler test. */
1129  if (nearly_parallel_normalized(dir1, dir2)) {
1130  return ANGLE_STRAIGHT;
1131  }
1132 
1133  /* Angles are in [0,pi]. Need to compare cross product with normal to see if they are reflex. */
1134  float cross[3];
1135  cross_v3_v3v3(cross, dir1, dir2);
1137  float *no;
1138  if (e1->fnext) {
1139  no = e1->fnext->no;
1140  }
1141  else if (e2->fprev) {
1142  no = e2->fprev->no;
1143  }
1144  else {
1145  no = v->no;
1146  }
1147 
1148  if (dot_v3v3(cross, no) < 0.0f) {
1149  return ANGLE_LARGER;
1150  }
1151  return ANGLE_SMALLER;
1152 }
1153 
1154 /* co should be approximately on the plane between e1 and e2, which share common vert v and common
1155  * face f (which cannot be NULL). Is it between those edges, sweeping CCW? */
1157  const float co[3], BMVert *v, BMFace *f, EdgeHalf *e1, EdgeHalf *e2)
1158 {
1159  float dir1[3], dir2[3], dirco[3], no[3];
1160 
1161  BMVert *v1 = BM_edge_other_vert(e1->e, v);
1162  BMVert *v2 = BM_edge_other_vert(e2->e, v);
1163  sub_v3_v3v3(dir1, v->co, v1->co);
1164  sub_v3_v3v3(dir2, v->co, v2->co);
1165  sub_v3_v3v3(dirco, v->co, co);
1166  normalize_v3(dir1);
1167  normalize_v3(dir2);
1168  normalize_v3(dirco);
1169  float ang11 = angle_normalized_v3v3(dir1, dir2);
1170  float ang1co = angle_normalized_v3v3(dir1, dirco);
1171  /* Angles are in [0,pi]. Need to compare cross product with normal to see if they are reflex. */
1172  cross_v3_v3v3(no, dir1, dir2);
1173  if (dot_v3v3(no, f->no) < 0.0f) {
1174  ang11 = (float)(M_PI * 2.0) - ang11;
1175  }
1176  cross_v3_v3v3(no, dir1, dirco);
1177  if (dot_v3v3(no, f->no) < 0.0f) {
1178  ang1co = (float)(M_PI * 2.0) - ang1co;
1179  }
1180  return (ang11 - ang1co > -BEVEL_EPSILON_ANG);
1181 }
1182 
1183 /* Is the angle swept from e1 to e2, CCW when viewed from the normal side of f,
1184  * not a reflex angle or a straight angle? Assume e1 and e2 share a vert. */
1185 static bool edge_edge_angle_less_than_180(const BMEdge *e1, const BMEdge *e2, const BMFace *f)
1186 {
1187  float dir1[3], dir2[3], cross[3];
1188  BLI_assert(f != NULL);
1189  BMVert *v, *v1, *v2;
1190  if (e1->v1 == e2->v1) {
1191  v = e1->v1;
1192  v1 = e1->v2;
1193  v2 = e2->v2;
1194  }
1195  else if (e1->v1 == e2->v2) {
1196  v = e1->v1;
1197  v1 = e1->v2;
1198  v2 = e2->v1;
1199  }
1200  else if (e1->v2 == e2->v1) {
1201  v = e1->v2;
1202  v1 = e1->v1;
1203  v2 = e2->v2;
1204  }
1205  else if (e1->v2 == e2->v2) {
1206  v = e1->v2;
1207  v1 = e1->v1;
1208  v2 = e2->v1;
1209  }
1210  else {
1211  BLI_assert(false);
1212  return false;
1213  }
1214  sub_v3_v3v3(dir1, v1->co, v->co);
1215  sub_v3_v3v3(dir2, v2->co, v->co);
1216  cross_v3_v3v3(cross, dir1, dir2);
1217  return dot_v3v3(cross, f->no) > 0.0f;
1218 }
1219 
1220 /* When the offset_type is BEVEL_AMT_PERCENT or BEVEL_AMT_ABSOLUTE, fill in the coordinates
1221  * of the lines whose intersection defines the boundary point between e1 and e2 with common
1222  * vert v, as defined in the parameters of offset_meet.
1223  */
1225  EdgeHalf *e1,
1226  EdgeHalf *e2,
1227  BMVert *v,
1228  float r_l1a[3],
1229  float r_l1b[3],
1230  float r_l2a[3],
1231  float r_l2b[3])
1232 {
1233  /* Get points the specified distance along each leg.
1234  * NOTE: not all BevVerts and EdgeHalfs have been made yet, so we have
1235  * to find required edges by moving around faces and use fake EdgeHalfs for
1236  * some of the edges. If there aren't faces to move around, we have to give up.
1237  * The legs we need are:
1238  * e0 : the next edge around e1->fnext (==f1) after e1.
1239  * e3 : the prev edge around e2->fprev (==f2) before e2.
1240  * e4 : the previous edge around f1 before e1 (may be e2).
1241  * e5 : the next edge around f2 after e2 (may be e1).
1242  */
1243  BMVert *v1, *v2;
1244  EdgeHalf e0, e3, e4, e5;
1245  BMFace *f1, *f2;
1246  float d0, d3, d4, d5;
1247  float e1_wt, e2_wt;
1248  v1 = BM_edge_other_vert(e1->e, v);
1249  v2 = BM_edge_other_vert(e2->e, v);
1250  f1 = e1->fnext;
1251  f2 = e2->fprev;
1252  bool no_offsets = f1 == NULL || f2 == NULL;
1253  if (!no_offsets) {
1255  e0.e = l->e;
1256  l = BM_face_vert_share_loop(f2, v2);
1257  e3.e = l->prev->e;
1258  l = BM_face_vert_share_loop(f1, v);
1259  e4.e = l->prev->e;
1260  l = BM_face_vert_share_loop(f2, v);
1261  e5.e = l->e;
1262  /* All the legs must be visible from their opposite legs. */
1263  no_offsets = !edge_edge_angle_less_than_180(e0.e, e1->e, f1) ||
1264  !edge_edge_angle_less_than_180(e1->e, e4.e, f1) ||
1265  !edge_edge_angle_less_than_180(e2->e, e3.e, f2) ||
1266  !edge_edge_angle_less_than_180(e5.e, e2->e, f1);
1267  if (!no_offsets) {
1268  if (bp->offset_type == BEVEL_AMT_ABSOLUTE) {
1269  d0 = d3 = d4 = d5 = bp->offset;
1270  }
1271  else {
1272  d0 = bp->offset * BM_edge_calc_length(e0.e) / 100.0f;
1273  d3 = bp->offset * BM_edge_calc_length(e3.e) / 100.0f;
1274  d4 = bp->offset * BM_edge_calc_length(e4.e) / 100.0f;
1275  d5 = bp->offset * BM_edge_calc_length(e5.e) / 100.0f;
1276  }
1277  if (bp->use_weights) {
1278  CustomData *cd = &bp->bm->edata;
1279  e1_wt = BM_elem_float_data_get(cd, e1->e, CD_BWEIGHT);
1280  e2_wt = BM_elem_float_data_get(cd, e2->e, CD_BWEIGHT);
1281  }
1282  else {
1283  e1_wt = 1.0f;
1284  e2_wt = 1.0f;
1285  }
1286  slide_dist(&e4, v, d4 * e1_wt, r_l1a);
1287  slide_dist(&e0, v1, d0 * e1_wt, r_l1b);
1288  slide_dist(&e5, v, d5 * e2_wt, r_l2a);
1289  slide_dist(&e3, v2, d3 * e2_wt, r_l2b);
1290  }
1291  }
1292  if (no_offsets) {
1293  copy_v3_v3(r_l1a, v->co);
1294  copy_v3_v3(r_l1b, v1->co);
1295  copy_v3_v3(r_l2a, v->co);
1296  copy_v3_v3(r_l2b, v2->co);
1297  }
1298 }
1299 
1318 static void offset_meet(BevelParams *bp,
1319  EdgeHalf *e1,
1320  EdgeHalf *e2,
1321  BMVert *v,
1322  BMFace *f,
1323  bool edges_between,
1324  float meetco[3],
1325  const EdgeHalf *e_in_plane)
1326 {
1327  /* Get direction vectors for two offset lines. */
1328  float dir1[3], dir2[3];
1329  sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co);
1330  sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co);
1331 
1332  float dir1n[3], dir2p[3];
1333  if (edges_between) {
1334  EdgeHalf *e1next = e1->next;
1335  EdgeHalf *e2prev = e2->prev;
1336  sub_v3_v3v3(dir1n, BM_edge_other_vert(e1next->e, v)->co, v->co);
1337  sub_v3_v3v3(dir2p, v->co, BM_edge_other_vert(e2prev->e, v)->co);
1338  }
1339  else {
1340  /* Shut up 'maybe unused' warnings. */
1341  zero_v3(dir1n);
1342  zero_v3(dir2p);
1343  }
1344 
1345  float ang = angle_v3v3(dir1, dir2);
1346  float norm_perp1[3];
1347  if (ang < BEVEL_EPSILON_ANG) {
1348  /* Special case: e1 and e2 are parallel; put offset point perp to both, from v.
1349  * need to find a suitable plane.
1350  * This code used to just use offset and dir1, but that makes for visible errors
1351  * on a circle with > 200 sides, which trips this "nearly perp" code (see T61214).
1352  * so use the average of the two, and the offset formula for angle bisector.
1353  * If offsets are different, we're out of luck:
1354  * Use the max of the two (so get consistent looking results if the same situation
1355  * arises elsewhere in the object but with opposite roles for e1 and e2. */
1356  float norm_v[3];
1357  if (f) {
1358  copy_v3_v3(norm_v, f->no);
1359  }
1360  else {
1361  /* Get average of face norms of faces between e and e2. */
1362  int fcount = 0;
1363  zero_v3(norm_v);
1364  for (EdgeHalf *eloop = e1; eloop != e2; eloop = eloop->next) {
1365  if (eloop->fnext != NULL) {
1366  add_v3_v3(norm_v, eloop->fnext->no);
1367  fcount++;
1368  }
1369  }
1370  if (fcount == 0) {
1371  copy_v3_v3(norm_v, v->no);
1372  }
1373  else {
1374  mul_v3_fl(norm_v, 1.0f / fcount);
1375  }
1376  }
1377  add_v3_v3(dir1, dir2);
1378  cross_v3_v3v3(norm_perp1, dir1, norm_v);
1379  normalize_v3(norm_perp1);
1380  float off1a[3];
1381  copy_v3_v3(off1a, v->co);
1382  float d = max_ff(e1->offset_r, e2->offset_l);
1383  d = d / cosf(ang / 2.0f);
1384  madd_v3_v3fl(off1a, norm_perp1, d);
1385  copy_v3_v3(meetco, off1a);
1386  }
1387  else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG) {
1388  /* Special case: e1 and e2 are antiparallel, so bevel is into a zero-area face.
1389  * Just make the offset point on the common line, at offset distance from v. */
1390  float d = max_ff(e1->offset_r, e2->offset_l);
1391  slide_dist(e2, v, d, meetco);
1392  }
1393  else {
1394  /* Get normal to plane where meet point should be, using cross product instead of f->no
1395  * in case f is non-planar.
1396  * Except: sometimes locally there can be a small angle between dir1 and dir2 that leads
1397  * to a normal that is actually almost perpendicular to the face normal;
1398  * in this case it looks wrong to use the local (cross-product) normal, so use the face normal
1399  * if the angle between dir1 and dir2 is smallish.
1400  * If e1-v-e2 is a reflex angle (viewed from vertex normal side), need to flip.
1401  * Use f->no to figure out which side to look at angle from, as even if f is non-planar,
1402  * will be more accurate than vertex normal. */
1403  float norm_v1[3], norm_v2[3];
1404  if (f && ang < BEVEL_SMALL_ANG) {
1405  copy_v3_v3(norm_v1, f->no);
1406  copy_v3_v3(norm_v2, f->no);
1407  }
1408  else if (!edges_between) {
1409  cross_v3_v3v3(norm_v1, dir2, dir1);
1410  normalize_v3(norm_v1);
1411  if (dot_v3v3(norm_v1, f ? f->no : v->no) < 0.0f) {
1412  negate_v3(norm_v1);
1413  }
1414  copy_v3_v3(norm_v2, norm_v1);
1415  }
1416  else {
1417  /* Separate faces; get face norms at corners for each separately. */
1418  cross_v3_v3v3(norm_v1, dir1n, dir1);
1419  normalize_v3(norm_v1);
1420  f = e1->fnext;
1421  if (dot_v3v3(norm_v1, f ? f->no : v->no) < 0.0f) {
1422  negate_v3(norm_v1);
1423  }
1424  cross_v3_v3v3(norm_v2, dir2, dir2p);
1425  normalize_v3(norm_v2);
1426  f = e2->fprev;
1427  if (dot_v3v3(norm_v2, f ? f->no : v->no) < 0.0f) {
1428  negate_v3(norm_v2);
1429  }
1430  }
1431 
1432  /* Get vectors perp to each edge, perp to norm_v, and pointing into face. */
1433  float norm_perp2[3];
1434  cross_v3_v3v3(norm_perp1, dir1, norm_v1);
1435  cross_v3_v3v3(norm_perp2, dir2, norm_v2);
1436  normalize_v3(norm_perp1);
1437  normalize_v3(norm_perp2);
1438 
1439  float off1a[3], off1b[3], off2a[3], off2b[3];
1441  offset_meet_lines_percent_or_absolute(bp, e1, e2, v, off1a, off1b, off2a, off2b);
1442  }
1443  else {
1444  /* Get points that are offset distances from each line, then another point on each line. */
1445  copy_v3_v3(off1a, v->co);
1446  madd_v3_v3fl(off1a, norm_perp1, e1->offset_r);
1447  add_v3_v3v3(off1b, off1a, dir1);
1448  copy_v3_v3(off2a, v->co);
1449  madd_v3_v3fl(off2a, norm_perp2, e2->offset_l);
1450  add_v3_v3v3(off2b, off2a, dir2);
1451  }
1452 
1453  /* Intersect the offset lines. */
1454  float isect2[3];
1455  int isect_kind = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2);
1456  if (isect_kind == 0) {
1457  /* Lines are collinear: we already tested for this, but this used a different epsilon. */
1458  copy_v3_v3(meetco, off1a); /* Just to do something. */
1459  }
1460  else {
1461  /* The lines intersect, but is it at a reasonable place?
1462  * One problem to check: if one of the offsets is 0, then we don't want an intersection
1463  * that is outside that edge itself. This can happen if angle between them is > 180 degrees,
1464  * or if the offset amount is > the edge length. */
1465  BMVert *closer_v;
1466  if (e1->offset_r == 0.0f && is_outside_edge(e1, meetco, &closer_v)) {
1467  copy_v3_v3(meetco, closer_v->co);
1468  }
1469  if (e2->offset_l == 0.0f && is_outside_edge(e2, meetco, &closer_v)) {
1470  copy_v3_v3(meetco, closer_v->co);
1471  }
1472  if (edges_between && e1->offset_r > 0.0f && e2->offset_l > 0.0f) {
1473  /* Try to drop meetco to a face between e1 and e2. */
1474  if (isect_kind == 2) {
1475  /* Lines didn't meet in 3d: get average of meetco and isect2. */
1476  mid_v3_v3v3(meetco, meetco, isect2);
1477  }
1478  for (EdgeHalf *e = e1; e != e2; e = e->next) {
1479  BMFace *fnext = e->fnext;
1480  if (!fnext) {
1481  continue;
1482  }
1483  float plane[4];
1484  plane_from_point_normal_v3(plane, v->co, fnext->no);
1485  float dropco[3];
1486  closest_to_plane_normalized_v3(dropco, plane, meetco);
1487  /* Don't drop to the faces next to the in plane edge. */
1488  if (e_in_plane) {
1489  ang = angle_v3v3(fnext->no, e_in_plane->fnext->no);
1490  if ((fabsf(ang) < BEVEL_SMALL_ANG) || (fabsf(ang - (float)M_PI) < BEVEL_SMALL_ANG)) {
1491  continue;
1492  }
1493  }
1494  if (point_between_edges(dropco, v, fnext, e, e->next)) {
1495  copy_v3_v3(meetco, dropco);
1496  break;
1497  }
1498  }
1499  }
1500  }
1501  }
1502 }
1503 
1504 /* This was changed from 0.25f to fix bug T86768.
1505  * Original bug T44961 remains fixed with this value.
1506  * Update: changed again from 0.0001f to fix bug T95335.
1507  * Original two bugs remained fixed.
1508  */
1509 #define BEVEL_GOOD_ANGLE 0.1f
1510 
1519 static bool offset_meet_edge(
1520  EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetco[3], float *r_angle)
1521 {
1522  float dir1[3], dir2[3];
1523  sub_v3_v3v3(dir1, BM_edge_other_vert(e1->e, v)->co, v->co);
1524  sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co);
1525  normalize_v3(dir1);
1526  normalize_v3(dir2);
1527 
1528  /* Find angle from dir1 to dir2 as viewed from vertex normal side. */
1529  float ang = angle_normalized_v3v3(dir1, dir2);
1530  if (fabsf(ang) < BEVEL_GOOD_ANGLE) {
1531  if (r_angle) {
1532  *r_angle = 0.0f;
1533  }
1534  return false;
1535  }
1536  float fno[3];
1537  cross_v3_v3v3(fno, dir1, dir2);
1538  if (dot_v3v3(fno, v->no) < 0.0f) {
1539  ang = 2.0f * (float)M_PI - ang; /* Angle is reflex. */
1540  if (r_angle) {
1541  *r_angle = ang;
1542  }
1543  return false;
1544  }
1545  if (r_angle) {
1546  *r_angle = ang;
1547  }
1548 
1549  if (fabsf(ang - (float)M_PI) < BEVEL_GOOD_ANGLE) {
1550  return false;
1551  }
1552 
1553  float sinang = sinf(ang);
1554 
1555  copy_v3_v3(meetco, v->co);
1556  if (e1->offset_r == 0.0f) {
1557  madd_v3_v3fl(meetco, dir1, e2->offset_l / sinang);
1558  }
1559  else {
1560  madd_v3_v3fl(meetco, dir2, e1->offset_r / sinang);
1561  }
1562  return true;
1563 }
1564 
1570 {
1571  float ang;
1572  float meet[3];
1573 
1574  return offset_meet_edge(e1, emid, v, meet, &ang) && offset_meet_edge(emid, e2, v, meet, &ang);
1575 }
1576 
1586  EdgeHalf *e1,
1587  EdgeHalf *e2,
1588  EdgeHalf *emid,
1589  BMVert *v,
1590  float meetco[3],
1591  float *r_sinratio)
1592 {
1593  bool retval = false;
1594 
1595  BLI_assert(e1->is_bev && e2->is_bev && !emid->is_bev);
1596 
1597  float ang1, ang2;
1598  float meet1[3], meet2[3];
1599  bool ok1 = offset_meet_edge(e1, emid, v, meet1, &ang1);
1600  bool ok2 = offset_meet_edge(emid, e2, v, meet2, &ang2);
1602  BMVert *v2 = BM_edge_other_vert(emid->e, v);
1603  if (bp->offset_type == BEVEL_AMT_PERCENT) {
1604  float wt = 1.0;
1605  if (bp->use_weights) {
1606  CustomData *cd = &bp->bm->edata;
1607  wt = 0.5f * (BM_elem_float_data_get(cd, e1->e, CD_BWEIGHT) +
1608  BM_elem_float_data_get(cd, e2->e, CD_BWEIGHT));
1609  }
1610  interp_v3_v3v3(meetco, v->co, v2->co, wt * bp->offset / 100.0f);
1611  }
1612  else {
1613  float dir[3];
1614  sub_v3_v3v3(dir, v2->co, v->co);
1615  normalize_v3(dir);
1616  madd_v3_v3v3fl(meetco, v->co, dir, bp->offset);
1617  }
1618  if (r_sinratio) {
1619  *r_sinratio = (ang1 == 0.0f) ? 1.0f : sinf(ang2) / sinf(ang1);
1620  }
1621  return true;
1622  }
1623  if (ok1 && ok2) {
1624  mid_v3_v3v3(meetco, meet1, meet2);
1625  if (r_sinratio) {
1626  /* ang1 should not be 0, but be paranoid. */
1627  *r_sinratio = (ang1 == 0.0f) ? 1.0f : sinf(ang2) / sinf(ang1);
1628  }
1629  retval = true;
1630  }
1631  else if (ok1 && !ok2) {
1632  copy_v3_v3(meetco, meet1);
1633  }
1634  else if (!ok1 && ok2) {
1635  copy_v3_v3(meetco, meet2);
1636  }
1637  else {
1638  /* Neither offset line met emid.
1639  * This should only happen if all three lines are on top of each other. */
1640  slide_dist(emid, v, e1->offset_r, meetco);
1641  }
1642 
1643  return retval;
1644 }
1645 
1646 /* Offset by e->offset in plane with normal plane_no, on left if left==true, else on right.
1647  * If plane_no is NULL, choose an arbitrary plane different from eh's direction. */
1648 static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, float r_co[3])
1649 {
1650  BMVert *v = e->is_rev ? e->e->v2 : e->e->v1;
1651 
1652  float dir[3], no[3];
1653  sub_v3_v3v3(dir, BM_edge_other_vert(e->e, v)->co, v->co);
1654  normalize_v3(dir);
1655  if (plane_no) {
1656  copy_v3_v3(no, plane_no);
1657  }
1658  else {
1659  zero_v3(no);
1660  if (fabsf(dir[0]) < fabsf(dir[1])) {
1661  no[0] = 1.0f;
1662  }
1663  else {
1664  no[1] = 1.0f;
1665  }
1666  }
1667 
1668  float fdir[3];
1669  if (left) {
1670  cross_v3_v3v3(fdir, dir, no);
1671  }
1672  else {
1673  cross_v3_v3v3(fdir, no, dir);
1674  }
1675  normalize_v3(fdir);
1676  copy_v3_v3(r_co, v->co);
1677  madd_v3_v3fl(r_co, fdir, left ? e->offset_l : e->offset_r);
1678 }
1679 
1680 /* Calculate the point on e where line (co_a, co_b) comes closest to and return it in projco. */
1681 static void project_to_edge(const BMEdge *e,
1682  const float co_a[3],
1683  const float co_b[3],
1684  float projco[3])
1685 {
1686  float otherco[3];
1687  if (!isect_line_line_v3(e->v1->co, e->v2->co, co_a, co_b, projco, otherco)) {
1688 #ifdef BEVEL_ASSERT_PROJECT
1689  BLI_assert_msg(0, "project meet failure");
1690 #endif
1691  copy_v3_v3(projco, e->v1->co);
1692  }
1693 }
1694 
1695 /* If there is a bndv->ebev edge, find the mid control point if necessary.
1696  * It is the closest point on the beveled edge to the line segment between bndv and bndv->next. */
1697 static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
1698 {
1699  bool do_linear_interp = true;
1700  EdgeHalf *e = bndv->ebev;
1701  Profile *pro = &bndv->profile;
1702 
1703  float start[3], end[3];
1704  copy_v3_v3(start, bndv->nv.co);
1705  copy_v3_v3(end, bndv->next->nv.co);
1706  if (e) {
1707  do_linear_interp = false;
1708  pro->super_r = bp->pro_super_r;
1709  /* Projection direction is direction of the edge. */
1710  sub_v3_v3v3(pro->proj_dir, e->e->v1->co, e->e->v2->co);
1711  if (e->is_rev) {
1712  negate_v3(pro->proj_dir);
1713  }
1714  normalize_v3(pro->proj_dir);
1715  project_to_edge(e->e, start, end, pro->middle);
1716  copy_v3_v3(pro->start, start);
1717  copy_v3_v3(pro->end, end);
1718  /* Default plane to project onto is the one with triangle start - middle - end in it. */
1719  float d1[3], d2[3];
1720  sub_v3_v3v3(d1, pro->middle, start);
1721  sub_v3_v3v3(d2, pro->middle, end);
1722  normalize_v3(d1);
1723  normalize_v3(d2);
1724  cross_v3_v3v3(pro->plane_no, d1, d2);
1725  normalize_v3(pro->plane_no);
1726  if (nearly_parallel(d1, d2)) {
1727  /* Start - middle - end are collinear.
1728  * It should be the case that beveled edge is coplanar with two boundary verts.
1729  * We want to move the profile to that common plane, if possible.
1730  * That makes the multi-segment bevels curve nicely in that plane, as users expect.
1731  * The new middle should be either v (when neighbor edges are unbeveled)
1732  * or the intersection of the offset lines (if they are).
1733  * If the profile is going to lead into unbeveled edges on each side
1734  * (that is, both BoundVerts are "on-edge" points on non-beveled edges). */
1735  copy_v3_v3(pro->middle, bv->v->co);
1736  if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) {
1737  /* Want mid at the meet point of next and prev offset edges. */
1738  float d3[3], d4[3], co4[3], meetco[3], isect2[3];
1739  int isect_kind;
1740 
1741  sub_v3_v3v3(d3, e->prev->e->v1->co, e->prev->e->v2->co);
1742  sub_v3_v3v3(d4, e->next->e->v1->co, e->next->e->v2->co);
1743  normalize_v3(d3);
1744  normalize_v3(d4);
1745  if (nearly_parallel(d3, d4)) {
1746  /* Offset lines are collinear - want linear interpolation. */
1747  mid_v3_v3v3(pro->middle, start, end);
1748  do_linear_interp = true;
1749  }
1750  else {
1751  float co3[3];
1752  add_v3_v3v3(co3, start, d3);
1753  add_v3_v3v3(co4, end, d4);
1754  isect_kind = isect_line_line_v3(start, co3, end, co4, meetco, isect2);
1755  if (isect_kind != 0) {
1756  copy_v3_v3(pro->middle, meetco);
1757  }
1758  else {
1759  /* Offset lines don't intersect - want linear interpolation. */
1760  mid_v3_v3v3(pro->middle, start, end);
1761  do_linear_interp = true;
1762  }
1763  }
1764  }
1765  copy_v3_v3(pro->end, end);
1766  sub_v3_v3v3(d1, pro->middle, start);
1767  normalize_v3(d1);
1768  sub_v3_v3v3(d2, pro->middle, end);
1769  normalize_v3(d2);
1770  cross_v3_v3v3(pro->plane_no, d1, d2);
1771  normalize_v3(pro->plane_no);
1772  if (nearly_parallel(d1, d2)) {
1773  /* Whole profile is collinear with edge: just interpolate. */
1774  do_linear_interp = true;
1775  }
1776  else {
1777  copy_v3_v3(pro->plane_co, bv->v->co);
1778  copy_v3_v3(pro->proj_dir, pro->plane_no);
1779  }
1780  }
1781  copy_v3_v3(pro->plane_co, start);
1782  }
1783  else if (bndv->is_arc_start) {
1784  /* Assume pro->middle was already set. */
1785  copy_v3_v3(pro->start, start);
1786  copy_v3_v3(pro->end, end);
1787  pro->super_r = PRO_CIRCLE_R;
1788  zero_v3(pro->plane_co);
1789  zero_v3(pro->plane_no);
1790  zero_v3(pro->proj_dir);
1791  do_linear_interp = false;
1792  }
1793  else if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
1794  copy_v3_v3(pro->start, start);
1795  copy_v3_v3(pro->middle, bv->v->co);
1796  copy_v3_v3(pro->end, end);
1797  pro->super_r = bp->pro_super_r;
1798  zero_v3(pro->plane_co);
1799  zero_v3(pro->plane_no);
1800  zero_v3(pro->proj_dir);
1801  do_linear_interp = false;
1802  }
1803 
1804  if (do_linear_interp) {
1805  pro->super_r = PRO_LINE_R;
1806  copy_v3_v3(pro->start, start);
1807  copy_v3_v3(pro->end, end);
1808  mid_v3_v3v3(pro->middle, start, end);
1809  /* Won't use projection for this line profile. */
1810  zero_v3(pro->plane_co);
1811  zero_v3(pro->plane_no);
1812  zero_v3(pro->proj_dir);
1813  }
1814 }
1815 
1823 static void move_profile_plane(BoundVert *bndv, BMVert *bmvert)
1824 {
1825  Profile *pro = &bndv->profile;
1826 
1827  /* Only do this if projecting, and start, end, and proj_dir are not coplanar. */
1828  if (is_zero_v3(pro->proj_dir)) {
1829  return;
1830  }
1831 
1832  float d1[3], d2[3];
1833  sub_v3_v3v3(d1, bmvert->co, pro->start);
1834  normalize_v3(d1);
1835  sub_v3_v3v3(d2, bmvert->co, pro->end);
1836  normalize_v3(d2);
1837  float no[3], no2[3], no3[3];
1838  cross_v3_v3v3(no, d1, d2);
1839  cross_v3_v3v3(no2, d1, pro->proj_dir);
1840  cross_v3_v3v3(no3, d2, pro->proj_dir);
1841 
1844  float dot2 = dot_v3v3(no, no2);
1845  float dot3 = dot_v3v3(no, no3);
1846  if (fabsf(dot2) < (1 - BEVEL_EPSILON_BIG) && fabsf(dot3) < (1 - BEVEL_EPSILON_BIG)) {
1847  copy_v3_v3(bndv->profile.plane_no, no);
1848  }
1849  }
1850 
1851  /* We've changed the parameters from their defaults, so don't recalculate them later. */
1852  pro->special_params = true;
1853 }
1854 
1862 static void move_weld_profile_planes(BevVert *bv, BoundVert *bndv1, BoundVert *bndv2)
1863 {
1864  /* Only do this if projecting, and d1, d2, and proj_dir are not coplanar. */
1865  if (is_zero_v3(bndv1->profile.proj_dir) || is_zero_v3(bndv2->profile.proj_dir)) {
1866  return;
1867  }
1868  float d1[3], d2[3], no[3];
1869  sub_v3_v3v3(d1, bv->v->co, bndv1->nv.co);
1870  sub_v3_v3v3(d2, bv->v->co, bndv2->nv.co);
1871  cross_v3_v3v3(no, d1, d2);
1872  float l1 = normalize_v3(no);
1873 
1874  /* "no" is new normal projection plane, but don't move if it is coplanar with both of the
1875  * projection directions. */
1876  float no2[3], no3[3];
1877  cross_v3_v3v3(no2, d1, bndv1->profile.proj_dir);
1878  float l2 = normalize_v3(no2);
1879  cross_v3_v3v3(no3, d2, bndv2->profile.proj_dir);
1880  float l3 = normalize_v3(no3);
1881  if (l1 > BEVEL_EPSILON && (l2 > BEVEL_EPSILON || l3 > BEVEL_EPSILON)) {
1882  float dot1 = fabsf(dot_v3v3(no, no2));
1883  float dot2 = fabsf(dot_v3v3(no, no3));
1884  if (fabsf(dot1 - 1.0f) > BEVEL_EPSILON) {
1885  copy_v3_v3(bndv1->profile.plane_no, no);
1886  }
1887  if (fabsf(dot2 - 1.0f) > BEVEL_EPSILON) {
1888  copy_v3_v3(bndv2->profile.plane_no, no);
1889  }
1890  }
1891 
1892  /* We've changed the parameters from their defaults, so don't recalculate them later. */
1893  bndv1->profile.special_params = true;
1894  bndv2->profile.special_params = true;
1895 }
1896 
1897 /* Return 1 if a and b are in CCW order on the normal side of f,
1898  * and -1 if they are reversed, and 0 if there is no shared face f. */
1899 static int bev_ccw_test(BMEdge *a, BMEdge *b, BMFace *f)
1900 {
1901  if (!f) {
1902  return 0;
1903  }
1904  BMLoop *la = BM_face_edge_share_loop(f, a);
1905  BMLoop *lb = BM_face_edge_share_loop(f, b);
1906  if (!la || !lb) {
1907  return 0;
1908  }
1909  return lb->next == la ? 1 : -1;
1910 }
1911 
1933 static bool make_unit_square_map(const float va[3],
1934  const float vmid[3],
1935  const float vb[3],
1936  float r_mat[4][4])
1937 {
1938  float vb_vmid[3], va_vmid[3];
1939  sub_v3_v3v3(va_vmid, vmid, va);
1940  sub_v3_v3v3(vb_vmid, vmid, vb);
1941 
1942  if (is_zero_v3(va_vmid) || is_zero_v3(vb_vmid)) {
1943  return false;
1944  }
1945 
1946  if (fabsf(angle_v3v3(va_vmid, vb_vmid) - (float)M_PI) <= BEVEL_EPSILON_ANG) {
1947  return false;
1948  }
1949 
1950  float vo[3], vd[3], vddir[3];
1951  sub_v3_v3v3(vo, va, vb_vmid);
1952  cross_v3_v3v3(vddir, vb_vmid, va_vmid);
1953  normalize_v3(vddir);
1954  add_v3_v3v3(vd, vo, vddir);
1955 
1956  /* The cols of m are: {vmid - va, vmid - vb, vmid + vd - va -vb, va + vb - vmid;
1957  * Blender transform matrices are stored such that m[i][*] is ith column;
1958  * the last elements of each col remain as they are in unity matrix. */
1959  sub_v3_v3v3(&r_mat[0][0], vmid, va);
1960  r_mat[0][3] = 0.0f;
1961  sub_v3_v3v3(&r_mat[1][0], vmid, vb);
1962  r_mat[1][3] = 0.0f;
1963  add_v3_v3v3(&r_mat[2][0], vmid, vd);
1964  sub_v3_v3(&r_mat[2][0], va);
1965  sub_v3_v3(&r_mat[2][0], vb);
1966  r_mat[2][3] = 0.0f;
1967  add_v3_v3v3(&r_mat[3][0], va, vb);
1968  sub_v3_v3(&r_mat[3][0], vmid);
1969  r_mat[3][3] = 1.0f;
1970 
1971  return true;
1972 }
1973 
1991  const float va[3], const float vb[3], const float vc[3], const float vd[3], float r_mat[4][4])
1992 {
1993  copy_v3_v3(r_mat[0], va);
1994  sub_v3_v3(r_mat[0], vb);
1995  sub_v3_v3(r_mat[0], vc);
1996  add_v3_v3(r_mat[0], vd);
1997  mul_v3_fl(r_mat[0], 0.5f);
1998  r_mat[0][3] = 0.0f;
1999  copy_v3_v3(r_mat[1], vb);
2000  sub_v3_v3(r_mat[1], va);
2001  sub_v3_v3(r_mat[1], vc);
2002  add_v3_v3(r_mat[1], vd);
2003  mul_v3_fl(r_mat[1], 0.5f);
2004  r_mat[1][3] = 0.0f;
2005  copy_v3_v3(r_mat[2], vc);
2006  sub_v3_v3(r_mat[2], va);
2007  sub_v3_v3(r_mat[2], vb);
2008  add_v3_v3(r_mat[2], vd);
2009  mul_v3_fl(r_mat[2], 0.5f);
2010  r_mat[2][3] = 0.0f;
2011  copy_v3_v3(r_mat[3], va);
2012  add_v3_v3(r_mat[3], vb);
2013  add_v3_v3(r_mat[3], vc);
2014  sub_v3_v3(r_mat[3], vd);
2015  mul_v3_fl(r_mat[3], 0.5f);
2016  r_mat[3][3] = 1.0f;
2017 }
2018 
2025 static double superellipse_co(double x, float r, bool rbig)
2026 {
2027  BLI_assert(r > 0.0f);
2028 
2029  /* If r<1, mirror the superellipse function by (y=x)-line to get a numerically stable range
2030  * Possible because of symmetry, later mirror back. */
2031  if (rbig) {
2032  return pow((1.0 - pow(x, r)), (1.0 / r));
2033  }
2034  return 1.0 - pow((1.0 - pow(1.0 - x, r)), (1.0 / r));
2035 }
2036 
2046 static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int nseg, float r_co[3])
2047 {
2048  if (bp->seg == 1) {
2049  if (i == 0) {
2050  copy_v3_v3(r_co, pro->start);
2051  }
2052  else {
2053  copy_v3_v3(r_co, pro->end);
2054  }
2055  }
2056 
2057  else {
2058  if (nseg == bp->seg) {
2059  BLI_assert(pro->prof_co != NULL);
2060  copy_v3_v3(r_co, pro->prof_co + 3 * i);
2061  }
2062  else {
2063  BLI_assert(is_power_of_2_i(nseg) && nseg <= bp->pro_spacing.seg_2);
2064  /* Find spacing between subsamples in prof_co_2. */
2065  int subsample_spacing = bp->pro_spacing.seg_2 / nseg;
2066  copy_v3_v3(r_co, pro->prof_co_2 + 3 * i * subsample_spacing);
2067  }
2068  }
2069 }
2070 
2075 static void calculate_profile_segments(const Profile *profile,
2076  const float map[4][4],
2077  const bool use_map,
2078  const bool reversed,
2079  const int ns,
2080  const double *xvals,
2081  const double *yvals,
2082  float *r_prof_co)
2083 {
2084  /* Iterate over the vertices along the boundary arc. */
2085  for (int k = 0; k <= ns; k++) {
2086  float co[3];
2087  if (k == 0) {
2088  copy_v3_v3(co, profile->start);
2089  }
2090  else if (k == ns) {
2091  copy_v3_v3(co, profile->end);
2092  }
2093  else {
2094  if (use_map) {
2095  const float p[3] = {
2096  reversed ? (float)yvals[ns - k] : (float)xvals[k],
2097  reversed ? (float)xvals[ns - k] : (float)yvals[k],
2098  0.0f,
2099  };
2100  /* Do the 2D->3D transformation of the profile coordinates. */
2101  mul_v3_m4v3(co, map, p);
2102  }
2103  else {
2104  interp_v3_v3v3(co, profile->start, profile->end, (float)k / (float)ns);
2105  }
2106  }
2107  /* Finish the 2D->3D transformation by projecting onto the final profile plane. */
2108  float *prof_co_k = r_prof_co + 3 * k;
2109  if (!is_zero_v3(profile->proj_dir)) {
2110  float co2[3];
2111  add_v3_v3v3(co2, co, profile->proj_dir);
2112  /* pro->plane_co and pro->plane_no are filled in #set_profile_params. */
2113  if (!isect_line_plane_v3(prof_co_k, co, co2, profile->plane_co, profile->plane_no)) {
2114  /* Shouldn't happen. */
2115  copy_v3_v3(prof_co_k, co);
2116  }
2117  }
2118  else {
2119  copy_v3_v3(prof_co_k, co);
2120  }
2121  }
2122 }
2123 
2132 static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, bool miter)
2133 {
2134  Profile *pro = &bndv->profile;
2135  ProfileSpacing *pro_spacing = (miter) ? &bp->pro_spacing_miter : &bp->pro_spacing;
2136 
2137  if (bp->seg == 1) {
2138  return;
2139  }
2140 
2141  bool need_2 = bp->seg != bp->pro_spacing.seg_2;
2142  if (pro->prof_co == NULL) {
2143  pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, sizeof(float[3]) * (bp->seg + 1));
2144  if (need_2) {
2145  pro->prof_co_2 = (float *)BLI_memarena_alloc(bp->mem_arena,
2146  sizeof(float[3]) * (bp->pro_spacing.seg_2 + 1));
2147  }
2148  else {
2149  pro->prof_co_2 = pro->prof_co;
2150  }
2151  }
2152 
2153  bool use_map;
2154  float map[4][4];
2156  use_map = false;
2157  }
2158  else {
2159  use_map = make_unit_square_map(pro->start, pro->middle, pro->end, map);
2160  }
2161 
2162  if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && use_map) {
2163  /* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the
2164  * un-transformed profile through the 2D->3D map and calculating the distance between them. */
2165  float bottom_corner[3] = {0.0f, 0.0f, 0.0f};
2166  mul_v3_m4v3(bottom_corner, map, bottom_corner);
2167  float top_corner[3] = {1.0f, 1.0f, 0.0f};
2168  mul_v3_m4v3(top_corner, map, top_corner);
2169 
2170  pro->height = len_v3v3(bottom_corner, top_corner);
2171  }
2172 
2173  /* Calculate the 3D locations for the profile points */
2175  pro, map, use_map, reversed, bp->seg, pro_spacing->xvals, pro_spacing->yvals, pro->prof_co);
2176  /* Also calculate for the seg_2 case if it's needed. */
2177  if (need_2) {
2179  map,
2180  use_map,
2181  reversed,
2182  bp->pro_spacing.seg_2,
2183  pro_spacing->xvals_2,
2184  pro_spacing->yvals_2,
2185  pro->prof_co_2);
2186  }
2187 }
2188 
2195 static void snap_to_superellipsoid(float co[3], const float super_r, bool midline)
2196 {
2197  float r = super_r;
2198  if (r == PRO_CIRCLE_R) {
2199  normalize_v3(co);
2200  return;
2201  }
2202 
2203  float a = max_ff(0.0f, co[0]);
2204  float b = max_ff(0.0f, co[1]);
2205  float c = max_ff(0.0f, co[2]);
2206  float x = a;
2207  float y = b;
2208  float z = c;
2210  /* Will only be called for 2d profile. */
2212  z = 0.0f;
2213  x = min_ff(1.0f, x);
2214  y = min_ff(1.0f, y);
2215  if (r == PRO_SQUARE_R) {
2216  /* Snap to closer of x==1 and y==1 lines, or maybe both. */
2217  float dx = 1.0f - x;
2218  float dy = 1.0f - y;
2219  if (dx < dy) {
2220  x = 1.0f;
2221  y = midline ? 1.0f : y;
2222  }
2223  else {
2224  y = 1.0f;
2225  x = midline ? 1.0f : x;
2226  }
2227  }
2228  else {
2229  /* Snap to closer of x==0 and y==0 lines, or maybe both. */
2230  if (x < y) {
2231  x = 0.0f;
2232  y = midline ? 0.0f : y;
2233  }
2234  else {
2235  y = 0.0f;
2236  x = midline ? 0.0f : x;
2237  }
2238  }
2239  }
2240  else {
2241  float rinv = 1.0f / r;
2242  if (a == 0.0f) {
2243  if (b == 0.0f) {
2244  x = 0.0f;
2245  y = 0.0f;
2246  z = powf(c, rinv);
2247  }
2248  else {
2249  x = 0.0f;
2250  y = powf(1.0f / (1.0f + powf(c / b, r)), rinv);
2251  z = c * y / b;
2252  }
2253  }
2254  else {
2255  x = powf(1.0f / (1.0f + powf(b / a, r) + powf(c / a, r)), rinv);
2256  y = b * x / a;
2257  z = c * x / a;
2258  }
2259  }
2260  co[0] = x;
2261  co[1] = y;
2262  co[2] = z;
2263 }
2264 
2265 #define BEV_EXTEND_EDGE_DATA_CHECK(eh, flag) (BM_elem_flag_test(eh->e, flag))
2266 
2267 static void check_edge_data_seam_sharp_edges(BevVert *bv, int flag, bool neg)
2268 {
2269  EdgeHalf *e = &bv->edges[0], *efirst = &bv->edges[0];
2270 
2271  /* First edge with seam or sharp edge data. */
2272  while ((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(e, flag)) ||
2273  (neg && BEV_EXTEND_EDGE_DATA_CHECK(e, flag))) {
2274  e = e->next;
2275  if (e == efirst) {
2276  break;
2277  }
2278  }
2279 
2280  /* If no such edge found, return. */
2281  if ((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(e, flag)) ||
2282  (neg && BEV_EXTEND_EDGE_DATA_CHECK(e, flag))) {
2283  return;
2284  }
2285 
2286  /* Set efirst to this first encountered edge. */
2287  efirst = e;
2288 
2289  do {
2290  int flag_count = 0;
2291  EdgeHalf *ne = e->next;
2292 
2293  while (((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(ne, flag)) ||
2294  (neg && BEV_EXTEND_EDGE_DATA_CHECK(ne, flag))) &&
2295  ne != efirst) {
2296  if (ne->is_bev) {
2297  flag_count++;
2298  }
2299  ne = ne->next;
2300  }
2301  if (ne == e || (ne == efirst && ((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(efirst, flag)) ||
2302  (neg && BEV_EXTEND_EDGE_DATA_CHECK(efirst, flag))))) {
2303  break;
2304  }
2305  /* Set seam_len / sharp_len of starting edge. */
2306  if (flag == BM_ELEM_SEAM) {
2307  e->rightv->seam_len = flag_count;
2308  }
2309  else if (flag == BM_ELEM_SMOOTH) {
2310  e->rightv->sharp_len = flag_count;
2311  }
2312  e = ne;
2313  } while (e != efirst);
2314 }
2315 
2317 {
2318  VMesh *vm = bv->vmesh;
2319 
2320  if (vm->mesh_kind == M_TRI_FAN) {
2321  return;
2322  }
2323 
2324  BoundVert *bcur = bv->vmesh->boundstart, *start = bcur;
2325 
2326  do {
2327  /* If current boundvert has a seam length > 0 then it has a seam running along its edges. */
2328  if (bcur->seam_len) {
2329  if (!bv->vmesh->boundstart->seam_len && start == bv->vmesh->boundstart) {
2330  start = bcur; /* Set start to first boundvert with seam_len > 0. */
2331  }
2332 
2333  /* Now for all the mesh_verts starting at current index and ending at idxlen
2334  * we go through outermost ring and through all its segments and add seams
2335  * for those edges. */
2336  int idxlen = bcur->index + bcur->seam_len;
2337  for (int i = bcur->index; i < idxlen; i++) {
2338  BMVert *v1 = mesh_vert(vm, i % vm->count, 0, 0)->v, *v2;
2339  BMEdge *e;
2340  for (int k = 1; k < vm->seg; k++) {
2341  v2 = mesh_vert(vm, i % vm->count, 0, k)->v;
2342 
2343  /* Here v1 & v2 are current and next BMverts,
2344  * we find common edge and set its edge data. */
2345  e = v1->e;
2346  while (e->v1 != v2 && e->v2 != v2) {
2347  if (e->v1 == v1) {
2348  e = e->v1_disk_link.next;
2349  }
2350  else {
2351  e = e->v2_disk_link.next;
2352  }
2353  }
2355  v1 = v2;
2356  }
2357  BMVert *v3 = mesh_vert(vm, (i + 1) % vm->count, 0, 0)->v;
2358  e = v1->e; /* Do same as above for first and last vert. */
2359  while (e->v1 != v3 && e->v2 != v3) {
2360  if (e->v1 == v1) {
2361  e = e->v1_disk_link.next;
2362  }
2363  else {
2364  e = e->v2_disk_link.next;
2365  }
2366  }
2368  bcur = bcur->next;
2369  }
2370  }
2371  else {
2372  bcur = bcur->next;
2373  }
2374  } while (bcur != start);
2375 
2376  bcur = bv->vmesh->boundstart;
2377  start = bcur;
2378  do {
2379  if (bcur->sharp_len) {
2380  if (!bv->vmesh->boundstart->sharp_len && start == bv->vmesh->boundstart) {
2381  start = bcur;
2382  }
2383 
2384  int idxlen = bcur->index + bcur->sharp_len;
2385  for (int i = bcur->index; i < idxlen; i++) {
2386  BMVert *v1 = mesh_vert(vm, i % vm->count, 0, 0)->v, *v2;
2387  BMEdge *e;
2388  for (int k = 1; k < vm->seg; k++) {
2389  v2 = mesh_vert(vm, i % vm->count, 0, k)->v;
2390 
2391  e = v1->e;
2392  while (e->v1 != v2 && e->v2 != v2) {
2393  if (e->v1 == v1) {
2394  e = e->v1_disk_link.next;
2395  }
2396  else {
2397  e = e->v2_disk_link.next;
2398  }
2399  }
2401  v1 = v2;
2402  }
2403  BMVert *v3 = mesh_vert(vm, (i + 1) % vm->count, 0, 0)->v;
2404  e = v1->e;
2405  while (e->v1 != v3 && e->v2 != v3) {
2406  if (e->v1 == v1) {
2407  e = e->v1_disk_link.next;
2408  }
2409  else {
2410  e = e->v2_disk_link.next;
2411  }
2412  }
2414  bcur = bcur->next;
2415  }
2416  }
2417  else {
2418  bcur = bcur->next;
2419  }
2420  } while (bcur != start);
2421 }
2422 
2423 /* Mark edges as sharp if they are between a smooth reconstructed face and a new face. */
2425 {
2426  BMIter fiter;
2427  BMFace *f;
2428  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
2429  if (!BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
2430  continue;
2431  }
2432  if (get_face_kind(bp, f) != F_RECON) {
2433  continue;
2434  }
2435  BMIter liter;
2436  BMLoop *l;
2437  BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
2438  /* Cases we care about will have exactly one adjacent face. */
2439  BMLoop *lother = l->radial_next;
2440  BMFace *fother = lother->f;
2441  if (lother != l && fother) {
2442  FKind fkind = get_face_kind(bp, lother->f);
2443  if (ELEM(fkind, F_EDGE, F_VERT)) {
2445  }
2446  }
2447  }
2448  }
2449 }
2450 
2460 {
2461  if (bp->offset == 0.0 || !bp->harden_normals) {
2462  return;
2463  }
2464 
2465  /* Recalculate all face and vertex normals. Side effect: ensures vertex, edge, face indices. */
2466  /* I suspect this is not necessary. TODO: test that guess. */
2468 
2469  int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
2470 
2471  /* If there is not already a custom split normal layer then making one (with BM_lnorspace_update)
2472  * will not respect the autosmooth angle between smooth faces. To get that to happen, we have
2473  * to mark the sharpen the edges that are only sharp because of the angle test -- otherwise would
2474  * be smooth. */
2475  if (cd_clnors_offset == -1) {
2478  }
2479 
2480  /* Ensure that bm->lnor_spacearr has properly stored loop normals.
2481  * Side effect: ensures loop indices. */
2483 
2484  if (cd_clnors_offset == -1) {
2485  cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
2486  }
2487 
2488  BMIter fiter;
2489  BMFace *f;
2490  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
2491  FKind fkind = get_face_kind(bp, f);
2492  if (ELEM(fkind, F_ORIG, F_RECON)) {
2493  continue;
2494  }
2495  BMIter liter;
2496  BMLoop *l;
2497  BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
2498  BMEdge *estep = l->prev->e; /* Causes CW walk around l->v fan. */
2499  BMLoop *lprev = BM_vert_step_fan_loop(l, &estep);
2500  estep = l->e; /* Causes CCW walk around l->v fan. */
2501  BMLoop *lnext = BM_vert_step_fan_loop(l, &estep);
2502  FKind fprevkind = lprev ? get_face_kind(bp, lprev->f) : F_NONE;
2503  FKind fnextkind = lnext ? get_face_kind(bp, lnext->f) : F_NONE;
2504 
2505  float norm[3];
2506  float *pnorm = NULL;
2507  if (fkind == F_EDGE) {
2508  if (fprevkind == F_EDGE && BM_elem_flag_test(l, BM_ELEM_LONG_TAG)) {
2509  add_v3_v3v3(norm, f->no, lprev->f->no);
2510  pnorm = norm;
2511  }
2512  else if (fnextkind == F_EDGE && BM_elem_flag_test(lnext, BM_ELEM_LONG_TAG)) {
2513  add_v3_v3v3(norm, f->no, lnext->f->no);
2514  pnorm = norm;
2515  }
2516  else if (fprevkind == F_RECON && BM_elem_flag_test(l, BM_ELEM_LONG_TAG)) {
2517  pnorm = lprev->f->no;
2518  }
2519  else if (fnextkind == F_RECON && BM_elem_flag_test(l->prev, BM_ELEM_LONG_TAG)) {
2520  pnorm = lnext->f->no;
2521  }
2522  else {
2523  // printf("unexpected harden case (edge)\n");
2524  }
2525  }
2526  else if (fkind == F_VERT) {
2527  if (fprevkind == F_VERT && fnextkind == F_VERT) {
2528  pnorm = l->v->no;
2529  }
2530  else if (fprevkind == F_RECON) {
2531  pnorm = lprev->f->no;
2532  }
2533  else if (fnextkind == F_RECON) {
2534  pnorm = lnext->f->no;
2535  }
2536  else {
2537  BMLoop *lprevprev, *lnextnext;
2538  if (lprev) {
2539  estep = lprev->prev->e;
2540  lprevprev = BM_vert_step_fan_loop(lprev, &estep);
2541  }
2542  else {
2543  lprevprev = NULL;
2544  }
2545  if (lnext) {
2546  estep = lnext->e;
2547  lnextnext = BM_vert_step_fan_loop(lnext, &estep);
2548  }
2549  else {
2550  lnextnext = NULL;
2551  }
2552  FKind fprevprevkind = lprevprev ? get_face_kind(bp, lprevprev->f) : F_NONE;
2553  FKind fnextnextkind = lnextnext ? get_face_kind(bp, lnextnext->f) : F_NONE;
2554  if (fprevkind == F_EDGE && fprevprevkind == F_RECON) {
2555  pnorm = lprevprev->f->no;
2556  }
2557  else if (fprevkind == F_EDGE && fnextkind == F_VERT && fprevprevkind == F_EDGE) {
2558  add_v3_v3v3(norm, lprev->f->no, lprevprev->f->no);
2559  pnorm = norm;
2560  }
2561  else if (fnextkind == F_EDGE && fprevkind == F_VERT && fnextnextkind == F_EDGE) {
2562  add_v3_v3v3(norm, lnext->f->no, lnextnext->f->no);
2563  pnorm = norm;
2564  }
2565  else {
2566  // printf("unexpected harden case (vert)\n");
2567  }
2568  }
2569  }
2570  if (pnorm) {
2571  if (pnorm == norm) {
2572  normalize_v3(norm);
2573  }
2574  int l_index = BM_elem_index_get(l);
2575  short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset);
2577  }
2578  }
2579  }
2580 }
2581 
2583 {
2584  const int mode = bp->face_strength_mode;
2585  const char *wn_layer_id = MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID;
2586  int cd_prop_int_idx = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, wn_layer_id);
2587 
2588  if (cd_prop_int_idx == -1) {
2589  BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, wn_layer_id);
2590  cd_prop_int_idx = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, wn_layer_id);
2591  }
2592  cd_prop_int_idx -= CustomData_get_layer_index(&bm->pdata, CD_PROP_INT32);
2593  const int cd_prop_int_offset = CustomData_get_n_offset(
2594  &bm->pdata, CD_PROP_INT32, cd_prop_int_idx);
2595 
2596  BMIter fiter;
2597  BMFace *f;
2598  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
2599  FKind fkind = get_face_kind(bp, f);
2600  bool do_set_strength = true;
2601  int strength;
2602  switch (fkind) {
2603  case F_VERT:
2604  strength = FACE_STRENGTH_WEAK;
2605  do_set_strength = (mode >= BEVEL_FACE_STRENGTH_NEW);
2606  break;
2607  case F_EDGE:
2608  strength = FACE_STRENGTH_MEDIUM;
2609  do_set_strength = (mode >= BEVEL_FACE_STRENGTH_NEW);
2610  break;
2611  case F_RECON:
2612  strength = FACE_STRENGTH_STRONG;
2613  do_set_strength = (mode >= BEVEL_FACE_STRENGTH_AFFECTED);
2614  break;
2615  case F_ORIG:
2616  strength = FACE_STRENGTH_STRONG;
2617  do_set_strength = (mode == BEVEL_FACE_STRENGTH_ALL);
2618  break;
2619  default:
2620  do_set_strength = false;
2621  }
2622  if (do_set_strength) {
2623  int *strength_ptr = BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset);
2624  *strength_ptr = strength;
2625  }
2626  }
2627 }
2628 
2629 /* Set the any_seam property for a BevVert and all its BoundVerts. */
2630 static void set_bound_vert_seams(BevVert *bv, bool mark_seam, bool mark_sharp)
2631 {
2632  bv->any_seam = false;
2633  BoundVert *v = bv->vmesh->boundstart;
2634  do {
2635  v->any_seam = false;
2636  for (EdgeHalf *e = v->efirst; e; e = e->next) {
2637  v->any_seam |= e->is_seam;
2638  if (e == v->elast) {
2639  break;
2640  }
2641  }
2642  bv->any_seam |= v->any_seam;
2643  } while ((v = v->next) != bv->vmesh->boundstart);
2644 
2645  if (mark_seam) {
2647  }
2648  if (mark_sharp) {
2650  }
2651 }
2652 
2654 {
2655  if (!bv->any_seam) {
2656  return 0;
2657  }
2658 
2659  int ans = 0;
2660  for (int i = 0; i < bv->edgecount; i++) {
2661  if (bv->edges[i].is_seam) {
2662  ans++;
2663  }
2664  }
2665  return ans;
2666 }
2667 
2668 /* Is e between two faces with a 180 degree angle between their normals? */
2669 static bool eh_on_plane(EdgeHalf *e)
2670 {
2671  if (e->fprev && e->fnext) {
2672  float dot = dot_v3v3(e->fprev->no, e->fnext->no);
2673  if (fabsf(dot + 1.0f) <= BEVEL_EPSILON_BIG || fabsf(dot - 1.0f) <= BEVEL_EPSILON_BIG) {
2674  return true;
2675  }
2676  }
2677  return false;
2678 }
2679 
2687 {
2688  BoundVert *bndv = vm->boundstart;
2689  do {
2690  /* In special cases the params will have already been set. */
2691  if (!bndv->profile.special_params) {
2692  set_profile_params(bp, bv, bndv);
2693  }
2694  bool miter_profile = false;
2695  bool reverse_profile = false;
2696  if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
2697  /* Use the miter profile spacing struct if the default is filled with the custom profile. */
2698  miter_profile = (bndv->is_arc_start || bndv->is_patch_start);
2699  /* Don't bother reversing the profile if it's a miter profile */
2700  reverse_profile = !bndv->is_profile_start && !miter_profile;
2701  }
2702  calculate_profile(bp, bndv, reverse_profile, miter_profile);
2703  } while ((bndv = bndv->next) != vm->boundstart);
2704 }
2705 
2706 /* Implements build_boundary for the vertex-only case. */
2707 static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool construct)
2708 {
2709  VMesh *vm = bv->vmesh;
2710 
2712 
2713  EdgeHalf *efirst = &bv->edges[0];
2714  EdgeHalf *e = efirst;
2715  do {
2716  float co[3];
2717  slide_dist(e, bv->v, e->offset_l, co);
2718  if (construct) {
2719  BoundVert *v = add_new_bound_vert(bp->mem_arena, vm, co);
2720  v->efirst = v->elast = e;
2721  e->leftv = e->rightv = v;
2722  }
2723  else {
2724  adjust_bound_vert(e->leftv, co);
2725  }
2726  } while ((e = e->next) != efirst);
2727 
2728  if (construct) {
2730  if (vm->count == 2) {
2731  vm->mesh_kind = M_NONE;
2732  }
2733  else if (bp->seg == 1) {
2734  vm->mesh_kind = M_POLY;
2735  }
2736  else {
2737  vm->mesh_kind = M_ADJ;
2738  }
2739  }
2740 }
2741 
2748  BevVert *bv,
2749  EdgeHalf *efirst,
2750  const bool construct)
2751 {
2752  MemArena *mem_arena = bp->mem_arena;
2753  VMesh *vm = bv->vmesh;
2754 
2755  EdgeHalf *e = efirst;
2756  float co[3];
2757  if (bv->edgecount == 2) {
2758  /* Only 2 edges in, so terminate the edge with an artificial vertex on the unbeveled edge.
2759  * If the offset type is BEVEL_AMT_PERCENT or BEVEL_AMT_ABSOLUTE, what to do is a bit
2760  * undefined (there aren't two "legs"), so just let the code do what it does. */
2761  const float *no = e->fprev ? e->fprev->no : (e->fnext ? e->fnext->no : NULL);
2762  offset_in_plane(e, no, true, co);
2763  if (construct) {
2764  BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co);
2765  bndv->efirst = bndv->elast = bndv->ebev = e;
2766  e->leftv = bndv;
2767  }
2768  else {
2769  adjust_bound_vert(e->leftv, co);
2770  }
2771  no = e->fnext ? e->fnext->no : (e->fprev ? e->fprev->no : NULL);
2772  offset_in_plane(e, no, false, co);
2773  if (construct) {
2774  BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co);
2775  bndv->efirst = bndv->elast = e;
2776  e->rightv = bndv;
2777  }
2778  else {
2779  adjust_bound_vert(e->rightv, co);
2780  }
2781  /* Make artificial extra point along unbeveled edge, and form triangle. */
2782  slide_dist(e->next, bv->v, e->offset_l, co);
2783  if (construct) {
2784  BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co);
2785  bndv->efirst = bndv->elast = e->next;
2786  e->next->leftv = e->next->rightv = bndv;
2788  }
2789  else {
2790  adjust_bound_vert(e->next->leftv, co);
2791  }
2792  }
2793  else {
2794  /* More than 2 edges in. Put on-edge verts on all the other edges and join with the beveled
2795  * edge to make a poly or adj mesh, because e->prev has offset 0, offset_meet will put co on
2796  * that edge. */
2797  /* TODO: should do something else if angle between e and e->prev > 180 */
2798  bool leg_slide = bp->offset_type == BEVEL_AMT_PERCENT || bp->offset_type == BEVEL_AMT_ABSOLUTE;
2799  if (leg_slide) {
2800  slide_dist(e->prev, bv->v, e->offset_l, co);
2801  }
2802  else {
2803  offset_meet(bp, e->prev, e, bv->v, e->fprev, false, co, NULL);
2804  }
2805  if (construct) {
2806  BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co);
2807  bndv->efirst = e->prev;
2808  bndv->elast = bndv->ebev = e;
2809  e->leftv = bndv;
2810  e->prev->leftv = e->prev->rightv = bndv;
2811  }
2812  else {
2813  adjust_bound_vert(e->leftv, co);
2814  }
2815  e = e->next;
2816  if (leg_slide) {
2817  slide_dist(e, bv->v, e->prev->offset_r, co);
2818  }
2819  else {
2820  offset_meet(bp, e->prev, e, bv->v, e->fprev, false, co, NULL);
2821  }
2822  if (construct) {
2823  BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co);
2824  bndv->efirst = e->prev;
2825  bndv->elast = e;
2826  e->leftv = e->rightv = bndv;
2827  e->prev->rightv = bndv;
2828  }
2829  else {
2830  adjust_bound_vert(e->leftv, co);
2831  }
2832  /* For the edges not adjacent to the beveled edge, slide the bevel amount along. */
2833  float d = efirst->offset_l_spec;
2834  if (bp->profile_type == BEVEL_PROFILE_CUSTOM || bp->profile < 0.25f) {
2835  d *= sqrtf(2.0f); /* Need to go further along the edge to make room for full profile area. */
2836  }
2837  for (e = e->next; e->next != efirst; e = e->next) {
2838  slide_dist(e, bv->v, d, co);
2839  if (construct) {
2840  BoundVert *bndv = add_new_bound_vert(mem_arena, vm, co);
2841  bndv->efirst = bndv->elast = e;
2842  e->leftv = e->rightv = bndv;
2843  }
2844  else {
2845  adjust_bound_vert(e->leftv, co);
2846  }
2847  }
2848  }
2849 
2850  if (bv->edgecount >= 3) {
2851  /* Special case: snap profile to plane of adjacent two edges. */
2852  BoundVert *bndv = vm->boundstart;
2853  BLI_assert(bndv->ebev != NULL);
2854  set_profile_params(bp, bv, bndv);
2855  move_profile_plane(bndv, bv->v);
2856  }
2857 
2858  if (construct) {
2860 
2861  if (vm->count == 2 && bv->edgecount == 3) {
2862  vm->mesh_kind = M_NONE;
2863  }
2864  else if (vm->count == 3) {
2865  bool use_tri_fan = true;
2866  if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
2867  /* Prevent overhanging edges: use M_POLY if the extra point is planar with the profile. */
2868  BoundVert *bndv = efirst->leftv;
2869  float profile_plane[4];
2870  plane_from_point_normal_v3(profile_plane, bndv->profile.plane_co, bndv->profile.plane_no);
2871  bndv = efirst->rightv->next; /* The added boundvert placed along the non-adjacent edge. */
2872  if (dist_squared_to_plane_v3(bndv->nv.co, profile_plane) < BEVEL_EPSILON_BIG) {
2873  use_tri_fan = false;
2874  }
2875  }
2876  vm->mesh_kind = (use_tri_fan) ? M_TRI_FAN : M_POLY;
2877  }
2878  else {
2879  vm->mesh_kind = M_POLY;
2880  }
2881  }
2882 }
2883 
2884 /* Helper for build_boundary to handle special miters. */
2885 static void adjust_miter_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
2886 {
2887  int miter_outer = bp->miter_outer;
2888 
2889  BoundVert *v1 = emiter->rightv;
2890  BoundVert *v2, *v3;
2891  if (miter_outer == BEVEL_MITER_PATCH) {
2892  v2 = v1->next;
2893  v3 = v2->next;
2894  }
2895  else {
2896  BLI_assert(miter_outer == BEVEL_MITER_ARC);
2897  v2 = NULL;
2898  v3 = v1->next;
2899  }
2900  BoundVert *v1prev = v1->prev;
2901  BoundVert *v3next = v3->next;
2902  float co2[3];
2903  copy_v3_v3(co2, v1->nv.co);
2904  if (v1->is_arc_start) {
2905  copy_v3_v3(v1->profile.middle, co2);
2906  }
2907 
2908  /* co1 is intersection of line through co2 in dir of emiter->e
2909  * and plane with normal the dir of emiter->e and through v1prev. */
2910  float co1[3], edge_dir[3], line_p[3];
2911  BMVert *vother = BM_edge_other_vert(emiter->e, bv->v);
2912  sub_v3_v3v3(edge_dir, bv->v->co, vother->co);
2913  normalize_v3(edge_dir);
2914  float d = bp->offset / (bp->seg / 2.0f); /* A fallback amount to move. */
2915  madd_v3_v3v3fl(line_p, co2, edge_dir, d);
2916  if (!isect_line_plane_v3(co1, co2, line_p, v1prev->nv.co, edge_dir)) {
2917  copy_v3_v3(co1, line_p);
2918  }
2919  adjust_bound_vert(v1, co1);
2920 
2921  /* co3 is similar, but plane is through v3next and line is other side of miter edge. */
2922  float co3[3];
2923  EdgeHalf *emiter_other = v3->elast;
2924  vother = BM_edge_other_vert(emiter_other->e, bv->v);
2925  sub_v3_v3v3(edge_dir, bv->v->co, vother->co);
2926  normalize_v3(edge_dir);
2927  madd_v3_v3v3fl(line_p, co2, edge_dir, d);
2928  if (!isect_line_plane_v3(co3, co2, line_p, v3next->nv.co, edge_dir)) {
2929  copy_v3_v3(co1, line_p);
2930  }
2931  adjust_bound_vert(v3, co3);
2932 }
2933 
2935 {
2936  BoundVert *vstart = bv->vmesh->boundstart;
2937  BoundVert *v = vstart;
2938  do {
2939  if (v->is_arc_start) {
2940  BoundVert *v3 = v->next;
2941  EdgeHalf *e = v->efirst;
2942  if (e != emiter) {
2943  float edge_dir[3], co[3];
2944  copy_v3_v3(co, v->nv.co);
2945  BMVert *vother = BM_edge_other_vert(e->e, bv->v);
2946  sub_v3_v3v3(edge_dir, vother->co, bv->v->co);
2947  normalize_v3(edge_dir);
2948  madd_v3_v3v3fl(v->nv.co, co, edge_dir, bp->spread);
2949  e = v3->elast;
2950  vother = BM_edge_other_vert(e->e, bv->v);
2951  sub_v3_v3v3(edge_dir, vother->co, bv->v->co);
2952  normalize_v3(edge_dir);
2953  madd_v3_v3v3fl(v3->nv.co, co, edge_dir, bp->spread);
2954  }
2955  v = v3->next;
2956  }
2957  else {
2958  v = v->next;
2959  }
2960  } while (v != vstart);
2961 }
2962 
2977 static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
2978 {
2979  MemArena *mem_arena = bp->mem_arena;
2980 
2981  /* Current bevel does nothing if only one edge into a vertex. */
2982  if (bv->edgecount <= 1) {
2983  return;
2984  }
2985 
2986  if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
2987  build_boundary_vertex_only(bp, bv, construct);
2988  return;
2989  }
2990 
2991  VMesh *vm = bv->vmesh;
2992 
2993  /* Find a beveled edge to be efirst. */
2994  EdgeHalf *efirst = next_bev(bv, NULL);
2995  BLI_assert(efirst->is_bev);
2996 
2997  if (bv->selcount == 1) {
2998  /* Special case: only one beveled edge in. */
2999  build_boundary_terminal_edge(bp, bv, efirst, construct);
3000  return;
3001  }
3002 
3003  /* Special miters outside only for 3 or more beveled edges. */
3004  int miter_outer = (bv->selcount >= 3) ? bp->miter_outer : BEVEL_MITER_SHARP;
3005  int miter_inner = bp->miter_inner;
3006 
3007  /* Keep track of the first beveled edge of an outside miter (there can be at most 1 per bv). */
3008  EdgeHalf *emiter = NULL;
3009 
3010  /* There is more than one beveled edge.
3011  * We make BoundVerts to connect the sides of the beveled edges.
3012  * Non-beveled edges in between will just join to the appropriate juncture point. */
3013  EdgeHalf *e = efirst;
3014  do {
3015  BLI_assert(e->is_bev);
3016  EdgeHalf *eon = NULL;
3017  /* Make the BoundVert for the right side of e; the other side will be made when the beveled
3018  * edge to the left of e is handled.
3019  * Analyze edges until next beveled edge: They are either "in plane" (preceding and subsequent
3020  * faces are coplanar) or not. The "non-in-plane" edges affect the silhouette and we prefer to
3021  * slide along one of those if possible. */
3022  int in_plane = 0; /* Counts of in-plane / not-in-plane. */
3023  int not_in_plane = 0;
3024  EdgeHalf *enip = NULL; /* Representatives of each type. */
3025  EdgeHalf *eip = NULL;
3026  EdgeHalf *e2;
3027  for (e2 = e->next; !e2->is_bev; e2 = e2->next) {
3028  if (eh_on_plane(e2)) {
3029  in_plane++;
3030  eip = e2;
3031  }
3032  else {
3033  not_in_plane++;
3034  enip = e2;
3035  }
3036  }
3037 
3038  float r, co[3];
3039  if (in_plane == 0 && not_in_plane == 0) {
3040  offset_meet(bp, e, e2, bv->v, e->fnext, false, co, NULL);
3041  }
3042  else if (not_in_plane > 0) {
3043  if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
3044  if (offset_on_edge_between(bp, e, e2, enip, bv->v, co, &r)) {
3045  eon = enip;
3046  }
3047  }
3048  else {
3049  offset_meet(bp, e, e2, bv->v, NULL, true, co, eip);
3050  }
3051  }
3052  else {
3053  /* n_in_plane > 0 and n_not_in_plane == 0. */
3054  if (bp->loop_slide && in_plane == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) {
3055  if (offset_on_edge_between(bp, e, e2, eip, bv->v, co, &r)) {
3056  eon = eip;
3057  }
3058  }
3059  else {
3060  /* Since all edges between e and e2 are in the same plane, it is OK
3061  * to treat this like the case where there are no edges between. */
3062  offset_meet(bp, e, e2, bv->v, e->fnext, false, co, NULL);
3063  }
3064  }
3065 
3066  if (construct) {
3068  v->efirst = e;
3069  v->elast = e2;
3070  v->ebev = e2;
3071  v->eon = eon;
3072  if (eon) {
3073  v->sinratio = r;
3074  }
3075  e->rightv = v;
3076  e2->leftv = v;
3077  for (EdgeHalf *e3 = e->next; e3 != e2; e3 = e3->next) {
3078  e3->leftv = e3->rightv = v;
3079  }
3080  AngleKind ang_kind = edges_angle_kind(e, e2, bv->v);
3081 
3082  /* Are we doing special mitering?
3083  * There can only be one outer reflex angle, so only one outer miter,
3084  * and emiter will be set to the first edge of such an edge.
3085  * A miter kind of BEVEL_MITER_SHARP means no special miter */
3086  if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) ||
3087  (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) {
3088  if (ang_kind == ANGLE_LARGER) {
3089  emiter = e;
3090  }
3091  /* Make one or two more boundverts; for now all will have same co. */
3092  BoundVert *v1 = v;
3093  v1->ebev = NULL;
3094  BoundVert *v2;
3095  if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
3096  v2 = add_new_bound_vert(mem_arena, vm, co);
3097  }
3098  else {
3099  v2 = NULL;
3100  }
3101  BoundVert *v3 = add_new_bound_vert(mem_arena, vm, co);
3102  v3->ebev = e2;
3103  v3->efirst = e2;
3104  v3->elast = e2;
3105  v3->eon = NULL;
3106  e2->leftv = v3;
3107  if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
3108  v1->is_patch_start = true;
3109  v2->eon = v1->eon;
3110  v2->sinratio = v1->sinratio;
3111  v2->ebev = NULL;
3112  v1->eon = NULL;
3113  v1->sinratio = 1.0f;
3114  v1->elast = e;
3115  if (e->next == e2) {
3116  v2->efirst = NULL;
3117  v2->elast = NULL;
3118  }
3119  else {
3120  v2->efirst = e->next;
3121  for (EdgeHalf *e3 = e->next; e3 != e2; e3 = e3->next) {
3122  e3->leftv = e3->rightv = v2;
3123  v2->elast = e3;
3124  }
3125  }
3126  }
3127  else {
3128  v1->is_arc_start = true;
3129  copy_v3_v3(v1->profile.middle, co);
3130  if (e->next == e2) {
3131  v1->elast = v1->efirst;
3132  }
3133  else {
3134  int between = in_plane + not_in_plane;
3135  int bet2 = between / 2;
3136  bool betodd = (between % 2) == 1;
3137  int i = 0;
3138  /* Put first half of in-between edges at index 0, second half at index bp->seg.
3139  * If between is odd, put middle one at mid-index. */
3140  for (EdgeHalf *e3 = e->next; e3 != e2; e3 = e3->next) {
3141  v1->elast = e3;
3142  if (i < bet2) {
3143  e3->profile_index = 0;
3144  }
3145  else if (betodd && i == bet2) {
3146  e3->profile_index = bp->seg / 2;
3147  }
3148  else {
3149  e3->profile_index = bp->seg;
3150  }
3151  i++;
3152  }
3153  }
3154  }
3155  }
3156  }
3157  else { /* construct == false. */
3158  AngleKind ang_kind = edges_angle_kind(e, e2, bv->v);
3159  if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) ||
3160  (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) {
3161  if (ang_kind == ANGLE_LARGER) {
3162  emiter = e;
3163  }
3164  BoundVert *v1 = e->rightv;
3165  BoundVert *v2;
3166  BoundVert *v3;
3167  if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
3168  v2 = v1->next;
3169  v3 = v2->next;
3170  }
3171  else {
3172  v2 = NULL;
3173  v3 = v1->next;
3174  }
3175  adjust_bound_vert(v1, co);
3176  if (v2) {
3177  adjust_bound_vert(v2, co);
3178  }
3179  adjust_bound_vert(v3, co);
3180  }
3181  else {
3182  adjust_bound_vert(e->rightv, co);
3183  }
3184  }
3185  e = e2;
3186  } while (e != efirst);
3187 
3188  if (miter_inner != BEVEL_MITER_SHARP) {
3189  adjust_miter_inner_coords(bp, bv, emiter);
3190  }
3191  if (emiter) {
3192  adjust_miter_coords(bp, bv, emiter);
3193  }
3194 
3195  if (construct) {
3196  set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
3197 
3198  if (vm->count == 2) {
3199  vm->mesh_kind = M_NONE;
3200  }
3201  else if (efirst->seg == 1) {
3202  vm->mesh_kind = M_POLY;
3203  }
3204  else {
3205  switch (bp->vmesh_method) {
3206  case BEVEL_VMESH_ADJ:
3207  vm->mesh_kind = M_ADJ;
3208  break;
3209  case BEVEL_VMESH_CUTOFF:
3210  vm->mesh_kind = M_CUTOFF;
3211  break;
3212  }
3213  }
3214  }
3215 }
3216 
3217 #ifdef DEBUG_ADJUST
3218 static void print_adjust_stats(BoundVert *vstart)
3219 {
3220  printf("\nSolution analysis\n");
3221  double even_residual2 = 0.0;
3222  double spec_residual2 = 0.0;
3223  double max_even_r = 0.0;
3224  double max_even_r_pct = 0.0;
3225  double max_spec_r = 0.0;
3226  double max_spec_r_pct = 0.0;
3227  printf("width matching\n");
3228  BoundVert *v = vstart;
3229  do {
3230  if (v->adjchain != NULL) {
3231  EdgeHalf *eright = v->efirst;
3232  EdgeHalf *eleft = v->adjchain->elast;
3233  double delta = fabs(eright->offset_r - eleft->offset_l);
3234  double delta_pct = 100.0 * delta / eright->offset_r_spec;
3235  printf("e%d r(%f) vs l(%f): abs(delta)=%f, delta_pct=%f\n",
3236  BM_elem_index_get(eright->e),
3237  eright->offset_r,
3238  eleft->offset_l,
3239  delta,
3240  delta_pct);
3241  even_residual2 += delta * delta;
3242  if (delta > max_even_r) {
3243  max_even_r = delta;
3244  }
3245  if (delta_pct > max_even_r_pct) {
3246  max_even_r_pct = delta_pct;
3247  }
3248  }
3249  v = v->adjchain;
3250  } while (v && v != vstart);
3251 
3252  printf("spec matching\n");
3253  v = vstart;
3254  do {
3255  if (v->adjchain != NULL) {
3256  EdgeHalf *eright = v->efirst;
3257  EdgeHalf *eleft = v->adjchain->elast;
3258  double delta = eright->offset_r - eright->offset_r_spec;
3259  double delta_pct = 100.0 * delta / eright->offset_r_spec;
3260  printf("e%d r(%f) vs r spec(%f): delta=%f, delta_pct=%f\n",
3261  BM_elem_index_get(eright->e),
3262  eright->offset_r,
3263  eright->offset_r_spec,
3264  delta,
3265  delta_pct);
3266  spec_residual2 += delta * delta;
3267  delta = fabs(delta);
3268  delta_pct = fabs(delta_pct);
3269  if (delta > max_spec_r) {
3270  max_spec_r = delta;
3271  }
3272  if (delta_pct > max_spec_r_pct) {
3273  max_spec_r_pct = delta_pct;
3274  }
3275 
3276  delta = eleft->offset_l - eleft->offset_l_spec;
3277  delta_pct = 100.0 * delta / eright->offset_l_spec;
3278  printf("e%d l(%f) vs l spec(%f): delta=%f, delta_pct=%f\n",
3279  BM_elem_index_get(eright->e),
3280  eleft->offset_l,
3281  eleft->offset_l_spec,
3282  delta,
3283  delta_pct);
3284  spec_residual2 += delta * delta;
3285  delta = fabs(delta);
3286  delta_pct = fabs(delta_pct);
3287  if (delta > max_spec_r) {
3288  max_spec_r = delta;
3289  }
3290  if (delta_pct > max_spec_r_pct) {
3291  max_spec_r_pct = delta_pct;
3292  }
3293  }
3294  v = v->adjchain;
3295  } while (v && v != vstart);
3296 
3297  printf("Analysis Result:\n");
3298  printf("even residual2 = %f, spec residual2 = %f\n", even_residual2, spec_residual2);
3299  printf("max even delta = %f, max as percent of spec = %f\n", max_even_r, max_even_r_pct);
3300  printf("max spec delta = %f, max as percent of spec = %f\n", max_spec_r, max_spec_r_pct);
3301 }
3302 #endif
3303 
3304 #ifdef FAST_ADJUST_CODE
3305 /* This code uses a direct solution to the adjustment problem for chains and certain cycles.
3306  * It is a two-step approach: first solve for the exact solution of the 'match widths' constraints
3307  * using the one degree of freedom that allows for expressing all other widths in terms of that.
3308  * And then minimize the spec-matching constraints using the derivative of the least squares
3309  * residual in terms of that one degree of freedom.
3310  * Unfortunately, the results are in some cases worse than the general least squares solution
3311  * for the combined (with weights) problem, so this code is not used.
3312  * But keep it here for a while in case performance issues demand that it be used sometimes. */
3313 static bool adjust_the_cycle_or_chain_fast(BoundVert *vstart, int np, bool iscycle)
3314 {
3315  float *g = MEM_mallocN(np * sizeof(float), "beveladjust");
3316  float *g_prod = MEM_mallocN(np * sizeof(float), "beveladjust");
3317 
3318  BoundVert *v = vstart;
3319  float spec_sum = 0.0f;
3320  int i = 0;
3321  do {
3322  g[i] = v->sinratio;
3323  if (iscycle || v->adjchain != NULL) {
3324  spec_sum += v->efirst->offset_r;
3325  }
3326  else {
3327  spec_sum += v->elast->offset_l;
3328  }
3329  i++;
3330  v = v->adjchain;
3331  } while (v && v != vstart);
3332 
3333  float gprod = 1.00f;
3334  float gprod_sum = 1.0f;
3335  for (i = np - 1; i > 0; i--) {
3336  gprod *= g[i];
3337  g_prod[i] = gprod;
3338  gprod_sum += gprod;
3339  }
3340  g_prod[0] = 1.0f;
3341  if (iscycle) {
3342  gprod *= g[0];
3343  if (fabs(gprod - 1.0f) > BEVEL_EPSILON) {
3344  /* Fast cycle calc only works if total product is 1. */
3345  MEM_freeN(g);
3346  MEM_freeN(g_prod);
3347  return false;
3348  }
3349  }
3350  if (gprod_sum == 0.0f) {
3351  MEM_freeN(g);
3352  MEM_freeN(g_prod);
3353  return false;
3354  }
3355  float p = spec_sum / gprod_sum;
3356 
3357  /* Apply the new offsets. */
3358  v = vstart;
3359  i = 0;
3360  do {
3361  if (iscycle || v->adjchain != NULL) {
3362  EdgeHalf *eright = v->efirst;
3363  EdgeHalf *eleft = v->elast;
3364  eright->offset_r = g_prod[(i + 1) % np] * p;
3365  if (iscycle || v != vstart) {
3366  eleft->offset_l = v->sinratio * eright->offset_r;
3367  }
3368  }
3369  else {
3370  /* Not a cycle, and last of chain. */
3371  EdgeHalf *eleft = v->elast;
3372  eleft->offset_l = p;
3373  }
3374  i++;
3375  v = v->adjchain;
3376  } while (v && v != vstart);
3377 
3378  MEM_freeN(g);
3379  MEM_freeN(g_prod);
3380  return true;
3381 }
3382 #endif
3383 
3397  EdgeHalf *start_edge,
3398  bool toward_bv,
3399  BevVert **r_bv)
3400 {
3401  /* Case 1: The next EdgeHalf is the other side of the BMEdge.
3402  * It's part of the same BMEdge, so we know the other EdgeHalf is also beveled. */
3403  if (!toward_bv) {
3404  return find_other_end_edge_half(bp, start_edge, r_bv);
3405  }
3406 
3407  /* Case 2: The next EdgeHalf is across a BevVert from the current EdgeHalf. */
3408  /* Skip all the logic if there's only one beveled edge at the vertex, we're at an end. */
3409  if ((*r_bv)->selcount == 1) {
3410  return NULL; /* No other edges to go to. */
3411  }
3412 
3413  /* The case with only one other edge connected to the vertex is special too. */
3414  if ((*r_bv)->selcount == 2) {
3415  /* Just find the next beveled edge, that's the only other option. */
3416  EdgeHalf *new_edge = start_edge;
3417  do {
3418  new_edge = new_edge->next;
3419  } while (!new_edge->is_bev);
3420 
3421  return new_edge;
3422  }
3423 
3424  /* Find the direction vector of the current edge (pointing INTO the BevVert).
3425  * v1 and v2 don't necessarily have an order, so we need to check which is closer to bv. */
3426  float dir_start_edge[3];
3427  if (start_edge->e->v1 == (*r_bv)->v) {
3428  sub_v3_v3v3(dir_start_edge, start_edge->e->v1->co, start_edge->e->v2->co);
3429  }
3430  else {
3431  sub_v3_v3v3(dir_start_edge, start_edge->e->v2->co, start_edge->e->v1->co);
3432  }
3433  normalize_v3(dir_start_edge);
3434 
3435  /* Find the beveled edge coming out of the BevVert that's most parallel to the current edge. */
3436  EdgeHalf *new_edge = start_edge->next;
3437  float second_best_dot = 0.0f, best_dot = 0.0f;
3438  EdgeHalf *next_edge = NULL;
3439  while (new_edge != start_edge) {
3440  if (!new_edge->is_bev) {
3441  new_edge = new_edge->next;
3442  continue;
3443  }
3444  /* Find direction vector of the possible next edge (pointing OUT of the BevVert). */
3445  float dir_new_edge[3];
3446  if (new_edge->e->v2 == (*r_bv)->v) {
3447  sub_v3_v3v3(dir_new_edge, new_edge->e->v1->co, new_edge->e->v2->co);
3448  }
3449  else {
3450  sub_v3_v3v3(dir_new_edge, new_edge->e->v2->co, new_edge->e->v1->co);
3451  }
3452  normalize_v3(dir_new_edge);
3453 
3454  /* Use this edge if it is the most parallel to the original so far. */
3455  float new_dot = dot_v3v3(dir_new_edge, dir_start_edge);
3456  if (new_dot > best_dot) {
3457  second_best_dot = best_dot; /* For remembering if the choice was too close. */
3458  best_dot = new_dot;
3459  next_edge = new_edge;
3460  }
3461  else if (new_dot > second_best_dot) {
3462  second_best_dot = new_dot;
3463  }
3464 
3465  new_edge = new_edge->next;
3466  }
3467 
3468  /* Only return a new Edge if one was found and if the choice of next edge was not too close. */
3469  if ((next_edge != NULL) && compare_ff(best_dot, second_best_dot, BEVEL_SMALL_ANG_DOT)) {
3470  return NULL;
3471  }
3472  return next_edge;
3473 }
3474 
3482 {
3483  BevVert *start_bv = find_bevvert(bp, bme->v1);
3484  EdgeHalf *start_edgehalf = find_edge_half(start_bv, bme);
3485  if (!start_edgehalf->is_bev || start_edgehalf->visited_rpo) {
3486  return;
3487  }
3488 
3489  /* Pick a BoundVert on one side of the profile to use for the starting side. Use the one highest
3490  * on the Z axis because even any rule is better than an arbitrary decision. */
3491  bool right_highest = start_edgehalf->leftv->nv.co[2] < start_edgehalf->rightv->nv.co[2];
3492  start_edgehalf->leftv->is_profile_start = right_highest;
3493  start_edgehalf->visited_rpo = true;
3494 
3495  /* First loop starts in the away from BevVert direction and the second starts toward it. */
3496  for (int i = 0; i < 2; i++) {
3497  EdgeHalf *edgehalf = start_edgehalf;
3498  BevVert *bv = start_bv;
3499  bool toward_bv = (i == 0);
3500  edgehalf = next_edgehalf_bev(bp, edgehalf, toward_bv, &bv);
3501 
3502  /* Keep traveling until there is no unvisited beveled edgehalf to visit next. */
3503  while (edgehalf && !edgehalf->visited_rpo) {
3504  /* Mark the correct BoundVert as the start of the newly visited profile.
3505  * The direction relative to the BevVert switches every step, so also switch
3506  * the orientation every step. */
3507  if (i == 0) {
3508  edgehalf->leftv->is_profile_start = toward_bv ^ right_highest;
3509  }
3510  else {
3511  /* The opposite side as the first direction because we're moving the other way. */
3512  edgehalf->leftv->is_profile_start = (!toward_bv) ^ right_highest;
3513  }
3514 
3515  /* The next jump will in the opposite direction relative to the BevVert. */
3516  toward_bv = !toward_bv;
3517 
3518  edgehalf->visited_rpo = true;
3519  edgehalf = next_edgehalf_bev(bp, edgehalf, toward_bv, &bv);
3520  }
3521  }
3522 }
3523 
3532 static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle)
3533 {
3534  int np = 0;
3535 #ifdef DEBUG_ADJUST
3536  printf("\nadjust the %s (with eigen)\n", iscycle ? "cycle" : "chain");
3537 #endif
3538  BoundVert *v = vstart;
3539  do {
3540 #ifdef DEBUG_ADJUST
3541  eleft = v->elast;
3542  eright = v->efirst;
3543  printf(" (left=e%d, right=e%d)", BM_elem_index_get(eleft->e), BM_elem_index_get(eright->e));
3544 #endif
3545  np++;
3546  v = v->adjchain;
3547  } while (v && v != vstart);
3548 #ifdef DEBUG_ADJUST
3549  printf(" -> %d parms\n", np);
3550 #endif
3551 
3552 #ifdef FAST_ADJUST_CODE
3553  if (adjust_the_cycle_or_chain_fast(vstart, np, iscycle)) {
3554  return;
3555  }
3556 #endif
3557 
3558  int nrows = iscycle ? 3 * np : 3 * np - 3;
3559 
3560  LinearSolver *solver = EIG_linear_least_squares_solver_new(nrows, np, 1);
3561 
3562  v = vstart;
3563  int i = 0;
3564  /* Sqrt of factor to weight down importance of spec match. */
3565  double weight = BEVEL_MATCH_SPEC_WEIGHT;
3566  EdgeHalf *eleft, *eright, *enextleft;
3567  do {
3568  /* Except at end of chain, v's indep variable is offset_r of v->efirst. */
3569  if (iscycle || i < np - 1) {
3570  eright = v->efirst;
3571  eleft = v->elast;
3572  enextleft = v->adjchain->elast;
3573 #ifdef DEBUG_ADJUST
3574  printf("p%d: e%d->offset_r = %f\n", i, BM_elem_index_get(eright->e), eright->offset_r);
3575  if (iscycle || v != vstart) {
3576  printf(" dependent: e%d->offset_l = %f * p%d\n",
3577  BM_elem_index_get(eleft->e),
3578  v->sinratio,
3579  i);
3580  }
3581 #endif
3582 
3583  /* Residue i: width difference between eright and eleft of next. */
3584  EIG_linear_solver_matrix_add(solver, i, i, 1.0);
3585  EIG_linear_solver_right_hand_side_add(solver, 0, i, 0.0);
3586  if (iscycle) {
3587  EIG_linear_solver_matrix_add(solver, i > 0 ? i - 1 : np - 1, i, -v->sinratio);
3588  }
3589  else {
3590  if (i > 0) {
3591  EIG_linear_solver_matrix_add(solver, i - 1, i, -v->sinratio);
3592  }
3593  }
3594 
3595  /* Residue np + 2*i (if cycle) else np - 1 + 2*i:
3596  * right offset for parameter i matches its spec; weighted. */
3597  int row = iscycle ? np + 2 * i : np - 1 + 2 * i;
3598  EIG_linear_solver_matrix_add(solver, row, i, weight);
3599  EIG_linear_solver_right_hand_side_add(solver, 0, row, weight * eright->offset_r);
3600 #ifdef DEBUG_ADJUST
3601  printf("b[%d]=%f * %f, for e%d->offset_r\n",
3602  row,
3603  weight,
3604  eright->offset_r,
3605  BM_elem_index_get(eright->e));
3606 #endif
3607 
3608  /* Residue np + 2*i + 1 (if cycle) else np - 1 + 2*i + 1:
3609  * left offset for parameter i matches its spec; weighted. */
3610  row = row + 1;
3612  solver, row, (i == np - 1) ? 0 : i + 1, weight * v->adjchain->sinratio);
3613  EIG_linear_solver_right_hand_side_add(solver, 0, row, weight * enextleft->offset_l);
3614 #ifdef DEBUG_ADJUST
3615  printf("b[%d]=%f * %f, for e%d->offset_l\n",
3616  row,
3617  weight,
3618  enextleft->offset_l,
3619  BM_elem_index_get(enextleft->e));
3620 #endif
3621  }
3622  else {
3623  /* Not a cycle, and last of chain. */
3624  eleft = v->elast;
3625 #ifdef DEBUG_ADJUST
3626  printf("p%d: e%d->offset_l = %f\n", i, BM_elem_index_get(eleft->e), eleft->offset_l);
3627 #endif
3628  /* Second part of residue i for last i. */
3629  EIG_linear_solver_matrix_add(solver, i - 1, i, -1.0);
3630  }
3631  i++;
3632  v = v->adjchain;
3633  } while (v && v != vstart);
3634  EIG_linear_solver_solve(solver);
3635 #ifdef DEBUG_ADJUST
3636  /* NOTE: this print only works after solve, but by that time b has been cleared. */
3638  printf("\nSolution:\n");
3639  for (i = 0; i < np; i++) {
3640  printf("p%d = %f\n", i, EIG_linear_solver_variable_get(solver, 0, i));
3641  }
3642 #endif
3643 
3644  /* Use the solution to set new widths. */
3645  v = vstart;
3646  i = 0;
3647  do {
3648  double val = EIG_linear_solver_variable_get(solver, 0, i);
3649  if (iscycle || i < np - 1) {
3650  eright = v->efirst;
3651  eleft = v->elast;
3652  eright->offset_r = (float)val;
3653 #ifdef DEBUG_ADJUST
3654  printf("e%d->offset_r = %f\n", BM_elem_index_get(eright->e), eright->offset_r);
3655 #endif
3656  if (iscycle || v != vstart) {
3657  eleft->offset_l = (float)(v->sinratio * val);
3658 #ifdef DEBUG_ADJUST
3659  printf("e%d->offset_l = %f\n", BM_elem_index_get(eleft->e), eleft->offset_l);
3660 #endif
3661  }
3662  }
3663  else {
3664  /* Not a cycle, and last of chain. */
3665  eleft = v->elast;
3666  eleft->offset_l = (float)val;
3667 #ifdef DEBUG_ADJUST
3668  printf("e%d->offset_l = %f\n", BM_elem_index_get(eleft->e), eleft->offset_l);
3669 #endif
3670  }
3671  i++;
3672  v = v->adjchain;
3673  } while (v && v != vstart);
3674 
3675 #ifdef DEBUG_ADJUST
3676  print_adjust_stats(vstart);
3678 #endif
3679 
3680  EIG_linear_solver_delete(solver);
3681 }
3682 
3695 {
3696  /* Find and process chains and cycles of unvisited BoundVerts that have eon set. */
3697  /* NOTE: for repeatability, iterate over all verts of mesh rather than over ghash'ed BMVerts. */
3698  BMIter iter;
3699  BMVert *bmv;
3700  BM_ITER_MESH (bmv, &iter, bm, BM_VERTS_OF_MESH) {
3701  if (!BM_elem_flag_test(bmv, BM_ELEM_TAG)) {
3702  continue;
3703  }
3704  BevVert *bv = find_bevvert(bp, bmv);
3705  BevVert *bvcur = bv;
3706  if (!bv) {
3707  continue;
3708  }
3709  BoundVert *vanchor = bv->vmesh->boundstart;
3710  do {
3711  if (vanchor->visited || !vanchor->eon) {
3712  continue;
3713  }
3714 
3715  /* Find one of (1) a cycle that starts and ends at v
3716  * where each v has v->eon set and had not been visited before;
3717  * or (2) a chain of v's where the start and end of the chain do not have
3718  * v->eon set but all else do.
3719  * It is OK for the first and last elements to
3720  * have been visited before, but not any of the inner ones.
3721  * We chain the v's together through v->adjchain, and are following
3722  * them in left->right direction, meaning that the left side of one edge
3723  * pairs with the right side of the next edge in the cycle or chain. */
3724 
3725  /* First follow paired edges in left->right direction. */
3726  BoundVert *v, *vchainstart, *vchainend;
3727  v = vchainstart = vchainend = vanchor;
3728 
3729  bool iscycle = false;
3730  int chainlen = 1;
3731  while (v->eon && !v->visited && !iscycle) {
3732  v->visited = true;
3733  if (!v->efirst) {
3734  break;
3735  }
3736  EdgeHalf *enext = find_other_end_edge_half(bp, v->efirst, &bvcur);
3737  if (!enext) {
3738  break;
3739  }
3740  BLI_assert(enext != NULL);
3741  BoundVert *vnext = enext->leftv;
3742  v->adjchain = vnext;
3743  vchainend = vnext;
3744  chainlen++;
3745  if (vnext->visited) {
3746  if (vnext != vchainstart) {
3747  break;
3748  }
3749  adjust_the_cycle_or_chain(vchainstart, true);
3750  iscycle = true;
3751  }
3752  v = vnext;
3753  }
3754  if (!iscycle) {
3755  /* right->left direction, changing vchainstart at each step. */
3756  v->adjchain = NULL;
3757  v = vchainstart;
3758  bvcur = bv;
3759  do {
3760  v->visited = true;
3761  if (!v->elast) {
3762  break;
3763  }
3764  EdgeHalf *enext = find_other_end_edge_half(bp, v->elast, &bvcur);
3765  if (!enext) {
3766  break;
3767  }
3768  BoundVert *vnext = enext->rightv;
3769  vnext->adjchain = v;
3770  chainlen++;
3771  vchainstart = vnext;
3772  v = vnext;
3773  } while (!v->visited && v->eon);
3774  if (chainlen >= 3 && !vchainstart->eon && !vchainend->eon) {
3775  adjust_the_cycle_or_chain(vchainstart, false);
3776  }
3777  }
3778  } while ((vanchor = vanchor->next) != bv->vmesh->boundstart);
3779  }
3780 
3781  /* Rebuild boundaries with new width specs. */
3782  BM_ITER_MESH (bmv, &iter, bm, BM_VERTS_OF_MESH) {
3783  if (BM_elem_flag_test(bmv, BM_ELEM_TAG)) {
3784  BevVert *bv = find_bevvert(bp, bmv);
3785  if (bv) {
3786  build_boundary(bp, bv, false);
3787  }
3788  }
3789  }
3790 }
3791 
3802 {
3803  VMesh *vm = bv->vmesh;
3804  if (vm->count < 3 || vm->count > 4 || bv->selcount < 3 || bv->selcount > 4) {
3805  return NULL;
3806  }
3807 
3808  /* Find v1, v2, v3 all with beveled edges, where v1 and v3 have collinear edges. */
3809  EdgeHalf *epipe = NULL;
3810  BoundVert *v1 = vm->boundstart;
3811  float dir1[3], dir3[3];
3812  do {
3813  BoundVert *v2 = v1->next;
3814  BoundVert *v3 = v2->next;
3815  if (v1->ebev && v2->ebev && v3->ebev) {
3816  sub_v3_v3v3(dir1, bv->v->co, BM_edge_other_vert(v1->ebev->e, bv->v)->co);
3817  sub_v3_v3v3(dir3, BM_edge_other_vert(v3->ebev->e, bv->v)->co, bv->v->co);
3818  normalize_v3(dir1);
3819  normalize_v3(dir3);
3820  if (angle_normalized_v3v3(dir1, dir3) < BEVEL_EPSILON_ANG) {
3821  epipe = v1->ebev;
3822  break;
3823  }
3824  }
3825  } while ((v1 = v1->next) != vm->boundstart);
3826 
3827  if (!epipe) {
3828  return NULL;
3829  }
3830 
3831  /* Check face planes: all should have normals perpendicular to epipe. */
3832  for (EdgeHalf *e = &bv->edges[0]; e != &bv->edges[bv->edgecount]; e++) {
3833  if (e->fnext) {
3834  if (fabsf(dot_v3v3(dir1, e->fnext->no)) > BEVEL_EPSILON_BIG) {
3835  return NULL;
3836  }
3837  }
3838  }
3839  return v1;
3840 }
3841 
3843 {
3844  VMesh *vm = (VMesh *)BLI_memarena_alloc(mem_arena, sizeof(VMesh));
3845  vm->count = count;
3846  vm->seg = seg;
3847  vm->boundstart = bounds;
3849  sizeof(NewVert) * count * (1 + seg / 2) * (1 + seg));
3850  vm->mesh_kind = M_ADJ;
3851  return vm;
3852 }
3853 
3864 static NewVert *mesh_vert_canon(VMesh *vm, int i, int j, int k)
3865 {
3866  int n = vm->count;
3867  int ns = vm->seg;
3868  int ns2 = ns / 2;
3869  int odd = ns % 2;
3870  BLI_assert(0 <= i && i <= n && 0 <= j && j <= ns && 0 <= k && k <= ns);
3871 
3872  if (!odd && j == ns2 && k == ns2) {
3873  return mesh_vert(vm, 0, j, k);
3874  }
3875  if (j <= ns2 - 1 + odd && k <= ns2) {
3876  return mesh_vert(vm, i, j, k);
3877  }
3878  if (k <= ns2) {
3879  return mesh_vert(vm, (i + n - 1) % n, k, ns - j);
3880  }
3881  return mesh_vert(vm, (i + 1) % n, ns - k, j);
3882 }
3883 
3884 static bool is_canon(VMesh *vm, int i, int j, int k)
3885 {
3886  int ns2 = vm->seg / 2;
3887  if (vm->seg % 2 == 1) { /* Odd. */
3888  return (j <= ns2 && k <= ns2);
3889  }
3890  /* Even. */
3891  return ((j < ns2 && k <= ns2) || (j == ns2 && k == ns2 && i == 0));
3892 }
3893 
3894 /* Copy the vertex data to all of vm verts from canonical ones. */
3896 {
3897  int n = vm->count;
3898  int ns = vm->seg;
3899  int ns2 = ns / 2;
3900  for (int i = 0; i < n; i++) {
3901  for (int j = 0; j <= ns2; j++) {
3902  for (int k = 0; k <= ns; k++) {
3903  if (is_canon(vm, i, j, k)) {
3904  continue;
3905  }
3906  NewVert *v1 = mesh_vert(vm, i, j, k);
3907  NewVert *v0 = mesh_vert_canon(vm, i, j, k);
3908  copy_v3_v3(v1->co, v0->co);
3909  v1->v = v0->v;
3910  }
3911  }
3912  }
3913 }
3914 
3915 /* Calculate and return in r_cent the centroid of the center poly. */
3916 static void vmesh_center(VMesh *vm, float r_cent[3])
3917 {
3918  int n = vm->count;
3919  int ns2 = vm->seg / 2;
3920  if (vm->seg % 2) {
3921  zero_v3(r_cent);
3922  for (int i = 0; i < n; i++) {
3923  add_v3_v3(r_cent, mesh_vert(vm, i, ns2, ns2)->co);
3924  }
3925  mul_v3_fl(r_cent, 1.0f / (float)n);
3926  }
3927  else {
3928  copy_v3_v3(r_cent, mesh_vert(vm, 0, ns2, ns2)->co);
3929  }
3930 }
3931 
3932 static void avg4(
3933  float co[3], const NewVert *v0, const NewVert *v1, const NewVert *v2, const NewVert *v3)
3934 {
3935  add_v3_v3v3(co, v0->co, v1->co);
3936  add_v3_v3(co, v2->co);
3937  add_v3_v3(co, v3->co);
3938  mul_v3_fl(co, 0.25f);
3939 }
3940 
3941 /* Gamma needed for smooth Catmull-Clark, Sabin modification. */
3942 static float sabin_gamma(int n)
3943 {
3944  /* pPrecalculated for common cases of n. */
3945  if (n < 3) {
3946  return 0.0f;
3947  }
3948  if (n == 3) {
3949  return 0.065247584f;
3950  }
3951  if (n == 4) {
3952  return 0.25f;
3953  }
3954  if (n == 5) {
3955  return 0.401983447f;
3956  }
3957  if (n == 6) {
3958  return 0.523423277f;
3959  }
3960  double k = cos(M_PI / (double)n);
3961  /* Need x, real root of x^3 + (4k^2 - 3)x - 2k = 0.
3962  * Answer calculated via Wolfram Alpha. */
3963  double k2 = k * k;
3964  double k4 = k2 * k2;
3965  double k6 = k4 * k2;
3966  double y = pow(M_SQRT3 * sqrt(64.0 * k6 - 144.0 * k4 + 135.0 * k2 - 27.0) + 9.0 * k, 1.0 / 3.0);
3967  double x = 0.480749856769136 * y - (0.231120424783545 * (12.0 * k2 - 9.0)) / y;
3968  return (k * x + 2.0 * k2 - 1.0) / (x * x * (k * x + 1.0));
3969 }
3970 
3971 /* Fill frac with fractions of the way along ring 0 for vertex i, for use with interp_range
3972  * function. */
3973 static void fill_vmesh_fracs(VMesh *vm, float *frac, int i)
3974 {
3975  float total = 0.0f;
3976 
3977  int ns = vm->seg;
3978  frac[0] = 0.0f;
3979  for (int k = 0; k < ns; k++) {
3980  total += len_v3v3(mesh_vert(vm, i, 0, k)->co, mesh_vert(vm, i, 0, k + 1)->co);
3981  frac[k + 1] = total;
3982  }
3983  if (total > 0.0f) {
3984  for (int k = 1; k <= ns; k++) {
3985  frac[k] /= total;
3986  }
3987  }
3988  else {
3989  frac[ns] = 1.0f;
3990  }
3991 }
3992 
3993 /* Like fill_vmesh_fracs but want fractions for profile points of bndv, with ns segments. */
3994 static void fill_profile_fracs(BevelParams *bp, BoundVert *bndv, float *frac, int ns)
3995 {
3996  float co[3], nextco[3];
3997  float total = 0.0f;
3998 
3999  frac[0] = 0.0f;
4000  copy_v3_v3(co, bndv->nv.co);
4001  for (int k = 0; k < ns; k++) {
4002  get_profile_point(bp, &bndv->profile, k + 1, ns, nextco);
4003  total += len_v3v3(co, nextco);
4004  frac[k + 1] = total;
4005  copy_v3_v3(co, nextco);
4006  }
4007  if (total > 0.0f) {
4008  for (int k = 1; k <= ns; k++) {
4009  frac[k] /= total;
4010  }
4011  }
4012  else {
4013  frac[ns] = 1.0f;
4014  }
4015 }
4016 
4017 /* Return i such that frac[i] <= f <= frac[i + 1], where frac[n] == 1.0
4018  * and put fraction of rest of way between frac[i] and frac[i + 1] into r_rest. */
4019 static int interp_range(const float *frac, int n, const float f, float *r_rest)
4020 {
4021  /* Could binary search in frac, but expect n to be reasonably small. */
4022  for (int i = 0; i < n; i++) {
4023  if (f <= frac[i + 1]) {
4024  float rest = f - frac[i];
4025  if (rest == 0) {
4026  *r_rest = 0.0f;
4027  }
4028  else {
4029  *r_rest = rest / (frac[i + 1] - frac[i]);
4030  }
4031  if (i == n - 1 && *r_rest == 1.0f) {
4032  i = n;
4033  *r_rest = 0.0f;
4034  }
4035  return i;
4036  }
4037  }
4038  *r_rest = 0.0f;
4039  return n;
4040 }
4041 
4042 /* Interpolate given vmesh to make one with target nseg border vertices on the profiles.
4043  * TODO(Hans): This puts the center mesh vert at a slightly off location sometimes, which seems to
4044  * be associated with the rest of that ring being shifted or connected slightly incorrectly to its
4045  * neighbors. */
4046 static VMesh *interp_vmesh(BevelParams *bp, VMesh *vm_in, int nseg)
4047 {
4048  int n_bndv = vm_in->count;
4049  int ns_in = vm_in->seg;
4050  int nseg2 = nseg / 2;
4051  int odd = nseg % 2;
4052  VMesh *vm_out = new_adj_vmesh(bp->mem_arena, n_bndv, nseg, vm_in->boundstart);
4053 
4054  float *prev_frac = BLI_array_alloca(prev_frac, (ns_in + 1));
4055  float *frac = BLI_array_alloca(frac, (ns_in + 1));
4056  float *new_frac = BLI_array_alloca(new_frac, (nseg + 1));
4057  float *prev_new_frac = BLI_array_alloca(prev_new_frac, (nseg + 1));
4058 
4059  fill_vmesh_fracs(vm_in, prev_frac, n_bndv - 1);
4060  BoundVert *bndv = vm_in->boundstart;
4061  fill_profile_fracs(bp, bndv->prev, prev_new_frac, nseg);
4062  for (int i = 0; i < n_bndv; i++) {
4063  fill_vmesh_fracs(vm_in, frac, i);
4064  fill_profile_fracs(bp, bndv, new_frac, nseg);
4065  for (int j = 0; j <= nseg2 - 1 + odd; j++) {
4066  for (int k = 0; k <= nseg2; k++) {
4067  /* Finding the locations where "fraction" fits into previous and current "frac". */
4068  float fraction = new_frac[k];
4069  float restk;
4070  float restkprev;
4071  int k_in = interp_range(frac, ns_in, fraction, &restk);
4072  fraction = prev_new_frac[nseg - j];
4073  int k_in_prev = interp_range(prev_frac, ns_in, fraction, &restkprev);
4074  int j_in = ns_in - k_in_prev;
4075  float restj = -restkprev;
4076  if (restj > -BEVEL_EPSILON) {
4077  restj = 0.0f;
4078  }
4079  else {
4080  j_in = j_in - 1;
4081  restj = 1.0f + restj;
4082  }
4083  /* Use bilinear interpolation within the source quad; could be smarter here. */
4084  float co[3];
4085  if (restj < BEVEL_EPSILON && restk < BEVEL_EPSILON) {
4086  copy_v3_v3(co, mesh_vert_canon(vm_in, i, j_in, k_in)->co);
4087  }
4088  else {
4089  int j0inc = (restj < BEVEL_EPSILON || j_in == ns_in) ? 0 : 1;
4090  int k0inc = (restk < BEVEL_EPSILON || k_in == ns_in) ? 0 : 1;
4091  float quad[4][3];
4092  copy_v3_v3(quad[0], mesh_vert_canon(vm_in, i, j_in, k_in)->co);
4093  copy_v3_v3(quad[1], mesh_vert_canon(vm_in, i, j_in, k_in + k0inc)->co);
4094  copy_v3_v3(quad[2], mesh_vert_canon(vm_in, i, j_in + j0inc, k_in + k0inc)->co);
4095  copy_v3_v3(quad[3], mesh_vert_canon(vm_in, i, j_in + j0inc, k_in)->co);
4096  interp_bilinear_quad_v3(quad, restk, restj, co);
4097  }
4098  copy_v3_v3(mesh_vert(vm_out, i, j, k)->co, co);
4099  }
4100  }
4101  bndv = bndv->next;
4102  memcpy(prev_frac, frac, sizeof(float) * (ns_in + 1));
4103  memcpy(prev_new_frac, new_frac, sizeof(float) * (nseg + 1));
4104  }
4105  if (!odd) {
4106  float center[3];
4107  vmesh_center(vm_in, center);
4108  copy_v3_v3(mesh_vert(vm_out, 0, nseg2, nseg2)->co, center);
4109  }
4110  vmesh_copy_equiv_verts(vm_out);
4111  return vm_out;
4112 }
4113 
4114 /* Do one step of cubic subdivision (Catmull-Clark), with special rules at boundaries.
4115  * For now, this is written assuming vm0->nseg is even and > 0.
4116  * We are allowed to modify vm_in, as it will not be used after this call.
4117  * See Levin 1999 paper: "Filling an N-sided hole using combined subdivision schemes". */
4118 static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
4119 {
4120  float co[3];
4121 
4122  int n_boundary = vm_in->count;
4123  int ns_in = vm_in->seg;
4124  int ns_in2 = ns_in / 2;
4125  BLI_assert(ns_in % 2 == 0);
4126  int ns_out = 2 * ns_in;
4127  VMesh *vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart);
4128 
4129  /* First we adjust the boundary vertices of the input mesh, storing in output mesh. */
4130  for (int i = 0; i < n_boundary; i++) {
4131  copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co);
4132  for (int k = 1; k < ns_in; k++) {
4133  copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co);
4134 
4135  /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */
4136  if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
4137  float co1[3], co2[3], acc[3];
4138  copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
4139  copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
4140 
4141  add_v3_v3v3(acc, co1, co2);
4142  madd_v3_v3fl(acc, co, -2.0f);
4143  madd_v3_v3fl(co, acc, -1.0f / 6.0f);
4144  }
4145 
4146  copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co);
4147  }
4148  }
4149  /* Now adjust odd boundary vertices in output mesh, based on even ones. */
4150  BoundVert *bndv = vm_out->boundstart;
4151  for (int i = 0; i < n_boundary; i++) {
4152  for (int k = 1; k < ns_out; k += 2) {
4153  get_profile_point(bp, &bndv->profile, k, ns_out, co);
4154 
4155  /* Smooth if using a non-custom profile. */
4156  if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
4157  float co1[3], co2[3], acc[3];
4158  copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co);
4159  copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co);
4160 
4161  add_v3_v3v3(acc, co1, co2);
4162  madd_v3_v3fl(acc, co, -2.0f);
4163  madd_v3_v3fl(co, acc, -1.0f / 6.0f);
4164  }
4165 
4166  copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co);
4167  }
4168  bndv = bndv->next;
4169  }
4170  vmesh_copy_equiv_verts(vm_out);
4171 
4172  /* Copy adjusted verts back into vm_in. */
4173  for (int i = 0; i < n_boundary; i++) {
4174  for (int k = 0; k < ns_in; k++) {
4175  copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co);
4176  }
4177  }
4178 
4179  vmesh_copy_equiv_verts(vm_in);
4180 
4181  /* Now we do the internal vertices, using standard Catmull-Clark
4182  * and assuming all boundary vertices have valence 4. */
4183 
4184  /* The new face vertices. */
4185  for (int i = 0; i < n_boundary; i++) {
4186  for (int j = 0; j < ns_in2; j++) {
4187  for (int k = 0; k < ns_in2; k++) {
4188  /* Face up and right from (j, k). */
4189  avg4(co,
4190  mesh_vert(vm_in, i, j, k),
4191  mesh_vert(vm_in, i, j, k + 1),
4192  mesh_vert(vm_in, i, j + 1, k),
4193  mesh_vert(vm_in, i, j + 1, k + 1));
4194  copy_v3_v3(mesh_vert(vm_out, i, 2 * j + 1, 2 * k + 1)->co, co);
4195  }
4196  }
4197  }
4198 
4199  /* The new vertical edge vertices. */
4200  for (int i = 0; i < n_boundary; i++) {
4201  for (int j = 0; j < ns_in2; j++) {
4202  for (int k = 1; k <= ns_in2; k++) {
4203  /* Vertical edge between (j, k) and (j+1, k). */
4204  avg4(co,
4205  mesh_vert(vm_in, i, j, k),
4206  mesh_vert(vm_in, i, j + 1, k),
4207  mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k - 1),
4208  mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
4209  copy_v3_v3(mesh_vert(vm_out, i, 2 * j + 1, 2 * k)->co, co);
4210  }
4211  }
4212  }
4213 
4214  /* The new horizontal edge vertices. */
4215  for (int i = 0; i < n_boundary; i++) {
4216  for (int j = 1; j < ns_in2; j++) {
4217  for (int k = 0; k < ns_in2; k++) {
4218  /* Horizontal edge between (j, k) and (j, k+1). */
4219  avg4(co,
4220  mesh_vert(vm_in, i, j, k),
4221  mesh_vert(vm_in, i, j, k + 1),
4222  mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k + 1),
4223  mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
4224  copy_v3_v3(mesh_vert(vm_out, i, 2 * j, 2 * k + 1)->co, co);
4225  }
4226  }
4227  }
4228 
4229  /* The new vertices, not on border. */
4230  float gamma = 0.25f;
4231  float beta = -gamma;
4232  for (int i = 0; i < n_boundary; i++) {
4233  for (int j = 1; j < ns_in2; j++) {
4234  for (int k = 1; k <= ns_in2; k++) {
4235  float co1[3], co2[3];
4236  /* co1 = centroid of adjacent new edge verts. */
4237  avg4(co1,
4238  mesh_vert_canon(vm_out, i, 2 * j, 2 * k - 1),
4239  mesh_vert_canon(vm_out, i, 2 * j, 2 * k + 1),
4240  mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k),
4241  mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k));
4242  /* co2 = centroid of adjacent new face verts. */
4243  avg4(co2,
4244  mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k - 1),
4245  mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k - 1),
4246  mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k + 1),
4247  mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
4248  /* Combine with original vert with alpha, beta, gamma factors. */
4249  copy_v3_v3(co, co1); /* Alpha = 1.0. */
4250  madd_v3_v3fl(co, co2, beta);
4251  madd_v3_v3fl(co, mesh_vert(vm_in, i, j, k)->co, gamma);
4252  copy_v3_v3(mesh_vert(vm_out, i, 2 * j, 2 * k)->co, co);
4253  }
4254  }
4255  }
4256 
4257  vmesh_copy_equiv_verts(vm_out);
4258 
4259  /* The center vertex is special. */
4260  gamma = sabin_gamma(n_boundary);
4261  beta = -gamma;
4262  /* Accumulate edge verts in co1, face verts in co2. */
4263  float co1[3], co2[3];
4264  zero_v3(co1);
4265  zero_v3(co2);
4266  for (int i = 0; i < n_boundary; i++) {
4267  add_v3_v3(co1, mesh_vert(vm_out, i, ns_in, ns_in - 1)->co);
4268  add_v3_v3(co2, mesh_vert(vm_out, i, ns_in - 1, ns_in - 1)->co);
4269  add_v3_v3(co2, mesh_vert(vm_out, i, ns_in - 1, ns_in + 1)->co);
4270  }
4271  copy_v3_v3(co, co1);
4272  mul_v3_fl(co, 1.0f / (float)n_boundary);
4273  madd_v3_v3fl(co, co2, beta / (2.0f * (float)n_boundary));
4274  madd_v3_v3fl(co, mesh_vert(vm_in, 0, ns_in2, ns_in2)->co, gamma);
4275  for (int i = 0; i < n_boundary; i++) {
4276  copy_v3_v3(mesh_vert(vm_out, i, ns_in, ns_in)->co, co);
4277  }
4278 
4279  /* Final step: Copy the profile vertices to the VMesh's boundary. */
4280  bndv = vm_out->boundstart;
4281  for (int i = 0; i < n_boundary; i++) {
4282  int inext = (i + 1) % n_boundary;
4283  for (int k = 0; k <= ns_out; k++) {
4284  get_profile_point(bp, &bndv->profile, k, ns_out, co);
4285  copy_v3_v3(mesh_vert(vm_out, i, 0, k)->co, co);
4286  if (k >= ns_in && k < ns_out) {
4287  copy_v3_v3(mesh_vert(vm_out, inext, ns_out - k, 0)->co, co);
4288  }
4289  }
4290  bndv = bndv->next;
4291  }
4292 
4293  return vm_out;
4294 }
4295 
4296 /* Special case for cube corner, when r is PRO_SQUARE_R, meaning straight sides. */
4298 {
4299  int ns2 = nseg / 2;
4300  VMesh *vm = new_adj_vmesh(mem_arena, 3, nseg, NULL);
4301  vm->count = 0; /* Reset, so the following loop will end up with correct count. */
4302  for (int i = 0; i < 3; i++) {
4303  float co[3] = {0.0f, 0.0f, 0.0f};
4304  co[i] = 1.0f;
4305  add_new_bound_vert(mem_arena, vm, co);
4306  }
4307  for (int i = 0; i < 3; i++) {
4308  for (int j = 0; j <= ns2; j++) {
4309  for (int k = 0; k <= ns2; k++) {
4310  if (!is_canon(vm, i, j, k)) {
4311  continue;
4312  }
4313  float co[3];
4314  co[i] = 1.0f;
4315  co[(i + 1) % 3] = (float)k * 2.0f / (float)nseg;
4316  co[(i + 2) % 3] = (float)j * 2.0f / (float)nseg;
4317  copy_v3_v3(mesh_vert(vm, i, j, k)->co, co);
4318  }
4319  }
4320  }
4322  return vm;
4323 }
4324 
4332 {
4333  int ns2 = nseg / 2;
4334  int odd = nseg % 2;
4335  VMesh *vm = new_adj_vmesh(mem_arena, 3, nseg, NULL);
4336  vm->count = 0; /* Reset, so following loop will end up with correct count. */
4337  for (int i = 0; i < 3; i++) {
4338  float co[3] = {0.0f, 0.0f, 0.0f};
4339  co[i] = 1.0f;
4340  add_new_bound_vert(mem_arena, vm, co);
4341  }
4342 
4343  float b;
4344  if (odd) {
4345  b = 2.0f / (2.0f * (float)ns2 + (float)M_SQRT2);
4346  }
4347  else {
4348  b = 2.0f / (float)nseg;
4349  }
4350  for (int i = 0; i < 3; i++) {
4351  for (int k = 0; k <= ns2; k++) {
4352  float co[3];
4353  co[i] = 1.0f - (float)k * b;
4354  co[(i + 1) % 3] = 0.0f;
4355  co[(i + 2) % 3] = 0.0f;
4356  copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co);
4357  co[(i + 1) % 3] = 1.0f - (float)k * b;
4358  co[(i + 2) % 3] = 0.0f;
4359  co[i] = 0.0f;
4360  copy_v3_v3(mesh_vert(vm, i, 0, nseg - k)->co, co);
4361  }
4362  }
4363  return vm;
4364 }
4365 
4373 {
4374  MemArena *mem_arena = bp->mem_arena;
4375  int nseg = bp->seg;
4376  float r = bp->pro_super_r;
4377 
4378  if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
4379  if (r == PRO_SQUARE_R) {
4380  return make_cube_corner_square(mem_arena, nseg);
4381  }
4382  if (r == PRO_SQUARE_IN_R) {
4383  return make_cube_corner_square_in(mem_arena, nseg);
4384  }
4385  }
4386 
4387  /* Initial mesh has 3 sides and 2 segments on each side. */
4388  VMesh *vm0 = new_adj_vmesh(mem_arena, 3, 2, NULL);
4389  vm0->count = 0; /* Reset, so the following loop will end up with correct count. */
4390  for (int i = 0; i < 3; i++) {
4391  float co[3] = {0.0f, 0.0f, 0.0f};
4392  co[i] = 1.0f;
4393  add_new_bound_vert(mem_arena, vm0, co);
4394  }
4395  BoundVert *bndv = vm0->boundstart;
4396  for (int i = 0; i < 3; i++) {
4397  float coc[3];
4398  /* Get point, 1/2 of the way around profile, on arc between this and next. */
4399  coc[i] = 1.0f;
4400  coc[(i + 1) % 3] = 1.0f;
4401  coc[(i + 2) % 3] = 0.0f;
4402  bndv->profile.super_r = r;
4403  copy_v3_v3(bndv->profile.start, bndv->nv.co);
4404  copy_v3_v3(bndv->profile.end, bndv->next->nv.co);
4405  copy_v3_v3(bndv->profile.middle, coc);
4406  copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->profile.start);
4407  copy_v3_v3(bndv->profile.plane_co, bndv->profile.start);
4408  cross_v3_v3v3(bndv->profile.plane_no, bndv->profile.start, bndv->profile.end);
4409  copy_v3_v3(bndv->profile.proj_dir, bndv->profile.plane_no);
4410  /* Calculate profiles again because we started over with new boundverts. */
4411  calculate_profile(bp, bndv, false, false); /* No custom profiles in this case. */
4412 
4413  /* Just building the boundaries here, so sample the profile halfway through. */
4414  get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co);
4415 
4416  bndv = bndv->next;
4417  }
4418  /* Center vertex. */
4419  float co[3];
4420  copy_v3_fl(co, (float)M_SQRT1_3);
4421 
4422  if (nseg > 2) {
4423  if (r > 1.5f) {
4424  mul_v3_fl(co, 1.4f);
4425  }
4426  else if (r < 0.75f) {
4427  mul_v3_fl(co, 0.6f);
4428  }
4429  }
4430  copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, co);
4431 
4433 
4434  VMesh *vm1 = vm0;
4435  while (vm1->seg < nseg) {
4436  vm1 = cubic_subdiv(bp, vm1);
4437  }
4438  if (vm1->seg != nseg) {
4439  vm1 = interp_vmesh(bp, vm1, nseg);
4440  }
4441 
4442  /* Now snap each vertex to the superellipsoid. */
4443  int ns2 = nseg / 2;
4444  for (int i = 0; i < 3; i++) {
4445  for (int j = 0; j <= ns2; j++) {
4446  for (int k = 0; k <= nseg; k++) {
4447  snap_to_superellipsoid(mesh_vert(vm1, i, j, k)->co, r, false);
4448  }
4449  }
4450  }
4451 
4452  return vm1;
4453 }
4454 
4455 /* Is this a good candidate for using tri_corner_adj_vmesh? */
4457 {
4458  int in_plane_e = 0;
4459 
4460  /* The superellipse snapping of this case isn't helpful with custom profiles enabled. */
4462  return -1;
4463  }
4464  if (bv->vmesh->count != 3) {
4465  return 0;
4466  }
4467 
4468  /* Only use the tri-corner special case if the offset is the same for every edge. */
4469  float offset = bv->edges[0].offset_l;
4470 
4471  float totang = 0.0f;
4472  for (int i = 0; i < bv->edgecount; i++) {
4473  EdgeHalf *e = &bv->edges[i];
4474  float ang = BM_edge_calc_face_angle_signed_ex(e->e, 0.0f);
4475  float absang = fabsf(ang);
4476  if (absang <= M_PI_4) {
4477  in_plane_e++;
4478  }
4479  else if (absang >= 3.0f * (float)M_PI_4) {
4480  return -1;
4481  }
4482 
4483  if (e->is_bev && !compare_ff(e->offset_l, offset, BEVEL_EPSILON)) {
4484  return -1;
4485  }
4486 
4487  totang += ang;
4488  }
4489  if (in_plane_e != bv->edgecount - 3) {
4490  return -1;
4491  }
4492  float angdiff = fabsf(fabsf(totang) - 3.0f * (float)M_PI_2);
4493  if ((bp->pro_super_r == PRO_SQUARE_R && angdiff > (float)M_PI / 16.0f) ||
4494  (angdiff > (float)M_PI_4)) {
4495  return -1;
4496  }
4497  if (bv->edgecount != 3 || bv->selcount != 3) {
4498  return 0;
4499  }
4500  return 1;
4501 }
4502 
4504 {
4505  BoundVert *bndv = bv->vmesh->boundstart;
4506 
4507  float co0[3], co1[3], co2[3];
4508  copy_v3_v3(co0, bndv->nv.co);
4509  bndv = bndv->next;
4510  copy_v3_v3(co1, bndv->nv.co);
4511  bndv = bndv->next;
4512  copy_v3_v3(co2, bndv->nv.co);
4513 
4514  float mat[4][4];
4515  make_unit_cube_map(co0, co1, co2, bv->v->co, mat);
4516  int ns = bp->seg;
4517  int ns2 = ns / 2;
4519  for (int i = 0; i < 3; i++) {
4520  for (int j = 0; j <= ns2; j++) {
4521  for (int k = 0; k <= ns; k++) {
4522  float v[4];
4523  copy_v3_v3(v, mesh_vert(vm, i, j, k)->co);
4524  v[3] = 1.0f;
4525  mul_m4_v4(mat, v);
4526  copy_v3_v3(mesh_vert(vm, i, j, k)->co, v);
4527  }
4528  }
4529  }
4530 
4531  return vm;
4532 }
4533 
4534 /* Makes the mesh that replaces the original vertex, bounded by the profiles on the sides. */
4536 {
4537  MemArena *mem_arena = bp->mem_arena;
4538 
4539  int n_bndv = bv->vmesh->count;
4540 
4541  /* Same bevel as that of 3 edges of vert in a cube. */
4542  if (n_bndv == 3 && tri_corner_test(bp, bv) != -1 && bp->pro_super_r != PRO_SQUARE_IN_R) {
4543  return tri_corner_adj_vmesh(bp, bv);
4544  }
4545 
4546  /* First construct an initial control mesh, with nseg == 2. */
4547  int nseg = bv->vmesh->seg;
4548  VMesh *vm0 = new_adj_vmesh(mem_arena, n_bndv, 2, bv->vmesh->boundstart);
4549 
4550  /* Find the center of the boundverts that make up the vmesh. */
4551  BoundVert *bndv = vm0->boundstart;
4552  float boundverts_center[3] = {0.0f, 0.0f, 0.0f};
4553  for (int i = 0; i < n_bndv; i++) {
4554  /* Boundaries just divide input polygon edges into 2 even segments. */
4555  copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->nv.co);
4556  get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co);
4557  add_v3_v3(boundverts_center, bndv->nv.co);
4558  bndv = bndv->next;
4559  }
4560  mul_v3_fl(boundverts_center, 1.0f / (float)n_bndv);
4561 
4562  /* To place the center vertex:
4563  * 'negative_fullest' is the reflection of the original vertex across the boundverts' center.
4564  * 'fullness' is the fraction of the way from the boundvert's centroid to the original vertex
4565  * (if positive) or to negative_fullest (if negative). */
4566  float original_vertex[3], negative_fullest[3];
4567  copy_v3_v3(original_vertex, bv->v->co);
4568  sub_v3_v3v3(negative_fullest, boundverts_center, original_vertex);
4569  add_v3_v3(negative_fullest, boundverts_center);
4570 
4571  /* Find the vertex mesh's start center with the profile's fullness. */
4572  float fullness = bp->pro_spacing.fullness;
4573  float center_direction[3];
4574  sub_v3_v3v3(center_direction, original_vertex, boundverts_center);
4575  if (len_squared_v3(center_direction) > BEVEL_EPSILON_SQ) {
4576  if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
4577  fullness *= 2.0f;
4578  madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, negative_fullest, center_direction, fullness);
4579  }
4580  else {
4581  madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, boundverts_center, center_direction, fullness);
4582  }
4583  }
4584  else {
4585  copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, boundverts_center);
4586  }
4588 
4589  /* Do the subdivision process to go from the two segment start mesh to the final vertex mesh. */
4590  VMesh *vm1 = vm0;
4591  do {
4592  vm1 = cubic_subdiv(bp, vm1);
4593  } while (vm1->seg < nseg);
4594  if (vm1->seg != nseg) {
4595  vm1 = interp_vmesh(bp, vm1, nseg);
4596  }
4597  return vm1;
4598 }
4599 
4606 static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
4607 {
4608  Profile *pro = &vpipe->profile;
4609  EdgeHalf *e = vpipe->ebev;
4610 
4611  if (compare_v3v3(pro->start, pro->end, BEVEL_EPSILON_D)) {
4612  copy_v3_v3(co, pro->start);
4613  return;
4614  }
4615 
4616  /* Get a plane with the normal pointing along the beveled edge. */
4617  float edir[3], plane[4];
4618  sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co);
4619  plane_from_point_normal_v3(plane, co, edir);
4620 
4621  float start_plane[3], end_plane[3], middle_plane[3];
4622  closest_to_plane_v3(start_plane, plane, pro->start);
4623  closest_to_plane_v3(end_plane, plane, pro->end);
4624  closest_to_plane_v3(middle_plane, plane, pro->middle);
4625 
4626  float m[4][4], minv[4][4];
4627  if (make_unit_square_map(start_plane, middle_plane, end_plane, m) && invert_m4_m4(minv, m)) {
4628  /* Transform co and project it onto superellipse. */
4629  float p[3];
4630  mul_v3_m4v3(p, minv, co);
4631  snap_to_superellipsoid(p, pro->super_r, midline);
4632 
4633  float snap[3];
4634  mul_v3_m4v3(snap, m, p);
4635  copy_v3_v3(co, snap);
4636  }
4637  else {
4638  /* Planar case: just snap to line start_plane--end_plane. */
4639  float p[3];
4640  closest_to_line_segment_v3(p, co, start_plane, end_plane);
4641  copy_v3_v3(co, p);
4642  }
4643 }
4644 
4651 {
4652  /* Some unnecessary overhead running this subdivision with custom profile snapping later on. */
4653  VMesh *vm = adj_vmesh(bp, bv);
4654 
4655  /* Now snap all interior coordinates to be on the epipe profile. */
4656  int n_bndv = bv->vmesh->count;
4657  int ns = bv->vmesh->seg;
4658  int half_ns = ns / 2;
4659  int ipipe1 = vpipe->index;
4660  int ipipe2 = vpipe->next->next->index;
4661 
4662  for (int i = 0; i < n_bndv; i++) {
4663  for (int j = 1; j <= half_ns; j++) {
4664  for (int k = 0; k <= half_ns; k++) {
4665  if (!is_canon(vm, i, j, k)) {
4666  continue;
4667  }
4668  /* With a custom profile just copy the shape of the profile at each ring. */
4669  if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
4670  /* Find both profile vertices that correspond to this point. */
4671  float *profile_point_pipe1, *profile_point_pipe2, f;
4672  if (ELEM(i, ipipe1, ipipe2)) {
4673  if (n_bndv == 3 && i == ipipe1) {
4674  /* This part of the vmesh is the triangular corner between the two pipe profiles. */
4675  int ring = max_ii(j, k);
4676  profile_point_pipe2 = mesh_vert(vm, i, 0, ring)->co;
4677  profile_point_pipe1 = mesh_vert(vm, i, ring, 0)->co;
4678  /* End profile index increases with k on one side and j on the other. */
4679  f = ((k < j) ? min_ff(j, k) : ((2.0f * ring) - j)) / (2.0f * ring);
4680  }
4681  else {
4682  /* This is part of either pipe profile boundvert area in the 4-way intersection. */
4683  profile_point_pipe1 = mesh_vert(vm, i, 0, k)->co;
4684  profile_point_pipe2 = mesh_vert(vm, (i == ipipe1) ? ipipe2 : ipipe1, 0, ns - k)->co;
4685  f = (float)j / (float)ns; /* The ring index brings us closer to the other side. */
4686  }
4687  }
4688  else {
4689  /* The profile vertices are on both ends of each of the side profile's rings. */
4690  profile_point_pipe1 = mesh_vert(vm, i, j, 0)->co;
4691  profile_point_pipe2 = mesh_vert(vm, i, j, ns)->co;
4692  f = (float)k / (float)ns; /* Ring runs along the pipe, so segment is used here. */
4693  }
4694 
4695  /* Place the vertex by interpolating between the two profile points using the factor. */
4696  interp_v3_v3v3(mesh_vert(vm, i, j, k)->co, profile_point_pipe1, profile_point_pipe2, f);
4697  }
4698  else {
4699  /* A tricky case is for the 'square' profiles and an even nseg: we want certain
4700  * vertices to snap to the midline on the pipe, not just to one plane or the other. */
4701  bool even = (ns % 2) == 0;
4702  bool midline = even && k == half_ns &&
4703  ((i == 0 && j == half_ns) || (ELEM(i, ipipe1, ipipe2)));
4704  snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co);
4705  }
4706  }
4707  }
4708  }
4709  return vm;
4710 }
4711 
4712 static void get_incident_edges(BMFace *f, BMVert *v, BMEdge **r_e1, BMEdge **r_e2)
4713 {
4714  *r_e1 = NULL;
4715  *r_e2 = NULL;
4716  if (!f) {
4717  return;
4718  }
4719 
4720  BMIter iter;
4721  BMEdge *e;
4722  BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) {
4723  if (e->v1 == v || e->v2 == v) {
4724  if (*r_e1 == NULL) {
4725  *r_e1 = e;
4726  }
4727  else if (*r_e2 == NULL) {
4728  *r_e2 = e;
4729  }
4730  }
4731  }
4732 }
4733 
4734 static BMEdge *find_closer_edge(float *co, BMEdge *e1, BMEdge *e2)
4735 {
4736  BLI_assert(e1 != NULL && e2 != NULL);
4737  float dsq1 = dist_squared_to_line_segment_v3(co, e1->v1->co, e1->v2->co);
4738  float dsq2 = dist_squared_to_line_segment_v3(co, e2->v1->co, e2->v2->co);
4739  if (dsq1 < dsq2) {
4740  return e1;
4741  }
4742  return e2;
4743 }
4744 
4754  const BMFace *f,
4755  BoundVert *(r_internal[3]))
4756 {
4757  if (f == NULL) {
4758  return 0;
4759  }
4760  int n_internal = 0;
4761  VMesh *vm = bv->vmesh;
4762  BLI_assert(vm != NULL);
4763  BoundVert *v = vm->boundstart;
4764  do {
4765  /* Possible speedup: do the matrix projection done by the following
4766  * once, outside the loop, or even better, cache it if ever done
4767  * in the course of Bevel. */
4768  if (BM_face_point_inside_test(f, v->nv.co)) {
4769  r_internal[n_internal++] = v;
4770  if (n_internal == 3) {
4771  break;
4772  }
4773  }
4774  } while ((v = v->next) != vm->boundstart);
4775  for (int i = n_internal; i < 3; i++) {
4776  r_internal[i] = NULL;
4777  }
4778  return n_internal;
4779 }
4780 
4790 {
4791  BMEdge *e1, *e2;
4792  VMesh *vm = bv->vmesh;
4793  float(*proj_co)[2] = BLI_array_alloca(proj_co, vm->count);
4794  float axis_mat[3][3];
4795  axis_dominant_v3_to_m3(axis_mat, f->no);
4796  get_incident_edges(f, bv->v, &e1, &e2);
4797  BLI_assert(e1 != NULL && e2 != NULL);
4798  BLI_assert(vm != NULL);
4799  BoundVert *v = vm->boundstart;
4800  int i = 0;
4801  BoundVert *unsnapped[3];
4802  find_face_internal_boundverts(bv, f, unsnapped);
4803  do {
4804  float *co = v->nv.v->co;
4805  if (v == unsnapped[0] || v == unsnapped[1] || v == unsnapped[2]) {
4806  mul_v2_m3v3(proj_co[i], axis_mat, co);
4807  }
4808  else {
4809  float snap1[3], snap2[3];
4810  closest_to_line_segment_v3(snap1, co, e1->v1->co, e1->v2->co);
4811  closest_to_line_segment_v3(snap2, co, e2->v1->co, e2->v2->co);
4812  float d1_sq = len_squared_v3v3(snap1, co);
4813  float d2_sq = len_squared_v3v3(snap2, co);
4814  if (d1_sq <= d2_sq) {
4815  mul_v2_m3v3(proj_co[i], axis_mat, snap1);
4816  }
4817  else {
4818  mul_v2_m3v3(proj_co[i], axis_mat, snap2);
4819  }
4820  }
4821  ++i;
4822  } while ((v = v->next) != vm->boundstart);
4823  float area = area_poly_v2(proj_co, vm->count);
4824  return area;
4825 }
4826 
4834 static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
4835 {
4836  BLI_assert(bv->vmesh != NULL);
4837  float area = projected_boundary_area(bv, frep);
4838  return area < BEVEL_EPSILON_BIG;
4839 }
4840 
4856 {
4857  int fcount = 0;
4858  BMFace *any_bmf = NULL;
4859  bool consider_all_faces = bv->selcount == 1;
4860  /* Make an array that can hold maximum possible number of choices. */
4861  BMFace **fchoices = BLI_array_alloca(fchoices, bv->edgecount);
4862  /* For each choice, need to remember the unsnapped BoundVerts. */
4863 
4864  for (int i = 0; i < bv->edgecount; i++) {
4865  if (!bv->edges[i].is_bev && !consider_all_faces) {
4866  continue;
4867  }
4868  BMFace *bmf1 = bv->edges[i].fprev;
4869  BMFace *bmf2 = bv->edges[i].fnext;
4870  BMFace *ftwo[2] = {bmf1, bmf2};
4871  BMFace *bmf = choose_rep_face(bp, ftwo, 2);
4872  if (bmf != NULL) {
4873  if (any_bmf == NULL) {
4874  any_bmf = bmf;
4875  }
4876  bool already_there = false;
4877  for (int j = fcount - 1; j >= 0; j--) {
4878  if (fchoices[j] == bmf) {
4879  already_there = true;
4880  break;
4881  }
4882  }
4883  if (!already_there) {
4884  if (bp->math_layer_info.has_math_layers) {
4885  if (is_bad_uv_poly(bv, bmf)) {
4886  continue;
4887  }
4888  }
4889  fchoices[fcount++] = bmf;
4890  }
4891  }
4892  }
4893  if (fcount == 0) {
4894  return any_bmf;
4895  }
4896  return choose_rep_face(bp, fchoices, fcount);
4897 }
4898 
4899 static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_nr)
4900 {
4901  VMesh *vm = bv->vmesh;
4902  BMVert **vv = NULL;
4903  BMFace **vf = NULL;
4904  BMEdge **ve = NULL;
4908 
4909  int ns2 = vm->seg / 2;
4910  BMFace *frep;
4911  BMEdge *frep_e1, *frep_e2;
4912  BoundVert *frep_unsnapped[3];
4913  if (bv->any_seam) {
4914  frep = frep_for_center_poly(bp, bv);
4915  get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
4916  find_face_internal_boundverts(bv, frep, frep_unsnapped);
4917  }
4918  else {
4919  frep = NULL;
4920  frep_e1 = frep_e2 = NULL;
4921  }
4922  BoundVert *v = vm->boundstart;
4923  do {
4924  int i = v->index;
4925  BLI_array_append(vv, mesh_vert(vm, i, ns2, ns2)->v);
4926  if (frep) {
4927  BLI_array_append(vf, frep);
4928  if (v == frep_unsnapped[0] || v == frep_unsnapped[1] || v == frep_unsnapped[2]) {
4929  BLI_array_append(ve, NULL);
4930  }
4931  else {
4932  BMEdge *frep_e = find_closer_edge(mesh_vert(vm, i, ns2, ns2)->v->co, frep_e1, frep_e2);
4933  BLI_array_append(ve, frep_e);
4934  }
4935  }
4936  else {
4938  BLI_array_append(ve, NULL);
4939  }
4940  } while ((v = v->next) != vm->boundstart);
4941  BMFace *f = bev_create_ngon(bm, vv, BLI_array_len(vv), vf, frep, ve, mat_nr, true);
4942  record_face_kind(bp, f, F_VERT);
4943 
4944  BLI_array_free(vv);
4945  BLI_array_free(vf);
4946  BLI_array_free(ve);
4947 }
4948 
4956 {
4957  VMesh *vm = bv->vmesh;
4958  int n = vm->count;
4959  int ns = vm->seg;
4960  int ns2 = ns / 2;
4961  int odd = ns % 2;
4962 
4963  for (int i = 0; i < n; i++) {
4964  for (int k = 1; k < ns; k++) {
4965  copy_v3_v3(mesh_vert(vm, i, 0, k)->co, mesh_vert(vm1, i, 0, k)->co);
4966  if (i > 0 && k <= ns2) {
4967  mesh_vert(vm, i, 0, k)->v = mesh_vert(vm, i - 1, 0, ns - k)->v;
4968  }
4969  else if (i == n - 1 && k > ns2) {
4970  mesh_vert(vm, i, 0, k)->v = mesh_vert(vm, 0, 0, ns - k)->v;
4971  }
4972  else {
4973  create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
4974  }
4975  }
4976  }
4977  if (odd) {
4978  for (int i = 0; i < n; i++) {
4979  mesh_vert(vm, i, ns2, ns2)->v = mesh_vert(vm, i, 0, ns2)->v;
4980  }
4981  build_center_ngon(bp, bm, bv, bp->mat_nr);
4982  }
4983 }
4984 
4988 static void closer_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float v[3])
4989 {
4990  if (len_squared_v3v3(a, v) <= len_squared_v3v3(b, v)) {
4991  copy_v3_v3(r, a);
4992  }
4993  else {
4994  copy_v3_v3(r, b);
4995  }
4996 }
4997 
5011 {
5012  int n_bndv = bv->vmesh->count;
5013  int ns = bv->vmesh->seg;
5014  int ns2 = ns / 2;
5015  int odd = ns % 2;
5016  float ns2inv = 1.0f / (float)ns2;
5017  VMesh *vm = new_adj_vmesh(bp->mem_arena, n_bndv, ns, bv->vmesh->boundstart);
5018  int clstride = 3 * (ns2 + 1);
5019  float *centerline = MEM_mallocN(sizeof(float) * clstride * n_bndv, "bevel");
5020  bool *cset = MEM_callocN(sizeof(bool) * n_bndv, "bevel");
5021 
5022  /* Find on_edge, place on bndv[i]'s elast where offset line would meet,
5023  * taking min-distance-to bv->v with position where next sector's offset line would meet. */
5024  BoundVert *bndv = vm->boundstart;
5025  for (int i = 0; i < n_bndv; i++) {
5026  float bndco[3];
5027  copy_v3_v3(bndco, bndv->nv.co);
5028  EdgeHalf *e1 = bndv->efirst;
5029  EdgeHalf *e2 = bndv->elast;
5030  AngleKind ang_kind = ANGLE_STRAIGHT;
5031  if (e1 && e2) {
5032  ang_kind = edges_angle_kind(e1, e2, bv->v);
5033  }
5034  if (bndv->is_patch_start) {
5035  mid_v3_v3v3(centerline + clstride * i, bndv->nv.co, bndv->next->nv.co);
5036  cset[i] = true;
5037  bndv = bndv->next;
5038  i++;
5039  mid_v3_v3v3(centerline + clstride * i, bndv->nv.co, bndv->next->nv.co);
5040  cset[i] = true;
5041  bndv = bndv->next;
5042  i++;
5043  /* Leave cset[i] where it was - probably false, unless i == n - 1. */
5044  }
5045  else if (bndv->is_arc_start) {
5046  e1 = bndv->efirst;
5047  e2 = bndv->next->efirst;
5048  copy_v3_v3(centerline + clstride * i, bndv->profile.middle);
5049  bndv = bndv->next;
5050  cset[i] = true;
5051  i++;
5052  /* Leave cset[i] where it was - probably false, unless i == n - 1. */
5053  }
5054  else if (ang_kind == ANGLE_SMALLER) {
5055  float dir1[3], dir2[3], co1[3], co2[3];
5056  sub_v3_v3v3(dir1, e1->e->v1->co, e1->e->v2->co);
5057  sub_v3_v3v3(dir2, e2->e->v1->co, e2->e->v2->co);
5058  add_v3_v3v3(co1, bndco, dir1);
5059  add_v3_v3v3(co2, bndco, dir2);
5060  /* Intersect e1 with line through bndv parallel to e2 to get v1co. */
5061  float meet1[3], meet2[3];
5062  int ikind = isect_line_line_v3(e1->e->v1->co, e1->e->v2->co, bndco, co2, meet1, meet2);
5063  float v1co[3];
5064  bool v1set;
5065  if (ikind == 0) {
5066  v1set = false;
5067  }
5068  else {
5069  /* If the lines are skew (ikind == 2), want meet1 which is on e1. */
5070  copy_v3_v3(v1co, meet1);
5071  v1set = true;
5072  }
5073  /* Intersect e2 with line through bndv parallel to e1 to get v2co. */
5074  ikind = isect_line_line_v3(e2->e->v1->co, e2->e->v2->co, bndco, co1, meet1, meet2);
5075  float v2co[3];
5076  bool v2set;
5077  if (ikind == 0) {
5078  v2set = false;
5079  }
5080  else {
5081  v2set = true;
5082  copy_v3_v3(v2co, meet1);
5083  }
5084 
5085  /* We want on_edge[i] to be min dist to bv->v of v2co and the v1co of next iteration. */
5086  float *on_edge_cur = centerline + clstride * i;
5087  int iprev = (i == 0) ? n_bndv - 1 : i - 1;
5088  float *on_edge_prev = centerline + clstride * iprev;
5089  if (v2set) {
5090  if (cset[i]) {
5091  closer_v3_v3v3v3(on_edge_cur, on_edge_cur, v2co, bv->v->co);
5092  }
5093  else {
5094  copy_v3_v3(on_edge_cur, v2co);
5095  cset[i] = true;
5096  }
5097  }
5098  if (v1set) {
5099  if (cset[iprev]) {
5100  closer_v3_v3v3v3(on_edge_prev, on_edge_prev, v1co, bv->v->co);
5101  }
5102  else {
5103  copy_v3_v3(on_edge_prev, v1co);
5104  cset[iprev] = true;
5105  }
5106  }
5107  }
5108  bndv = bndv->next;
5109  }
5110  /* Maybe not everything was set by the previous loop. */
5111  bndv = vm->boundstart;
5112  for (int i = 0; i < n_bndv; i++) {
5113  if (!cset[i]) {
5114  float *on_edge_cur = centerline + clstride * i;
5115  EdgeHalf *e1 = bndv->next->efirst;
5116  float co1[3], co2[3];
5117  copy_v3_v3(co1, bndv->nv.co);
5118  copy_v3_v3(co2, bndv->next->nv.co);
5119  if (e1) {
5120  if (bndv->prev->is_arc_start && bndv->next->is_arc_start) {
5121  float meet1[3], meet2[3];
5122  int ikind = isect_line_line_v3(e1->e->v1->co, e1->e->v2->co, co1, co2, meet1, meet2);
5123  if (ikind != 0) {
5124  copy_v3_v3(on_edge_cur, meet1);
5125  cset[i] = true;
5126  }
5127  }
5128  else {
5129  if (bndv->prev->is_arc_start) {
5130  closest_to_line_segment_v3(on_edge_cur, co1, e1->e->v1->co, e1->e->v2->co);
5131  }
5132  else {
5133  closest_to_line_segment_v3(on_edge_cur, co2, e1->e->v1->co, e1->e->v2->co);
5134  }
5135  cset[i] = true;
5136  }
5137  }
5138  if (!cset[i]) {
5139  mid_v3_v3v3(on_edge_cur, co1, co2);
5140  cset[i] = true;
5141  }
5142  }
5143  bndv = bndv->next;
5144  }
5145 
5146  /* Fill in rest of center-lines by interpolation. */
5147  float co1[3], co2[3];
5148  copy_v3_v3(co2, bv->v->co);
5149  bndv = vm->boundstart;
5150  for (int i = 0; i < n_bndv; i++) {
5151  if (odd) {
5152  float ang = 0.5f * angle_v3v3v3(bndv->nv.co, co1, bndv->next->nv.co);
5153  float finalfrac;
5154  if (ang > BEVEL_SMALL_ANG) {
5155  /* finalfrac is the length along arms of isosceles triangle with top angle 2*ang
5156  * such that the base of the triangle is 1.
5157  * This is used in interpolation along center-line in odd case.
5158  * To avoid too big a drop from bv, cap finalfrac a 0.8 arbitrarily */
5159  finalfrac = 0.5f / sinf(ang);
5160  if (finalfrac > 0.8f) {
5161  finalfrac = 0.8f;
5162  }
5163  }
5164  else {
5165  finalfrac = 0.8f;
5166  }
5167  ns2inv = 1.0f / (ns2 + finalfrac);
5168  }
5169 
5170  float *p = centerline + clstride * i;
5171  copy_v3_v3(co1, p);
5172  p += 3;
5173  for (int j = 1; j <= ns2; j++) {
5174  interp_v3_v3v3(p, co1, co2, j * ns2inv);
5175  p += 3;
5176  }
5177  bndv = bndv->next;
5178  }
5179 
5180  /* Coords of edges and mid or near-mid line. */
5181  bndv = vm->boundstart;
5182  for (int i = 0; i < n_bndv; i++) {
5183  copy_v3_v3(co1, bndv->nv.co);
5184  copy_v3_v3(co2, centerline + clstride * (i == 0 ? n_bndv - 1 : i - 1));
5185  for (int j = 0; j < ns2 + odd; j++) {
5186  interp_v3_v3v3(mesh_vert(vm, i, j, 0)->co, co1, co2, j * ns2inv);
5187  }
5188  copy_v3_v3(co2, centerline + clstride * i);
5189  for (int k = 1; k <= ns2; k++) {
5190  interp_v3_v3v3(mesh_vert(vm, i, 0, k)->co, co1, co2, k * ns2inv);
5191  }
5192  bndv = bndv->next;
5193  }
5194  if (!odd) {
5195  copy_v3_v3(mesh_vert(vm, 0, ns2, ns2)->co, bv->v->co);
5196  }
5198 
5199  /* Fill in interior points by interpolation from edges to center-lines. */
5200  bndv = vm->boundstart;
5201  for (int i = 0; i < n_bndv; i++) {
5202  int im1 = (i == 0) ? n_bndv - 1 : i - 1;
5203  for (int j = 1; j < ns2 + odd; j++) {
5204  for (int k = 1; k <= ns2; k++) {
5205  float meet1[3], meet2[3];
5206  int ikind = isect_line_line_v3(mesh_vert(vm, i, 0, k)->co,
5207  centerline + clstride * im1 + 3 * k,
5208  mesh_vert(vm, i, j, 0)->co,
5209  centerline + clstride * i + 3 * j,
5210  meet1,
5211  meet2);
5212  if (ikind == 0) {
5213  /* How can this happen? fall back on interpolation in one direction if it does. */
5214  interp_v3_v3v3(mesh_vert(vm, i, j, k)->co,
5215  mesh_vert(vm, i, 0, k)->co,
5216  centerline + clstride * im1 + 3 * k,
5217  j * ns2inv);
5218  }
5219  else if (ikind == 1) {
5220  copy_v3_v3(mesh_vert(vm, i, j, k)->co, meet1);
5221  }
5222  else {
5223  mid_v3_v3v3(mesh_vert(vm, i, j, k)->co, meet1, meet2);
5224  }
5225  }
5226  }
5227  bndv = bndv->next;
5228  }
5229 
5231 
5232  MEM_freeN(centerline);
5233  MEM_freeN(cset);
5234  return vm;
5235 }
5236 
5238  int n_bndv,
5239  BMEdge *eprev,
5240  BMEdge *enext,
5241  BMFace **bndv_rep_faces,
5242  BMFace *center_frep,
5243  const bool *frep_beats_next)
5244 {
5245  int previ = (i + n_bndv - 1) % n_bndv;
5246  int nexti = (i + 1) % n_bndv;
5247 
5248  if (frep_beats_next[previ] && bndv_rep_faces[previ] == center_frep) {
5249  return eprev;
5250  }
5251  if (!frep_beats_next[i] && bndv_rep_faces[nexti] == center_frep) {
5252  return enext;
5253  }
5254  /* If n_bndv > 3 then we won't snap in the boundvert regions
5255  * that are not directly adjacent to the center-winning boundvert.
5256  * This is probably wrong, maybe getting UV positions outside the
5257  * original area, but the alternative may be even worse. */
5258  return NULL;
5259 }
5260 
5282 static void snap_edges_for_vmesh_vert(int i,
5283  int j,
5284  int k,
5285  int ns,
5286  int ns2,
5287  int n_bndv,
5288  BMEdge *eprev,
5289  BMEdge *enext,
5290  BMEdge *enextnext,
5291  BMFace **bndv_rep_faces,
5292  BMFace *center_frep,
5293  const bool *frep_beats_next,
5294  BMEdge *r_snap_edges[4])
5295 {
5296  BLI_assert(0 <= i && i < n_bndv && 0 <= j && j < ns2 && 0 <= k && k <= ns2);
5297  for (int corner = 0; corner < 4; corner++) {
5298  r_snap_edges[corner] = NULL;
5299  if (ns % 2 == 0) {
5300  continue;
5301  }
5302  int previ = (i + n_bndv - 1) % n_bndv;
5303  /* Make jj and kk be the j and k indices for this corner. */
5304  int jj = corner < 2 ? j : j + 1;
5305  int kk = (corner == 0 || corner == 3) ? k : k + 1;
5306  if (jj < ns2 && kk < ns2) {
5307  ; /* No snap. */
5308  }
5309  else if (jj < ns2 && kk == ns2) {
5310  /* On the left side of the center strip quads, but not on center poly. */
5311  if (!frep_beats_next[i]) {
5312  r_snap_edges[corner] = enext;
5313  }
5314  }
5315  else if (jj < ns2 && kk == ns2 + 1) {
5316  /* On the right side of the center strip quads, but not on center poly. */
5317  if (frep_beats_next[i]) {
5318  r_snap_edges[corner] = enext;
5319  }
5320  }
5321  else if (jj == ns2 && kk < ns2) {
5322  /* On the top of the top strip quads, but not on center poly. */
5323  if (frep_beats_next[previ]) {
5324  r_snap_edges[corner] = eprev;
5325  }
5326  }
5327  else if (jj == ns2 && kk == ns2) {
5328  /* Center poly vert for boundvert i. */
5329  r_snap_edges[corner] = snap_edge_for_center_vmesh_vert(
5330  i, n_bndv, eprev, enext, bndv_rep_faces, center_frep, frep_beats_next);
5331  }
5332  else if (jj == ns2 && kk == ns2 + 1) {
5333  /* Center poly vert for boundvert i+1. */
5334  int nexti = (i + 1) % n_bndv;
5335  r_snap_edges[corner] = snap_edge_for_center_vmesh_vert(
5336  nexti, n_bndv, enext, enextnext, bndv_rep_faces, center_frep, frep_beats_next);
5337  }
5338  }
5339 }
5340 
5346 static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert *vpipe)
5347 {
5348  int mat_nr = bp->mat_nr;
5349 
5350  int n_bndv = bv->vmesh->count;
5351  int ns = bv->vmesh->seg;
5352  int ns2 = ns / 2;
5353  int odd = ns % 2;
5354  BLI_assert(n_bndv >= 3 && ns > 1);
5355 
5356  VMesh *vm1;
5357  if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd &&
5359  vm1 = square_out_adj_vmesh(bp, bv);
5360  }
5361  else if (vpipe) {
5362  vm1 = pipe_adj_vmesh(bp, bv, vpipe);
5363  }
5364  else if (tri_corner_test(bp, bv) == 1) {
5365  vm1 = tri_corner_adj_vmesh(bp, bv);
5366  /* The PRO_SQUARE_IN_R profile has boundary edges that merge
5367  * and no internal ring polys except possibly center ngon. */
5369  build_square_in_vmesh(bp, bm, bv, vm1);
5370  return;
5371  }
5372  }
5373  else {
5374  vm1 = adj_vmesh(bp, bv);
5375  }
5376 
5377  /* Copy final vmesh into bv->vmesh, make BMVerts and BMFaces. */
5378  VMesh *vm = bv->vmesh;
5379  for (int i = 0; i < n_bndv; i++) {
5380  for (int j = 0; j <= ns2; j++) {
5381  for (int k = 0; k <= ns; k++) {
5382  if (j == 0 && (ELEM(k, 0, ns))) {
5383  continue; /* Boundary corners already made. */
5384  }
5385  if (!is_canon(vm, i, j, k)) {
5386  continue;
5387  }
5388  copy_v3_v3(mesh_vert(vm, i, j, k)->co, mesh_vert(vm1, i, j, k)->co);
5389  create_mesh_bmvert(bm, vm, i, j, k, bv->v);
5390  }
5391  }
5392  }
5394 
5395  /* Find and store the interpolation face for each BoundVert. */
5396  BMFace **bndv_rep_faces = BLI_array_alloca(bndv_rep_faces, n_bndv);
5397  BoundVert *bndv = vm->boundstart;
5398  do {
5399  int i = bndv->index;
5400  bndv_rep_faces[i] = boundvert_rep_face(bndv, NULL);
5401  } while ((bndv = bndv->next) != vm->boundstart);
5402 
5403  /* If odd number of segments, need data to break interpolation ties. */
5404  BMVert **center_verts = NULL;
5405  BMEdge **center_edge_snaps = NULL;
5406  BMFace **center_face_interps = NULL;
5407  bool *frep_beats_next = NULL;
5408  BMFace *center_frep = NULL;
5409  if (odd && bp->affect_type == BEVEL_AFFECT_EDGES) {
5410  center_verts = BLI_array_alloca(center_verts, n_bndv);
5411  center_edge_snaps = BLI_array_alloca(center_edge_snaps, n_bndv);
5412  center_face_interps = BLI_array_alloca(center_face_interps, n_bndv);
5413  frep_beats_next = BLI_array_alloca(frep_beats_next, n_bndv);
5414  center_frep = frep_for_center_poly(bp, bv);
5415  for (int i = 0; i < n_bndv; i++) {
5416  center_edge_snaps[i] = NULL;
5417  /* frep_beats_next[i] == true if frep for i is chosen over that for i + 1. */
5418  int inext = (i + 1) % n_bndv;
5419  BMFace *fchoices[2] = {bndv_rep_faces[i], bndv_rep_faces[inext]};
5420  BMFace *fwinner = choose_rep_face(bp, fchoices, 2);
5421  frep_beats_next[i] = fwinner == bndv_rep_faces[i];
5422  }
5423  }
5424  /* Make the polygons. */
5425  bndv = vm->boundstart;
5426  do {
5427  int i = bndv->index;
5428  int inext = bndv->next->index;
5429  BMFace *f = bndv_rep_faces[i];
5430  BMFace *f2 = bndv_rep_faces[inext];
5431  BMFace *fc = NULL;
5432  if (odd && bp->affect_type == BEVEL_AFFECT_EDGES) {
5433  fc = frep_beats_next[i] ? f : f2;
5434  }
5435 
5436  EdgeHalf *e, *eprev, *enext;
5437  if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
5438  e = bndv->efirst;
5439  eprev = bndv->prev->efirst;
5440  enext = bndv->next->efirst;
5441  }
5442  else {
5443  e = bndv->ebev;
5444  eprev = bndv->prev->ebev;
5445  enext = bndv->next->ebev;
5446  }
5447  BMEdge *bme = e ? e->e : NULL;
5448  BMEdge *bmeprev = eprev ? eprev->e : NULL;
5449  BMEdge *bmenext = enext ? enext->e : NULL;
5450  /* For odd ns, make polys with lower left corner at (i,j,k) for
5451  * j in [0, ns2-1], k in [0, ns2]. And then the center ngon.
5452  * For even ns,
5453  * j in [0, ns2-1], k in [0, ns2-1].
5454  *
5455  * Recall: j is ring index, k is segment index.
5456  */
5457  for (int j = 0; j < ns2; j++) {
5458  for (int k = 0; k < ns2 + odd; k++) {
5459  /* We will create a quad with these four corners. */
5460  BMVert *bmv1 = mesh_vert(vm, i, j, k)->v;
5461  BMVert *bmv2 = mesh_vert(vm, i, j, k + 1)->v;
5462  BMVert *bmv3 = mesh_vert(vm, i, j + 1, k + 1)->v;
5463  BMVert *bmv4 = mesh_vert(vm, i, j + 1, k)->v;
5464  BMVert *bmvs[4] = {bmv1, bmv2, bmv3, bmv4};
5465  BLI_assert(bmv1 && bmv2 && bmv3 && bmv4);
5466  /* For each created quad, the UV's etc. will be interpolated
5467  * in potentially a different face for each corner and may need
5468  * to snap to a particular edge before interpolating.
5469  * The fr and se arrays will be filled with the interpolation faces
5470  * and snapping edges for the for corners in the order given
5471  * in the bmvs array.
5472  */
5473  BMFace *fr[4];
5474  BMEdge *se[4] = {NULL, NULL, NULL, NULL};
5475  if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
5476  fr[0] = fr[1] = fr[2] = fr[3] = f2;
5477  if (j < k) {
5478  if (k == ns2 && j == ns2 - 1) {
5479  se[2] = bndv->next->efirst->e;
5480  se[3] = bme;
5481  }
5482  }
5483  else if (j == k) {
5484  /* Only one edge attached to v, since vertex only. */
5485  se[0] = se[2] = bme;
5486  if (!e->is_seam) {
5487  fr[3] = f;
5488  }
5489  }
5490  }
5491  else { /* Edge bevel. */
5492  fr[0] = fr[1] = fr[2] = fr[3] = f;
5493  if (odd) {
5494  BMEdge *b1 = (eprev && eprev->is_seam) ? bmeprev : NULL;
5495  BMEdge *b2 = (e && e->is_seam) ? bme : NULL;
5496  BMEdge *b3 = (enext && enext->is_seam) ? bmenext : NULL;
5498  j,
5499  k,
5500  ns,
5501  ns2,
5502  n_bndv,
5503  b1,
5504  b2,
5505  b3,
5506  bndv_rep_faces,
5507  center_frep,
5508  frep_beats_next,
5509  se);
5510  if (k == ns2) {
5511  if (!e || e->is_seam) {
5512  fr[0] = fr[1] = fr[2] = fr[3] = fc;
5513  }
5514  else {
5515  fr[0] = fr[3] = f;
5516  fr[1] = fr[2] = f2;
5517  }
5518  if (j == ns2 - 1) {
5519  /* Use the 4th vertex of these faces as the ones used for the center polygon. */
5520  center_verts[i] = bmvs[3];
5521  center_edge_snaps[i] = se[3];
5522  center_face_interps[i] = bv->any_seam ? center_frep : f;
5523  }
5524  }
5525  }
5526  else { /* Edge bevel, Even number of segments. */
5527  if (k == ns2 - 1) {
5528  se[1] = bme;
5529  }
5530  if (j == ns2 - 1 && bndv->prev->ebev) {
5531  se[3] = bmeprev;
5532  }
5533  se[2] = se[1] != NULL ? se[1] : se[3];
5534  }
5535  }
5536  BMFace *r_f = bev_create_ngon(bm, bmvs, 4, fr, NULL, se, mat_nr, true);
5537  record_face_kind(bp, r_f, F_VERT);
5538  }
5539  }
5540  } while ((bndv = bndv->next) != vm->boundstart);
5541 
5542  /* Fix UVs along center lines if even number of segments. */
5543  if (!odd) {
5544  bndv = vm->boundstart;
5545  do {
5546  int i = bndv->index;
5547  if (!bndv->any_seam) {
5548  for (int ring = 1; ring < ns2; ring++) {
5549  BMVert *v_uv = mesh_vert(vm, i, ring, ns2)->v;
5550  if (v_uv) {
5551  bev_merge_uvs(bm, v_uv);
5552  }
5553  }
5554  }
5555  } while ((bndv = bndv->next) != vm->boundstart);
5556  BMVert *bmv = mesh_vert(vm, 0, ns2, ns2)->v;
5557  if (bp->affect_type == BEVEL_AFFECT_VERTICES || count_bound_vert_seams(bv) <= 1) {
5558  bev_merge_uvs(bm, bmv);
5559  }
5560  }
5561 
5562  /* Center ngon. */
5563  if (odd) {
5564  if (bp->affect_type == BEVEL_AFFECT_EDGES) {
5565  BMFace *frep = NULL;
5566  if (bv->any_seam) {
5567  frep = frep_for_center_poly(bp, bv);
5568  }
5569  BMFace *cen_f = bev_create_ngon(
5570  bm, center_verts, n_bndv, center_face_interps, frep, center_edge_snaps, mat_nr, true);
5571  record_face_kind(bp, cen_f, F_VERT);
5572  }
5573  else {
5574  build_center_ngon(bp, bm, bv, mat_nr);
5575  }
5576  }
5577 }
5578 
5591 {
5592 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5593  printf("BEVEL BUILD CUTOFF\n");
5594 # define F3(v) (v)[0], (v)[1], (v)[2]
5595 #endif
5596  int n_bndv = bv->vmesh->count;
5597 
5598  /* Find the locations for the corner vertices at the bottom of the cutoff faces. */
5599  BoundVert *bndv = bv->vmesh->boundstart;
5600  do {
5601  int i = bndv->index;
5602 
5603  /* Find the "down" direction for this side of the cutoff face. */
5604  /* Find the direction along the intersection of the two adjacent profile normals. */
5605  float down_direction[3];
5606  cross_v3_v3v3(down_direction, bndv->profile.plane_no, bndv->prev->profile.plane_no);
5607  if (dot_v3v3(down_direction, bv->v->no) > 0.0f) {
5608  negate_v3(down_direction);
5609  }
5610 
5611  /* Move down from the boundvert by average profile height from the two adjacent profiles. */
5612  float length = (bndv->profile.height / sqrtf(2.0f) +
5613  bndv->prev->profile.height / sqrtf(2.0f)) /
5614  2;
5615  float new_vert[3];
5616  madd_v3_v3v3fl(new_vert, bndv->nv.co, down_direction, length);
5617 
5618  /* Use this location for this profile's first corner vert and the last profile's second. */
5619  copy_v3_v3(mesh_vert(bv->vmesh, i, 1, 0)->co, new_vert);
5620  copy_v3_v3(mesh_vert(bv->vmesh, bndv->prev->index, 1, 1)->co, new_vert);
5621 
5622  } while ((bndv = bndv->next) != bv->vmesh->boundstart);
5623 
5624 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5625  printf("Corner vertices:\n");
5626  for (int j = 0; j < n_bndv; j++) {
5627  printf(" (%.3f, %.3f, %.3f)\n", F3(mesh_vert(bv->vmesh, j, 1, 0)->co));
5628  }
5629 #endif
5630 
5631  /* Disable the center face if the corner vertices share the same location. */
5632  bool build_center_face = true;
5633  if (n_bndv == 3) { /* Vertices only collapse with a 3-way VMesh. */
5634  build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 0, 1, 0)->co,
5635  mesh_vert(bv->vmesh, 1, 1, 0)->co) > BEVEL_EPSILON;
5636  build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 0, 1, 0)->co,
5637  mesh_vert(bv->vmesh, 2, 1, 0)->co) > BEVEL_EPSILON;
5638  build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 1, 1, 0)->co,
5639  mesh_vert(bv->vmesh, 2, 1, 0)->co) > BEVEL_EPSILON;
5640  }
5641 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5642  printf("build_center_face: %d\n", build_center_face);
5643 #endif
5644 
5645  /* Create the corner vertex BMVerts. */
5646  if (build_center_face) {
5647  do {
5648  int i = bndv->index;
5649  create_mesh_bmvert(bm, bv->vmesh, i, 1, 0, bv->v);
5650  /* The second corner vertex for the previous profile shares this BMVert. */
5651  mesh_vert(bv->vmesh, bndv->prev->index, 1, 1)->v = mesh_vert(bv->vmesh, i, 1, 0)->v;
5652 
5653  } while ((bndv = bndv->next) != bv->vmesh->boundstart);
5654  }
5655  else {
5656  /* Use the same BMVert for all of the corner vertices. */
5657  create_mesh_bmvert(bm, bv->vmesh, 0, 1, 0, bv->v);
5658  for (int i = 1; i < n_bndv; i++) {
5659  mesh_vert(bv->vmesh, i, 1, 0)->v = mesh_vert(bv->vmesh, 0, 1, 0)->v;
5660  }
5661  }
5662 
5663  /* Build the profile cutoff faces. */
5664  /* Extra one or two for corner vertices and one for last point along profile, or the size of the
5665  * center face array if it's bigger. */
5666 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5667  printf("Building profile cutoff faces.\n");
5668 #endif
5669  BMVert **face_bmverts = BLI_memarena_alloc(
5670  bp->mem_arena, sizeof(BMVert *) * max_ii(bp->seg + 2 + build_center_face, n_bndv));
5671  bndv = bv->vmesh->boundstart;
5672  do {
5673  int i = bndv->index;
5674  BMEdge **bmedges = NULL;
5675  BMFace **bmfaces = NULL;
5678 
5679  /* Add the first corner vertex under this boundvert. */
5680  face_bmverts[0] = mesh_vert(bv->vmesh, i, 1, 0)->v;
5681 
5682 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5683  printf("Profile Number %d:\n", i);
5684  if (bndv->is_patch_start || bndv->is_arc_start) {
5685  printf(" Miter profile\n");
5686  }
5687  printf(" Corner 1: (%0.3f, %0.3f, %0.3f)\n", F3(mesh_vert(bv->vmesh, i, 1, 0)->co));
5688 #endif
5689 
5690  /* Add profile point vertices to the face, including the last one. */
5691  for (int k = 0; k < bp->seg + 1; k++) {
5692  face_bmverts[k + 1] = mesh_vert(bv->vmesh, i, 0, k)->v; /* Leave room for first vert. */
5693 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5694  printf(" Profile %d: (%0.3f, %0.3f, %0.3f)\n", k, F3(mesh_vert(bv->vmesh, i, 0, k)->co));
5695 #endif
5696  }
5697 
5698  /* Add the second corner vert to complete the bottom of the face. */
5699  if (build_center_face) {
5700  face_bmverts[bp->seg + 2] = mesh_vert(bv->vmesh, i, 1, 1)->v;
5701 #ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
5702  printf(" Corner 2: (%0.3f, %0.3f, %0.3f)\n", F3(mesh_vert(bv->vmesh, i, 1, 1)->co));
5703 #endif
5704  }
5705 
5706  /* Create the profile cutoff face for this boundvert. */
5707  /* repface = boundvert_rep_face(bndv, NULL); */
5709  face_bmverts,
5710  bp->seg + 2 + build_center_face,
5711  bmfaces,
5712  NULL,
5713  bmedges,
5714  bp->mat_nr,
5715  true);
5716 
5717  BLI_array_free(bmedges);
5718  BLI_array_free(bmfaces);
5719  } while ((bndv = bndv->next) != bv->vmesh->boundstart);
5720 
5721  /* Create the bottom face if it should be built, reusing previous face_bmverts allocation. */
5722  if (build_center_face) {
5723  BMEdge **bmedges = NULL;
5724  BMFace **bmfaces = NULL;
5727 
5728  /* Add all of the corner vertices to this face. */
5729  for (int i = 0; i < n_bndv; i++) {
5730  /* Add verts from each cutoff face. */
5731  face_bmverts[i] = mesh_vert(bv->vmesh, i, 1, 0)->v;
5732  }
5733  // BLI_array_append(bmfaces, repface);
5734  bev_create_ngon(bm, face_bmverts, n_bndv, bmfaces, NULL, bmedges, bp->mat_nr, true);
5735 
5736  BLI_array_free(bmedges);
5737  BLI_array_free(bmfaces);
5738  }
5739 }
5740 
5742 {
5743  VMesh *vm = bv->vmesh;
5744  BMVert **bmverts = NULL;
5745  BMEdge **bmedges = NULL;
5746  BMFace **bmfaces = NULL;
5750 
5751  BMFace *repface;
5752  BMEdge *repface_e1, *repface_e2;
5753  BoundVert *unsnapped[3];
5754  if (bv->any_seam) {
5755  repface = frep_for_center_poly(bp, bv);
5756  get_incident_edges(repface, bv->v, &repface_e1, &repface_e2);
5757  find_face_internal_boundverts(bv, repface, unsnapped);
5758  }
5759  else {
5760  repface = NULL;
5761  repface_e1 = repface_e2 = NULL;
5762  }
5763  BoundVert *bndv = vm->boundstart;
5764  int n = 0;
5765  do {
5766  /* Accumulate vertices for vertex ngon. */
5767  /* Also accumulate faces in which uv interpolation is to happen for each. */
5768  BLI_array_append(bmverts, bndv->nv.v);
5769  if (repface) {
5770  BLI_array_append(bmfaces, repface);
5771  if (bndv == unsnapped[0] || bndv == unsnapped[1] || bndv == unsnapped[2]) {
5772  BLI_array_append(bmedges, NULL);
5773  }
5774  else {
5775  BMEdge *frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2);
5776  BLI_array_append(bmedges, frep_e);
5777  }
5778  }
5779  else {
5780  BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
5781  BLI_array_append(bmedges, NULL);
5782  }
5783  n++;
5784  if (bndv->ebev && bndv->ebev->seg > 1) {
5785  for (int k = 1; k < bndv->ebev->seg; k++) {
5786  BLI_array_append(bmverts, mesh_vert(vm, bndv->index, 0, k)->v);
5787  if (repface) {
5788  BLI_array_append(bmfaces, repface);
5789  BMEdge *frep_e = find_closer_edge(
5790  mesh_vert(vm, bndv->index, 0, k)->v->co, repface_e1, repface_e2);
5791  BLI_array_append(bmedges, k < bndv->ebev->seg / 2 ? NULL : frep_e);
5792  }
5793  else {
5794  BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
5795  BLI_array_append(bmedges, NULL);
5796  }
5797  n++;
5798  }
5799  }
5800  } while ((bndv = bndv->next) != vm->boundstart);
5801 
5802  BMFace *f;
5803  if (n > 2) {
5804  f = bev_create_ngon(bm, bmverts, n, bmfaces, repface, bmedges, bp->mat_nr, true);
5805  record_face_kind(bp, f, F_VERT);
5806  }
5807  else {
5808  f = NULL;
5809  }
5810  BLI_array_free(bmverts);
5811  BLI_array_free(bmedges);
5812  BLI_array_free(bmfaces);
5813  return f;
5814 }
5815 
5817 {
5818  BLI_assert(next_bev(bv, NULL)->seg == 1 || bv->selcount == 1);
5819 
5820  BMFace *f = bevel_build_poly(bp, bm, bv);
5821 
5822  if (f == NULL) {
5823  return;
5824  }
5825 
5826  /* We have a polygon which we know starts at the previous vertex, make it into a fan. */
5827  BMLoop *l_fan = BM_FACE_FIRST_LOOP(f)->prev;
5828  BMVert *v_fan = l_fan->v;
5829 
5830  while (f->len > 3) {
5831  BMLoop *l_new;
5832  BMFace *f_new;
5833  BLI_assert(v_fan == l_fan->v);
5834  f_new = BM_face_split(bm, f, l_fan, l_fan->next->next, &l_new, NULL, false);
5835  flag_out_edge(bm, l_new->e);
5836 
5837  if (f_new->len > f->len) {
5838  f = f_new;
5839  if (l_new->v == v_fan) {
5840  l_fan = l_new;
5841  }
5842  else if (l_new->next->v == v_fan) {
5843  l_fan = l_new->next;
5844  }
5845  else if (l_new->prev->v == v_fan) {
5846  l_fan = l_new->prev;
5847  }
5848  else {
5849  BLI_assert(0);
5850  }
5851  }
5852  else {
5853  if (l_fan->v == v_fan) { /* l_fan = l_fan. */
5854  }
5855  else if (l_fan->next->v == v_fan) {
5856  l_fan = l_fan->next;
5857  }
5858  else if (l_fan->prev->v == v_fan) {
5859  l_fan = l_fan->prev;
5860  }
5861  else {
5862  BLI_assert(0);
5863  }
5864  }
5865  record_face_kind(bp, f_new, F_VERT);
5866  }
5867 }
5868 
5869 /* Special case: vertex bevel with only two boundary verts.
5870  * Want to make a curved edge if seg > 0.
5871  * If there are no faces in the original mesh at the original vertex,
5872  * there will be no rebuilt face to make the edge between the boundary verts,
5873  * we have to make it here. */
5875 {
5876  VMesh *vm = bv->vmesh;
5877 
5878  BLI_assert(vm->count == 2 && bp->affect_type == BEVEL_AFFECT_VERTICES);
5879 
5880  BMVert *v1 = mesh_vert(vm, 0, 0, 0)->v;
5881  BMVert *v2 = mesh_vert(vm, 1, 0, 0)->v;
5882 
5883  int ns = vm->seg;
5884  if (ns > 1) {
5885  /* Set up profile parameters. */
5886  BoundVert *bndv = vm->boundstart;
5887  Profile *pro = &bndv->profile;
5888  pro->super_r = bp->pro_super_r;
5889  copy_v3_v3(pro->start, v1->co);
5890  copy_v3_v3(pro->end, v2->co);
5891  copy_v3_v3(pro->middle, bv->v->co);
5892  /* Don't use projection. */
5893  zero_v3(pro->plane_co);
5894  zero_v3(pro->plane_no);
5895  zero_v3(pro->proj_dir);
5896 
5897  for (int k = 1; k < ns; k++) {
5898  float co[3];
5899  get_profile_point(bp, pro, k, ns, co);
5900  copy_v3_v3(mesh_vert(vm, 0, 0, k)->co, co);
5901  create_mesh_bmvert(bm, vm, 0, 0, k, bv->v);
5902  }
5903  copy_v3_v3(mesh_vert(vm, 0, 0, ns)->co, v2->co);
5904  for (int k = 1; k < ns; k++) {
5905  copy_mesh_vert(vm, 1, 0, ns - k, 0, 0, k);
5906  }
5907  }
5908 
5909  if (BM_vert_face_check(bv->v) == false) {
5910  BMEdge *e_eg = bv->edges[0].e;
5911  BLI_assert(e_eg != NULL);
5912  for (int k = 0; k < ns; k++) {
5913  v1 = mesh_vert(vm, 0, 0, k)->v;
5914  v2 = mesh_vert(vm, 0, 0, k + 1)->v;
5915  BLI_assert(v1 != NULL && v2 != NULL);
5916  BMEdge *bme = BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE);
5917  if (bme) {
5918  flag_out_edge(bm, bme);
5919  }
5920  }
5921  }
5922 }
5923 
5924 /* Given that the boundary is built, now make the actual BMVerts
5925  * for the boundary and the interior of the vertex mesh. */
5926 static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
5927 {
5928  VMesh *vm = bv->vmesh;
5929  float co[3];
5930 
5931  int n = vm->count;
5932  int ns = vm->seg;
5933  int ns2 = ns / 2;
5934 
5936  sizeof(NewVert) * n * (ns2 + 1) * (ns + 1));
5937 
5938  /* Special case: just two beveled edges welded together. */
5939  const bool weld = (bv->selcount == 2) && (vm->count == 2);
5940  BoundVert *weld1 = NULL; /* Will hold two BoundVerts involved in weld. */
5941  BoundVert *weld2 = NULL;
5942 
5943  /* Make (i, 0, 0) mesh verts for all i boundverts. */
5944  BoundVert *bndv = vm->boundstart;
5945  do {
5946  int i = bndv->index;
5947  copy_v3_v3(mesh_vert(vm, i, 0, 0)->co, bndv->nv.co); /* Mesh NewVert to boundary NewVert. */
5948  create_mesh_bmvert(bm, vm, i, 0, 0, bv->v); /* Create BMVert for that NewVert. */
5949  bndv->nv.v = mesh_vert(vm, i, 0, 0)->v; /* Use the BMVert for the BoundVert's NewVert. */
5950 
5951  /* Find boundverts and move profile planes if this is a weld case. */
5952  if (weld && bndv->ebev) {
5953  if (!weld1) {
5954  weld1 = bndv;
5955  }
5956  else { /* Get the last of the two BoundVerts. */
5957  weld2 = bndv;
5958  set_profile_params(bp, bv, weld1);
5959  set_profile_params(bp, bv, weld2);
5960  move_weld_profile_planes(bv, weld1, weld2);
5961  }
5962  }
5963  } while ((bndv = bndv->next) != vm->boundstart);
5964 
5965  /* It's simpler to calculate all profiles only once at a single moment, so keep just a single
5966  * profile calculation here, the last point before actual mesh verts are created. */
5967  calculate_vm_profiles(bp, bv, vm);
5968 
5969  /* Create new vertices and place them based on the profiles. */
5970  /* Copy other ends to (i, 0, ns) for all i, and fill in profiles for edges. */
5971  bndv = vm->boundstart;
5972  do {
5973  int i = bndv->index;
5974  /* bndv's last vert along the boundary arc is the first of the next BoundVert's arc. */
5975  copy_mesh_vert(vm, i, 0, ns, bndv->next->index, 0, 0);
5976 
5977  if (vm->mesh_kind != M_ADJ) {
5978  for (int k = 1; k < ns; k++) {
5979  if (bndv->ebev) {
5980  get_profile_point(bp, &bndv->profile, k, ns, co);
5981  copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co);
5982  if (!weld) {
5983  /* This is done later with (possibly) better positions for the weld case. */
5984  create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
5985  }
5986  }
5987  else if (n == 2 && !bndv->ebev) {
5988  /* case of one edge beveled and this is the v without ebev */
5989  /* want to copy the verts from other v, in reverse order */
5990  copy_mesh_vert(bv->vmesh, i, 0, k, 1 - i, 0, ns - k);
5991  }
5992  }
5993  }
5994  } while ((bndv = bndv->next) != vm->boundstart);
5995 
5996  /* Build the profile for the weld case (just a connection between the two boundverts). */
5997  if (weld) {
5998  bv->vmesh->mesh_kind = M_NONE;
5999  for (int k = 1; k < ns; k++) {
6000  float *v_weld1 = mesh_vert(bv->vmesh, weld1->index, 0, k)->co;
6001  float *v_weld2 = mesh_vert(bv->vmesh, weld2->index, 0, ns - k)->co;
6002  if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
6003  /* Don't bother with special case profile check from below. */
6004  mid_v3_v3v3(co, v_weld1, v_weld2);
6005  }
6006  else {
6007  /* Use the point from the other profile if one is in a special case. */
6008  if (weld1->profile.super_r == PRO_LINE_R && weld2->profile.super_r != PRO_LINE_R) {
6009  copy_v3_v3(co, v_weld2);
6010  }
6011  else if (weld2->profile.super_r == PRO_LINE_R && weld1->profile.super_r != PRO_LINE_R) {
6012  copy_v3_v3(co, v_weld1);
6013  }
6014  else {
6015  /* In case the profiles aren't snapped to the same plane, use their midpoint. */
6016  mid_v3_v3v3(co, v_weld1, v_weld2);
6017  }
6018  }
6019  copy_v3_v3(mesh_vert(bv->vmesh, weld1->index, 0, k)->co, co);
6020  create_mesh_bmvert(bm, bv->vmesh, weld1->index, 0, k, bv->v);
6021  }
6022  for (int k = 1; k < ns; k++) {
6023  copy_mesh_vert(bv->vmesh, weld2->index, 0, ns - k, weld1->index, 0, k);
6024  }
6025  }
6026 
6027  /* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */
6028  BoundVert *vpipe = NULL;
6029  if (ELEM(vm->count, 3, 4) && bp->seg > 1) {
6030  /* Result is passed to bevel_build_rings to avoid overhead. */
6031  vpipe = pipe_test(bv);
6032  if (vpipe) {
6033  vm->mesh_kind = M_ADJ;
6034  }
6035  }
6036 
6037  switch (vm->mesh_kind) {
6038  case M_NONE:
6039  if (n == 2 && bp->affect_type == BEVEL_AFFECT_VERTICES) {
6040  bevel_vert_two_edges(bp, bm, bv);
6041  }
6042  break;
6043  case M_POLY:
6044  bevel_build_poly(bp, bm, bv);
6045  break;
6046  case M_ADJ:
6047  bevel_build_rings(bp, bm, bv, vpipe);
6048  break;
6049  case M_TRI_FAN:
6050  bevel_build_trifan(bp, bm, bv);
6051  break;
6052  case M_CUTOFF:
6053  bevel_build_cutoff(bp, bm, bv);
6054  }
6055 }
6056 
6057 /* Return the angle between the two faces adjacent to e.
6058  * If there are not two, return 0. */
6060 {
6061  if (e->fprev && e->fnext) {
6062  /* Angle between faces is supplement of angle between face normals. */
6063  return (float)M_PI - angle_normalized_v3v3(e->fprev->no, e->fnext->no);
6064  }
6065  return 0.0f;
6066 }
6067 
6068 /* Take care, this flag isn't cleared before use, it just so happens that its not set. */
6069 #define BM_BEVEL_EDGE_TAG_ENABLE(bme) BM_ELEM_API_FLAG_ENABLE((bme), _FLAG_OVERLAP)
6070 #define BM_BEVEL_EDGE_TAG_DISABLE(bme) BM_ELEM_API_FLAG_DISABLE((bme), _FLAG_OVERLAP)
6071 #define BM_BEVEL_EDGE_TAG_TEST(bme) BM_ELEM_API_FLAG_TEST((bme), _FLAG_OVERLAP)
6072 
6082 static int bevel_edge_order_extend(BMesh *bm, BevVert *bv, int i)
6083 {
6084  BMEdge **sucs = NULL;
6085  BMEdge **save_path = NULL;
6086  BLI_array_staticdeclare(sucs, 4); /* Likely very few faces attached to same edge. */
6088 
6089  /* Fill sucs with all unmarked edges of bmesh. */
6090  BMEdge *bme = bv->edges[i].e;
6091  BMIter iter;
6092  BMLoop *l;
6093  BM_ITER_ELEM (l, &iter, bme, BM_LOOPS_OF_EDGE) {
6094  BMEdge *bme2 = (l->v == bv->v) ? l->prev->e : l->next->e;
6095  if (!BM_BEVEL_EDGE_TAG_TEST(bme2)) {
6096  BLI_array_append(sucs, bme2);
6097  }
6098  }
6099  int nsucs = BLI_array_len(sucs);
6100 
6101  int bestj = i;
6102  int j = i;
6103  for (int sucindex = 0; sucindex < nsucs; sucindex++) {
6104  BMEdge *nextbme = sucs[sucindex];
6105  BLI_assert(nextbme != NULL);
6107  BLI_assert(j + 1 < bv->edgecount);
6108  bv->edges[j + 1].e = nextbme;
6109  BM_BEVEL_EDGE_TAG_ENABLE(nextbme);
6110  int tryj = bevel_edge_order_extend(bm, bv, j + 1);
6111  if (tryj > bestj ||
6112  (tryj == bestj && edges_face_connected_at_vert(bv->edges[tryj].e, bv->edges[0].e))) {
6113  bestj = tryj;
6114  BLI_array_clear(save_path);
6115  for (int k = j + 1; k <= bestj; k++) {
6116  BLI_array_append(save_path, bv->edges[k].e);
6117  }
6118  }
6119  /* Now reset to path only-going-to-j state. */
6120  for (int k = j + 1; k <= tryj; k++) {
6122  bv->edges[k].e = NULL;
6123  }
6124  }
6125  /* At this point we should be back at invariant on entrance: path up to j. */
6126  if (bestj > j) {
6127  /* Save_path should have from j + 1 to bestj inclusive.
6128  * Edges to add to edges[] before returning. */
6129  for (int k = j + 1; k <= bestj; k++) {
6130  BLI_assert(save_path[k - (j + 1)] != NULL);
6131  bv->edges[k].e = save_path[k - (j + 1)];
6133  }
6134  }
6135  BLI_array_free(sucs);
6136  BLI_array_free(save_path);
6137  return bestj;
6138 }
6139 
6140 /* See if we have usual case for bevel edge order:
6141  * there is an ordering such that all the faces are between
6142  * successive edges and form a manifold "cap" at bv.
6143  * If this is the case, set bv->edges to such an order
6144  * and return true; else return unmark any partial path and return false.
6145  * Assume the first edge is already in bv->edges[0].e and it is tagged. */
6146 #ifdef FASTER_FASTORDER
6147 /* The alternative older code is O(n^2) where n = # of edges incident to bv->v.
6148  * This implementation is O(n * m) where m = average number of faces attached to an edge incident
6149  * to bv->v, which is almost certainly a small constant except in very strange cases.
6150  * But this code produces different choices of ordering than the legacy system,
6151  * leading to differences in vertex orders etc. in user models,
6152  * so for now will continue to use the legacy code. */
6153 static bool fast_bevel_edge_order(BevVert *bv)
6154 {
6155  for (int j = 1; j < bv->edgecount; j++) {
6156  BMEdge *bme = bv->edges[j - 1].e;
6157  BMEdge *bmenext = NULL;
6158  int nsucs = 0;
6159  BMIter iter;
6160  BMLoop *l;
6161  BM_ITER_ELEM (l, &iter, bme, BM_LOOPS_OF_EDGE) {
6162  BMEdge *bme2 = (l->v == bv->v) ? l->prev->e : l->next->e;
6163  if (!BM_BEVEL_EDGE_TAG_TEST(bme2)) {
6164  nsucs++;
6165  if (bmenext == NULL) {
6166  bmenext = bme2;
6167  }
6168  }
6169  }
6170  if (nsucs == 0 || (nsucs == 2 && j != 1) || nsucs > 2 ||
6171  (j == bv->edgecount - 1 && !edges_face_connected_at_vert(bmenext, bv->edges[0].e))) {
6172  for (int k = 1; k < j; k++) {
6174  bv->edges[k].e = NULL;
6175  }
6176  return false;
6177  }
6178  bv->edges[j].e = bmenext;
6179  BM_BEVEL_EDGE_TAG_ENABLE(bmenext);
6180  }
6181  return true;
6182 }
6183 #else
6185 {
6186  int ntot = bv->edgecount;
6187 
6188  /* Add edges to bv->edges in order that keeps adjacent edges sharing
6189  * a unique face, if possible. */
6190  EdgeHalf *e = &bv->edges[0];
6191  BMEdge *bme = e->e;
6192  if (!bme->l) {
6193  return false;
6194  }
6195 
6196  for (int i = 1; i < ntot; i++) {
6197  /* Find an unflagged edge bme2 that shares a face f with previous bme. */
6198  int num_shared_face = 0;
6199  BMEdge *first_suc = NULL; /* Keep track of first successor to match legacy behavior. */
6200  BMIter iter;
6201  BMEdge *bme2;
6202  BM_ITER_ELEM (bme2, &iter, bv->v, BM_EDGES_OF_VERT) {
6203  if (BM_BEVEL_EDGE_TAG_TEST(bme2)) {
6204  continue;
6205  }
6206 
6207  BMIter iter2;
6208  BMFace *f;
6209  BM_ITER_ELEM (f, &iter2, bme2, BM_FACES_OF_EDGE) {
6210  if (BM_face_edge_share_loop(f, bme)) {
6211  num_shared_face++;
6212  if (first_suc == NULL) {
6213  first_suc = bme2;
6214  }
6215  }
6216  }
6217  if (num_shared_face >= 3) {
6218  break;
6219  }
6220  }
6221  if (num_shared_face == 1 || (i == 1 && num_shared_face == 2)) {
6222  e = &bv->edges[i];
6223  e->e = bme = first_suc;
6225  }
6226  else {
6227  for (int k = 1; k < i; k++) {
6229  bv->edges[k].e = NULL;
6230  }
6231  return false;
6232  }
6233  }
6234  return true;
6235 }
6236 #endif
6237 
6238 /* Fill in bv->edges with a good ordering of non-wire edges around bv->v.
6239  * Use only edges where BM_BEVEL_EDGE_TAG is disabled so far (if edge beveling, others are wire).
6240  * first_bme is a good edge to start with. */
6241 static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme)
6242 {
6243  int ntot = bv->edgecount;
6244  for (int i = 0;;) {
6245  BLI_assert(first_bme != NULL);
6246  bv->edges[i].e = first_bme;
6247  BM_BEVEL_EDGE_TAG_ENABLE(first_bme);
6248  if (i == 0 && fast_bevel_edge_order(bv)) {
6249  break;
6250  }
6251  i = bevel_edge_order_extend(bm, bv, i);
6252  i++;
6253  if (i >= bv->edgecount) {
6254  break;
6255  }
6256  /* Not done yet: find a new first_bme. */
6257  first_bme = NULL;
6258  BMIter iter;
6259  BMEdge *bme;
6260  BM_ITER_ELEM (bme, &iter, bv->v, BM_EDGES_OF_VERT) {
6261  if (BM_BEVEL_EDGE_TAG_TEST(bme)) {
6262  continue;
6263  }
6264  if (!first_bme) {
6265  first_bme = bme;
6266  }
6267  if (BM_edge_face_count(bme) == 1) {
6268  first_bme = bme;
6269  break;
6270  }
6271  }
6272  }
6273  /* Now fill in the faces. */
6274  for (int i = 0; i < ntot; i++) {
6275  EdgeHalf *e = &bv->edges[i];
6276  EdgeHalf *e2 = (i == bv->edgecount - 1) ? &bv->edges[0] : &bv->edges[i + 1];
6277  BMEdge *bme = e->e;
6278  BMEdge *bme2 = e2->e;
6279  BLI_assert(bme != NULL);
6280  if (e->fnext != NULL || e2->fprev != NULL) {
6281  continue;
6282  }
6283  /* Which faces have successive loops that are for bme and bme2?
6284  * There could be more than one. E.g., in manifold ntot==2 case.
6285  * Prefer one that has loop in same direction as e. */
6286  BMFace *bestf = NULL;
6287  BMIter iter;
6288  BMLoop *l;
6289  BM_ITER_ELEM (l, &iter, bme, BM_LOOPS_OF_EDGE) {
6290  BMFace *f = l->f;
6291  if ((l->prev->e == bme2 || l->next->e == bme2)) {
6292  if (!bestf || l->v == bv->v) {
6293  bestf = f;
6294  }
6295  }
6296  if (bestf) {
6297  e->fnext = e2->fprev = bestf;
6298  }
6299  }
6300  }
6301 }
6302 
6303 /* Construction around the vertex. */
6305 {
6306  /* Gather input selected edges.
6307  * Only bevel selected edges that have exactly two incident faces.
6308  * Want edges to be ordered so that they share faces.
6309  * There may be one or more chains of shared faces broken by
6310  * gaps where there are no faces.
6311  * Want to ignore wire edges completely for edge beveling.
6312  * TODO: make following work when more than one gap. */
6313 
6314  int nsel = 0;
6315  int tot_edges = 0;
6316  int tot_wire = 0;
6317  BMEdge *first_bme = NULL;
6318  BMIter iter;
6319  BMEdge *bme;
6320  BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) {
6321  int face_count = BM_edge_face_count(bme);
6324  BLI_assert(face_count == 2);
6325  nsel++;
6326  if (!first_bme) {
6327  first_bme = bme;
6328  }
6329  }
6330  if (face_count == 1) {
6331  /* Good to start face chain from this edge. */
6332  first_bme = bme;
6333  }
6334  if (face_count > 0 || bp->affect_type == BEVEL_AFFECT_VERTICES) {
6335  tot_edges++;
6336  }
6337  if (BM_edge_is_wire(bme)) {
6338  tot_wire++;
6339  /* If edge beveling, exclude wire edges from edges array.
6340  * Mark this edge as "chosen" so loop below won't choose it. */
6341  if (bp->affect_type != BEVEL_AFFECT_VERTICES) {
6343  }
6344  }
6345  }
6346  if (!first_bme) {
6347  first_bme = v->e;
6348  }
6349 
6350  if ((nsel == 0 && bp->affect_type != BEVEL_AFFECT_VERTICES) ||
6351  (tot_edges < 2 && bp->affect_type == BEVEL_AFFECT_VERTICES)) {
6352  /* Signal this vert isn't being beveled. */
6354  return NULL;
6355  }
6356 
6357  BevVert *bv = (BevVert *)BLI_memarena_alloc(bp->mem_arena, sizeof(BevVert));
6358  bv->v = v;
6359  bv->edgecount = tot_edges;
6360  bv->selcount = nsel;
6361  bv->wirecount = tot_wire;
6362  bv->offset = bp->offset;
6363  bv->edges = (EdgeHalf *)BLI_memarena_alloc(bp->mem_arena, sizeof(EdgeHalf) * tot_edges);
6364  if (tot_wire) {
6365  bv->wire_edges = (BMEdge **)BLI_memarena_alloc(bp->mem_arena, sizeof(BMEdge *) * tot_wire);
6366  }
6367  else {
6368  bv->wire_edges = NULL;
6369  }
6370  bv->vmesh = (VMesh *)BLI_memarena_alloc(bp->mem_arena, sizeof(VMesh));
6371  bv->vmesh->seg = bp->seg;
6372 
6373  BLI_ghash_insert(bp->vert_hash, v, bv);
6374 
6375  find_bevel_edge_order(bm, bv, first_bme);
6376 
6377  /* Fill in other attributes of EdgeHalfs. */
6378  for (int i = 0; i < tot_edges; i++) {
6379  EdgeHalf *e = &bv->edges[i];
6380  bme = e->e;
6382  e->is_bev = true;
6383  e->seg = bp->seg;
6384  }
6385  else {
6386  e->is_bev = false;
6387  e->seg = 0;
6388  }
6389  e->is_rev = (bme->v2 == v);
6390  e->leftv = e->rightv = NULL;
6391  e->profile_index = 0;
6392  }
6393 
6394  /* Now done with tag flag. */
6395  BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) {
6397  }
6398 
6399  /* If edge array doesn't go CCW around vertex from average normal side,
6400  * reverse the array, being careful to reverse face pointers too. */
6401  if (tot_edges > 1) {
6402  int ccw_test_sum = 0;
6403  for (int i = 0; i < tot_edges; i++) {
6404  ccw_test_sum += bev_ccw_test(
6405  bv->edges[i].e, bv->edges[(i + 1) % tot_edges].e, bv->edges[i].fnext);
6406  }
6407  if (ccw_test_sum < 0) {
6408  for (int i = 0; i <= (tot_edges / 2) - 1; i++) {
6409  SWAP(EdgeHalf, bv->edges[i], bv->edges[tot_edges - i - 1]);
6410  SWAP(BMFace *, bv->edges[i].fprev, bv->edges[i].fnext);
6411  SWAP(BMFace *, bv->edges[tot_edges - i - 1].fprev, bv->edges[tot_edges - i - 1].fnext);
6412  }
6413  if (tot_edges % 2 == 1) {
6414  int i = tot_edges / 2;
6415  SWAP(BMFace *, bv->edges[i].fprev, bv->edges[i].fnext);
6416  }
6417  }
6418  }
6419 
6420  float weight;
6421  float vert_axis[3] = {0, 0, 0};
6422  if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
6423  /* Modify the offset by the vertex group or bevel weight if they are specified. */
6424  if (bp->dvert != NULL && bp->vertex_group != -1) {
6426  bv->offset *= weight;
6427  }
6428  else if (bp->use_weights) {
6429  weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT);
6430  bv->offset *= weight;
6431  }
6432  /* Find center axis. NOTE: Don't use vert normal, can give unwanted results. */
6434  float edge_dir[3];
6435  EdgeHalf *e = bv->edges;
6436  for (int i = 0; i < tot_edges; i++, e++) {
6437  BMVert *v2 = BM_edge_other_vert(e->e, bv->v);
6438  sub_v3_v3v3(edge_dir, bv->v->co, v2->co);
6439  normalize_v3(edge_dir);
6440  add_v3_v3v3(vert_axis, vert_axis, edge_dir);
6441  }
6442  }
6443  }
6444 
6445  /* Set offsets for each beveled edge. */
6446  EdgeHalf *e = bv->edges;
6447  for (int i = 0; i < tot_edges; i++, e++) {
6448  e->next = &bv->edges[(i + 1) % tot_edges];
6449  e->prev = &bv->edges[(i + tot_edges - 1) % tot_edges];
6450 
6451  if (e->is_bev) {
6452  /* Convert distance as specified by user into offsets along
6453  * faces on the left side and right sides of this edgehalf.
6454  * Except for percent method, offset will be same on each side. */
6455 
6456  switch (bp->offset_type) {
6457  case BEVEL_AMT_OFFSET: {
6458  e->offset_l_spec = bp->offset;
6459  break;
6460  }
6461  case BEVEL_AMT_WIDTH: {
6462  float z = fabsf(2.0f * sinf(edge_face_angle(e) / 2.0f));
6463  if (z < BEVEL_EPSILON) {
6464  e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */
6465  }
6466  else {
6467  e->offset_l_spec = bp->offset / z;
6468  }
6469  break;
6470  }
6471  case BEVEL_AMT_DEPTH: {
6472  float z = fabsf(cosf(edge_face_angle(e) / 2.0f));
6473  if (z < BEVEL_EPSILON) {
6474  e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */
6475  }
6476  else {
6477  e->offset_l_spec = bp->offset / z;
6478  }
6479  break;
6480  }
6481  case BEVEL_AMT_PERCENT: {
6482  /* Offset needs to meet adjacent edges at percentage of their lengths.
6483  * Since the width isn't constant, we don't store a width at all, but
6484  * rather the distance along the adjacent edge that we need to go
6485  * at this end of the edge.
6486  */
6487 
6488  e->offset_l_spec = BM_edge_calc_length(e->prev->e) * bp->offset / 100.0f;
6489  e->offset_r_spec = BM_edge_calc_length(e->next->e) * bp->offset / 100.0f;
6490 
6491  break;
6492  }
6493  case BEVEL_AMT_ABSOLUTE: {
6494  /* Like Percent, but the amount gives the absolute distance along adjacent edges. */
6495  e->offset_l_spec = bp->offset;
6496  e->offset_r_spec = bp->offset;
6497  break;
6498  }
6499  default: {
6500  BLI_assert_msg(0, "bad bevel offset kind");
6501  e->offset_l_spec = bp->offset;
6502  break;
6503  }
6504  }
6506  e->offset_r_spec = e->offset_l_spec;
6507  }
6508  if (bp->use_weights) {
6509  weight = BM_elem_float_data_get(&bm->edata, e->e, CD_BWEIGHT);
6510  e->offset_l_spec *= weight;
6511  e->offset_r_spec *= weight;
6512  }
6513  }
6514  else if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
6515  /* Weight has already been applied to bv->offset, if present.
6516  * Transfer to e->offset_[lr]_spec according to offset_type. */
6517  float edge_dir[3];
6518  switch (bp->offset_type) {
6519  case BEVEL_AMT_OFFSET: {
6520  e->offset_l_spec = bv->offset;
6521  break;
6522  }
6523  case BEVEL_AMT_WIDTH: {
6524  BMVert *v2 = BM_edge_other_vert(e->e, bv->v);
6525  sub_v3_v3v3(edge_dir, bv->v->co, v2->co);
6526  float z = fabsf(2.0f * sinf(angle_v3v3(vert_axis, edge_dir)));
6527  if (z < BEVEL_EPSILON) {
6528  e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */
6529  }
6530  else {
6531  e->offset_l_spec = bp->offset / z;
6532  }
6533  break;
6534  }
6535  case BEVEL_AMT_DEPTH: {
6536  BMVert *v2 = BM_edge_other_vert(e->e, bv->v);
6537  sub_v3_v3v3(edge_dir, bv->v->co, v2->co);
6538  float z = fabsf(cosf(angle_v3v3(vert_axis, edge_dir)));
6539  if (z < BEVEL_EPSILON) {
6540  e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */
6541  }
6542  else {
6543  e->offset_l_spec = bp->offset / z;
6544  }
6545  break;
6546  }
6547  case BEVEL_AMT_PERCENT: {
6548  e->offset_l_spec = BM_edge_calc_length(e->e) * bv->offset / 100.0f;
6549  break;
6550  }
6551  case BEVEL_AMT_ABSOLUTE: {
6552  e->offset_l_spec = bv->offset;
6553  break;
6554  }
6555  }
6556  e->offset_r_spec = e->offset_l_spec;
6557  }
6558  else {
6559  e->offset_l_spec = e->offset_r_spec = 0.0f;
6560  }
6561  e->offset_l = e->offset_l_spec;
6562  e->offset_r = e->offset_r_spec;
6563 
6564  if (e->fprev && e->fnext) {
6565  e->is_seam = !contig_ldata_across_edge(bm, e->e, e->fprev, e->fnext);
6566  }
6567  else {
6568  e->is_seam = true;
6569  }
6570  }
6571 
6572  /* Collect wire edges if we found any earlier. */
6573  if (tot_wire != 0) {
6574  int i = 0;
6575  BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) {
6576  if (BM_edge_is_wire(bme)) {
6577  BLI_assert(i < bv->wirecount);
6578  bv->wire_edges[i++] = bme;
6579  }
6580  }
6581  BLI_assert(i == bv->wirecount);
6582  }
6583 
6584  return bv;
6585 }
6586 
6587 /* Face f has at least one beveled vertex. Rebuild f. */
6589 {
6590  bool do_rebuild = false;
6591  BMVert **vv = NULL;
6592  BMVert **vv_fix = NULL;
6593  BMEdge **ee = NULL;
6597 
6598  BMIter liter;
6599  BMLoop *l;
6600  BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
6601  if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
6602  BMLoop *lprev = l->prev;
6603  BevVert *bv = find_bevvert(bp, l->v);
6604  VMesh *vm = bv->vmesh;
6605  EdgeHalf *e = find_edge_half(bv, l->e);
6606  BLI_assert(e != NULL);
6607  BMEdge *bme = e->e;
6608  EdgeHalf *eprev = find_edge_half(bv, lprev->e);
6609  BLI_assert(eprev != NULL);
6610 
6611  /* Which direction around our vertex do we travel to match orientation of f? */
6612  bool go_ccw;
6613  if (e->prev == eprev) {
6614  if (eprev->prev == e) {
6615  /* Valence 2 vertex: use f is one of e->fnext or e->fprev to break tie. */
6616  go_ccw = (e->fnext != f);
6617  }
6618  else {
6619  go_ccw = true; /* Going CCW around bv to trace this corner. */
6620  }
6621  }
6622  else if (eprev->prev == e) {
6623  go_ccw = false; /* Going cw around bv to trace this corner. */
6624  }
6625  else {
6626  /* Edges in face are non-contiguous in our ordering around bv.
6627  * Which way should we go when going from eprev to e? */
6628  if (count_ccw_edges_between(eprev, e) < count_ccw_edges_between(e, eprev)) {
6629  /* Go counter-clockwise from eprev to e. */
6630  go_ccw = true;
6631  }
6632  else {
6633  /* Go clockwise from eprev to e. */
6634  go_ccw = false;
6635  }
6636  }
6637  bool on_profile_start = false;
6638  BoundVert *vstart;
6639  BoundVert *vend;
6640  if (go_ccw) {
6641  vstart = eprev->rightv;
6642  vend = e->leftv;
6643  if (e->profile_index > 0) {
6644  vstart = vstart->prev;
6645  on_profile_start = true;
6646  }
6647  }
6648  else {
6649  vstart = eprev->leftv;
6650  vend = e->rightv;
6651  if (eprev->profile_index > 0) {
6652  vstart = vstart->next;
6653  on_profile_start = true;
6654  }
6655  }
6656  BLI_assert(vstart != NULL && vend != NULL);
6657  BoundVert *v = vstart;
6658  if (!on_profile_start) {
6659  BLI_array_append(vv, v->nv.v);
6660  BLI_array_append(ee, bme);
6661  }
6662  while (v != vend) {
6663  /* Check for special case: multi-segment 3rd face opposite a beveled edge with no vmesh. */
6664  bool corner3special = (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev);
6665  if (go_ccw) {
6666  int i = v->index;
6667  int kstart, kend;
6668  if (on_profile_start) {
6669  kstart = e->profile_index;
6670  on_profile_start = false;
6671  }
6672  else {
6673  kstart = 1;
6674  }
6675  if (eprev->rightv == v && eprev->profile_index > 0) {
6676  kend = eprev->profile_index;
6677  }
6678  else {
6679  kend = vm->seg;
6680  }
6681  for (int k = kstart; k <= kend; k++) {
6682  BMVert *bmv = mesh_vert(vm, i, 0, k)->v;
6683  if (bmv) {
6684  BLI_array_append(vv, bmv);
6685  BLI_array_append(ee, bme); /* TODO: Maybe better edge here. */
6686  if (corner3special && v->ebev && !bv->any_seam && k != vm->seg) {
6687  BLI_array_append(vv_fix, bmv);
6688  }
6689  }
6690  }
6691  v = v->next;
6692  }
6693  else {
6694  /* Going cw. */
6695  int i = v->prev->index;
6696  int kstart, kend;
6697  if (on_profile_start) {
6698  kstart = eprev->profile_index;
6699  on_profile_start = false;
6700  }
6701  else {
6702  kstart = vm->seg - 1;
6703  }
6704  if (e->rightv == v->prev && e->profile_index > 0) {
6705  kend = e->profile_index;
6706  }
6707  else {
6708  kend = 0;
6709  }
6710  for (int k = kstart; k >= kend; k--) {
6711  BMVert *bmv = mesh_vert(vm, i, 0, k)->v;
6712  if (bmv) {
6713  BLI_array_append(vv, bmv);
6714  BLI_array_append(ee, bme);
6715  if (corner3special && v->ebev && !bv->any_seam && k != 0) {
6716  BLI_array_append(vv_fix, bmv);
6717  }
6718  }
6719  }
6720  v = v->prev;
6721  }
6722  }
6723  do_rebuild = true;
6724  }
6725  else {
6726  BLI_array_append(vv, l->v);
6727  BLI_array_append(ee, l->e);
6728  }
6729  }
6730  if (do_rebuild) {
6731  int n = BLI_array_len(vv);
6732  BMFace *f_new = bev_create_ngon(bm, vv, n, NULL, f, NULL, -1, true);
6733 
6734  for (int k = 0; k < BLI_array_len(vv_fix); k++) {
6735  bev_merge_uvs(bm, vv_fix[k]);
6736  }
6737 
6738  /* Copy attributes from old edges. */
6739  BLI_assert(n == BLI_array_len(ee));
6740  BMEdge *bme_prev = ee[n - 1];
6741  for (int k = 0; k < n; k++) {
6742  BMEdge *bme_new = BM_edge_exists(vv[k], vv[(k + 1) % n]);
6743  BLI_assert(ee[k] && bme_new);
6744  if (ee[k] != bme_new) {
6745  BM_elem_attrs_copy(bm, bm, ee[k], bme_new);
6746  /* Want to undo seam and smooth for corner segments
6747  * if those attrs aren't contiguous around face. */
6748  if (k < n - 1 && ee[k] == ee[k + 1]) {
6749  if (BM_elem_flag_test(ee[k], BM_ELEM_SEAM) &&
6750  !BM_elem_flag_test(bme_prev, BM_ELEM_SEAM)) {
6752  }
6753  /* Actually want "sharp" to be contiguous, so reverse the test. */
6754  if (!BM_elem_flag_test(ee[k], BM_ELEM_SMOOTH) &&
6755  BM_elem_flag_test(bme_prev, BM_ELEM_SMOOTH)) {
6757  }
6758  }
6759  else {
6760  bme_prev = ee[k];
6761  }
6762  }
6763  }
6764 
6765  /* Don't select newly or return created boundary faces. */
6766  if (f_new) {
6767  record_face_kind(bp, f_new, F_RECON);
6769  /* Also don't want new edges that aren't part of a new bevel face. */
6770  BMIter eiter;
6771  BMEdge *bme;
6772  BM_ITER_ELEM (bme, &eiter, f_new, BM_EDGES_OF_FACE) {
6773  bool keep = false;
6774  BMIter fiter;
6775  BMFace *f_other;
6776  BM_ITER_ELEM (f_other, &fiter, bme, BM_FACES_OF_EDGE) {
6777  if (BM_elem_flag_test(f_other, BM_ELEM_TAG)) {
6778  keep = true;
6779  break;
6780  }
6781  }
6782  if (!keep) {
6783  disable_flag_out_edge(bm, bme);
6784  }
6785  }
6786  }
6787  }
6788 
6789  BLI_array_free(vv);
6790  BLI_array_free(vv_fix);
6791  BLI_array_free(ee);
6792  return do_rebuild;
6793 }
6794 
6795 /* All polygons touching v need rebuilding because beveling v has made new vertices. */
6797 {
6798  void *faces_stack[BM_DEFAULT_ITER_STACK_SIZE];
6799  int faces_len, f_index;
6801  bm, BM_FACES_OF_VERT, v, &faces_len, faces_stack, BM_DEFAULT_ITER_STACK_SIZE);
6802 
6803  if (LIKELY(faces != NULL)) {
6804  for (f_index = 0; f_index < faces_len; f_index++) {
6805  BMFace *f = faces[f_index];
6806  if (bev_rebuild_polygon(bm, bp, f)) {
6807  BM_face_kill(bm, f);
6808  }
6809  }
6810 
6811  if (faces != (BMFace **)faces_stack) {
6812  MEM_freeN(faces);
6813  }
6814  }
6815 }
6816 
6817 /* If there were any wire edges, they need to be reattached somewhere. */
6819 {
6820  BevVert *bv = find_bevvert(bp, v);
6821  if (!bv || bv->wirecount == 0 || !bv->vmesh) {
6822  return;
6823  }
6824 
6825  for (int i = 0; i < bv->wirecount; i++) {
6826  BMEdge *e = bv->wire_edges[i];
6827  /* Look for the new vertex closest to the other end of e. */
6828  BMVert *vclosest = NULL;
6829  float dclosest = FLT_MAX;
6830  BMVert *votherclosest = NULL;
6831  BMVert *vother = BM_edge_other_vert(e, v);
6832  BevVert *bvother = NULL;
6833  if (BM_elem_flag_test(vother, BM_ELEM_TAG)) {
6834  bvother = find_bevvert(bp, vother);
6835  if (!bvother || !bvother->vmesh) {
6836  return; /* Shouldn't happen. */
6837  }
6838  }
6839  BoundVert *bndv = bv->vmesh->boundstart;
6840  do {
6841  if (bvother) {
6842  BoundVert *bndvother = bvother->vmesh->boundstart;
6843  do {
6844  float d = len_squared_v3v3(bndvother->nv.co, bndv->nv.co);
6845  if (d < dclosest) {
6846  vclosest = bndv->nv.v;
6847  votherclosest = bndvother->nv.v;
6848  dclosest = d;
6849  }
6850  } while ((bndvother = bndvother->next) != bvother->vmesh->boundstart);
6851  }
6852  else {
6853  float d = len_squared_v3v3(vother->co, bndv->nv.co);
6854  if (d < dclosest) {
6855  vclosest = bndv->nv.v;
6856  votherclosest = vother;
6857  dclosest = d;
6858  }
6859  }
6860  } while ((bndv = bndv->next) != bv->vmesh->boundstart);
6861  if (vclosest) {
6862  BM_edge_create(bm, vclosest, votherclosest, e, BM_CREATE_NO_DOUBLE);
6863  }
6864  }
6865 }
6866 
6868 {
6869  VMesh *vm = bv->vmesh;
6870 
6871  int nseg = e->seg;
6872  int i = e->leftv->index;
6873  for (int k = 1; k < nseg; k++) {
6874  bev_merge_uvs(bm, mesh_vert(vm, i, 0, k)->v);
6875  }
6876 }
6877 
6878 /*
6879  * Is this BevVert the special case of a weld (no vmesh) where there are
6880  * four edges total, two are beveled, and the other two are on opposite sides?
6881  */
6883 {
6884  return (bv->edgecount == 4 && bv->selcount == 2 &&
6885  ((bv->edges[0].is_bev && bv->edges[2].is_bev) ||
6886  (bv->edges[1].is_bev && bv->edges[3].is_bev)));
6887 }
6888 
6908 static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex, EdgeHalf *e)
6909 {
6910  BMEdge *bme_prev = NULL;
6911  BMEdge *bme_next = NULL;
6912  for (int i = 0; i < 4; i++) {
6913  if (&bv->edges[i] == e) {
6914  bme_prev = bv->edges[(i + 3) % 4].e;
6915  bme_next = bv->edges[(i + 1) % 4].e;
6916  break;
6917  }
6918  }
6919  BLI_assert(bme_prev && bme_next);
6920 
6921  /* Want seams and sharp edges to cross only if that way on both sides. */
6922  bool disable_seam = BM_elem_flag_test(bme_prev, BM_ELEM_SEAM) !=
6923  BM_elem_flag_test(bme_next, BM_ELEM_SEAM);
6924  bool enable_smooth = BM_elem_flag_test(bme_prev, BM_ELEM_SMOOTH) !=
6925  BM_elem_flag_test(bme_next, BM_ELEM_SMOOTH);
6926 
6927  int nseg = e->seg;
6928  for (int i = 0; i < nseg; i++) {
6929  BMEdge *bme = BM_edge_exists(mesh_vert(vm, vmindex, 0, i)->v,
6930  mesh_vert(vm, vmindex, 0, i + 1)->v);
6931  BLI_assert(bme);
6932  BM_elem_attrs_copy(bm, bm, bme_prev, bme);
6933  if (disable_seam) {
6935  }
6936  if (enable_smooth) {
6938  }
6939  }
6940 }
6941 
6946 {
6947  int mat_nr = bp->mat_nr;
6948 
6949  if (!BM_edge_is_manifold(bme)) {
6950  return;
6951  }
6952 
6953  BevVert *bv1 = find_bevvert(bp, bme->v1);
6954  BevVert *bv2 = find_bevvert(bp, bme->v2);
6955 
6956  BLI_assert(bv1 && bv2);
6957 
6958  EdgeHalf *e1 = find_edge_half(bv1, bme);
6959  EdgeHalf *e2 = find_edge_half(bv2, bme);
6960 
6961  BLI_assert(e1 && e2);
6962 
6963  /*
6964  * bme->v1
6965  * / | \
6966  * v1--|--v4
6967  * | | |
6968  * | | |
6969  * v2--|--v3
6970  * \ | /
6971  * bme->v2
6972  */
6973  int nseg = e1->seg;
6974  BLI_assert(nseg > 0 && nseg == e2->seg);
6975 
6976  BMVert *bmv1 = e1->leftv->nv.v;
6977  BMVert *bmv4 = e1->rightv->nv.v;
6978  BMVert *bmv2 = e2->rightv->nv.v;
6979  BMVert *bmv3 = e2->leftv->nv.v;
6980 
6981  BLI_assert(bmv1 && bmv2 && bmv3 && bmv4);
6982 
6983  BMFace *f1 = e1->fprev;
6984  BMFace *f2 = e1->fnext;
6985  BMFace *faces[4] = {f1, f1, f2, f2};
6986 
6987  int i1 = e1->leftv->index;
6988  int i2 = e2->leftv->index;
6989  VMesh *vm1 = bv1->vmesh;
6990  VMesh *vm2 = bv2->vmesh;
6991 
6992  BMVert *verts[4];
6993  verts[0] = bmv1;
6994  verts[1] = bmv2;
6995  int odd = nseg % 2;
6996  int mid = nseg / 2;
6997  BMEdge *center_bme = NULL;
6998  BMFace *fchoices[2] = {f1, f2};
6999  BMFace *f_choice = NULL;
7000  int center_adj_k = -1;
7001  if (odd & e1->is_seam) {
7002  f_choice = choose_rep_face(bp, fchoices, 2);
7003  if (nseg > 1) {
7004  center_adj_k = f_choice == f1 ? mid + 2 : mid;
7005  }
7006  }
7007  for (int k = 1; k <= nseg; k++) {
7008  verts[3] = mesh_vert(vm1, i1, 0, k)->v;
7009  verts[2] = mesh_vert(vm2, i2, 0, nseg - k)->v;
7010  BMFace *r_f;
7011  if (odd && k == mid + 1) {
7012  if (e1->is_seam) {
7013  /* Straddles a seam: choose to interpolate in f_choice and snap the loops whose verts
7014  * are in the non-chosen face to bme for interpolation purposes.
7015  */
7016  BMEdge *edges[4];
7017  if (f_choice == f1) {
7018  edges[0] = edges[1] = NULL;
7019  edges[2] = edges[3] = bme;
7020  }
7021  else {
7022  edges[0] = edges[1] = bme;
7023  edges[2] = edges[3] = NULL;
7024  }
7025  r_f = bev_create_ngon(bm, verts, 4, NULL, f_choice, edges, mat_nr, true);
7026  }
7027  else {
7028  /* Straddles but not a seam: interpolate left half in f1, right half in f2. */
7029  r_f = bev_create_ngon(bm, verts, 4, faces, f_choice, NULL, mat_nr, true);
7030  }
7031  }
7032  else if (odd && k == center_adj_k && e1->is_seam) {
7033  /* The strip adjacent to the center one, in another UV island.
7034  * Snap the edge near the seam to bme to match what happens in
7035  * the bevel rings.
7036  */
7037  BMEdge *edges[4];
7038  BMFace *f_interp;
7039  if (k == mid) {
7040  edges[0] = edges[1] = NULL;
7041  edges[2] = edges[3] = bme;
7042  f_interp = f1;
7043  }
7044  else {
7045  edges[0] = edges[1] = bme;
7046  edges[2] = edges[3] = NULL;
7047  f_interp = f2;
7048  }
7049  r_f = bev_create_ngon(bm, verts, 4, NULL, f_interp, edges, mat_nr, true);
7050  }
7051  else if (!odd && k == mid) {
7052  /* Left poly that touches an even center line on right. */
7053  BMEdge *edges[4] = {NULL, NULL, bme, bme};
7054  r_f = bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
7055  center_bme = BM_edge_exists(verts[2], verts[3]);
7056  BLI_assert(center_bme != NULL);
7057  }
7058  else if (!odd && k == mid + 1) {
7059  /* Right poly that touches an even center line on left. */
7060  BMEdge *edges[4] = {bme, bme, NULL, NULL};
7061  r_f = bev_create_ngon(bm, verts, 4, NULL, f2, edges, mat_nr, true);
7062  }
7063  else {
7064  /* Doesn't cross or touch the center line, so interpolate in appropriate f1 or f2. */
7065  BMFace *f = (k <= mid) ? f1 : f2;
7066  r_f = bev_create_ngon(bm, verts, 4, NULL, f, NULL, mat_nr, true);
7067  }
7068  record_face_kind(bp, r_f, F_EDGE);
7069  /* Tag the long edges: those out of verts[0] and verts[2]. */
7070  BMIter iter;
7071  BMLoop *l;
7072  BM_ITER_ELEM (l, &iter, r_f, BM_LOOPS_OF_FACE) {
7073  if (ELEM(l->v, verts[0], verts[2])) {
7075  }
7076  }
7077  verts[0] = verts[3];
7078  verts[1] = verts[2];
7079  }
7080  if (!odd) {
7081  if (!e1->is_seam) {
7082  bev_merge_edge_uvs(bm, center_bme, mesh_vert(vm1, i1, 0, mid)->v);
7083  }
7084  if (!e2->is_seam) {
7085  bev_merge_edge_uvs(bm, center_bme, mesh_vert(vm2, i2, 0, mid)->v);
7086  }
7087  }
7088 
7089  /* Fix UVs along end edge joints. A nop unless other side built already. */
7090  /* TODO: If some seam, may want to do selective merge. */
7091  if (!bv1->any_seam && bv1->vmesh->mesh_kind == M_NONE) {
7092  bev_merge_end_uvs(bm, bv1, e1);
7093  }
7094  if (!bv2->any_seam && bv2->vmesh->mesh_kind == M_NONE) {
7095  bev_merge_end_uvs(bm, bv2, e2);
7096  }
7097 
7098  /* Copy edge data to first and last edge. */
7099  BMEdge *bme1 = BM_edge_exists(bmv1, bmv2);
7100  BMEdge *bme2 = BM_edge_exists(bmv3, bmv4);
7101  BLI_assert(bme1 && bme2);
7102  BM_elem_attrs_copy(bm, bm, bme, bme1);
7103  BM_elem_attrs_copy(bm, bm, bme, bme2);
7104 
7105  /* If either end is a "weld cross", want continuity of edge attributes across end edge(s). */
7106  if (bevvert_is_weld_cross(bv1)) {
7107  weld_cross_attrs_copy(bm, bv1, vm1, i1, e1);
7108  }
7109  if (bevvert_is_weld_cross(bv2)) {
7110  weld_cross_attrs_copy(bm, bv2, vm2, i2, e2);
7111  }
7112 }
7113 
7114 /* Find xnew > x0 so that distance((x0,y0), (xnew, ynew)) = dtarget.
7115  * False position Illinois method used because the function is somewhat linear
7116  * -> linear interpolation converges fast.
7117  * Assumes that the gradient is always between 1 and -1 for x in [x0, x0+dtarget]. */
7118 static double find_superellipse_chord_endpoint(double x0, double dtarget, float r, bool rbig)
7119 {
7120  double y0 = superellipse_co(x0, r, rbig);
7121  const double tol = 1e-13; /* accumulates for many segments so use low value. */
7122  const int maxiter = 10;
7123 
7124  /* For gradient between -1 and 1, xnew can only be in [x0 + sqrt(2)/2*dtarget, x0 + dtarget]. */
7125  double xmin = x0 + M_SQRT2 / 2.0 * dtarget;
7126  if (xmin > 1.0) {
7127  xmin = 1.0;
7128  }
7129  double xmax = x0 + dtarget;
7130  if (xmax > 1.0) {
7131  xmax = 1.0;
7132  }
7133  double ymin = superellipse_co(xmin, r, rbig);
7134  double ymax = superellipse_co(xmax, r, rbig);
7135 
7136  /* NOTE: using distance**2 (no sqrt needed) does not converge that well. */
7137  double dmaxerr = sqrt(pow((xmax - x0), 2) + pow((ymax - y0), 2)) - dtarget;
7138  double dminerr = sqrt(pow((xmin - x0), 2) + pow((ymin - y0), 2)) - dtarget;
7139 
7140  double xnew = xmax - dmaxerr * (xmax - xmin) / (dmaxerr - dminerr);
7141  bool lastupdated_upper = true;
7142 
7143  for (int iter = 0; iter < maxiter; iter++) {
7144  double ynew = superellipse_co(xnew, r, rbig);
7145  double dnewerr = sqrt(pow((xnew - x0), 2) + pow((ynew - y0), 2)) - dtarget;
7146  if (fabs(dnewerr) < tol) {
7147  break;
7148  }
7149  if (dnewerr < 0) {
7150  xmin = xnew;
7151  ymin = ynew;
7152  dminerr = dnewerr;
7153  if (!lastupdated_upper) {
7154  xnew = (dmaxerr / 2 * xmin - dminerr * xmax) / (dmaxerr / 2 - dminerr);
7155  }
7156  else {
7157  xnew = xmax - dmaxerr * (xmax - xmin) / (dmaxerr - dminerr);
7158  }
7159  lastupdated_upper = false;
7160  }
7161  else {
7162  xmax = xnew;
7163  ymax = ynew;
7164  dmaxerr = dnewerr;
7165  if (lastupdated_upper) {
7166  xnew = (dmaxerr * xmin - dminerr / 2 * xmax) / (dmaxerr - dminerr / 2);
7167  }
7168  else {
7169  xnew = xmax - dmaxerr * (xmax - xmin) / (dmaxerr - dminerr);
7170  }
7171  lastupdated_upper = true;
7172  }
7173  }
7174  return xnew;
7175 }
7176 
7186 static void find_even_superellipse_chords_general(int seg, float r, double *xvals, double *yvals)
7187 {
7188  const int smoothitermax = 10;
7189  const double error_tol = 1e-7;
7190  int imax = (seg + 1) / 2 - 1; /* Ceiling division - 1. */
7191 
7192  bool seg_odd = seg % 2;
7193 
7194  bool rbig;
7195  double mx;
7196  if (r > 1.0f) {
7197  rbig = true;
7198  mx = pow(0.5, 1.0 / r);
7199  }
7200  else {
7201  rbig = false;
7202  mx = 1 - pow(0.5, 1.0 / r);
7203  }
7204 
7205  /* Initial positions, linear spacing along x axis. */
7206  for (int i = 0; i <= imax; i++) {
7207  xvals[i] = i * mx / seg * 2;
7208  yvals[i] = superellipse_co(xvals[i], r, rbig);
7209  }
7210  yvals[0] = 1;
7211 
7212  /* Smooth distance loop. */
7213  for (int iter = 0; iter < smoothitermax; iter++) {
7214  double sum = 0.0;
7215  double dmin = 2.0;
7216  double dmax = 0.0;
7217  /* Update distances between neighbor points. Store the highest and
7218  * lowest to see if the maximum error to average distance (which isn't
7219  * known yet) is below required precision. */
7220  for (int i = 0; i < imax; i++) {
7221  double d = sqrt(pow((xvals[i + 1] - xvals[i]), 2) + pow((yvals[i + 1] - yvals[i]), 2));
7222  sum += d;
7223  if (d > dmax) {
7224  dmax = d;
7225  }
7226  if (d < dmin) {
7227  dmin = d;
7228  }
7229  }
7230  /* For last distance, weight with 1/2 if seg_odd. */
7231  double davg;
7232  if (seg_odd) {
7233  sum += M_SQRT2 / 2 * (yvals[imax] - xvals[imax]);
7234  davg = sum / (imax + 0.5);
7235  }
7236  else {
7237  sum += sqrt(pow((xvals[imax] - mx), 2) + pow((yvals[imax] - mx), 2));
7238  davg = sum / (imax + 1.0);
7239  }
7240  /* Max error in tolerance? -> Quit. */
7241  bool precision_reached = true;
7242  if (dmax - davg > error_tol) {
7243  precision_reached = false;
7244  }
7245  if (dmin - davg < error_tol) {
7246  precision_reached = false;
7247  }
7248  if (precision_reached) {
7249  break;
7250  }
7251 
7252  /* Update new coordinates. */
7253  for (int i = 1; i <= imax; i++) {
7254  xvals[i] = find_superellipse_chord_endpoint(xvals[i - 1], davg, r, rbig);
7255  yvals[i] = superellipse_co(xvals[i], r, rbig);
7256  }
7257  }
7258 
7259  /* Fill remaining. */
7260  if (!seg_odd) {
7261  xvals[imax + 1] = mx;
7262  yvals[imax + 1] = mx;
7263  }
7264  for (int i = imax + 1; i <= seg; i++) {
7265  yvals[i] = xvals[seg - i];
7266  xvals[i] = yvals[seg - i];
7267  }
7268 
7269  if (!rbig) {
7270  for (int i = 0; i <= seg; i++) {
7271  double temp = xvals[i];
7272  xvals[i] = 1.0 - yvals[i];
7273  yvals[i] = 1.0 - temp;
7274  }
7275  }
7276 }
7277 
7286 static void find_even_superellipse_chords(int n, float r, double *xvals, double *yvals)
7287 {
7288  bool seg_odd = n % 2;
7289  int n2 = n / 2;
7290 
7291  /* Special cases. */
7292  if (r == PRO_LINE_R) {
7293  /* Linear spacing. */
7294  for (int i = 0; i <= n; i++) {
7295  xvals[i] = (double)i / n;
7296  yvals[i] = 1.0 - (double)i / n;
7297  }
7298  return;
7299  }
7300  if (r == PRO_CIRCLE_R) {
7301  double temp = M_PI_2 / n;
7302  /* Angle spacing. */
7303  for (int i = 0; i <= n; i++) {
7304  xvals[i] = sin(i * temp);
7305  yvals[i] = cos(i * temp);
7306  }
7307  return;
7308  }
7309  if (r == PRO_SQUARE_IN_R) {
7310  /* n is even, distribute first and second half linear. */
7311  if (!seg_odd) {
7312  for (int i = 0; i <= n2; i++) {
7313  xvals[i] = 0.0;
7314  yvals[i] = 1.0 - (double)i / n2;
7315  xvals[n - i] = yvals[i];
7316  yvals[n - i] = xvals[i];
7317  }
7318  }
7319  /* n is odd, so get one corner-cut chord. */
7320  else {
7321  double temp = 1.0 / (n2 + M_SQRT2 / 2.0);
7322  for (int i = 0; i <= n2; i++) {
7323  xvals[i] = 0.0;
7324  yvals[i] = 1.0 - (double)i * temp;
7325  xvals[n - i] = yvals[i];
7326  yvals[n - i] = xvals[i];
7327  }
7328  }
7329  return;
7330  }
7331  if (r == PRO_SQUARE_R) {
7332  /* n is even, distribute first and second half linear. */
7333  if (!seg_odd) {
7334  for (int i = 0; i <= n2; i++) {
7335  xvals[i] = (double)i / n2;
7336  yvals[i] = 1.0;
7337  xvals[n - i] = yvals[i];
7338  yvals[n - i] = xvals[i];
7339  }
7340  }
7341  /* n is odd, so get one corner-cut chord. */
7342  else {
7343  double temp = 1.0 / (n2 + M_SQRT2 / 2);
7344  for (int i = 0; i <= n2; i++) {
7345  xvals[i] = (double)i * temp;
7346  yvals[i] = 1.0;
7347  xvals[n - i] = yvals[i];
7348  yvals[n - i] = xvals[i];
7349  }
7350  }
7351  return;
7352  }
7353  /* For general case use the more expensive search algorithm. */
7354  find_even_superellipse_chords_general(n, r, xvals, yvals);
7355 }
7356 
7363 {
7364  int nseg = bp->seg;
7365 
7366  /* Precalculated fullness for circle profile radius and more common low seg values. */
7367 #define CIRCLE_FULLNESS_SEGS 11
7368  static const float circle_fullness[CIRCLE_FULLNESS_SEGS] = {
7369  0.0f, /* nsegs == 1 */
7370  0.559f, /* 2 */
7371  0.642f, /* 3 */
7372  0.551f, /* 4 */
7373  0.646f, /* 5 */
7374  0.624f, /* 6 */
7375  0.646f, /* 7 */
7376  0.619f, /* 8 */
7377  0.647f, /* 9 */
7378  0.639f, /* 10 */
7379  0.647f, /* 11 */
7380  };
7381 
7382  float fullness;
7383  if (bp->profile_type == BEVEL_PROFILE_CUSTOM) {
7384  /* Set fullness to the average "height" of the profile's sampled points. */
7385  fullness = 0.0f;
7386  for (int i = 0; i < nseg; i++) { /* Don't use the end points. */
7387  fullness += (float)(bp->pro_spacing.xvals[i] + bp->pro_spacing.yvals[i]) / (2.0f * nseg);
7388  }
7389  }
7390  else {
7391  /* An offline optimization process found fullness that led to closest fit to sphere as
7392  * a function of r and ns (for case of cube corner). */
7393  if (bp->pro_super_r == PRO_LINE_R) {
7394  fullness = 0.0f;
7395  }
7396  else if (bp->pro_super_r == PRO_CIRCLE_R && nseg > 0 && nseg <= CIRCLE_FULLNESS_SEGS) {
7397  fullness = circle_fullness[nseg - 1];
7398  }
7399  else {
7400  /* Linear regression fit found best linear function, separately for even/odd segs. */
7401  if (nseg % 2 == 0) {
7402  fullness = 2.4506f * bp->profile - 0.00000300f * nseg - 0.6266f;
7403  }
7404  else {
7405  fullness = 2.3635f * bp->profile + 0.000152f * nseg - 0.6060f;
7406  }
7407  }
7408  }
7409  return fullness;
7410 }
7411 
7423 static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bool custom)
7424 {
7425  int seg = bp->seg;
7426 
7427  if (seg <= 1) {
7428  /* Only 1 segment, we don't need any profile information. */
7429  pro_spacing->xvals = NULL;
7430  pro_spacing->yvals = NULL;
7431  pro_spacing->xvals_2 = NULL;
7432  pro_spacing->yvals_2 = NULL;
7433  pro_spacing->seg_2 = 0;
7434  return;
7435  }
7436 
7437  int seg_2 = max_ii(power_of_2_max_i(bp->seg), 4);
7438 
7439  /* Sample the seg_2 segments used during vertex mesh subdivision. */
7440  bp->pro_spacing.seg_2 = seg_2;
7441  if (seg_2 == seg) {
7442  pro_spacing->xvals_2 = pro_spacing->xvals;
7443  pro_spacing->yvals_2 = pro_spacing->yvals;
7444  }
7445  else {
7446  pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
7447  sizeof(double) * (seg_2 + 1));
7448  pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
7449  sizeof(double) * (seg_2 + 1));
7450  if (custom) {
7451  /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */
7452  BKE_curveprofile_init((CurveProfile *)bp->custom_profile, (short)seg_2);
7453 
7454  /* Copy segment locations into the profile spacing struct. */
7455  for (int i = 0; i < seg_2 + 1; i++) {
7456  pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y;
7457  pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x;
7458  }
7459  }
7460  else {
7462  seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2);
7463  }
7464  }
7465 
7466  /* Sample the input number of segments. */
7467  pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena, sizeof(double) * (seg + 1));
7468  pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena, sizeof(double) * (seg + 1));
7469  if (custom) {
7470  /* Make sure the curve profile's sample table is full. */
7471  if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) {
7473  }
7474 
7475  /* Copy segment locations into the profile spacing struct. */
7476  for (int i = 0; i < seg + 1; i++) {
7477  pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y;
7478  pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x;
7479  }
7480  }
7481  else {
7482  find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals);
7483  }
7484 }
7485 
7510 {
7511  float no_collide_offset = bp->offset + 1e6;
7512  float limit = no_collide_offset;
7513  if (bp->offset == 0.0f) {
7514  return no_collide_offset;
7515  }
7516  float kb = eb->offset_l_spec;
7517  EdgeHalf *ea = eb->next; /* NOTE: this is in direction b --> a. */
7518  float ka = ea->offset_r_spec;
7519  BMVert *vb, *vc;
7520  if (eb->is_rev) {
7521  vc = eb->e->v1;
7522  vb = eb->e->v2;
7523  }
7524  else {
7525  vb = eb->e->v1;
7526  vc = eb->e->v2;
7527  }
7528  BMVert *va = ea->is_rev ? ea->e->v1 : ea->e->v2;
7529  BevVert *bvc = NULL;
7530  EdgeHalf *ebother = find_other_end_edge_half(bp, eb, &bvc);
7531  EdgeHalf *ec;
7532  BMVert *vd;
7533  float kc;
7535  if (ea->is_bev && ebother != NULL && ebother->prev->is_bev) {
7536  if (bp->offset_type == BEVEL_AMT_PERCENT) {
7537  return 50.0f;
7538  }
7539  /* This is only right sometimes. The exact answer is very hard to calculate. */
7540  float blen = BM_edge_calc_length(eb->e);
7541  return bp->offset > blen / 2.0f ? blen / 2.0f : blen;
7542  }
7543  return no_collide_offset;
7544  }
7545  if (ebother != NULL) {
7546  ec = ebother->prev; /* NOTE: this is in direction c --> d. */
7547  vc = bvc->v;
7548  kc = ec->offset_l_spec;
7549  vd = ec->is_rev ? ec->e->v1 : ec->e->v2;
7550  }
7551  else {
7552  /* No bevvert for w, so C can't be beveled. */
7553  kc = 0.0f;
7554  ec = NULL;
7555  /* Find an edge from c that has same face. */
7556  if (eb->fnext == NULL) {
7557  return no_collide_offset;
7558  }
7559  BMLoop *lb = BM_face_edge_share_loop(eb->fnext, eb->e);
7560  if (!lb) {
7561  return no_collide_offset;
7562  }
7563  if (lb->next->v == vc) {
7564  vd = lb->next->next->v;
7565  }
7566  else if (lb->v == vc) {
7567  vd = lb->prev->v;
7568  }
7569  else {
7570  return no_collide_offset;
7571  }
7572  }
7573  if (ea->e == eb->e || (ec && ec->e == eb->e)) {
7574  return no_collide_offset;
7575  }
7576  ka = ka / bp->offset;
7577  kb = kb / bp->offset;
7578  kc = kc / bp->offset;
7579  float th1 = angle_v3v3v3(va->co, vb->co, vc->co);
7580  float th2 = angle_v3v3v3(vb->co, vc->co, vd->co);
7581 
7582  /* First calculate offset at which edge B collapses, which happens
7583  * when advancing clones of A, B, C all meet at a point.
7584  * This only happens if at least two of those three edges have non-zero k's. */
7585  float sin1 = sinf(th1);
7586  float sin2 = sinf(th2);
7587  if ((ka > 0.0f) + (kb > 0.0f) + (kc > 0.0f) >= 2) {
7588  float tan1 = tanf(th1);
7589  float tan2 = tanf(th2);
7590  float g = tan1 * tan2;
7591  float h = sin1 * sin2;
7592  float den = g * (ka * sin2 + kc * sin1) + kb * h * (tan1 + tan2);
7593  if (den != 0.0f) {
7594  float t = BM_edge_calc_length(eb->e);
7595  t *= g * h / den;
7596  if (t >= 0.0f) {
7597  limit = t;
7598  }
7599  }
7600  }
7601 
7602  /* Now check edge slide cases. */
7603  if (kb > 0.0f && ka == 0.0f /*&& bvb->selcount == 1 && bvb->edgecount > 2 */) {
7604  float t = BM_edge_calc_length(ea->e);
7605  t *= sin1 / kb;
7606  if (t >= 0.0f && t < limit) {
7607  limit = t;
7608  }
7609  }
7610  if (kb > 0.0f && kc == 0.0f /* && bvc && ec && bvc->selcount == 1 && bvc->edgecount > 2 */) {
7611  float t = BM_edge_calc_length(ec->e);
7612  t *= sin2 / kb;
7613  if (t >= 0.0f && t < limit) {
7614  limit = t;
7615  }
7616  }
7617  return limit;
7618 }
7619 
7626 {
7627  float no_collide_offset = bp->offset + 1e6;
7628  if (bp->offset == 0.0f) {
7629  return no_collide_offset;
7630  }
7631  float ka = ea->offset_l_spec / bp->offset;
7632  EdgeHalf *eb = find_other_end_edge_half(bp, ea, NULL);
7633  float kb = eb ? eb->offset_l_spec / bp->offset : 0.0f;
7634  float kab = ka + kb;
7635  float la = BM_edge_calc_length(ea->e);
7636  if (kab <= 0.0f) {
7637  return no_collide_offset;
7638  }
7639  return la / kab;
7640 }
7641 
7648 {
7649  float limited_offset = bp->offset;
7650  BMIter iter;
7651  BMVert *bmv;
7652  BM_ITER_MESH (bmv, &iter, bm, BM_VERTS_OF_MESH) {
7653  if (!BM_elem_flag_test(bmv, BM_ELEM_TAG)) {
7654  continue;
7655  }
7656  BevVert *bv = find_bevvert(bp, bmv);
7657  if (!bv) {
7658  continue;
7659  }
7660  for (int i = 0; i < bv->edgecount; i++) {
7661  EdgeHalf *eh = &bv->edges[i];
7662  if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
7663  float collision_offset = vertex_collide_offset(bp, eh);
7664  if (collision_offset < limited_offset) {
7665  limited_offset = collision_offset;
7666  }
7667  }
7668  else {
7669  float collision_offset = geometry_collide_offset(bp, eh);
7670  if (collision_offset < limited_offset) {
7671  limited_offset = collision_offset;
7672  }
7673  }
7674  }
7675  }
7676 
7677  if (limited_offset < bp->offset) {
7678  /* All current offset specs have some number times bp->offset,
7679  * so we can just multiply them all by the reduction factor
7680  * of the offset to have the effect of recalculating the specs
7681  * with the new limited_offset.
7682  */
7683  float offset_factor = limited_offset / bp->offset;
7684  BM_ITER_MESH (bmv, &iter, bm, BM_VERTS_OF_MESH) {
7685  if (!BM_elem_flag_test(bmv, BM_ELEM_TAG)) {
7686  continue;
7687  }
7688  BevVert *bv = find_bevvert(bp, bmv);
7689  if (!bv) {
7690  continue;
7691  }
7692  for (int i = 0; i < bv->edgecount; i++) {
7693  EdgeHalf *eh = &bv->edges[i];
7694  eh->offset_l_spec *= offset_factor;
7695  eh->offset_r_spec *= offset_factor;
7696  eh->offset_l *= offset_factor;
7697  eh->offset_r *= offset_factor;
7698  }
7699  }
7700  bp->offset = limited_offset;
7701  }
7702 }
7703 
7705  const float offset,
7706  const int offset_type,
7707  const int profile_type,
7708  const int segments,
7709  const float profile,
7710  const bool affect_type,
7711  const bool use_weights,
7712  const bool limit_offset,
7713  const struct MDeformVert *dvert,
7714  const int vertex_group,
7715  const int mat,
7716  const bool loop_slide,
7717  const bool mark_seam,
7718  const bool mark_sharp,
7719  const bool harden_normals,
7720  const int face_strength_mode,
7721  const int miter_outer,
7722  const int miter_inner,
7723  const float spread,
7724  const float smoothresh,
7725  const struct CurveProfile *custom_profile,
7726  const int vmesh_method)
7727 {
7728  BMIter iter, liter;
7729  BMVert *v, *v_next;
7730  BMEdge *e;
7731  BMFace *f;
7732  BMLoop *l;
7733  BevVert *bv;
7734  BevelParams bp = {
7735  .bm = bm,
7736  .offset = offset,
7737  .offset_type = offset_type,
7738  .seg = max_ii(segments, 1),
7739  .profile = profile,
7740  .pro_super_r = -logf(2.0) / logf(sqrtf(profile)), /* Convert to superellipse exponent. */
7741  .affect_type = affect_type,
7742  .use_weights = use_weights,
7743  .loop_slide = loop_slide,
7744  .limit_offset = limit_offset,
7745  .offset_adjust = (bp.affect_type != BEVEL_AFFECT_VERTICES) &&
7746  !ELEM(offset_type, BEVEL_AMT_PERCENT, BEVEL_AMT_ABSOLUTE),
7747  .dvert = dvert,
7748  .vertex_group = vertex_group,
7749  .mat_nr = mat,
7750  .mark_seam = mark_seam,
7751  .mark_sharp = mark_sharp,
7752  .harden_normals = harden_normals,
7753  .face_strength_mode = face_strength_mode,
7754  .miter_outer = miter_outer,
7755  .miter_inner = miter_inner,
7756  .spread = spread,
7757  .smoothresh = smoothresh,
7758  .face_hash = NULL,
7759  .profile_type = profile_type,
7760  .custom_profile = custom_profile,
7761  .vmesh_method = vmesh_method,
7762  };
7763 
7764  if (bp.offset <= 0) {
7765  return;
7766  }
7767 
7768 #ifdef BEVEL_DEBUG_TIME
7769  double start_time = PIL_check_seconds_timer();
7770 #endif
7771 
7772  /* Disable the miters with the cutoff vertex mesh method, the combination isn't useful anyway. */
7773  if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) {
7776  }
7777 
7778  if (profile >= 0.950f) { /* r ~ 692, so PRO_SQUARE_R is 1e4 */
7780  }
7781  else if (fabsf(bp.pro_super_r - PRO_CIRCLE_R) < 1e-4) {
7783  }
7784  else if (fabsf(bp.pro_super_r - PRO_LINE_R) < 1e-4) {
7785  bp.pro_super_r = PRO_LINE_R;
7786  }
7787  else if (bp.pro_super_r < 1e-4) {
7789  }
7790 
7791  /* Primary alloc. */
7792  bp.vert_hash = BLI_ghash_ptr_new(__func__);
7793  bp.mem_arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), __func__);
7795 
7796  /* Get the 2D profile point locations from either the superellipse or the custom profile. */
7798 
7799  /* Get the 'fullness' of the profile for the ADJ vertex mesh method. */
7800  if (bp.seg > 1) {
7802  }
7803 
7804  /* Get separate non-custom profile samples for the miter profiles if they are needed */
7805  if (bp.profile_type == BEVEL_PROFILE_CUSTOM &&
7807  set_profile_spacing(&bp, &bp.pro_spacing_miter, false);
7808  }
7809 
7810  bp.face_hash = BLI_ghash_ptr_new(__func__);
7812 
7813  math_layer_info_init(&bp, bm);
7814 
7815  /* Analyze input vertices, sorting edges and assigning initial new vertex positions. */
7816  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
7818  bv = bevel_vert_construct(bm, &bp, v);
7819  if (!limit_offset && bv) {
7820  build_boundary(&bp, bv, true);
7821  }
7822  }
7823  }
7824 
7825  /* Perhaps clamp offset to avoid geometry collisions. */
7826  if (limit_offset) {
7827  bevel_limit_offset(&bp, bm);
7828 
7829  /* Assign initial new vertex positions. */
7830  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
7832  bv = find_bevvert(&bp, v);
7833  if (bv) {
7834  build_boundary(&bp, bv, true);
7835  }
7836  }
7837  }
7838  }
7839 
7840  /* Perhaps do a pass to try to even out widths. */
7841  if (bp.offset_adjust) {
7842  adjust_offsets(&bp, bm);
7843  }
7844 
7845  /* Maintain consistent orientations for the asymmetrical custom profiles. */
7846  if (bp.profile_type == BEVEL_PROFILE_CUSTOM) {
7847  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
7850  }
7851  }
7852  }
7853 
7854  /* Build the meshes around vertices, now that positions are final. */
7855  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
7857  bv = find_bevvert(&bp, v);
7858  if (bv) {
7859  build_vmesh(&bp, bm, bv);
7860  }
7861  }
7862  }
7863 
7864  /* Build polygons for edges. */
7865  if (bp.affect_type != BEVEL_AFFECT_VERTICES) {
7866  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
7869  }
7870  }
7871  }
7872 
7873  /* Extend edge data like sharp edges and precompute normals for harden. */
7874  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
7876  bv = find_bevvert(&bp, v);
7877  if (bv) {
7879  }
7880  }
7881  }
7882 
7883  /* Rebuild face polygons around affected vertices. */
7884  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
7887  bevel_reattach_wires(bm, &bp, v);
7888  }
7889  }
7890 
7891  BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
7893  BLI_assert(find_bevvert(&bp, v) != NULL);
7894  BM_vert_kill(bm, v);
7895  }
7896  }
7897 
7898  if (bp.harden_normals) {
7899  bevel_harden_normals(&bp, bm);
7900  }
7903  }
7904 
7905  /* When called from operator (as opposed to modifier), bm->use_toolflags
7906  * will be set, and we need to transfer the oflags to BM_ELEM_TAGs. */
7907  if (bm->use_toolflags) {
7908  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
7909  if (BMO_vert_flag_test(bm, v, VERT_OUT)) {
7911  }
7912  }
7913  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
7914  if (BMO_edge_flag_test(bm, e, EDGE_OUT)) {
7916  }
7917  }
7918  }
7919 
7920  /* Clear the BM_ELEM_LONG_TAG tags, which were only set on some edges in F_EDGE faces. */
7921  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
7922  if (get_face_kind(&bp, f) != F_EDGE) {
7923  continue;
7924  }
7925  BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
7927  }
7928  }
7929 
7930  /* Primary free. */
7934 
7935 #ifdef BEVEL_DEBUG_TIME
7936  double end_time = PIL_check_seconds_timer();
7937  printf("BMESH BEVEL TIME = %.3f\n", end_time - start_time);
7938 #endif
7939 }
typedef float(TangentPoint)[2]
void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_number_of_layers(const struct CustomData *data, int type)
bool CustomData_has_layer(const struct CustomData *data, int type)
int CustomData_get_named_layer_index(const struct CustomData *data, int type, const char *name)
bool CustomData_data_equals(int type, const void *data1, const void *data2)
Definition: customdata.cc:3973
int CustomData_get_layer_index(const struct CustomData *data, int type)
int CustomData_get_n_offset(const struct CustomData *data, int type, int n)
bool CustomData_layer_has_math(const struct CustomData *data, int layer_n)
int CustomData_get_offset(const struct CustomData *data, int type)
support for deformation groups and hooks.
float BKE_defvert_find_weight(const struct MDeformVert *dvert, int defgroup)
Definition: deform.c:704
void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2])
#define BLI_array_alloca(arr, realsize)
Definition: BLI_alloca.h:22
A (mainly) macro array library.
#define BLI_array_append(arr, item)
Definition: BLI_array.h:98
#define BLI_array_staticdeclare(arr, maxstatic)
Definition: BLI_array.h:58
#define BLI_array_len(arr)
Definition: BLI_array.h:63
#define BLI_array_clear(arr)
Definition: BLI_array.h:128
#define BLI_array_free(arr)
Definition: BLI_array.h:113
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define BLI_INLINE
sqrt(x)+1/max(0
@ GHASH_FLAG_ALLOW_DUPES
Definition: BLI_ghash.h:55
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:734
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
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_flag_set(GHash *gh, unsigned int flag)
Definition: BLI_ghash.c:875
#define M_SQRT3
Definition: BLI_math_base.h:35
MINLINE float max_ff(float a, float b)
MINLINE int power_of_2_max_i(int n)
#define M_SQRT2
Definition: BLI_math_base.h:29
MINLINE float min_ff(float a, float b)
#define M_SQRT1_3
Definition: BLI_math_base.h:38
MINLINE int max_ii(int a, int b)
#define M_PI_2
Definition: BLI_math_base.h:23
MINLINE int is_power_of_2_i(int n)
#define BLI_ASSERT_UNIT_V3(v)
#define M_PI
Definition: BLI_math_base.h:20
#define M_PI_4
Definition: BLI_math_base.h:26
MINLINE int compare_ff(float a, float b, float max_diff)
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition: math_geom.c:209
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:485
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 dist_squared_to_plane_v3(const float p[3], const float plane[4])
Definition: math_geom.c:436
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
void closest_to_plane_normalized_v3(float r_close[3], const float plane[4], const float pt[3])
Definition: math_geom.c:408
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
Definition: math_geom.c:3527
void closest_to_plane_v3(float r_close[3], const float plane[4], const float pt[3])
Definition: math_geom.c:401
bool isect_line_plane_v3(float r_isect_co[3], const float l1[3], const float l2[3], const float plane_co[3], const float plane_no[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_geom.c:2078
float area_poly_v2(const float verts[][2], unsigned int nr)
Definition: math_geom.c:174
void interp_bilinear_quad_v3(float data[4][3], float u, float v, float res[3])
Definition: math_geom.c:4484
void mul_m4_v4(const float M[4][4], float r[4])
Definition: math_matrix.c:862
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
Definition: math_matrix.c:917
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
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_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
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)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
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
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 void negate_v3(float r[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
Definition: math_vector.c:237
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:361
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])
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
Definition: BLI_memarena.c:94
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
Definition: BLI_memarena.c:64
void BLI_memarena_use_calloc(struct MemArena *ma) ATTR_NONNULL(1)
Definition: BLI_memarena.c:76
void * BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
Definition: BLI_memarena.c:116
#define SWAP(type, a, b)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define LIKELY(x)
typedef double(DMatrix)[4][4]
@ CD_CUSTOMLOOPNORMAL
@ CD_PROP_INT32
@ CD_BWEIGHT
@ CD_MLOOPUV
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
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 GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble z
_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 GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_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 i1
_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 GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Read Guarded memory(de)allocation.
#define MEM_SIZE_OPTIMAL(size)
Platform independent time functions.
__forceinline void dot3(const avxf &a, const avxf &b, float &den, float &den2)
Definition: avxf.h:119
static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
Definition: bmesh_bevel.c:1697
static float find_profile_fullness(BevelParams *bp)
Definition: bmesh_bevel.c:7362
struct Profile Profile
static double find_superellipse_chord_endpoint(double x0, double dtarget, float r, bool rbig)
Definition: bmesh_bevel.c:7118
static void vmesh_center(VMesh *vm, float r_cent[3])
Definition: bmesh_bevel.c:3916
static int find_face_internal_boundverts(const BevVert *bv, const BMFace *f, BoundVert *(r_internal[3]))
Definition: bmesh_bevel.c:4753
static bool make_unit_square_map(const float va[3], const float vmid[3], const float vb[3], float r_mat[4][4])
Definition: bmesh_bevel.c:1933
#define BM_BEVEL_EDGE_TAG_DISABLE(bme)
Definition: bmesh_bevel.c:6070
struct BevelParams BevelParams
static void regularize_profile_orientation(BevelParams *bp, BMEdge *bme)
Definition: bmesh_bevel.c:3481
static BMFace * boundvert_rep_face(BoundVert *v, BMFace **r_fother)
Definition: bmesh_bevel.c:599
static VMesh * pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe)
Definition: bmesh_bevel.c:4650
static void get_incident_edges(BMFace *f, BMVert *v, BMEdge **r_e1, BMEdge **r_e2)
Definition: bmesh_bevel.c:4712
static BMFace * choose_rep_face(BevelParams *bp, BMFace **face, int nfaces)
Definition: bmesh_bevel.c:937
AngleKind
Definition: bmesh_bevel.c:295
@ ANGLE_STRAIGHT
Definition: bmesh_bevel.c:299
@ ANGLE_SMALLER
Definition: bmesh_bevel.c:297
@ ANGLE_LARGER
Definition: bmesh_bevel.c:301
static int count_ccw_edges_between(EdgeHalf *e1, EdgeHalf *e2)
Definition: bmesh_bevel.c:562
static void move_profile_plane(BoundVert *bndv, BMVert *bmvert)
Definition: bmesh_bevel.c:1823
static bool fast_bevel_edge_order(BevVert *bv)
Definition: bmesh_bevel.c:6184
static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert *vpipe)
Definition: bmesh_bevel.c:5346
#define BEVEL_EPSILON_ANG
Definition: bmesh_bevel.c:44
static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf *efirst, const bool construct)
Definition: bmesh_bevel.c:2747
static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int nseg, float r_co[3])
Definition: bmesh_bevel.c:2046
static BMEdge * find_closer_edge(float *co, BMEdge *e1, BMEdge *e2)
Definition: bmesh_bevel.c:4734
#define BEVEL_SMALL_ANG_DOT
Definition: bmesh_bevel.c:47
static FKind get_face_kind(BevelParams *bp, BMFace *f)
Definition: bmesh_bevel.c:414
static void disable_flag_out_edge(BMesh *bm, BMEdge *bme)
Definition: bmesh_bevel.c:400
static bool point_between_edges(const float co[3], BMVert *v, BMFace *f, EdgeHalf *e1, EdgeHalf *e2)
Definition: bmesh_bevel.c:1156
static NewVert * mesh_vert(VMesh *vm, int i, int j, int k)
Definition: bmesh_bevel.c:482
#define VERT_OUT
Definition: bmesh_bevel.c:382
BLI_INLINE void adjust_bound_vert(BoundVert *bv, const float co[3])
Definition: bmesh_bevel.c:472
static void swap_face_components(int *face_component, int totface, int c1, int c2)
Definition: bmesh_bevel.c:787
static void snap_edges_for_vmesh_vert(int i, int j, int k, int ns, int ns2, int n_bndv, BMEdge *eprev, BMEdge *enext, BMEdge *enextnext, BMFace **bndv_rep_faces, BMFace *center_frep, const bool *frep_beats_next, BMEdge *r_snap_edges[4])
Definition: bmesh_bevel.c:5282
static void adjust_miter_inner_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
Definition: bmesh_bevel.c:2934
static BevVert * find_bevvert(BevelParams *bp, BMVert *bmv)
Definition: bmesh_bevel.c:518
static void offset_meet_lines_percent_or_absolute(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float r_l1a[3], float r_l1b[3], float r_l2a[3], float r_l2b[3])
Definition: bmesh_bevel.c:1224
#define BM_BEVEL_EDGE_TAG_TEST(bme)
Definition: bmesh_bevel.c:6071
static float sabin_gamma(int n)
Definition: bmesh_bevel.c:3942
static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex, EdgeHalf *e)
Definition: bmesh_bevel.c:6908
static void bevel_set_weighted_normal_face_strength(BMesh *bm, BevelParams *bp)
Definition: bmesh_bevel.c:2582
static void flag_out_vert(BMesh *bm, BMVert *bmv)
Definition: bmesh_bevel.c:393
static void flag_out_edge(BMesh *bm, BMEdge *bme)
Definition: bmesh_bevel.c:386
#define BEVEL_EPSILON_ANG_DOT
Definition: bmesh_bevel.c:49
static void bevel_rebuild_existing_polygons(BMesh *bm, BevelParams *bp, BMVert *v)
Definition: bmesh_bevel.c:6796
static void record_face_kind(BevelParams *bp, BMFace *f, FKind fkind)
Definition: bmesh_bevel.c:407
#define BEVEL_SMALL_ANG
Definition: bmesh_bevel.c:45
struct NewVert NewVert
#define BEVEL_GOOD_ANGLE
Definition: bmesh_bevel.c:1509
static int tri_corner_test(BevelParams *bp, BevVert *bv)
Definition: bmesh_bevel.c:4456
#define VEC_VALUE_LEN
static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
Definition: bmesh_bevel.c:6945
static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
Definition: bmesh_bevel.c:6588
struct EdgeHalf EdgeHalf
static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
Definition: bmesh_bevel.c:5926
static BMFace * frep_for_center_poly(BevelParams *bp, BevVert *bv)
Definition: bmesh_bevel.c:4855
static BMEdge * snap_edge_for_center_vmesh_vert(int i, int n_bndv, BMEdge *eprev, BMEdge *enext, BMFace **bndv_rep_faces, BMFace *center_frep, const bool *frep_beats_next)
Definition: bmesh_bevel.c:5237
static bool good_offset_on_edge_between(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v)
Definition: bmesh_bevel.c:1569
static void bev_merge_edge_uvs(BMesh *bm, BMEdge *bme, BMVert *v)
Definition: bmesh_bevel.c:1042
static bool bevvert_is_weld_cross(BevVert *bv)
Definition: bmesh_bevel.c:6882
static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle)
Definition: bmesh_bevel.c:3532
static void bevel_limit_offset(BevelParams *bp, BMesh *bm)
Definition: bmesh_bevel.c:7647
struct MathLayerInfo MathLayerInfo
static EdgeHalf * next_bev(BevVert *bv, EdgeHalf *from_e)
Definition: bmesh_bevel.c:547
#define BEV_EXTEND_EDGE_DATA_CHECK(eh, flag)
Definition: bmesh_bevel.c:2265
static void calculate_vm_profiles(BevelParams *bp, BevVert *bv, VMesh *vm)
Definition: bmesh_bevel.c:2686
static AngleKind edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v)
Definition: bmesh_bevel.c:1118
static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool construct)
Definition: bmesh_bevel.c:2707
static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, float r_co[3])
Definition: bmesh_bevel.c:1648
#define EDGE_OUT
Definition: bmesh_bevel.c:381
FKind
Definition: bmesh_bevel.c:281
@ F_RECON
Definition: bmesh_bevel.c:291
@ F_VERT
Definition: bmesh_bevel.c:287
@ F_ORIG
Definition: bmesh_bevel.c:285
@ F_EDGE
Definition: bmesh_bevel.c:289
@ F_NONE
Definition: bmesh_bevel.c:283
static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
Definition: bmesh_bevel.c:5874
#define BEVEL_EPSILON_SQ
Definition: bmesh_bevel.c:41
static void closer_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float v[3])
Definition: bmesh_bevel.c:4988
static BoundVert * add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3])
Definition: bmesh_bevel.c:442
#define BM_BEVEL_EDGE_TAG_ENABLE(bme)
Definition: bmesh_bevel.c:6069
static VMesh * new_adj_vmesh(MemArena *mem_arena, int count, int seg, BoundVert *bounds)
Definition: bmesh_bevel.c:3842
static void set_bound_vert_seams(BevVert *bv, bool mark_seam, bool mark_sharp)
Definition: bmesh_bevel.c:2630
static void move_weld_profile_planes(BevVert *bv, BoundVert *bndv1, BoundVert *bndv2)
Definition: bmesh_bevel.c:1862
static void bev_merge_end_uvs(BMesh *bm, BevVert *bv, EdgeHalf *e)
Definition: bmesh_bevel.c:6867
static bool edge_edge_angle_less_than_180(const BMEdge *e1, const BMEdge *e2, const BMFace *f)
Definition: bmesh_bevel.c:1185
static void create_mesh_bmvert(BMesh *bm, VMesh *vm, int i, int j, int k, BMVert *eg)
Definition: bmesh_bevel.c:490
static void calculate_profile_segments(const Profile *profile, const float map[4][4], const bool use_map, const bool reversed, const int ns, const double *xvals, const double *yvals, float *r_prof_co)
Definition: bmesh_bevel.c:2075
static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
Definition: bmesh_bevel.c:5816
static EdgeHalf * next_edgehalf_bev(BevelParams *bp, EdgeHalf *start_edge, bool toward_bv, BevVert **r_bv)
Definition: bmesh_bevel.c:3396
static void vmesh_copy_equiv_verts(VMesh *vm)
Definition: bmesh_bevel.c:3895
static int count_bound_vert_seams(BevVert *bv)
Definition: bmesh_bevel.c:2653
static bool offset_meet_edge(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, float meetco[3], float *r_angle)
Definition: bmesh_bevel.c:1519
static EdgeHalf * find_edge_half(BevVert *bv, BMEdge *bme)
Definition: bmesh_bevel.c:507
static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
Definition: bmesh_bevel.c:2977
static VMesh * make_cube_corner_square_in(MemArena *mem_arena, int nseg)
Definition: bmesh_bevel.c:4331
static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_nr)
Definition: bmesh_bevel.c:4899
#define BEVEL_EPSILON
Definition: bmesh_bevel.c:40
static NewVert * mesh_vert_canon(VMesh *vm, int i, int j, int k)
Definition: bmesh_bevel.c:3864
static bool edges_face_connected_at_vert(BMEdge *bme1, BMEdge *bme2)
Definition: bmesh_bevel.c:580
static void make_unit_cube_map(const float va[3], const float vb[3], const float vc[3], const float vd[3], float r_mat[4][4])
Definition: bmesh_bevel.c:1990
#define BEVEL_EPSILON_D
Definition: bmesh_bevel.c:39
static void adjust_offsets(BevelParams *bp, BMesh *bm)
Definition: bmesh_bevel.c:3694
static void offset_meet(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, bool edges_between, float meetco[3], const EdgeHalf *e_in_plane)
Definition: bmesh_bevel.c:1318
static bool offset_on_edge_between(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v, float meetco[3], float *r_sinratio)
Definition: bmesh_bevel.c:1585
static VMesh * cubic_subdiv(BevelParams *bp, VMesh *vm_in)
Definition: bmesh_bevel.c:4118
static void bevel_edges_sharp_boundary(BMesh *bm, BevelParams *bp)
Definition: bmesh_bevel.c:2424
struct ProfileSpacing ProfileSpacing
static void fill_profile_fracs(BevelParams *bp, BoundVert *bndv, float *frac, int ns)
Definition: bmesh_bevel.c:3994
static bool nearly_parallel(const float d1[3], const float d2[3])
Definition: bmesh_bevel.c:421
static void build_square_in_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv, VMesh *vm1)
Definition: bmesh_bevel.c:4955
static bool is_canon(VMesh *vm, int i, int j, int k)
Definition: bmesh_bevel.c:3884
static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_v)
Definition: bmesh_bevel.c:1097
static void find_even_superellipse_chords(int n, float r, double *xvals, double *yvals)
Definition: bmesh_bevel.c:7286
static BevVert * bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
Definition: bmesh_bevel.c:6304
static int interp_range(const float *frac, int n, const float f, float *r_rest)
Definition: bmesh_bevel.c:4019
static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb)
Definition: bmesh_bevel.c:7509
static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bool custom)
Definition: bmesh_bevel.c:7423
#define PRO_CIRCLE_R
Definition: bmesh_bevel.c:146
static void slide_dist(EdgeHalf *e, BMVert *v, float d, float r_slideco[3])
Definition: bmesh_bevel.c:1083
static void fill_vmesh_fracs(VMesh *vm, float *frac, int i)
Definition: bmesh_bevel.c:3973
static void bev_merge_uvs(BMesh *bm, BMVert *v)
Definition: bmesh_bevel.c:1010
#define PRO_SQUARE_IN_R
Definition: bmesh_bevel.c:148
static void avg4(float co[3], const NewVert *v0, const NewVert *v1, const NewVert *v2, const NewVert *v3)
Definition: bmesh_bevel.c:3932
static float projected_boundary_area(BevVert *bv, BMFace *f)
Definition: bmesh_bevel.c:4789
static void check_edge_data_seam_sharp_edges(BevVert *bv, int flag, bool neg)
Definition: bmesh_bevel.c:2267
#define BEVEL_EPSILON_BIG
Definition: bmesh_bevel.c:42
#define PRO_SQUARE_R
Definition: bmesh_bevel.c:145
static void math_layer_info_init(BevelParams *bp, BMesh *bm)
Definition: bmesh_bevel.c:808
#define PRO_LINE_R
Definition: bmesh_bevel.c:147
static VMesh * make_cube_corner_square(MemArena *mem_arena, int nseg)
Definition: bmesh_bevel.c:4297
static VMesh * make_cube_corner_adj_vmesh(BevelParams *bp)
Definition: bmesh_bevel.c:4372
static void bevel_harden_normals(BevelParams *bp, BMesh *bm)
Harden normals for bevel.
Definition: bmesh_bevel.c:2459
static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
Definition: bmesh_bevel.c:5590
static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
Definition: bmesh_bevel.c:4606
static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme)
Definition: bmesh_bevel.c:6241
static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
Definition: bmesh_bevel.c:7625
static EdgeHalf * find_other_end_edge_half(BevelParams *bp, EdgeHalf *e, BevVert **r_bvother)
Definition: bmesh_bevel.c:528
static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, bool miter)
Definition: bmesh_bevel.c:2132
void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type, const int profile_type, const int segments, const float profile, const bool affect_type, const bool use_weights, const bool limit_offset, const struct MDeformVert *dvert, const int vertex_group, const int mat, const bool loop_slide, const bool mark_seam, const bool mark_sharp, const bool harden_normals, const int face_strength_mode, const int miter_outer, const int miter_inner, const float spread, const float smoothresh, const struct CurveProfile *custom_profile, const int vmesh_method)
Definition: bmesh_bevel.c:7704
#define BM_ELEM_LONG_TAG
Definition: bmesh_bevel.c:378
static int bevel_edge_order_extend(BMesh *bm, BevVert *bv, int i)
Definition: bmesh_bevel.c:6082
static BMFace * bev_create_ngon(BMesh *bm, BMVert **vert_arr, const int totv, BMFace **face_arr, BMFace *facerep, BMEdge **snap_edge_arr, int mat_nr, bool do_interp)
Definition: bmesh_bevel.c:663
#define BEVEL_MATCH_SPEC_WEIGHT
Definition: bmesh_bevel.c:52
static float edge_face_angle(EdgeHalf *e)
Definition: bmesh_bevel.c:6059
static BoundVert * pipe_test(BevVert *bv)
Definition: bmesh_bevel.c:3801
static void adjust_miter_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
Definition: bmesh_bevel.c:2885
static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f2)
Definition: bmesh_bevel.c:740
static VMesh * tri_corner_adj_vmesh(BevelParams *bp, BevVert *bv)
Definition: bmesh_bevel.c:4503
static void snap_to_superellipsoid(float co[3], const float super_r, bool midline)
Definition: bmesh_bevel.c:2195
static void bevel_reattach_wires(BMesh *bm, BevelParams *bp, BMVert *v)
Definition: bmesh_bevel.c:6818
static void find_even_superellipse_chords_general(int seg, float r, double *xvals, double *yvals)
Definition: bmesh_bevel.c:7186
static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
Definition: bmesh_bevel.c:4834
static BMFace * bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
Definition: bmesh_bevel.c:5741
static bool contig_ldata_across_loops(BMesh *bm, BMLoop *l1, BMLoop *l2, int layer_index)
Definition: bmesh_bevel.c:728
static bool nearly_parallel_normalized(const float d1[3], const float d2[3])
Definition: bmesh_bevel.c:431
static void copy_mesh_vert(VMesh *vm, int ito, int jto, int kto, int ifrom, int jfrom, int kfrom)
Definition: bmesh_bevel.c:498
static double superellipse_co(double x, float r, bool rbig)
Definition: bmesh_bevel.c:2025
static void bevel_extend_edge_data(BevVert *bv)
Definition: bmesh_bevel.c:2316
struct BevVert BevVert
struct BoundVert BoundVert
static VMesh * square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
Definition: bmesh_bevel.c:5010
static bool eh_on_plane(EdgeHalf *e)
Definition: bmesh_bevel.c:2669
static VMesh * adj_vmesh(BevelParams *bp, BevVert *bv)
Definition: bmesh_bevel.c:4535
#define CIRCLE_FULLNESS_SEGS
struct VMesh VMesh
static int bev_ccw_test(BMEdge *a, BMEdge *b, BMFace *f)
Definition: bmesh_bevel.c:1899
static void project_to_edge(const BMEdge *e, const float co_a[3], const float co_b[3], float projco[3])
Definition: bmesh_bevel.c:1681
static VMesh * interp_vmesh(BevelParams *bp, VMesh *vm_in, int nseg)
Definition: bmesh_bevel.c:4046
#define BM_DEFAULT_NGON_STACK_SIZE
Definition: bmesh_class.h:640
#define BM_FACE_FIRST_LOOP(p)
Definition: bmesh_class.h:622
#define BM_DEFAULT_ITER_STACK_SIZE
Definition: bmesh_class.h:645
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_ELEM_SEAM
Definition: bmesh_class.h:473
@ BM_ELEM_SELECT
Definition: bmesh_class.h:471
@ BM_ELEM_SMOOTH
Definition: bmesh_class.h:477
@ BM_ELEM_TAG
Definition: bmesh_class.h:484
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
Definition: bmesh_class.h:541
void BM_elem_attrs_copy(BMesh *bm_src, BMesh *bm_dst, const void *ele_src, void *ele_dst)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition: bmesh_core.c:41
BMFace * BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges)
Definition: bmesh_core.c:464
void BM_vert_kill(BMesh *bm, BMVert *v)
Definition: bmesh_core.c:939
void BM_face_kill(BMesh *bm, BMFace *f)
Definition: bmesh_core.c:828
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
Definition: bmesh_core.c:123
@ BM_CREATE_NOP
Definition: bmesh_core.h:12
@ BM_CREATE_NO_DOUBLE
Definition: bmesh_core.h:14
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:110
#define BM_elem_flag_disable(ele, hflag)
Definition: bmesh_inline.h:15
#define BM_elem_flag_set(ele, hflag, val)
Definition: bmesh_inline.h:16
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:12
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:14
float BM_elem_float_data_get(CustomData *cd, void *element, int type)
Definition: bmesh_interp.c:990
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name)
Definition: bmesh_interp.c:857
void BM_loop_interp_from_face(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, const bool do_multires)
Definition: bmesh_interp.c:682
void * BM_iter_as_arrayN(BMesh *bm, const char itype, void *data, int *r_len, void **stack_array, int stack_array_size)
Iterator as Array.
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_EDGE
@ BM_EDGES_OF_VERT
@ BM_EDGES_OF_FACE
@ BM_LOOPS_OF_FACE
#define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.cc:558
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.cc:446
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
Definition: bmesh_mesh.h:115
void BM_lnorspace_update(BMesh *bm)
void BM_mesh_normals_update(BMesh *bm)
void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
Definition: bmesh_mods.c:179
#define BMO_edge_flag_test(bm, e, oflag)
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_vert_flag_enable(bm, e, oflag)
#define BMO_vert_flag_test(bm, e, oflag)
#define BMO_edge_flag_disable(bm, e, oflag)
@ BEVEL_VMESH_ADJ
@ BEVEL_VMESH_CUTOFF
@ BEVEL_PROFILE_SUPERELLIPSE
@ BEVEL_PROFILE_CUSTOM
@ BEVEL_AFFECT_VERTICES
@ BEVEL_AFFECT_EDGES
@ FACE_STRENGTH_STRONG
@ FACE_STRENGTH_WEAK
@ FACE_STRENGTH_MEDIUM
@ BEVEL_FACE_STRENGTH_NONE
@ BEVEL_FACE_STRENGTH_AFFECTED
@ BEVEL_FACE_STRENGTH_NEW
@ BEVEL_FACE_STRENGTH_ALL
@ BEVEL_MITER_PATCH
@ BEVEL_MITER_SHARP
@ BEVEL_MITER_ARC
@ BEVEL_AMT_WIDTH
@ BEVEL_AMT_ABSOLUTE
@ BEVEL_AMT_PERCENT
@ BEVEL_AMT_OFFSET
@ BEVEL_AMT_DEPTH
void BM_face_calc_center_bounds(const BMFace *f, float r_cent[3])
bool BM_face_point_inside_test(const BMFace *f, const float co[3])
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
Definition: bmesh_query.c:1553
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
Definition: bmesh_query.c:553
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
Definition: bmesh_query.c:1115
int BM_edge_face_count(const BMEdge *e)
Definition: bmesh_query.c:629
float BM_edge_calc_length(const BMEdge *e)
Definition: bmesh_query.c:528
BMLoop * BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step)
Definition: bmesh_query.c:461
bool BM_vert_face_check(const BMVert *v)
Definition: bmesh_query.c:674
float BM_edge_calc_face_angle_signed_ex(const BMEdge *e, const float fallback)
BMESH EDGE/FACE ANGLE.
Definition: bmesh_query.c:1369
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
Definition: bmesh_query.c:1100
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition: btDbvt.cpp:299
static T sum(const btAlignedObjectArray< T > &items)
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition: btVector3.h:263
#define logf(x)
Definition: cuda/compat.h:105
#define sinf(x)
Definition: cuda/compat.h:102
#define cosf(x)
Definition: cuda/compat.h:101
#define tanf(x)
Definition: cuda/compat.h:104
#define powf(x, y)
Definition: cuda/compat.h:103
int len
Definition: draw_manager.c:108
static float verts[][3]
GPUBatch * quad
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void EIG_linear_solver_print_matrix(LinearSolver *solver)
void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_rhs)
void EIG_linear_solver_delete(LinearSolver *solver)
double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
bool EIG_linear_solver_solve(LinearSolver *solver)
static MemArena * mem_arena
Definition: makesdna.c:58
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_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 pow(float3 v, float e)
Definition: math_float3.h:533
static int left
static char faces[256]
#define fabsf(x)
Definition: metal/compat.h:219
#define sqrtf(x)
Definition: metal/compat.h:243
ccl_device_inline float frac(float x, int *ix)
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
static void area(int d1, int d2, int e1, int e2, float weights[2])
vec_base< T, 3 > cross(const vec_base< T, 3 > &a, const vec_base< T, 3 > &b)
T length(const vec_base< T, Size > &a)
static MEdge new_edge(const int v1, const int v2)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static const pxr::TfToken g("g", pxr::TfToken::Immortal)
vector snap(vector a, vector b)
Definition: node_math.h:59
SocketIndexByIdentifierMap * map
BMVert * v1
Definition: bmesh_class.h:122
BMVert * v2
Definition: bmesh_class.h:122
struct BMLoop * l
Definition: bmesh_class.h:128
short mat_nr
Definition: bmesh_class.h:281
int len
Definition: bmesh_class.h:267
float no[3]
Definition: bmesh_class.h:271
void * data
Definition: bmesh_class.h:51
BMHeader head
Definition: bmesh_class.h:145
struct BMVert * v
Definition: bmesh_class.h:153
struct BMEdge * e
Definition: bmesh_class.h:164
struct BMLoop * radial_next
Definition: bmesh_class.h:204
struct BMLoop * prev
Definition: bmesh_class.h:233
struct BMFace * f
Definition: bmesh_class.h:171
struct BMLoop * next
Definition: bmesh_class.h:233
float co[3]
Definition: bmesh_class.h:87
struct BMEdge * e
Definition: bmesh_class.h:97
float no[3]
Definition: bmesh_class.h:88
struct MLoopNorSpaceArray * lnor_spacearr
Definition: bmesh_class.h:343
CustomData vdata
Definition: bmesh_class.h:337
CustomData edata
Definition: bmesh_class.h:337
uint use_toolflags
Definition: bmesh_class.h:333
CustomData pdata
Definition: bmesh_class.h:337
CustomData ldata
Definition: bmesh_class.h:337
int totface
Definition: bmesh_class.h:297
BMVert * v
Definition: bmesh_bevel.c:255
bool any_seam
Definition: bmesh_bevel.c:265
char _pad[6]
Definition: bmesh_bevel.c:269
float offset
Definition: bmesh_bevel.c:263
int edgecount
Definition: bmesh_bevel.c:257
bool visited
Definition: bmesh_bevel.c:267
int selcount
Definition: bmesh_bevel.c:259
int wirecount
Definition: bmesh_bevel.c:261
VMesh * vmesh
Definition: bmesh_bevel.c:274
BMEdge ** wire_edges
Definition: bmesh_bevel.c:272
EdgeHalf * edges
Definition: bmesh_bevel.c:270
MathLayerInfo math_layer_info
Definition: bmesh_bevel.c:317
const struct CurveProfile * custom_profile
Definition: bmesh_bevel.c:350
bool mark_seam
Definition: bmesh_bevel.c:343
const struct MDeformVert * dvert
Definition: bmesh_bevel.c:352
ProfileSpacing pro_spacing
Definition: bmesh_bevel.c:313
char _pad[1]
Definition: bmesh_bevel.c:348
GHash * vert_hash
Definition: bmesh_bevel.c:307
float pro_super_r
Definition: bmesh_bevel.c:333
bool loop_slide
Definition: bmesh_bevel.c:337
ProfileSpacing pro_spacing_miter
Definition: bmesh_bevel.c:315
bool harden_normals
Definition: bmesh_bevel.c:347
float offset
Definition: bmesh_bevel.c:321
bool use_weights
Definition: bmesh_bevel.c:335
int vmesh_method
Definition: bmesh_bevel.c:364
float spread
Definition: bmesh_bevel.c:366
GHash * face_hash
Definition: bmesh_bevel.c:309
int profile_type
Definition: bmesh_bevel.c:325
BMesh * bm
Definition: bmesh_bevel.c:319
bool limit_offset
Definition: bmesh_bevel.c:339
MemArena * mem_arena
Definition: bmesh_bevel.c:311
float smoothresh
Definition: bmesh_bevel.c:368
float profile
Definition: bmesh_bevel.c:331
int vertex_group
Definition: bmesh_bevel.c:354
int face_strength_mode
Definition: bmesh_bevel.c:358
bool mark_sharp
Definition: bmesh_bevel.c:345
bool offset_adjust
Definition: bmesh_bevel.c:341
struct BoundVert * next
Definition: bmesh_bevel.c:196
Profile profile
Definition: bmesh_bevel.c:212
bool visited
Definition: bmesh_bevel.c:216
NewVert nv
Definition: bmesh_bevel.c:197
EdgeHalf * eon
Definition: bmesh_bevel.c:202
EdgeHalf * elast
Definition: bmesh_bevel.c:200
struct BoundVert * prev
Definition: bmesh_bevel.c:196
bool any_seam
Definition: bmesh_bevel.c:214
struct BoundVert * adjchain
Definition: bmesh_bevel.c:210
bool is_patch_start
Definition: bmesh_bevel.c:220
bool is_profile_start
Definition: bmesh_bevel.c:222
EdgeHalf * efirst
Definition: bmesh_bevel.c:199
int sharp_len
Definition: bmesh_bevel.c:227
EdgeHalf * ebev
Definition: bmesh_bevel.c:204
float sinratio
Definition: bmesh_bevel.c:208
bool is_arc_start
Definition: bmesh_bevel.c:218
char _pad[3]
Definition: bmesh_bevel.c:223
int seam_len
Definition: bmesh_bevel.c:225
CurveProfilePoint * segments
CustomDataLayer * layers
float offset_r
Definition: bmesh_bevel.c:91
BMFace * fprev
Definition: bmesh_bevel.c:77
char _pad[4]
Definition: bmesh_bevel.c:104
struct EdgeHalf * next
Definition: bmesh_bevel.c:73
float offset_r_spec
Definition: bmesh_bevel.c:95
BMEdge * e
Definition: bmesh_bevel.c:75
struct BoundVert * leftv
Definition: bmesh_bevel.c:81
bool is_seam
Definition: bmesh_bevel.c:101
BMFace * fnext
Definition: bmesh_bevel.c:79
struct BoundVert * rightv
Definition: bmesh_bevel.c:83
bool is_bev
Definition: bmesh_bevel.c:97
bool is_rev
Definition: bmesh_bevel.c:99
int profile_index
Definition: bmesh_bevel.c:85
float offset_l_spec
Definition: bmesh_bevel.c:93
bool visited_rpo
Definition: bmesh_bevel.c:103
float offset_l
Definition: bmesh_bevel.c:89
struct EdgeHalf * prev
Definition: bmesh_bevel.c:73
unsigned int v1
unsigned int v2
MLoopNorSpace ** lspacearr
Definition: BKE_mesh.h:560
bool has_math_layers
Definition: bmesh_bevel.c:187
int * face_component
Definition: bmesh_bevel.c:185
char _pad[4]
Definition: bmesh_bevel.c:65
BMVert * v
Definition: bmesh_bevel.c:63
float co[3]
Definition: bmesh_bevel.c:64
double * yvals_2
Definition: bmesh_bevel.c:163
double * xvals_2
Definition: bmesh_bevel.c:161
double * yvals
Definition: bmesh_bevel.c:159
double * xvals
Definition: bmesh_bevel.c:157
bool special_params
Definition: bmesh_bevel.c:143
float end[3]
Definition: bmesh_bevel.c:131
float * prof_co
Definition: bmesh_bevel.c:139
float plane_co[3]
Definition: bmesh_bevel.c:135
float proj_dir[3]
Definition: bmesh_bevel.c:137
float * prof_co_2
Definition: bmesh_bevel.c:141
float start[3]
Definition: bmesh_bevel.c:127
float plane_no[3]
Definition: bmesh_bevel.c:133
float super_r
Definition: bmesh_bevel.c:123
float height
Definition: bmesh_bevel.c:125
float middle[3]
Definition: bmesh_bevel.c:129
int seg
Definition: bmesh_bevel.c:239
int _pad
Definition: bmesh_bevel.c:249
enum VMesh::@168 mesh_kind
BoundVert * boundstart
Definition: bmesh_bevel.c:235
@ M_TRI_FAN
Definition: bmesh_bevel.c:245
@ M_CUTOFF
Definition: bmesh_bevel.c:246
@ M_POLY
Definition: bmesh_bevel.c:243
@ M_NONE
Definition: bmesh_bevel.c:242
int count
Definition: bmesh_bevel.c:237
NewVert * mesh
Definition: bmesh_bevel.c:233
double PIL_check_seconds_timer(void)
Definition: time.c:64
ccl_device_inline float beta(float x, float y)
Definition: util/math.h:775