Cleanup: remove redundant doxygen \file argument
[blender.git] / source / blender / bmesh / operators / bmo_inset.c
index 2324360..a0b9185 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Contributor(s): Campbell Barton
- *
- * ***** END GPL LICENSE BLOCK *****
  */
 
-/** \file blender/bmesh/operators/bmo_inset.c
- *  \ingroup bmesh
+/** \file \ingroup bmesh
  *
  * Inset face regions.
  * Inset individual faces.
- *
  */
 
 #include "MEM_guardedalloc.h"
 
 #include "BLI_math.h"
-#include "BLI_array.h"
+#include "BLI_alloca.h"
 #include "BLI_memarena.h"
 #include "BKE_customdata.h"
 
 
 #include "intern/bmesh_operators_private.h" /* own include */
 
-#define ELE_NEW                1
+/* Merge loop-data that diverges, see: T41445 */
+#define USE_LOOP_CUSTOMDATA_MERGE
 
+#define ELE_NEW                1
 
 
 /* -------------------------------------------------------------------- */
-/* Inset Individual */
+/* Generic Interp Face (use for both types of inset) */
 
+/**
+ * Interpolation, this is more complex for regions since we're not creating new faces
+ * and throwing away old ones, so instead, store face data needed for interpolation.
+ *
+ * \note This uses CustomData functions in quite a low-level way which should be
+ * avoided, but in this case its hard to do without storing a duplicate mesh. */
 
-/* Holds Per-Face Inset Edge Data */
-typedef struct EdgeInsetInfo {
-       float no[3];
-       BMEdge *e_old;
-       BMEdge *e_new;
-} EdgeInsetInfo;
+/* just enough of a face to store interpolation data we can use once the inset is done */
+typedef struct InterpFace {
+       BMFace *f;
+       void **blocks_l;
+       void **blocks_v;
+       float (*cos_2d)[2];
+       float axis_mat[3][3];
+} InterpFace;
+
+/* basically a clone of #BM_vert_interp_from_face */
+static void bm_interp_face_store(InterpFace *iface, BMesh *bm, BMFace *f, MemArena *interp_arena)
+{
+       BMLoop *l_iter, *l_first;
+       void **blocks_l    = iface->blocks_l = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks_l) * f->len);
+       void **blocks_v    = iface->blocks_v = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks_v) * f->len);
+       float (*cos_2d)[2] = iface->cos_2d = BLI_memarena_alloc(interp_arena, sizeof(*iface->cos_2d) * f->len);
+       void *axis_mat     = iface->axis_mat;
+       int i;
+
+       BLI_assert(BM_face_is_normal_valid(f));
+
+       axis_dominant_v3_to_m3(axis_mat, f->no);
 
+       iface->f = f;
+
+       i = 0;
+       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+       do {
+               mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
+               blocks_l[i] = NULL;
+               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_iter->head.data, &blocks_l[i]);
+               /* if we were not modifying the loops later we would do... */
+               // blocks[i] = l_iter->head.data;
+
+               blocks_v[i] = NULL;
+               CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, l_iter->v->head.data, &blocks_v[i]);
+
+               /* use later for index lookups */
+               BM_elem_index_set(l_iter, i); /* set_dirty */
+       } while ((void)i++, (l_iter = l_iter->next) != l_first);
+       bm->elem_index_dirty |= BM_LOOP;
+}
+static void bm_interp_face_free(InterpFace *iface, BMesh *bm)
+{
+       void **blocks_l = iface->blocks_l;
+       void **blocks_v = iface->blocks_v;
+       int i;
+
+       for (i = 0; i < iface->f->len; i++) {
+               CustomData_bmesh_free_block(&bm->ldata, &blocks_l[i]);
+               CustomData_bmesh_free_block(&bm->vdata, &blocks_v[i]);
+       }
+}
+
+#ifdef USE_LOOP_CUSTOMDATA_MERGE
 /**
- * Individual Face Inset.
- * Find all tagged faces (f), duplicate edges around faces, inset verts of
- * created edges, create new faces between old and new edges, fill face
- * between connected new edges, kill old face (f).
+ * This function merges loop customdata (UV's)
+ * where interpolating the values across the face causes values to diverge.
  */
