Add miter pattern options.
authorHoward Trickey <howard.trickey@gmail.com>
Fri, 18 Jan 2019 17:54:10 +0000 (12:54 -0500)
committerHoward Trickey <howard.trickey@gmail.com>
Fri, 18 Jan 2019 17:54:10 +0000 (12:54 -0500)
Will document the new options in release notes, then in manual.
Still a bit of work to do on the bulging shape that appears
on cube corners if using arc inner miters, but will do that later.
Also need to do something smarter in clamp overlap.

13 files changed:
release/scripts/addons
release/scripts/addons_contrib
release/scripts/startup/bl_ui/properties_data_modifier.py
source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/intern/bmesh_operator_api.h
source/blender/bmesh/intern/bmesh_operators.h
source/blender/bmesh/operators/bmo_bevel.c
source/blender/bmesh/tools/bmesh_bevel.c
source/blender/bmesh/tools/bmesh_bevel.h
source/blender/editors/mesh/editmesh_bevel.c
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesrna/intern/rna_modifier.c
source/blender/modifiers/intern/MOD_bevel.c

index 5f7fba0..ba97e19 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 5f7fba0565a7c9ae93eae31a08fc9bbbd16d333a
+Subproject commit ba97e19e5b3df449784a4cc4ed89ce7b511ec3e4
index fecc0db..272b1a4 160000 (submodule)
@@ -1 +1 @@
-Subproject commit fecc0db5600405a0c14c70120ae279222861ef80
+Subproject commit 272b1a4ef07717beb3d0bfcb7380c2164fd008a3
index a6bab56..23bba83 100644 (file)
@@ -166,6 +166,11 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         layout.label(text="Set Face Strength Mode")
         layout.row().prop(md, "face_strength_mode", expand=True)
 
+        layout.label(text="Miter Patterns")
+        layout.row().prop(md, "miter_outer")
+        layout.row().prop(md, "miter_inner")
+        layout.row().prop(md, "spread")
+
     def BOOLEAN(self, layout, ob, md):
         split = layout.split()
 
index db4985c..dd913d9 100644 (file)
@@ -1743,6 +1743,13 @@ static BMO_FlagSet bmo_enum_bevel_face_strength_type[] = {
        {0, NULL},
 };
 
+static BMO_FlagSet bmo_enum_bevel_miter_type[] = {
+       {BEVEL_MITER_SHARP, "SHARP"},
+       {BEVEL_MITER_PATCH, "PATCH"},
+       {BEVEL_MITER_ARC, "ARC"},
+       {0, NULL},
+};
+
 /*
  * Bevel.
  *
@@ -1765,6 +1772,11 @@ static BMOpDefine bmo_bevel_def = {
         {"harden_normals", BMO_OP_SLOT_BOOL},  /* harden normals */
         {"face_strength_mode", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
                bmo_enum_bevel_face_strength_type}, /* whether to set face strength, and which faces to set if so */
+        {"miter_outer", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
+               bmo_enum_bevel_miter_type},         /* outer miter kind */
+        {"miter_inner", BMO_OP_SLOT_INT, {(int)BMO_OP_SLOT_SUBTYPE_INT_ENUM},
+               bmo_enum_bevel_miter_type},         /* outer miter kind */
+        {"spread", BMO_OP_SLOT_FLT},           /* amount to offset beveled edge */
         {"smoothresh", BMO_OP_SLOT_FLT},       /* for passing mesh's smoothresh, used in hardening */
         {{'\0'}},
        },
index fd01bec..e609500 100644 (file)
@@ -246,7 +246,7 @@ typedef struct BMOpSlot {
                   ((slot >= (op)->slots_out) && (slot < &(op)->slots_out[BMO_OP_MAX_SLOTS])))
 
 /* way more than probably needed, compiler complains if limit hit */
-#define BMO_OP_MAX_SLOTS 16
+#define BMO_OP_MAX_SLOTS 20
 
 /* BMOpDefine->type_flag */
 typedef enum {
index 365b5eb..dd9c57d 100644 (file)
@@ -125,6 +125,13 @@ enum {
        BEVEL_FACE_STRENGTH_ALL,
 };
 
+/* Bevel miter slot values */
+enum {
+       BEVEL_MITER_SHARP,
+       BEVEL_MITER_PATCH,
+       BEVEL_MITER_ARC,
+};
+
 extern const BMOpDefine *bmo_opdefines[];
 extern const int         bmo_opdefines_total;
 
index 3277824..853d11a 100644 (file)
@@ -43,10 +43,13 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
        const bool  clamp_overlap = BMO_slot_bool_get(op->slots_in,  "clamp_overlap");
        const int   material      = BMO_slot_int_get(op->slots_in,   "material");
        const bool  loop_slide    = BMO_slot_bool_get(op->slots_in,  "loop_slide");
-       const bool      mark_seam         = BMO_slot_bool_get(op->slots_in, "mark_seam");
-       const bool      mark_sharp        = BMO_slot_bool_get(op->slots_in, "mark_sharp");
+       const bool      mark_seam         = BMO_slot_bool_get(op->slots_in,  "mark_seam");
+       const bool      mark_sharp        = BMO_slot_bool_get(op->slots_in,  "mark_sharp");
        const bool  harden_normals = BMO_slot_bool_get(op->slots_in, "harden_normals");
        const int   face_strength_mode = BMO_slot_int_get(op->slots_in, "face_strength_mode");
+       const int   miter_outer   = BMO_slot_int_get(op->slots_in,   "miter_outer");
+       const int   miter_inner   = BMO_slot_int_get(op->slots_in,   "miter_inner");
+       const float spread        = BMO_slot_float_get(op->slots_in, "spread");
        const float smoothresh    = BMO_slot_float_get(op->slots_in, "smoothresh");
 
        if (offset > 0) {
@@ -73,7 +76,8 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
 
                BM_mesh_bevel(
                        bm, offset, offset_type, seg, profile, vonly, false, clamp_overlap, NULL, -1, material,
-                       loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode, smoothresh);
+                       loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode,
+                               miter_outer, miter_inner, spread, smoothresh);
 
                BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "faces.out", BM_FACE, BM_ELEM_TAG);
                BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "edges.out", BM_EDGE, BM_ELEM_TAG);
