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