-void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
+static void bm_loop_customdata_merge(
+        BMesh *bm,
+        BMEdge *e_connect,
+        BMLoop *l_a_outer, BMLoop *l_b_outer,
+        BMLoop *l_a_inner, BMLoop *l_b_inner)
 {
-       BMEdge **f_edges = NULL;
-       BMVert **f_verts = NULL;
-       BMFace *f;
-
-       BMOIter oiter;
-       EdgeInsetInfo *eiinfo_arr = NULL;
+       /**
+        * Check for diverged values at the vert shared by
+        * \a l_a_inner & \a l_b_inner.
+        *
+        * <pre>
+        *  -----------------------+
+        *           l_a_outer--> /|<--l_b_outer
+        *                       / |
+        *      (face a)        /  |
+        *                     / <--e_connect
+        *                    /    |
+        * e_a  l_a_inner--> / <--l_b_inner
+        * -----------------+      |
+        *                 /|      |
+        * l_a/b_inner_inset| (face b)
+        *               /  |      |
+        *              /   |e_b   |
+        *  (inset face(s)) |      |
+        *            /     |      |
+        * </pre>
+        */
+
+       const bool is_flip = (l_a_inner->next == l_a_outer);
+       BMLoop *l_a_inner_inset, *l_b_inner_inset;
+       BMEdge *e_a, *e_b;
+       int layer_n;
+
+       /* paranoid sanity checks */
+       BLI_assert(l_a_outer->v == l_b_outer->v);
+       BLI_assert(l_a_inner->v == l_b_inner->v);
+
+       BLI_assert(l_b_inner->f != l_a_inner->f);
+
+       BLI_assert(l_a_outer->f == l_a_inner->f);
+       BLI_assert(l_b_outer->f == l_b_inner->f);
+
+       (void) e_connect;
+       BLI_assert(BM_edge_in_face(e_connect, l_a_inner->f));
+       BLI_assert(BM_edge_in_face(e_connect, l_b_inner->f));
+
+       if (is_flip) {
+               e_a = l_a_inner->prev->e;
+               e_b = l_b_inner->e;
+       }
+       else {
+               e_a = l_a_inner->e;
+               e_b = l_b_inner->prev->e;
+       }
 
-       BLI_array_declare(eiinfo_arr);
-       BLI_array_declare(f_edges);
-       BLI_array_declare(f_verts);
+       l_a_inner_inset = BM_edge_other_loop(e_a, l_a_inner);
+       l_b_inner_inset = BM_edge_other_loop(e_b, l_b_inner);
+       BLI_assert(l_a_inner_inset->v == l_b_inner_inset->v);
 
-       const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
-       const float depth = BMO_slot_float_get(op->slots_in, "depth");
-       const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
-       const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
+       /* check if there is no chance of diversion */
+       if (l_a_inner_inset->f == l_b_inner_inset->f) {
+               return;
+       }
 
-       /* Only tag faces in slot */
-       BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
+       for (layer_n = 0; layer_n < bm->ldata.totlayer; layer_n++) {
+               const int type = bm->ldata.layers[layer_n].type;
+               const int offset = bm->ldata.layers[layer_n].offset;
+               if (!CustomData_layer_has_math(&bm->ldata, layer_n))
+                       continue;
 
-       BMO_slot_buffer_hflag_enable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false);
+               /* check we begin with merged data */
+               if ((CustomData_data_equals(
+                        type,
+                        BM_ELEM_CD_GET_VOID_P(l_a_outer, offset),
+                        BM_ELEM_CD_GET_VOID_P(l_b_outer, offset))  == true)
 
-       BMO_ITER(f, &oiter, op->slots_in, "faces", BM_FACE) {
-               BMFace *f_new_inner;
-               BMLoop *l_iter, *l_first;
-               BMLoop *l_iter_inner = NULL;
-               int i;
-
-               BLI_array_empty(f_verts);
-               BLI_array_empty(f_edges);
-               BLI_array_empty(eiinfo_arr);
-               BLI_array_grow_items(f_verts, f->len);
-               BLI_array_grow_items(f_edges, f->len);
-               BLI_array_grow_items(eiinfo_arr, f->len);
-
-               /* create verts */
-               i = 0;
-               l_iter = l_first = BM_FACE_FIRST_LOOP(f);
-               do {
-                       f_verts[i] = BM_vert_create(bm, l_iter->v->co, l_iter->v, 0);
-                       i++;
-               } while ((l_iter = l_iter->next) != l_first);
+                   /* epsilon for comparing UV's is too big, gives noticable problems */
+#if 0
+                   &&
+                   /* check if the data ends up diverged */
+                   (CustomData_data_equals(
+                        type,
+                        BM_ELEM_CD_GET_VOID_P(l_a_inner, offset),
+                        BM_ELEM_CD_GET_VOID_P(l_b_inner, offset)) == false)
+#endif
+                   )
+               {
+                       /* no need to allocate a temp block:
+                        * a = (a + b);
+                        * a *= 0.5f;
+                        * b = a;
+                        */
+                       const void *data_src;
+
+                       CustomData_data_mix_value(
+                               type,
+                               BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset),
+                               BM_ELEM_CD_GET_VOID_P(l_b_inner_inset, offset),
+                               CDT_MIX_MIX, 0.5f);
+                       CustomData_data_copy_value(
+                               type,
+                               BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset),
+                               BM_ELEM_CD_GET_VOID_P(l_b_inner_inset, offset));
+
+                       /* use this as a reference (could be 'l_b_inner_inset' too) */
+                       data_src = BM_ELEM_CD_GET_VOID_P(l_a_inner_inset, offset);
+
+                       /* check if the 2 faces share an edge */
+                       if (is_flip ?
+                           (l_b_inner_inset->e == l_a_inner_inset->prev->e) :
+                           (l_a_inner_inset->e == l_b_inner_inset->prev->e))
+                       {
+                               /* simple case, we have all loops already */
+                       }
+                       else {
+                               /* compare with (l_a_inner / l_b_inner) and assign the blended value if they match */
+                               BMIter iter;
+                               BMLoop *l_iter;
+                               const void *data_cmp_a = BM_ELEM_CD_GET_VOID_P(l_b_inner, offset);
+                               const void *data_cmp_b = BM_ELEM_CD_GET_VOID_P(l_a_inner, offset);
+                               BM_ITER_ELEM (l_iter, &iter, l_a_inner_inset->v, BM_LOOPS_OF_VERT) {
+                                       if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG)) {
+                                               if (!ELEM(l_iter, l_a_inner, l_b_inner, l_a_inner_inset, l_b_inner_inset)) {
+                                                       void *data_dst = BM_ELEM_CD_GET_VOID_P(l_iter, offset);
+
+                                                       if (CustomData_data_equals(type, data_dst, data_cmp_a) ||
+                                                           CustomData_data_equals(type, data_dst, data_cmp_b))
+                                                       {
+                                                               CustomData_data_copy_value(type, data_src, data_dst);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
 
-               /* make edges */
-               i = 0;
-               l_iter = l_first;
-               do {
-                       f_edges[i] = BM_edge_create(bm, f_verts[i], f_verts[(i + 1) % f->len], l_iter->e, 0);
+                       CustomData_data_copy_value(type, data_src, BM_ELEM_CD_GET_VOID_P(l_b_inner, offset));
+                       CustomData_data_copy_value(type, data_src, BM_ELEM_CD_GET_VOID_P(l_a_inner, offset));
+               }
+       }
+}
+#endif  /* USE_LOOP_CUSTOMDATA_MERGE */
 
-                       eiinfo_arr[i].e_new = f_edges[i];
-                       eiinfo_arr[i].e_old = l_iter->e;
-                       BM_edge_calc_face_tangent(l_iter->e, l_iter, eiinfo_arr[i].no);
 
-                       /* Tagging (old elements) required when iterating over edges
-                        * connected to verts for translation vector calculation */
-                       BM_elem_flag_enable(l_iter->e, BM_ELEM_TAG);
-                       BM_elem_index_set(l_iter->e, i);  /* set_dirty! */
-                       i++;
-               } while ((l_iter = l_iter->next) != l_first);
-               /* done with edges */
+/* -------------------------------------------------------------------- */
+/* Inset Individual */
 
-               bm->elem_index_dirty |= BM_EDGE;
+static void bmo_face_inset_individual(
+        BMesh *bm, BMFace *f, MemArena *interp_arena,
+        const float thickness, const float depth,
+        const bool use_even_offset, const bool use_relative_offset, const bool use_interpolate)
+{
+       InterpFace *iface = NULL;
 
-               /* Calculate translation vector for new  */
-               l_iter = l_first;
-               do {
-                       EdgeInsetInfo *ei_prev = &eiinfo_arr[BM_elem_index_get(l_iter->prev->e)];
-                       EdgeInsetInfo *ei_next = &eiinfo_arr[BM_elem_index_get(l_iter->e)];
-                       float tvec[3];
-                       float v_new_co[3];
-                       int index = 0;
+       /* stores verts split away from the face (aligned with face verts) */
+       BMVert **verts = BLI_array_alloca(verts, f->len);
+       /* store edge normals (aligned with face-loop-edges) */
+       float (*edge_nors)[3] = BLI_array_alloca(edge_nors, f->len);
+       float (*coords)[3] = BLI_array_alloca(coords, f->len);
 
-                       add_v3_v3v3(tvec, ei_prev->no, ei_next->no);
-                       normalize_v3(tvec);
+       BMLoop *l_iter, *l_first;
+       BMLoop *l_other;
+       uint i;
+       float e_length_prev;
 
-                       /* l->e is traversed in order */
-                       index = BM_elem_index_get(l_iter->e);
+       l_first = BM_FACE_FIRST_LOOP(f);
 
-                       copy_v3_v3(v_new_co, eiinfo_arr[index].e_new->v1->co);
+       /* split off all loops */
+       l_iter = l_first;
+       i = 0;
+       do {
+               BMVert *v_other = l_iter->v;
+               BMVert *v_sep = BM_face_loop_separate(bm, l_iter);
+               if (v_sep == v_other) {
+                       v_other = BM_vert_create(bm, l_iter->v->co, l_iter->v, BM_CREATE_NOP);
+               }
+               verts[i] = v_other;
 
-                       if (use_even_offset) {
-                               mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(ei_prev->no,  ei_next->no) / 2.0f));
-                       }
+               /* unrelated to splitting, but calc here */
+               BM_edge_calc_face_tangent(l_iter->e, l_iter, edge_nors[i]);
+       } while ((void)i++, ((l_iter = l_iter->next) != l_first));
 
-                       /* Modify vertices and their normals */
-                       madd_v3_v3fl(v_new_co, tvec, thickness);
 
-                       /* Set normal, add depth and write new vertex position*/
-                       copy_v3_v3(eiinfo_arr[index].e_new->v1->no, f->no);
+       /* build rim faces */
+       l_iter = l_first;
+       i = 0;
+       do {
+               BMFace *f_new_outer;
+               BMVert *v_other = verts[i];
+               BMVert *v_other_next = verts[(i + 1) % f->len];
+
+               BMEdge *e_other = BM_edge_create(bm, v_other, v_other_next, l_iter->e, BM_CREATE_NO_DOUBLE);
+               (void)e_other;
+
+               f_new_outer = BM_face_create_quad_tri(bm,
+                                                     v_other,
+                                                     v_other_next,
+                                                     l_iter->next->v,
+                                                     l_iter->v,
+                                                     f, BM_CREATE_NOP);
+               BMO_face_flag_enable(bm, f_new_outer, ELE_NEW);
+
+               /* copy loop data */
+               l_other = l_iter->radial_next;
+               BM_elem_attrs_copy(bm, bm, l_iter->next, l_other->prev);
+               BM_elem_attrs_copy(bm, bm, l_iter, l_other->next->next);
+
+               if (use_interpolate == false) {
+                       BM_elem_attrs_copy(bm, bm, l_iter->next, l_other);
+                       BM_elem_attrs_copy(bm, bm, l_iter, l_other->next);
+               }
+       } while ((void)i++, ((l_iter = l_iter->next) != l_first));
 
-                       madd_v3_v3fl(v_new_co, f->no, depth);
+       /* hold interpolation values */
+       if (use_interpolate) {
+               iface = BLI_memarena_alloc(interp_arena, sizeof(*iface));
+               bm_interp_face_store(iface, bm, f, interp_arena);
+       }
 
-                       copy_v3_v3(eiinfo_arr[index].e_new->v1->co, v_new_co);
-               } while ((l_iter = l_iter->next) != l_first);
+       /* Calculate translation vector for new */
+       l_iter = l_first;
+       i = 0;
 