index 452dfa2..043a40a 100644 (file)
@@ -154,6 +154,8 @@ typedef struct BoundVert {
        Profile profile;    /* edge profile between this and next BoundVert */
        bool any_seam;      /* are any of the edges attached here seams? */
        bool visited;       /* used during delta adjust pass */
+       bool is_arc_start;      /* this boundvert begins an arc profile */
+       bool is_patch_start; /* this boundvert begins a patch profile */
        int seam_len;           /* length of seam starting from current boundvert to next boundvert with ccw ordering */
        int sharp_len;          /* Same as seam_len but defines length of sharp edges */
 //     int _pad;
@@ -222,6 +224,9 @@ typedef struct BevelParams {
        int vertex_group;       /* vertex group index, maybe set if vertex_only */
        int mat_nr;             /* if >= 0, material number for bevel; else material comes from adjacent faces */
        int face_strength_mode; /* setting face strength if > 0 */
+       int miter_outer;        /* what kind of miter pattern to use on reflex angles */
+       int miter_inner;                /* what kind of miter pattern to use on non-reflex angles */
+       float spread;                   /* amount to spread when doing inside miter */
        float smoothresh;               /* mesh's smoothresh, used if hardening */
 } BevelParams;
 
@@ -306,6 +311,9 @@ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float
        ans->adjchain = NULL;
        ans->sinratio = 1.0f;
        ans->visited = false;
+       ans->any_seam = false;
+       ans->is_arc_start = false;
+       ans->is_patch_start = false;
        vm->count++;
        return ans;
 }
@@ -450,7 +458,7 @@ static BMFace *boundvert_rep_face(BoundVert *v, BMFace **r_fother)
                if (v->efirst->fprev != frep)
                        frep2 = v->efirst->fprev;
        }
-       else {
+       else if (v->efirst) {
                frep = v->efirst->fprev;
                if (frep) {
                        if (v->elast->fnext != frep)
@@ -469,6 +477,18 @@ static BMFace *boundvert_rep_face(BoundVert *v, BMFace **r_fother)
                        frep = v->elast->fprev;
                }
        }
+       else if (v->prev->elast) {
+               frep = v->prev->elast->fnext;
+               if (v->next->efirst) {
+                       if (frep)
+                               frep2 = v->next->efirst->fprev;
+                       else
+                               frep = v->next->efirst->fprev;
+               }
+       }
+       else {
+               frep = NULL;
+       }
        if (r_fother)
                *r_fother = frep2;
        return frep;
@@ -737,6 +757,33 @@ static bool is_outside_edge(EdgeHalf *e, const float co[3], BMVert **ret_closer_
        }
 }
 
+/* Return -1, 0, or 1 as angle from e1 to e2 is <. =, or > 180 degrees */
+static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v)
+{
+       BMVert *v1, *v2;
+       float dir1[3], dir2[3], cross[3], *no, dot;
+
+       v1 = BM_edge_other_vert(e1->e, v);
+       v2 = BM_edge_other_vert(e2->e, v);
+       sub_v3_v3v3(dir1, v->co, v1->co);
+       sub_v3_v3v3(dir2, v->co, v2->co);
+       /* angles are in [0,pi]. need to compare cross product with normal to see if they are reflex */
+       cross_v3_v3v3(cross, dir1, dir2);
+       if (e1->fnext)
+               no = e1->fnext->no;
+       else if (e2->fprev)
+               no = e2->fprev->no;
+       else
+               no = v->no;
+       dot = dot_v3v3(cross, no);
+       if (fabsf(dot) < BEVEL_EPSILON_BIG)
+               return 0;
+       else if (dot < 0.0f)
+               return 1;
+       else
+               return -1;
+}
+
 /* co should be approximately on the plane between e1 and e2, which share common vert v
  * and common face f (which cannot be NULL).
  * Is it between those edges, sweeping CCW? */
@@ -1181,6 +1228,16 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
                }
                copy_v3_v3(pro->plane_co, co1);
        }
