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