+       if (depth != 0.0f) {
+               e_length_prev = BM_edge_calc_length(l_iter->prev->e);
+       }
 
-               /* Create New Inset Faces */
-               f_new_inner = BM_face_create(bm, f_verts, f_edges, f->len, 0);
-               BLI_assert(f_new_inner != NULL);  /* no reason it should fail */
+       do {
+               const float *eno_prev = edge_nors[(i ? i : f->len) - 1];
+               const float *eno_next = edge_nors[i];
+               float tvec[3];
+               float v_new_co[3];
 
+               add_v3_v3v3(tvec, eno_prev, eno_next);
+               normalize_v3(tvec);
 
-               // Don't tag, gives more useful inner/outer select option
-               // BMO_elem_flag_enable(bm, f_new_inner, ELE_NEW);
+               copy_v3_v3(v_new_co, l_iter->v->co);
 
+               if (use_even_offset) {
+                       mul_v3_fl(tvec, shell_v3v3_mid_normalized_to_dist(eno_prev,  eno_next));
+               }
 
-               /* Copy Face Data */
-               /* interpolate loop data or just stretch */
-               if (use_interpolate) {
-                       BM_face_interp_from_face(bm, f_new_inner, f, true);
+               /* Modify vertices and their normals */
+               if (use_relative_offset) {
+                       mul_v3_fl(tvec, (BM_edge_calc_length(l_iter->e) + BM_edge_calc_length(l_iter->prev->e)) / 2.0f);
                }
-               else {
-                       BM_elem_attrs_copy(bm, bm, f, f_new_inner);
+
+               madd_v3_v3fl(v_new_co, tvec, thickness);
+
+               /* Set normal, add depth and write new vertex position*/
+               copy_v3_v3(l_iter->v->no, f->no);
+
+               if (depth != 0.0f) {
+                       const float e_length = BM_edge_calc_length(l_iter->e);
+                       const float fac = depth * (use_relative_offset ? ((e_length_prev + e_length) * 0.5f) : 1.0f);
+                       e_length_prev = e_length;
+
+                       madd_v3_v3fl(v_new_co, f->no, fac);
                }
 
-               l_iter_inner = BM_FACE_FIRST_LOOP(f_new_inner);
+
+
+               copy_v3_v3(coords[i], v_new_co);
+       } while ((void)i++, ((l_iter = l_iter->next) != l_first));
+
+       /* update the coords */
+       l_iter = l_first;
+       i = 0;
+       do {
+               copy_v3_v3(l_iter->v->co, coords[i]);
+       } while ((void)i++, ((l_iter = l_iter->next) != l_first));
+
+
+       if (use_interpolate) {
+               BM_face_interp_from_face_ex(
+                       bm, iface->f, iface->f, true,
+                       (const void **)iface->blocks_l, (const void **)iface->blocks_v,
+                       iface->cos_2d, iface->axis_mat);
+
+               /* build rim faces */
                l_iter = l_first;
                do {
-                       BMFace *f_new_outer;
+                       /* copy loop data */
+                       l_other = l_iter->radial_next;
 
-                       BMLoop *l_a;
-                       BMLoop *l_b;
-
-                       if (use_interpolate == false) {
-                               BM_elem_attrs_copy(bm, bm, l_iter, l_iter_inner);
-                       }
+                       BM_elem_attrs_copy(bm, bm, l_iter->next, l_other);
+                       BM_elem_attrs_copy(bm, bm, l_iter, l_other->next);
+               } while ((l_iter = l_iter->next) != l_first);
 
-                       f_new_outer = BM_face_create_quad_tri(bm,
-                                                             l_iter->v,
-                                                             l_iter->next->v,
-                                                             l_iter_inner->next->v,
-                                                             l_iter_inner->v,
-                                                             f, false);
+               bm_interp_face_free(iface, bm);
+       }
+}
 
-                       BLI_assert(f_new_outer != NULL);  /* no reason it should fail */
 
-                       BM_elem_attrs_copy(bm, bm, f, f_new_outer);
-                       BMO_elem_flag_enable(bm, f_new_outer, ELE_NEW);
-                       BM_elem_flag_enable(f_new_outer, BM_ELEM_TAG);
+/**
+ * Individual Face Inset.
+ * Find all tagged faces (f), duplicate edges around faces, inset verts of
+ * created edges, create new faces between old and new edges, fill face
+ * between connected new edges, kill old face (f).
+ */
+void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
+{
+       BMFace *f;
 
-                       /* Copy Loop Data */
-                       l_a = BM_FACE_FIRST_LOOP(f_new_outer);
-                       l_b = l_a->next;
+       BMOIter oiter;
+       MemArena *interp_arena = NULL;
 
-                       /* first pair */
-                       BM_elem_attrs_copy(bm, bm, l_iter, l_a);
-                       BM_elem_attrs_copy(bm, bm,  l_iter->next, l_b);
+       const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
+       const float depth = BMO_slot_float_get(op->slots_in, "depth");
+       const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
+       const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
+       const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
 
+       /* Only tag faces in slot */
+       BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
 
-                       /* Move to the last two loops in new face */
-                       l_a = l_b->next;
-                       l_b = l_a->next;
+       BMO_slot_buffer_hflag_enable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false);
 
-                       /* This loop should always have >1 radials
-                        * (associated edge connects new and old face) */
-                       BM_elem_attrs_copy(bm, bm, l_iter_inner, l_b);
-                       BM_elem_attrs_copy(bm, bm, use_interpolate ? l_iter_inner->next : l_iter->next, l_a);
+       if (use_interpolate) {
+               interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+       }
 
-               } while ((l_iter_inner = l_iter_inner->next),
-                        (l_iter = l_iter->next) != l_first);
+       BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
+               bmo_face_inset_individual(
+                       bm, f, interp_arena,
+                       thickness, depth,
+                       use_even_offset, use_relative_offset, use_interpolate);
 