+       else if (bndv->is_arc_start) {
+               /* assume pro->midco was alredy set */
+               copy_v3_v3(pro->coa, co1);
+               copy_v3_v3(pro->cob, co2);
+               pro->super_r = PRO_CIRCLE_R;
+               zero_v3(pro->plane_co);
+               zero_v3(pro->plane_no);
+               zero_v3(pro->proj_dir);
+               do_linear_interp = false;
+       }
        if (do_linear_interp) {
                pro->super_r = PRO_LINE_R;
                copy_v3_v3(pro->coa, co1);
@@ -2129,6 +2186,87 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf
        }
 }
 
+/* Helper for build_boundary to handle special miters */
+static void adjust_miter_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
+{
+       float co1[3], co2[3], co3[3], edge_dir[3], line_p[3];
+       BoundVert *v1, *v2, *v3, *v1prev, *v3next;
+       BMVert *vother;
+       EdgeHalf *emiter_other;
+       int miter_outer = bp->miter_outer;
+
+       v1 = emiter->rightv;
+       if (miter_outer == BEVEL_MITER_PATCH) {
+               v2 = v1->next;
+               v3 = v2->next;
+       }
+       else {
+               BLI_assert(miter_outer == BEVEL_MITER_ARC);
+               v2 = NULL;
+               v3 = v1->next;
+       }
+       v1prev = v1->prev;
+       v3next = v3->next;
+       copy_v3_v3(co2, v1->nv.co);
+       if (v1->is_arc_start)
+               copy_v3_v3(v1->profile.midco, co2);
+
+       /* co1 is intersection of line through co2 in dir of emiter->e
+        * and plane with normal the dir of emiter->e and through v1prev */
+       vother = BM_edge_other_vert(emiter->e, bv->v);
+       sub_v3_v3v3(edge_dir, bv->v->co, vother->co);
+       normalize_v3(edge_dir);
+       float d = bp->offset / (bp->seg / 2.0f);  /* a fallback amount to move */
+       madd_v3_v3v3fl(line_p, co2, edge_dir, d);
+       if (!isect_line_plane_v3(co1, co2, line_p, v1prev->nv.co, edge_dir)) {
+               copy_v3_v3(co1, line_p);
+       }
+       adjust_bound_vert(v1, co1);
+
+       /* co3 is similar, but plane is through v3next and line is other side of miter edge */
+       emiter_other = v3->efirst;
+       vother = BM_edge_other_vert(emiter_other->e, bv->v);
+       sub_v3_v3v3(edge_dir, bv->v->co,  vother->co);
+       normalize_v3(edge_dir);
+       madd_v3_v3v3fl(line_p, co2, edge_dir, d);
+       if (!isect_line_plane_v3(co3, co2, line_p, v3next->nv.co, edge_dir)) {
+               copy_v3_v3(co1, line_p);
+       }
+       adjust_bound_vert(v3, co3);
+}
+
+static void adjust_miter_inner_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
+{
+       BoundVert *v, *vstart, *v3;
+       EdgeHalf *e;
+       BMVert *vother;
+       float edge_dir[3], co[3];
+
+       v = vstart = bv->vmesh->boundstart;
+       do {
+               if (v->is_arc_start) {
+                       v3 = v->next;
+                       e = v->efirst;
+                       if (e != emiter) {
+                               copy_v3_v3(co, v->nv.co);
+                               vother = BM_edge_other_vert(e->e, bv->v);
+                               sub_v3_v3v3(edge_dir, vother->co, bv->v->co);
+                               normalize_v3(edge_dir);
+                               madd_v3_v3v3fl(v->nv.co, co, edge_dir, bp->spread);
+                               e = v3->elast;
+                               vother = BM_edge_other_vert(e->e, bv->v);
+                               sub_v3_v3v3(edge_dir, vother->co, bv->v->co);
+                               normalize_v3(edge_dir);
+                               madd_v3_v3v3fl(v3->nv.co, co, edge_dir, bp->spread);
+                       }
+                       v = v3->next;
+               }
+               else {
+                       v = v->next;
+               }
+       } while (v != vstart);
+}
+
 /* Make a circular list of BoundVerts for bv, each of which has the coordinates
  * of a vertex on the boundary of the beveled vertex bv->v.
  * This may adjust some EdgeHalf widths, and there might have to be
@@ -2144,11 +2282,12 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf
 static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
 {
        MemArena *mem_arena = bp->mem_arena;
-       EdgeHalf *efirst, *e, *e2, *e3, *enip, *eip, *eon;
-       BoundVert *v;
+       EdgeHalf *efirst, *e, *e2, *e3, *enip, *eip, *eon, *emiter;
+       BoundVert *v, *v1, *v2, *v3;
        VMesh *vm;
        float co[3], r;
-       int nip, nnip;
+       int nip, nnip, miter_outer, miter_inner;
+       int ang_kind;
 
        /* Current bevel does nothing if only one edge into a vertex */
        if (bv->edgecount <= 1)
@@ -2171,6 +2310,13 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
                return;
        }
 
+       /* Special miters outside only for 3 or more beveled edges */
+       miter_outer = (bv->selcount >= 3) ? bp->miter_outer : BEVEL_MITER_SHARP;
+       miter_inner = bp->miter_inner;
+
+       /* keep track of the first beveled edge of an outside miter (there can be at most 1 per bv */
+       emiter = NULL;
+
        /* Here: there is more than one beveled edge.
         * We make BoundVerts to connect the sides of the beveled edges.
         * Non-beveled edges in between will just join to the appropriate juncture point. */
