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