-               BM_face_kill(bm, f);
+               if (use_interpolate) {
+                       BLI_memarena_clear(interp_arena);
+               }
        }
 
        /* we could flag new edges/verts too, is it useful? */
        BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, ELE_NEW);
 
-       BLI_array_free(f_verts);
-       BLI_array_free(f_edges);
-       BLI_array_free(eiinfo_arr);
+       if (use_interpolate) {
+               BLI_memarena_free(interp_arena);
+       }
 }
 
 
@@ -245,66 +459,6 @@ typedef struct SplitEdgeInfo {
        BMLoop *l;
 } SplitEdgeInfo;
 
-
-/**
- * Interpolation, this is more complex for regions since we're not creating new faces
- * and throwing away old ones, so instead, store face data needed for interpolation.
- *
- * \note This uses CustomData functions in quite a low-level way which should be
- * avoided, but in this case its hard to do without storing a duplicate mesh. */
-
-/* just enough of a face to store interpolation data we can use once the inset is done */
-typedef struct InterpFace {
-       BMFace *f;
-       void **blocks_l;
-       void **blocks_v;
-       float (*cos_2d)[2];
-       float axis_mat[3][3];
-} InterpFace;
-
-/* basically a clone of #BM_vert_interp_from_face */
-static void bm_interp_face_store(InterpFace *iface, BMesh *bm, BMFace *f, MemArena *interp_arena)
-{
-       BMLoop *l_iter, *l_first;
-       void **blocks_l    = iface->blocks_l = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks_l) * f->len);
-       void **blocks_v    = iface->blocks_v = BLI_memarena_alloc(interp_arena, sizeof(*iface->blocks_v) * f->len);
-       float (*cos_2d)[2] = iface->cos_2d = BLI_memarena_alloc(interp_arena, sizeof(*iface->cos_2d) * f->len);
-       void *axis_mat     = iface->axis_mat;
-       int i;
-
-       axis_dominant_v3_to_m3(axis_mat, f->no);
-
-       iface->f = f;
-
-       i = 0;
-       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
-       do {
-               mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
-               blocks_l[i] = NULL;
-               CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_iter->head.data, &blocks_l[i]);
-               /* if we were not modifying the loops later we would do... */
-               // blocks[i] = l_iter->head.data;
-
-               blocks_v[i] = NULL;
-               CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, l_iter->v->head.data, &blocks_v[i]);
-
-               /* use later for index lookups */
-               BM_elem_index_set(l_iter, i); /* set_ok */
-       } while (i++, (l_iter = l_iter->next) != l_first);
-}
-static void bm_interp_face_free(InterpFace *iface, BMesh *bm)
-{
-       void **blocks_l = iface->blocks_l;
-       void **blocks_v = iface->blocks_v;
-       int i;
-
-       for (i = 0; i < iface->f->len; i++) {
-               CustomData_bmesh_free_block(&bm->ldata, &blocks_l[i]);
-               CustomData_bmesh_free_block(&bm->vdata, &blocks_v[i]);
-       }
-}
-
-
 /**
  * return the tag loop where there is...
  * - only 1 tagged face attached to this edge.
@@ -343,6 +497,27 @@ static BMLoop *bm_edge_is_mixed_face_tag(BMLoop *l)
        }
 }
 
+static float bm_edge_info_average_length(BMVert *v, SplitEdgeInfo *edge_info)
+{
+       BMIter iter;
+       BMEdge *e;
+
+       float len = 0.0f;
+       int tot = 0;
+
+
+       BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
+               const int i = BM_elem_index_get(e);
+               if (i != -1) {
+                       len += edge_info[i].length;
+                       tot++;
+               }
+       }
+
+       BLI_assert(tot != 0);
+       return len / (float)tot;
+}
+
 /**
  * implementation is as follows...
  *
@@ -358,11 +533,15 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        const bool use_outset          = BMO_slot_bool_get(op->slots_in, "use_outset");
        const bool use_boundary        = BMO_slot_bool_get(op->slots_in, "use_boundary") && (use_outset == false);
        const bool use_even_offset     = BMO_slot_bool_get(op->slots_in, "use_even_offset");
-       const bool use_even_boundry    = use_even_offset; /* could make own option */
+       const bool use_even_boundary   = use_even_offset; /* could make own option */
        const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