@@ -2231,13 +2377,92 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
                        for (e3 = e->next; e3 != e2; e3 = e3->next) {
                                e3->leftv = e3->rightv = v;
                        }
+                       ang_kind = edges_angle_kind(e, e2, bv->v);
+                       if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == 1) ||
+                               (miter_inner != BEVEL_MITER_SHARP && ang_kind == -1))
+                       {
+                               if (ang_kind == 1)
+                                       emiter = e;  /* a reflex angle, i.e., the (only) outer miter, if any */
+                               /* make one or two more boundverts; for now all will have same co */
+                               v1 = v;
+                               v1->ebev = NULL;
+                               if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+                                       v1->is_patch_start = true;
+                                       v2 = add_new_bound_vert(mem_arena, vm, co);
+                                       v2->eon = v1->eon;
+                                       v2->sinratio = v1->sinratio;
+                                       v2->ebev = NULL;
+                                       v1->eon = NULL;
+                                       v1->sinratio = 1.0f;
+                                       v1->elast = e;
+                                       if (e->next == e2) {
+                                               v2->efirst = NULL;
+                                               v2->elast = NULL;
+                                       }
+                                       else {
+                                               v2->efirst = e->next;
+                                               for (e3 = e->next; e3 != e2; e3 = e3->next) {
+                                                       e3->leftv = e3->rightv = v2;
+                                                       v2->elast = e3;
+                                               }
+                                       }
+                               }
+                               else {
+                                       v1->is_arc_start = true;
+                                       copy_v3_v3(v1->profile.midco, co);
+                                       v2 = NULL;
+                                       if (e->next == e2) {
+                                               v1->elast = v1->efirst;
+                                       }
+                                       else {
+                                               for (e3 = e->next; e3 != e2; e3 = e3->next) {
+                                                       v1->elast = e3;
+                                               }
+                                       }
+                               }
+                               v3 = add_new_bound_vert(mem_arena, vm, co);
+                               v3->ebev = e2;
+                               v3->efirst = e2;
+                               v3->elast = e2;
+                               v3->eon = NULL;
+                               e2->leftv = v3;
+                       }
                }
                else {
-                       adjust_bound_vert(e->rightv, co);
+                       ang_kind = edges_angle_kind(e, e2, bv->v);
+                       if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == 1) ||
+                               (miter_inner != BEVEL_MITER_SHARP && ang_kind == -1))
+                       {
+                               if (ang_kind == 1)
+                                       emiter = e;
+                               v1 = e->rightv;
+                               if (ang_kind == 1  && miter_outer == BEVEL_MITER_PATCH) {
+                                       v2 = v1->next;
+                                       v3 = v2->next;
+                               }
+                               else {
+                                       v2 = NULL;
+                                       v3 = v1->next;
+                               }
+                               adjust_bound_vert(v1, co);
+                               if (v2)
+                                       adjust_bound_vert(v2, co);
+                               adjust_bound_vert(v3, co);
+                       }
+                       else {
+                               adjust_bound_vert(e->rightv, co);
+                       }
                }
                e = e2;
        } while (e != efirst);
 