+       const bool use_edge_rail       = BMO_slot_bool_get(op->slots_in, "use_edge_rail");
        const bool use_interpolate     = BMO_slot_bool_get(op->slots_in, "use_interpolate");
        const float thickness          = BMO_slot_float_get(op->slots_in, "thickness");
        const float depth              = BMO_slot_float_get(op->slots_in, "depth");
+#ifdef USE_LOOP_CUSTOMDATA_MERGE
+       const bool has_math_ldata      = (use_interpolate && CustomData_has_math(&bm->ldata));
+#endif
 
        int edge_info_len = 0;
 
@@ -376,10 +555,15 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        int          iface_array_len;
        MemArena *interp_arena = NULL;
 
+       /* BMVert original location storage */
+       const bool use_vert_coords_orig = use_edge_rail;
+       MemArena *vert_coords_orig = NULL;
+       GHash *vert_coords = NULL;
+
        BMVert *v;
        BMEdge *e;
        BMFace *f;
-       int i, j, k;
+       int i, k;
 
        if (use_interpolate) {
                interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
@@ -395,6 +579,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        else {
                BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false);
                BMO_slot_buffer_hflag_disable(bm, op->slots_in, "faces", BM_FACE, BM_ELEM_TAG, false);
+               BMO_slot_buffer_hflag_disable(bm, op->slots_in, "faces_exclude", BM_FACE, BM_ELEM_TAG, false);
        }
 
        /* first count all inset edges we will split */