+       if (miter_inner != BEVEL_MITER_SHARP) {
+               adjust_miter_inner_coords(bp, bv, emiter);
+       }
+       if (emiter) {
+               adjust_miter_coords(bp, bv, emiter);
+       }
+
        calculate_vm_profiles(bp, bv, vm);
 
        if (construct) {
@@ -3304,6 +3529,8 @@ static int tri_corner_test(BevelParams *bp, BevVert *bv)
 
        if (bp->vertex_only)
                return -1;
+       if (bv->vmesh->count != 3)
+               return 0;
        totang = 0.0f;
        for (i = 0; i < bv->edgecount; i++) {
                e = &bv->edges[i];
@@ -3658,10 +3885,11 @@ static void closer_v3_v3v3v3(float r[3], float a[3], float b[3], float v[3])
  * We have to move the boundary edges too -- the usual method is to make one profile plane between
  * successive BoundVerts, but for the effect we want here, there will be two planes, one on each side
  * of the original edge.
+ * At the moment, this is not called for odd number of segments, though code does something if it is.
  */
 static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
 {
-       int n, ns, ns2, odd, i, j, k, ikind, im1, clstride;
+       int n, ns, ns2, odd, i, j, k, ikind, im1, clstride, iprev, akind;
        float bndco[3], dir1[3], dir2[3], co1[3], co2[3], meet1[3], meet2[3], v1co[3], v2co[3];
        float *on_edge_cur, *on_edge_prev, *p;
        float ns2inv, finalfrac, ang;
@@ -3669,6 +3897,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
        EdgeHalf *e1, *e2;
        VMesh *vm;
        float *centerline;
+       bool *cset, v1set, v2set;
 
        n = bv->vmesh->count;
        ns = bv->vmesh->seg;
@@ -3678,52 +3907,118 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
        vm = new_adj_vmesh(bp->mem_arena, n, ns, bv->vmesh->boundstart);
        clstride = 3 * (ns2 + 1);
        centerline = MEM_mallocN(clstride * n * sizeof(float), "bevel");
+       cset = MEM_callocN(n * sizeof(bool), "bevel");
 
        /* find on_edge, place on bndv[i]'s elast where offset line would meet,
-        * averaging with position where next sector's offset line would meet */
+        * taking min-distance-to bv->v with position where next sector's offset line would meet */
        bndv = vm->boundstart;
        for (i = 0; i < n; i++) {
                copy_v3_v3(bndco, bndv->nv.co);
                e1 = bndv->efirst;
                e2 = bndv->elast;
-               sub_v3_v3v3(dir1, e1->e->v1->co, e1->e->v2->co);
-               sub_v3_v3v3(dir2, e2->e->v1->co, e2->e->v2->co);
-               add_v3_v3v3(co1, bndco, dir1);
-               add_v3_v3v3(co2, bndco, dir2);
-               /* intersect e1 with line through bndv parallel to e2 to get v1co */
-               ikind = isect_line_line_v3(e1->e->v1->co, e1->e->v2->co, bndco, co2, meet1, meet2);
-
-               if (ikind == 0) {
-                       /* Placeholder: this should get eliminated by min dist test with adjacent edge */
-                       mid_v3_v3v3(v1co, e1->e->v1->co, e1->e->v2->co);
-               }
-               else {
-                       /* if the lines are skew (ikind == 2), want meet1 which is on e1 */
-                       copy_v3_v3(v1co, meet1);
-               }
-               /* intersect e2 with line through bndv parallel to e1 to get v2co */
-               ikind = isect_line_line_v3(e2->e->v1->co, e2->e->v2->co, bndco, co1, meet1, meet2);
-               if (ikind == 0) {
-                       mid_v3_v3v3(v2co, e2->e->v1->co, e2->e->v2->co);
-               }
-               else {
-                       copy_v3_v3(v2co, meet1);
-               }
+               akind = 0;
+               if (e1 && e2)
+                       akind = edges_angle_kind(e1, e2, bv->v);
+               if (bndv->is_patch_start) {
+                       mid_v3_v3v3(centerline + clstride * i, bndv->nv.co, bndv->next->nv.co);
+                       cset[i] = true;
+                       bndv = bndv->next;
+                       i++;
+                       mid_v3_v3v3(centerline + clstride * i, bndv->nv.co, bndv->next->nv.co);
+                       cset[i] = true;
+                       bndv = bndv->next;
+                       i++;
+                       /* leave cset[i] where it was - probably false, unless i == n - 1 */
+               }
+               else if (bndv->is_arc_start) {
+                       e1 = bndv->efirst;
+                       e2 = bndv->next->efirst;
+                       copy_v3_v3(centerline + clstride * i, bndv->profile.midco);
+                       bndv = bndv->next;
+                       cset[i] = true;
+                       i++;
+                       /* leave cset[i] where it was - probably false, unless i == n - 1 */
+               }
+               else if (akind < 0) {
+                       sub_v3_v3v3(dir1, e1->e->v1->co, e1->e->v2->co);
+                       sub_v3_v3v3(dir2, e2->e->v1->co, e2->e->v2->co);
+                       add_v3_v3v3(co1, bndco, dir1);
+                       add_v3_v3v3(co2, bndco, dir2);
+                       /* intersect e1 with line through bndv parallel to e2 to get v1co */
+                       ikind = isect_line_line_v3(e1->e->v1->co, e1->e->v2->co, bndco, co2, meet1, meet2);
+                       if (ikind == 0) {
+                               v1set = false;
+                       }
+                       else {
+                               /* if the lines are skew (ikind == 2), want meet1 which is on e1 */
+                               copy_v3_v3(v1co, meet1);
+                               v1set = true;
+                       }
+                       /* intersect e2 with line through bndv parallel to e1 to get v2co */
+                       ikind = isect_line_line_v3(e2->e->v1->co, e2->e->v2->co, bndco, co1, meet1, meet2);
+                       if (ikind == 0) {
+                               v2set = false;
+                       }
+                       else {
+                               v2set = true;
+                               copy_v3_v3(v2co, meet1);
+                       }
 
-               /* want on_edge[i] to be min dist to bv->v of v2co and the v1co of next iteration */
-               on_edge_cur = centerline + clstride * i;
-               on_edge_prev = centerline + clstride * ((i == 0) ? n - 1 : i - 1);
-               if (i == 0) {
-                       copy_v3_v3(on_edge_cur, v2co);
-                       copy_v3_v3(on_edge_prev, v1co);
-               }
-               else if (i == n - 1) {
-                       closer_v3_v3v3v3(on_edge_cur, on_edge_cur, v2co, bv->v->co);
-                       closer_v3_v3v3v3(on_edge_prev, on_edge_prev, v1co, bv->v->co);
+                       /* want on_edge[i] to be min dist to bv->v of v2co and the v1co of next iteration */
+                       on_edge_cur = centerline + clstride * i;
+                       iprev = (i == 0) ? n - 1 : i - 1;
+                       on_edge_prev = centerline + clstride * iprev;
+                       if (v2set) {
+                               if (cset[i]) {
+                                       closer_v3_v3v3v3(on_edge_cur, on_edge_cur, v2co, bv->v->co);
+                               }
+                               else {
+                                       copy_v3_v3(on_edge_cur, v2co);
+                                       cset[i] = true;
+                               }
+                       }
+                       if (v1set) {
+                               if (cset[iprev]) {
+                                       closer_v3_v3v3v3(on_edge_prev, on_edge_prev, v1co, bv->v->co);
+                               }
+                               else {
+                                       copy_v3_v3(on_edge_prev, v1co);
+                                       cset[iprev] = true;
+                               }
+                       }
                }
-               else {
-                       copy_v3_v3(on_edge_cur, v2co);
-                       closer_v3_v3v3v3(on_edge_prev, on_edge_prev, v1co, bv->v->co);
+               bndv = bndv->next;
+       }
+       /* Maybe not everything was set by the previous loop */
+       bndv = vm->boundstart;
+       for (i = 0; i < n; i++) {
+               if (!cset[i]) {
+                       on_edge_cur = centerline + clstride * i;
+                       e1 = bndv->next->efirst;
+                       copy_v3_v3(co1, bndv->nv.co);
+                       copy_v3_v3(co2, bndv->next->nv.co);
+                       if (e1) {
+                               if (bndv->prev->is_arc_start && bndv->next->is_arc_start) {
+                                       ikind = isect_line_line_v3(e1->e->v1->co, e1->e->v2->co, co1, co2, meet1, meet2);
+                                       if (ikind != 0) {
+                                               copy_v3_v3(on_edge_cur, meet1);
+                                               cset[i] = true;
+                                       }
+                               }
+                               else {
+                                       if (bndv->prev->is_arc_start) {
+                                               closest_to_line_segment_v3(on_edge_cur, co1, e1->e->v1->co, e1->e->v2->co);
+                                       }
+                                       else {
+                                               closest_to_line_segment_v3(on_edge_cur, co2, e1->e->v1->co, e1->e->v2->co);
+                                       }
+                                       cset[i] = true;
+                               }
+                       }
+                       if (!cset[i]) {
+                               mid_v3_v3v3(on_edge_cur, co1, co2);
+                               cset[i] = true;
+                       }
                }
                bndv = bndv->next;
        }
@@ -3806,6 +4101,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
        vmesh_copy_equiv_verts(vm);
 
        MEM_freeN(centerline);
+       MEM_freeN(cset);
        return vm;
 }
 
@@ -3889,8 +4185,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
                        e = v->efirst;
                else
                        e = v->ebev;
-               BLI_assert(e != NULL);
-               bme = e->e;
+               bme = e ? e->e : NULL;
                /* For odd ns, make polys with lower left corner at (i,j,k) for
                 *    j in [0, ns2-1], k in [0, ns2].  And then the center ngon.
                 * For even ns,
@@ -3930,7 +4225,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
                                else { /* edge bevel */
                                        if (odd) {
                                                if (k == ns2) {
-                                                       if (e->is_seam) {
+                                                       if (e && e->is_seam) {
                                                                r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
                                                                                   NULL, bme, bme, NULL, mat_nr);
                                                        }
@@ -3944,7 +4239,9 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
                                        }
                                        else {
                                                bme1 = k == ns2 - 1 ? bme : NULL;
-                                               bme3 = j == ns2 - 1 ? v->prev->ebev->e : NULL;
+                                               bme3 = NULL;
+                                               if (j == ns2 - 1 && v->prev->ebev)
+                                                       bme3 = v->prev->ebev->e;
                                                bme2 = bme1 != NULL ? bme1 : bme3;
                                                r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
                                                                   NULL, bme1, bme2, bme3, mat_nr);
@@ -5714,12 +6011,14 @@ static void bevel_limit_offset(BevelParams *bp)
  * \warning all tagged edges _must_ be manifold.
  */
 void BM_mesh_bevel(
-        BMesh *bm, const float offset, const int offset_type,
-        const float segments, const float profile,
-        const bool vertex_only, const bool use_weights, const bool limit_offset,
-        const struct MDeformVert *dvert, const int vertex_group, const int mat,
-        const bool loop_slide, const bool mark_seam, const bool mark_sharp,
-        const bool harden_normals, const int face_strength_mode, const float smoothresh)
+       BMesh *bm, const float offset, const int offset_type,
+       const float segments, const float profile,
+       const bool vertex_only, const bool use_weights, const bool limit_offset,
+       const struct MDeformVert *dvert, const int vertex_group, const int mat,
+       const bool loop_slide, const bool mark_seam, const bool mark_sharp,
+       const bool harden_normals, const int face_strength_mode,
+       const int miter_outer, const int miter_inner, const float spread,
+       const float smoothresh)
 {
        BMIter iter, liter;
        BMVert *v, *v_next;
@@ -5738,7 +6037,7 @@ void BM_mesh_bevel(
        bp.use_weights = use_weights;
        bp.loop_slide = loop_slide;
        bp.limit_offset = limit_offset;
-       bp.offset_adjust = true;
+       bp.offset_adjust = false; // DEBUG true;
        bp.dvert = dvert;
        bp.vertex_group = vertex_group;
        bp.mat_nr = mat;
@@ -5746,6 +6045,9 @@ void BM_mesh_bevel(
        bp.mark_sharp = mark_sharp;
        bp.harden_normals = harden_normals;
        bp.face_strength_mode = face_strength_mode;
+       bp.miter_outer = miter_outer;
+       bp.miter_inner = miter_inner;
+       bp.spread = spread;
        bp.smoothresh = smoothresh;
        bp.face_hash = NULL;
 
index 36a80ee..ff0d1f0 100644 (file)
@@ -34,6 +34,7 @@ void BM_mesh_bevel(
         const float profile, const bool vertex_only, const bool use_weights,
         const bool limit_offset, const struct MDeformVert *dvert, const int vertex_group,
         const int mat, const bool loop_slide, const bool mark_seam, const bool mark_sharp,
-        const bool harden_normals, const int face_strength_mode, const float smoothresh);
+        const bool harden_normals, const int face_strength_mode, const int miter_outer,
+        const int miter_inner, const float spread, const float smoothresh);
 
 #endif /* __BMESH_BEVEL_H__ */
index f2ac54b..4e796b9 100644 (file)
@@ -156,7 +156,8 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
 
        {
                uint ob_store_len = 0;
-               Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &ob_store_len);
+               Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
+                       view_layer, CTX_wm_view3d(C), &ob_store_len);
                opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
                for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
                        Object *obedit = objects[ob_index];
@@ -234,7 +235,9 @@ static bool edbm_bevel_calc(wmOperator *op)
        const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
        const bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
        const int face_strength_mode = RNA_enum_get(op->ptr, "face_strength_mode");
-
+       const int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
+       const int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
+       const float spread = RNA_float_get(op->ptr, "spread");
 
        for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
                em = opdata->ob_store[ob_index].em;
@@ -259,10 +262,11 @@ static bool edbm_bevel_calc(wmOperator *op)
                        em, &bmop, op,
                        "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f "
                        "clamp_overlap=%b material=%i loop_slide=%b mark_seam=%b mark_sharp=%b "
-                       "harden_normals=%b face_strength_mode=%i smoothresh=%f",
+                       "harden_normals=%b face_strength_mode=%i "
+                       "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f",
                        BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile,
                        clamp_overlap, material, loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode,
-                       me->smoothresh);
+                       miter_outer, miter_inner, spread, me->smoothresh);
 
                BMO_op_exec(em->bm, &bmop);
 
@@ -691,6 +695,19 @@ void MESH_OT_bevel(wmOperatorType *ot)
                {0, NULL, 0, NULL, NULL},
        };
 
+       static const EnumPropertyItem miter_outer_items[] = {
+               {BEVEL_MITER_SHARP, "SHARP", 0, "Sharp", "Outside of miter is sharp"},
+               {BEVEL_MITER_PATCH, "PATCH", 0, "Patch", "Outside of miter is squared-off patch"},
+               {BEVEL_MITER_ARC, "ARC", 0, "Arc", "Outside of miter is arc"},
+               {0, NULL, 0, NULL, NULL},
+       };
+
+       static const EnumPropertyItem miter_inner_items[] = {
+               {BEVEL_MITER_SHARP, "SHARP", 0, "Sharp", "Inside of miter is sharp"},
+               {BEVEL_MITER_ARC, "ARC", 0, "Arc", "Inside of miter is arc"},
+               {0, NULL, 0, NULL, NULL},
+       };
+
        /* identifiers */
        ot->name = "Bevel";
        ot->description = "Edge Bevel";
@@ -720,9 +737,16 @@ void MESH_OT_bevel(wmOperatorType *ot)
        RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp");
        RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material",
                "Material for bevel faces (-1 means use adjacent faces)", -1, 100);
-       RNA_def_boolean(ot->srna, "harden_normals", false, "Harden Normals", "Match normals of new faces to adjacent faces");
+       RNA_def_boolean(ot->srna, "harden_normals", false, "Harden Normals",
+               "Match normals of new faces to adjacent faces");
        RNA_def_enum(ot->srna, "face_strength_mode", face_strength_mode_items, BEVEL_FACE_STRENGTH_NONE,
                "Face Strength Mode", "Whether to set face strength, and which faces to set face strength on");
+       RNA_def_enum(ot->srna, "miter_outer", miter_outer_items, BEVEL_MITER_SHARP,
+               "Outer Miter", "Pattern to use for outside of miters");
+       RNA_def_enum(ot->srna, "miter_inner", miter_inner_items, BEVEL_MITER_SHARP,
+               "Inner Miter", "Pattern to use for inside of miters");
+       RNA_def_float(ot->srna, "spread", 0.1f, 0.0f, 1e6f, "Spread",
+               "Amount to spread arcs for arc inner miters", 0.0f, 100.0f);
        prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
        RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
 
index 101d655..9a06af1 100644 (file)
@@ -384,12 +384,16 @@ typedef struct BevelModifierData {
        short mat;
        short edge_flags;
        short face_str_mode;
+       /* patterns to use for mitering non-reflex and reflex miter edges */
+       short miter_inner;
+       short miter_outer;
        short pad2;
        /** Controls profile shape (0->1, .5 is round). */
        float profile;
        /** if the MOD_BEVEL_ANGLE is set,
         * this will be how "sharp" an edge must be before it gets beveled */
        float bevel_angle;
+       float spread;
        /** if the MOD_BEVEL_VWEIGHT option is set,
         * this will be the name of the vert group, MAX_VGROUP_NAME */
        char defgrp_name[64];
@@ -439,6 +443,13 @@ enum {
        MOD_BEVEL_FACE_STRENGTH_ALL,
 };
 
+/* BevelModifier->miter_inner and ->miter_outer */
+enum {
+       MOD_BEVEL_MITER_SHARP,
+       MOD_BEVEL_MITER_PATCH,
+       MOD_BEVEL_MITER_ARC,
+};
+
 typedef struct SmokeModifierData {
        ModifierData modifier;
 
index 2ef4773..97785c5 100644 (file)
@@ -3057,6 +3057,13 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
                { 0, NULL, 0, NULL, NULL },
        };
 
+       static EnumPropertyItem prop_miter_items[] = {
+               { MOD_BEVEL_MITER_SHARP, "MITER_SHARP", 0, "Sharp", "Default sharp miter" },
+               { MOD_BEVEL_MITER_PATCH, "MITER_PATCH", 0, "Patch", "Miter with extra corner" },
+               { MOD_BEVEL_MITER_ARC, "MITER_ARC", 0, "Arc", "Miter with curved arc" },
+               { 0, NULL, 0, NULL, NULL },
+       };
+
        srna = RNA_def_struct(brna, "BevelModifier", "Modifier");
        RNA_def_struct_ui_text(srna, "Bevel Modifier", "Bevel modifier to make edges and vertices more rounded");
        RNA_def_struct_sdna(srna, "BevelModifierData");
@@ -3154,7 +3161,7 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
        prop = RNA_def_property(srna, "harden_normals", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_BEVEL_HARDEN_NORMALS);
        RNA_def_property_ui_text(prop, "Harden Normals",
-                                "Match normals of new faces to adjacent faces");
+               "Match normals of new faces to adjacent faces");
        RNA_def_property_update(prop, 0, "rna_Modifier_update");
 
        prop = RNA_def_property(srna, "face_strength_mode", PROP_ENUM, PROP_NONE);
@@ -3162,6 +3169,25 @@ static void rna_def_modifier_bevel(BlenderRNA *brna)
        RNA_def_property_enum_items(prop, prop_harden_normals_items);
        RNA_def_property_ui_text(prop, "Set Face Strength", "Whether to set face strength, and which faces to set it on");
        RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+       prop = RNA_def_property(srna, "miter_outer", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "miter_outer");
+       RNA_def_property_enum_items(prop, prop_miter_items);
+       RNA_def_property_ui_text(prop, "Outer Miter", "Pattern to use for outside of miters");
+       RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+       prop = RNA_def_property(srna, "miter_inner", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "miter_inner");
+       RNA_def_property_enum_items(prop, prop_miter_items);
+       RNA_def_property_ui_text(prop, "Inner Miter", "Pattern to use for inside of miters");
+       RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+       prop = RNA_def_property(srna, "spread", PROP_FLOAT, PROP_DISTANCE);
+       RNA_def_property_float_sdna(prop, NULL, "value");
+       RNA_def_property_range(prop, 0, FLT_MAX);
+       RNA_def_property_ui_range(prop, 0.0f, 100.0f, 0.1, 4);
+       RNA_def_property_ui_text(prop, "Spread", "Spread distance for inner miter arcs");
+       RNA_def_property_update(prop, 0, "rna_Modifier_update");
 }
 
 static void rna_def_modifier_shrinkwrap(BlenderRNA *brna)
index 0501549..568cdcf 100644 (file)
@@ -67,6 +67,9 @@ static void initData(ModifierData *md)
        bmd->e_flags = 0;
        bmd->edge_flags = 0;
        bmd->face_str_mode = MOD_BEVEL_FACE_STRENGTH_NONE;
+       bmd->miter_inner = MOD_BEVEL_MITER_SHARP;
+       bmd->miter_outer = MOD_BEVEL_MITER_SHARP;
+       bmd->spread = 0.1f;
        bmd->mat = -1;
        bmd->profile = 0.5f;
        bmd->bevel_angle = DEG2RADF(30.0f);
@@ -119,6 +122,9 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
        const bool mark_sharp = (bmd->edge_flags & MOD_BEVEL_MARK_SHARP);
        bool harden_normals = (bmd->flags & MOD_BEVEL_HARDEN_NORMALS);
        const int face_strength_mode = bmd->face_str_mode;
+       const int miter_outer = bmd->miter_outer;
+       const int miter_inner = bmd->miter_inner;
+       const float spread = bmd->spread;
 
        bm = BKE_mesh_to_bmesh_ex(
                mesh,
@@ -196,7 +202,8 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes
        BM_mesh_bevel(bm, value, offset_type, bmd->res, bmd->profile,
                      vertex_only, bmd->lim_flags & MOD_BEVEL_WEIGHT, do_clamp,
                      dvert, vgroup, mat, loop_slide, mark_seam, mark_sharp,
-                     harden_normals, face_strength_mode, mesh->smoothresh);
+                     harden_normals, face_strength_mode,
+                     miter_outer, miter_inner, spread, mesh->smoothresh);
 
        result = BKE_mesh_from_bmesh_for_eval_nomain(bm, 0);