@@ -440,6 +625,26 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                }
        }
 
+
+       if (use_vert_coords_orig) {
+               vert_coords_orig = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+               vert_coords = BLI_ghash_ptr_new(__func__);
+       }
+
+       /* util macros */
+#define VERT_ORIG_STORE(_v)  { \
+               float *_co = BLI_memarena_alloc(vert_coords_orig, sizeof(float[3])); \
+               copy_v3_v3(_co, (_v)->co); \
+               BLI_ghash_insert(vert_coords, _v, _co); \
+       } (void)0
+#define VERT_ORIG_GET(_v)  \
+       (const float *)BLI_ghash_lookup_default(vert_coords, (_v), (_v)->co)
+       /* memory for the coords isn't given back to the arena,
+        * acceptable in this case since it runs a fixed number of times. */
+#define VERT_ORIG_REMOVE(_v)  \
+       BLI_ghash_remove(vert_coords, (_v), NULL, NULL)
+
+
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
                if ((es->l = bm_edge_is_mixed_face_tag(es->e_old->l))) {
                        /* do nothing */
@@ -448,9 +653,10 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                        es->l = es->e_old->l; /* must be a boundary */
                }
 
-
                /* run the separate arg */
-               bmesh_edge_separate(bm, es->e_old, es->l);
+               if (!BM_edge_is_boundary(es->e_old)) {
+                       bmesh_kernel_edge_separate(bm, es->e_old, es->l, false);
+               }
 
                /* calc edge-split info */
                es->e_new = es->l->e;
@@ -458,7 +664,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
 
                if (es->e_new == es->e_old) { /* happens on boundary edges */
                        /* take care here, we're creating this double edge which _must_ have its verts replaced later on */
-                       es->e_old = BM_edge_create(bm, es->e_new->v1, es->e_new->v2, es->e_new, 0);
+                       es->e_old = BM_edge_create(bm, es->e_new->v1, es->e_new->v2, es->e_new, BM_CREATE_NOP);
                }
 
                /* store index back to original in 'edge_info' */
@@ -501,8 +707,8 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
 
                mid_v3_v3v3(tvec, es->e_new->v1->co, es->e_new->v2->co);
 
-               v1 = BM_vert_create(bm, tvec, NULL);
-               v2 = BM_vert_create(bm, tvec, NULL);
+               v1 = BM_vert_create(bm, tvec, NULL, BM_CREATE_NOP);
+               v2 = BM_vert_create(bm, tvec, NULL, BM_CREATE_NOP);
                madd_v3_v3fl(v2->co, es->no, 0.1f);
                BM_edge_create(bm, v1, v2, NULL, 0);
        }
@@ -512,7 +718,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
         * here but don't do this since we will be splitting them off (iterating stuff you modify is bad juju)
         * instead loop over edges then their verts */
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
-               for (j = 0; j < 2; j++) {
+               for (int j = 0; j < 2; j++) {
                        v = (j == 0) ? es->e_new->v1 : es->e_new->v2;
 
                        /* end confusing part - just pretend this is a typical loop on verts */
@@ -528,11 +734,14 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                /* disable touching twice, this _will_ happen if the flags not disabled */
                                BM_elem_flag_disable(v, BM_ELEM_TAG);
 
-                               bmesh_vert_separate(bm, v, &vout, &r_vout_len);
+                               bmesh_kernel_vert_separate(bm, v, &vout, &r_vout_len, false);
                                v = NULL; /* don't use again */
 
                                /* in some cases the edge doesn't split off */
                                if (r_vout_len == 1) {
+                                       if (use_vert_coords_orig) {
+                                               VERT_ORIG_STORE(vout[0]);
+                                       }
                                        MEM_freeN(vout);
                                        continue;
                                }
@@ -544,6 +753,10 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                        int vert_edge_tag_tot = 0;
                                        int vecpair[2];
 
+                                       if (use_vert_coords_orig) {
+                                               VERT_ORIG_STORE(v_split);
+                                       }
+
                                        /* find adjacent */
                                        BM_ITER_ELEM (e, &iter, v_split, BM_EDGES_OF_VERT) {
                                                if (BM_elem_flag_test(e, BM_ELEM_TAG) &&
@@ -578,15 +791,18 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                                        BMFace *f_a = e_info_a->l->f;
                                                        BMFace *f_b = e_info_b->l->f;
 
+                                                       /* set to true when we're not in-between (e_info_a->no, e_info_b->no) exactly
+                                                        * in this case use a check the angle of the tvec when calculating shell thickness */
+                                                       bool is_mid = true;
+
                                                        /* we use this as either the normal OR to find the right direction for the
                                                         * cross product between both face normals */
                                                        add_v3_v3v3(tvec, e_info_a->no, e_info_b->no);
 
-                                                       /* epsilon increased to fix [#32329] */
-                                                       if ((f_a == f_b) || compare_v3v3(f_a->no, f_b->no, 0.001f)) {
-                                                               normalize_v3(tvec);
+                                                       if (use_edge_rail == false) {
+                                                               /* pass */
                                                        }
-                                                       else {
+                                                       else if (f_a != f_b) {
                                                                /* these lookups are very quick */
                                                                BMLoop *l_other_a = BM_loop_other_vert_loop(e_info_a->l, v_split);
                                                                BMLoop *l_other_b = BM_loop_other_vert_loop(e_info_b->l, v_split);
@@ -594,9 +810,26 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                                                if (l_other_a->v == l_other_b->v) {
                                                                        /* both edges faces are adjacent, but we don't need to know the shared edge
                                                                         * having both verts is enough. */
-                                                                       sub_v3_v3v3(tvec, l_other_a->v->co, v_split->co);
+                                                                       const float *co_other;
+
+                                                                       /* note that we can't use 'l_other_a->v' directly since it
+                                                                        * may be inset and give a feedback loop. */
+                                                                       if (use_vert_coords_orig) {
+                                                                               co_other = VERT_ORIG_GET(l_other_a->v);
+                                                                       }
+                                                                       else {
+                                                                               co_other = l_other_a->v->co;
+                                                                       }
+
+                                                                       sub_v3_v3v3(tvec, co_other, v_split->co);
+                                                                       is_mid = false;
                                                                }
-                                                               else {
+
+                                                               /* distable gives odd results at times, see [#39288] */
+#if 0
+                                                               else if (compare_v3v3(f_a->no, f_b->no, 0.001f) == false) {
+                                                                       /* epsilon increased to fix [#32329] */
+
                                                                        /* faces don't touch,
                                                                         * just get cross product of their normals, its *good enough*
                                                                         */
@@ -606,15 +839,26 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                                                                negate_v3(tno);
                                                                        }
                                                                        copy_v3_v3(tvec, tno);
+                                                                       is_mid = false;
                                                                }
-
-                                                               normalize_v3(tvec);
+#endif
                                                        }
+                                                       normalize_v3(tvec);
 
                                                        /* scale by edge angle */
                                                        if (use_even_offset) {
-                                                               mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e_info_a->no,
-                                                                                                                         e_info_b->no) / 2.0f));
+                                                               if (is_mid) {
+                                                                       mul_v3_fl(tvec, shell_v3v3_mid_normalized_to_dist(e_info_a->no,
+                                                                                                                         e_info_b->no));
+                                                               }
+                                                               else {
+                                                                       /* use the largest angle */
+                                                                       mul_v3_fl(tvec,
+                                                                                 shell_v3v3_normalized_to_dist(tvec,
+                                                                                                               len_squared_v3v3(tvec, e_info_a->no) >
+                                                                                                               len_squared_v3v3(tvec, e_info_b->no) ?
+                                                                                                                   e_info_a->no : e_info_b->no));
+                                                               }
                                                        }
 
                                                        /* scale relative to edge lengths */
@@ -625,7 +869,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                                else if (vert_edge_tag_tot == 1) { /* 1 edge user - boundary vert, not so common */
                                                        const float *e_no_a = edge_info[vecpair[0]].no;
 
-                                                       if (use_even_boundry) {
+                                                       if (use_even_boundary) {
 
                                                                /* This case where only one edge attached to v_split
                                                                 * is used - ei - the face to inset is on a boundary.
@@ -684,7 +928,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                                                normalize_v3(tvec);
 
                                                                if (use_even_offset) {
-                                                                       mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e_no_a, tvec)));
+                                                                       mul_v3_fl(tvec, shell_v3v3_normalized_to_dist(e_no_a, tvec));
                                                                }
                                                        }
                                                        else {
@@ -724,7 +968,11 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                                                                v_glue = v_split;
                                                        }
                                                        else {
-                                                               BM_vert_splice(bm, v_split, v_glue);
+                                                               if (BM_vert_splice(bm, v_glue, v_split)) {
+                                                                       if (use_vert_coords_orig) {
+                                                                               VERT_ORIG_REMOVE(v_split);
+                                                                       }
+                                                               }
                                                        }
                                                }
                                        }
@@ -736,12 +984,19 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                }
        }
 
+       if (use_vert_coords_orig) {
+               BLI_memarena_free(vert_coords_orig);
+               BLI_ghash_free(vert_coords, NULL, NULL);
+       }
+
        if (use_interpolate) {
                for (i = 0; i < iface_array_len; i++) {
                        if (iface_array[i]) {
                                InterpFace *iface = iface_array[i];
-                               BM_face_interp_from_face_ex(bm, iface->f, iface->f, true,
-                                                           iface->blocks_l, iface->blocks_v, iface->cos_2d, iface->axis_mat);
+                               BM_face_interp_from_face_ex(
+                                       bm, iface->f, iface->f, true,
+                                       (const void **)iface->blocks_l, (const void **)iface->blocks_v,
+                                       iface->cos_2d, iface->axis_mat);
                        }
                }
        }
@@ -749,6 +1004,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
        /* create faces */
        for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
                BMVert *varr[4] = {NULL};
+               int j;
                /* get the verts in the correct order */
                BM_edge_ordered_verts_ex(es->e_new, &varr[1], &varr[0], es->l);
 #if 0
@@ -780,8 +1036,8 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
 #endif
                /* no need to check doubles, we KNOW there won't be any */
                /* yes - reverse face is correct in this case */
-               f = BM_face_create_quad_tri_v(bm, varr, j, es->l->f, false);
-               BMO_elem_flag_enable(bm, f, ELE_NEW);
+               f = BM_face_create_verts(bm, varr, j, es->l->f, BM_CREATE_NOP, true);
+               BMO_face_flag_enable(bm, f, ELE_NEW);
 
                /* copy for loop data, otherwise UV's and vcols are no good.
                 * tiny speedup here we could be more clever and copy from known adjacent data
@@ -789,7 +1045,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
 #if 0
                /* don't use this because face boundaries have no adjacent loops and won't be filled in.
                 * instead copy from the opposite side with the code below */
-               BM_face_copy_shared(bm, f);
+               BM_face_copy_shared(bm, f, NULL, NULL);
 #else
                {
                        /* 2 inner loops on the edge between the new face and the original */
@@ -807,17 +1063,68 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                        BM_elem_attrs_copy(bm, bm, l_a_other, l_a);
                        BM_elem_attrs_copy(bm, bm, l_b_other, l_b);
 
+                       BLI_assert(l_a->f != l_a_other->f);
+                       BLI_assert(l_b->f != l_b_other->f);
+
                        /* step around to the opposite side of the quad - warning, this may have no other edges! */
                        l_a = l_a->next->next;
                        l_b = l_a->next;
 
+                       /**
+                        * Loops vars from newly created face (face_a/b)
+                        * <pre>
+                        *              l_a->e & l_b->prev->e
+                        * +------------------------------------+
+                        * |\ l_a                          l_b /|
+                        * | \ l_a->prev->e            l_b->e / |
+                        * |  \ l_a->prev          l_b->next /  |
+                        * |   +----------------------------+   |
+                        * |   |l_a_other    ^     l_b_other|   |
+                        * |   |        l_b->next->e &...   |   |
+                        * |   |        l_a->prev->prev->e  |   |
+                        * |   |        (inset face)        |   |
+                        * |   +----------------------------+   |
+                        * |  /                              \  |
+                        * | /                                \ |
+                        * |/                                  \|
+                        * +------------------------------------+
+                        * </pre>
+                        */
+
                        /* swap a<->b intentionally */
                        if (use_interpolate) {
                                InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)];
                                const int i_a = BM_elem_index_get(l_a_other);
                                const int i_b = BM_elem_index_get(l_b_other);
+                               CustomData_bmesh_free_block_data(&bm->ldata, l_b->head.data);
+                               CustomData_bmesh_free_block_data(&bm->ldata, l_a->head.data);
                                CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_a], &l_b->head.data);
                                CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_b], &l_a->head.data);
+
+#ifdef USE_LOOP_CUSTOMDATA_MERGE
+                               if (has_math_ldata) {
+                                       BMEdge *e_connect;
+
+                                       /* connecting edge 'a' */
+                                       e_connect = l_a->prev->e;
+                                       if (BM_edge_is_manifold(e_connect)) {
+                                               bm_loop_customdata_merge(
+                                                       bm, e_connect,
+                                                       l_a,       BM_edge_other_loop(e_connect, l_a),
+                                                       l_a->prev, BM_edge_other_loop(e_connect, l_a->prev));
+                                       }
+
+                                       /* connecting edge 'b' */
+                                       e_connect = l_b->e;
+                                       if (BM_edge_is_manifold(e_connect)) {
+                                               /* swap arg order to maintain winding */
+                                               bm_loop_customdata_merge(
+                                                       bm, e_connect,
+                                                       l_b,       BM_edge_other_loop(e_connect, l_b),
+                                                       l_b->next, BM_edge_other_loop(e_connect, l_b->next));
+                                       }
+                               }
+#endif  /* USE_LOOP_CUSTOMDATA_MERGE */
                        }
                        else {
                                BM_elem_attrs_copy(bm, bm, l_a_other, l_b);
@@ -853,7 +1160,7 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                        zero_v3(es->e_new->v2->no);
                }
                for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
-                       float *no = es->l->f->no;
+                       const float *no = es->l->f->no;
                        add_v3_v3(es->e_new->v1->no, no);
                        add_v3_v3(es->e_new->v2->no, no);
                }
@@ -887,8 +1194,8 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
                BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
                        if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
                                const float fac = (depth *
-                                                  (use_relative_offset ? BM_vert_calc_mean_tagged_edge_length(v) : 1.0f) *
-                                                  (use_even_boundry    ? BM_vert_calc_shell_factor(v) : 1.0f));
+                                                  (use_relative_offset ? bm_edge_info_average_length(v, edge_info) : 1.0f) *
+                                                  (use_even_boundary   ? BM_vert_calc_shell_factor(v) : 1.0f));
                                madd_v3_v3v3fl(varr_co[i], v->co, v->no, fac);
                        }
                }