BMesh: edge-offset feature (Ctrl+Shift+R)
authorCampbell Barton <ideasman42@gmail.com>
Mon, 15 Jun 2015 00:58:07 +0000 (10:58 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 15 Jun 2015 01:03:13 +0000 (11:03 +1000)
Ability to quickly add 2x edge loops  on either side of selected loops.

release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/bmesh/CMakeLists.txt
source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/intern/bmesh_operators_private.h
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/mesh/mesh_intern.h
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform_ops.c

index 362b7b0112059aeffd2f95126efcc223a9264112..14e8bd09ef93ac67debe18338d735d6cd360f589 100644 (file)
@@ -327,6 +327,7 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, Panel):
         col.operator("mesh.edge_face_add")
         col.operator("mesh.subdivide")
         col.operator("mesh.loopcut_slide")
         col.operator("mesh.edge_face_add")
         col.operator("mesh.subdivide")
         col.operator("mesh.loopcut_slide")
+        col.operator("mesh.offset_edge_loops_slide")
         col.operator("mesh.duplicate_move", text="Duplicate")
         row = col.row(align=True)
         row.operator("mesh.spin")
         col.operator("mesh.duplicate_move", text="Duplicate")
         row = col.row(align=True)
         row.operator("mesh.spin")
index 2946e1b52ce319fb8a4bcfc905a694797a6e0146..66ee184a5bc3dc780423fb29426dd4be1a012d3e 100644 (file)
@@ -61,6 +61,7 @@ set(SRC
        operators/bmo_mesh_conv.c
        operators/bmo_mirror.c
        operators/bmo_normals.c
        operators/bmo_mesh_conv.c
        operators/bmo_mirror.c
        operators/bmo_normals.c
+       operators/bmo_offset_edgeloops.c
        operators/bmo_planar_faces.c
        operators/bmo_poke.c
        operators/bmo_primitive.c
        operators/bmo_planar_faces.c
        operators/bmo_poke.c
        operators/bmo_primitive.c
index 096c307d9184f40be9428cd4622b5030db659a03..766db1db5b1660b573d50d29e740d3b53fe22dc4 100644 (file)
@@ -1865,6 +1865,26 @@ static BMOpDefine bmo_inset_region_def = {
         BMO_OPTYPE_FLAG_SELECT_FLUSH),
 };
 
         BMO_OPTYPE_FLAG_SELECT_FLUSH),
 };
 
+/*
+ * Edgeloop Offset.
+ *
+ * Creates edge loops based on simple edge-outset method.
+ */
+static BMOpDefine bmo_offset_edgeloops_def = {
+       "offset_edgeloops",
+       /* slots_in */
+       {{"edges", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}},    /* input faces */
+        {{'\0'}},
+       },
+       /* slots_out */
+       {{"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, /* output faces */
+        {{'\0'}},
+       },
+       bmo_offset_edgeloops_exec,
+       (BMO_OPTYPE_FLAG_NORMALS_CALC |
+        BMO_OPTYPE_FLAG_SELECT_FLUSH),
+};
+
 /*
  * Wire Frame.
  *
 /*
  * Wire Frame.
  *
@@ -2021,6 +2041,7 @@ const BMOpDefine *bmo_opdefines[] = {
        &bmo_duplicate_def,
        &bmo_holes_fill_def,
        &bmo_face_attribute_fill_def,
        &bmo_duplicate_def,
        &bmo_holes_fill_def,
        &bmo_face_attribute_fill_def,
+       &bmo_offset_edgeloops_def,
        &bmo_edgeloop_fill_def,
        &bmo_edgenet_fill_def,
        &bmo_edgenet_prepare_def,
        &bmo_edgeloop_fill_def,
        &bmo_edgenet_fill_def,
        &bmo_edgenet_prepare_def,
index 25340eee86838b7c0544d93a021dc3c907dc634e..5548ee7c36158967f76bf71fb846b9af51a232b7 100644 (file)
@@ -82,6 +82,7 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op);
 void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op);
 void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op);
 void bmo_poke_exec(BMesh *bm, BMOperator *op);
 void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op);
 void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op);
 void bmo_poke_exec(BMesh *bm, BMOperator *op);
+void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op);
 void bmo_planar_faces_exec(BMesh *bm, BMOperator *op);
 void bmo_region_extend_exec(BMesh *bm, BMOperator *op);
 void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op);
 void bmo_planar_faces_exec(BMesh *bm, BMOperator *op);
 void bmo_region_extend_exec(BMesh *bm, BMOperator *op);
 void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op);
index 62aec7883060da13b8707e1d84dd76869c096d43..202ce8d68a6d42290d795921dbc78db5991d58a3 100644 (file)
@@ -5181,6 +5181,57 @@ void MESH_OT_wireframe(wmOperatorType *ot)
        RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
 }
 
        RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
 }
 
+static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op)
+{
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = BKE_editmesh_from_object(obedit);
+       BMOperator bmop;
+
+       EDBM_op_init(
+               em, &bmop, op,
+               "offset_edgeloops edges=%he",
+               BM_ELEM_SELECT);
+
+       BMO_op_exec(em->bm, &bmop);
+
+       BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
+
+       /* If in face-only select mode, switch to edge select mode so that
+        * an edge-only selection is not inconsistent state */
+       if (em->selectmode == SCE_SELECT_FACE) {
+               em->selectmode = SCE_SELECT_EDGE;
+               EDBM_selectmode_set(em);
+               EDBM_selectmode_to_scene(C);
+       }
+
+       BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
+
+       if (!EDBM_op_finish(em, &bmop, op, true)) {
+               return OPERATOR_CANCELLED;
+       }
+       else {
+               EDBM_update_generic(em, true, true);
+               return OPERATOR_FINISHED;
+       }
+}
+
+void MESH_OT_offset_edge_loops(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Offset Edge Loop";
+       ot->idname = "MESH_OT_offset_edge_loops";
+       ot->description = "Creates offset edge loop from the current selection";
+
+       /* api callbacks */
+       ot->exec = edbm_offset_edgeloop_exec;
+       ot->poll = ED_operator_editmesh;
+
+       /* Keep internal, since this is only meant to be accessed via 'MESH_OT_offset_edge_loops_slide' */
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
 #ifdef WITH_BULLET
 static int edbm_convex_hull_exec(bContext *C, wmOperator *op)
 {
 #ifdef WITH_BULLET
 static int edbm_convex_hull_exec(bContext *C, wmOperator *op)
 {
index 8c44d5a7b3f25dd472c126e1f8a47e5ec7244ed4..c56465f570a3a9f69179a8b0bd470dee9e70589a 100644 (file)
@@ -176,6 +176,7 @@ void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot);
 void MESH_OT_face_make_planar(struct wmOperatorType *ot);
 void MESH_OT_edge_split(struct wmOperatorType *ot);
 void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot);
 void MESH_OT_face_make_planar(struct wmOperatorType *ot);
 void MESH_OT_edge_split(struct wmOperatorType *ot);
 void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot);
+void MESH_OT_offset_edge_loops(struct wmOperatorType *ot);
 void MESH_OT_wireframe(struct wmOperatorType *ot);
 void MESH_OT_convex_hull(struct wmOperatorType *ot);
 void MESH_OT_symmetrize(struct wmOperatorType *ot);
 void MESH_OT_wireframe(struct wmOperatorType *ot);
 void MESH_OT_convex_hull(struct wmOperatorType *ot);
 void MESH_OT_symmetrize(struct wmOperatorType *ot);
index 20f222bf4254352cad8b85a3b2bdc9b07ae0451b..9718e63f01207bfdbfb20c827a7d754bf068dcd6 100644 (file)
@@ -177,6 +177,7 @@ void ED_operatortypes_mesh(void)
 
        WM_operatortype_append(MESH_OT_bridge_edge_loops);
        WM_operatortype_append(MESH_OT_inset);
 
        WM_operatortype_append(MESH_OT_bridge_edge_loops);
        WM_operatortype_append(MESH_OT_inset);
+       WM_operatortype_append(MESH_OT_offset_edge_loops);
        WM_operatortype_append(MESH_OT_intersect);
        WM_operatortype_append(MESH_OT_face_split_by_edges);
        WM_operatortype_append(MESH_OT_poke);
        WM_operatortype_append(MESH_OT_intersect);
        WM_operatortype_append(MESH_OT_face_split_by_edges);
        WM_operatortype_append(MESH_OT_poke);
@@ -225,6 +226,13 @@ void ED_operatormacros_mesh(void)
        otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide");
        RNA_boolean_set(otmacro->ptr, "release_confirm", false);
 
        otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide");
        RNA_boolean_set(otmacro->ptr, "release_confirm", false);
 
+       ot = WM_operatortype_append_macro("MESH_OT_offset_edge_loops_slide", "Offset Edge Slide", "Offset edge loop slide",
+                                         OPTYPE_UNDO | OPTYPE_REGISTER);
+       WM_operatortype_macro_define(ot, "MESH_OT_offset_edge_loops");
+       otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide");
+       RNA_boolean_set(otmacro->ptr, "release_confirm", false);
+       RNA_boolean_set(otmacro->ptr, "single_side", true);
+
        ot = WM_operatortype_append_macro("MESH_OT_duplicate_move", "Add Duplicate", "Duplicate mesh and move",
                                          OPTYPE_UNDO | OPTYPE_REGISTER);
        WM_operatortype_macro_define(ot, "MESH_OT_duplicate");
        ot = WM_operatortype_append_macro("MESH_OT_duplicate_move", "Add Duplicate", "Duplicate mesh and move",
                                          OPTYPE_UNDO | OPTYPE_REGISTER);
        WM_operatortype_macro_define(ot, "MESH_OT_duplicate");
@@ -302,7 +310,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf)
        keymap = WM_keymap_find(keyconf, "Mesh", 0, 0);
        keymap->poll = ED_operator_editmesh;
        
        keymap = WM_keymap_find(keyconf, "Mesh", 0, 0);
        keymap->poll = ED_operator_editmesh;
        
-       WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0);        
+       WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "MESH_OT_offset_edge_loops_slide", RKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
        WM_keymap_add_item(keymap, "MESH_OT_inset", IKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "MESH_OT_poke", PKEY, KM_PRESS, KM_ALT, 0);
        kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "MESH_OT_inset", IKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "MESH_OT_poke", PKEY, KM_PRESS, KM_ALT, 0);
        kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL, 0);
index f2c44e64d432e87cb5eec0af6c14b3ddf04b2ba5..a2bfef8d89c250d5ab6d769c5e6d2189ea75ec4e 100644 (file)
@@ -167,6 +167,7 @@ static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
 static void initBoneRoll(TransInfo *t);
 static void applyBoneRoll(TransInfo *t, const int mval[2]);
 
 static void initBoneRoll(TransInfo *t);
 static void applyBoneRoll(TransInfo *t, const int mval[2]);
 
+static void initEdgeSlide_ex(TransInfo *t, bool use_double_side);
 static void initEdgeSlide(TransInfo *t);
 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
 static void initEdgeSlide(TransInfo *t);
 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
@@ -2229,8 +2230,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
                        initBoneEnvelope(t);
                        break;
                case TFM_EDGE_SLIDE:
                        initBoneEnvelope(t);
                        break;
                case TFM_EDGE_SLIDE:
-                       initEdgeSlide(t);
+               {
+                       const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
+                       initEdgeSlide_ex(t, use_double_side);
                        break;
                        break;
+               }
                case TFM_VERT_SLIDE:
                        initVertSlide(t);
                        break;
                case TFM_VERT_SLIDE:
                        initVertSlide(t);
                        break;
@@ -5684,8 +5688,11 @@ static void calcEdgeSlide_mval_range(
        float projectMat[4][4];
        BMBVHTree *btree;
 
        float projectMat[4][4];
        BMBVHTree *btree;
 
+       /* only for use_calc_direction */
+       float (*loop_dir)[3] = NULL, *loop_maxdist = NULL;
+
        float mval_start[2], mval_end[2];
        float mval_start[2], mval_end[2];
-       float mval_dir[3], dist_best_sq, (*loop_dir)[3], *loop_maxdist;
+       float mval_dir[3], dist_best_sq;
        BMIter iter;
        BMEdge *e;
 
        BMIter iter;
        BMEdge *e;
 
@@ -5715,9 +5722,11 @@ static void calcEdgeSlide_mval_range(
        zero_v3(mval_dir);
        dist_best_sq = -1.0f;
 
        zero_v3(mval_dir);
        dist_best_sq = -1.0f;
 
-       loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir");
-       loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist");
-       copy_vn_fl(loop_maxdist, loop_nr, -1.0f);
+       if (use_calc_direction) {
+               loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir");
+               loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist");
+               copy_vn_fl(loop_maxdist, loop_nr, -1.0f);
+       }
 
        BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
                if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
 
        BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
                if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
@@ -5773,11 +5782,13 @@ static void calcEdgeSlide_mval_range(
                                                sub_v3_v3v3(mval_dir, sco_b, sco_a);
                                        }
 
                                                sub_v3_v3v3(mval_dir, sco_b, sco_a);
                                        }
 
-                                       /* per loop direction */
-                                       l_nr = sv_array[j].loop_nr;
-                                       if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) {
-                                               loop_maxdist[l_nr] = dist_sq;
-                                               sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a);
+                                       if (use_calc_direction) {
+                                               /* per loop direction */
+                                               l_nr = sv_array[j].loop_nr;
+                                               if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) {
+                                                       loop_maxdist[l_nr] = dist_sq;
+                                                       sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a);
+                                               }
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -5795,6 +5806,9 @@ static void calcEdgeSlide_mval_range(
                                SWAP(BMVert *, sv_array->v_side[0], sv_array->v_side[1]);
                        }
                }
                                SWAP(BMVert *, sv_array->v_side[0], sv_array->v_side[1]);
                        }
                }
+
+               MEM_freeN(loop_dir);
+               MEM_freeN(loop_maxdist);
        }
 
        /* possible all of the edge loops are pointing directly at the view */
        }
 
        /* possible all of the edge loops are pointing directly at the view */
@@ -5819,9 +5833,6 @@ static void calcEdgeSlide_mval_range(
        if (btree) {
                BKE_bmbvh_free(btree);
        }
        if (btree) {
                BKE_bmbvh_free(btree);
        }
-
-       MEM_freeN(loop_dir);
-       MEM_freeN(loop_maxdist);
 }
 
 static void calcEdgeSlide_non_proportional(
 }
 
 static void calcEdgeSlide_non_proportional(
@@ -5870,7 +5881,7 @@ static void calcEdgeSlide_non_proportional(
        }
 }
 
        }
 }
 
-static bool createEdgeSlideVerts(TransInfo *t)
+static bool createEdgeSlideVerts_double_side(TransInfo *t)
 {
        BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
        BMesh *bm = em->bm;
 {
        BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
        BMesh *bm = em->bm;
@@ -6211,6 +6222,198 @@ static bool createEdgeSlideVerts(TransInfo *t)
        return true;
 }
 
        return true;
 }
 
+/**
+ * A simple version of #createEdgeSlideVerts
+ * Which assumes the longest unselected.
+ */
+static bool createEdgeSlideVerts_single_side(TransInfo *t)
+{
+       BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
+       BMesh *bm = em->bm;
+       BMIter iter;
+       BMEdge *e;
+       BMVert *v;
+       TransDataEdgeSlideVert *sv_array;
+       int sv_tot;
+       int *sv_table;  /* BMVert -> sv_array index */
+       EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
+       float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
+       int i, j, loop_nr;
+       bool use_btree_disp = false;
+       View3D *v3d = NULL;
+       RegionView3D *rv3d = NULL;
+
+       if (t->spacetype == SPACE_VIEW3D) {
+               /* background mode support */
+               v3d = t->sa ? t->sa->spacedata.first : NULL;
+               rv3d = t->ar ? t->ar->regiondata : NULL;
+       }
+
+       slide_origdata_init_flag(t, &sld->orig_data);
+
+       sld->is_proportional = true;
+       sld->curr_sv_index = 0;
+       /* heppans to be best for single-sided */
+       sld->flipped_vtx = true;
+
+       /* ensure valid selection */
+       j = 0;
+       BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+               if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+                       float len_sq_max = -1.0f;
+                       BMIter iter2;
+                       BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
+                               if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+                                       float len_sq = BM_edge_calc_length_squared(e);
+                                       if (len_sq > len_sq_max) {
+                                               len_sq_max = len_sq;
+                                               v->e = e;
+                                       }
+                               }
+                       }
+
+                       if (len_sq_max != -1.0f) {
+                               j++;
+                       }
+               }
+               BM_elem_index_set(v, i); /* set_inline */
+       }
+       bm->elem_index_dirty &= ~BM_VERT;
+
+       if (!j) {
+               return false;
+       }
+
+
+       sv_tot = j;
+       BLI_assert(sv_tot != 0);
+       /* over alloc */
+       sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array");
+
+       /* same loop for all loops, weak but we dont connect loops in this case */
+       loop_nr = 1;
+
+       sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
+
+       j = 0;
+       BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+               sv_table[i] = -1;
+               if ((v->e != NULL) && (BM_elem_flag_test(v, BM_ELEM_SELECT))) {
+                       if (BM_elem_flag_test(v->e, BM_ELEM_SELECT) == 0) {
+                               TransDataEdgeSlideVert *sv;
+                               sv = &sv_array[j];
+                               sv->v = v;
+                               copy_v3_v3(sv->v_co_orig, v->co);
+                               sv->v_side[0] = BM_edge_other_vert(v->e, v);
+                               sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co);
+                               sv->loop_nr = 0;
+                               sv_table[i] = j;
+                               j += 1;
+                       }
+               }
+       }
+
+       /* check for wire vertices,
+        * interpolate the directions of wire verts between non-wire verts */
+       if (sv_tot != bm->totvert) {
+               const int sv_tot_nowire = sv_tot;
+               TransDataEdgeSlideVert *sv_iter = sv_array;
+               int i;
+               for (i = 0; i < sv_tot_nowire; i++, sv_iter++) {
+                       BMIter eiter;
+                       BM_ITER_ELEM (e, &eiter, sv_iter->v, BM_EDGES_OF_VERT) {
+                               /* walk over wire */
+                               TransDataEdgeSlideVert *sv_end = NULL;
+                               BMEdge *e_step = e;
+                               BMVert *v = sv_iter->v;
+
+                               j = sv_tot;
+
+                               while (1) {
+                                       BMVert *v_other = BM_edge_other_vert(e_step, v);
+                                       int endpoint = (
+                                               (sv_table[BM_elem_index_get(v_other)] != -1) +
+                                               (BM_vert_is_edge_pair(v_other) == false));
+
+                                       if ((BM_elem_flag_test(e_step, BM_ELEM_SELECT) &&
+                                            BM_elem_flag_test(v_other, BM_ELEM_SELECT)) &&
+                                            (endpoint == 0))
+                                       {
+                                               /* scan down the list */
+                                               TransDataEdgeSlideVert *sv;
+                                               BLI_assert(sv_table[BM_elem_index_get(v_other)] == -1);
+                                               sv_table[BM_elem_index_get(v_other)] = j;
+                                               sv = &sv_array[j];
+                                               sv->v = v_other;
+                                               copy_v3_v3(sv->v_co_orig, v_other->co);
+                                               copy_v3_v3(sv->dir_side[0], sv_iter->dir_side[0]);
+                                               j++;
+
+                                               /* advance! */
+                                               v = v_other;
+                                               e_step = BM_DISK_EDGE_NEXT(e_step, v_other);
+                                       }
+                                       else {
+                                               if ((endpoint == 2) && (sv_tot != j)) {
+                                                       BLI_assert(BM_elem_index_get(v_other) != -1);
+                                                       sv_end = &sv_array[sv_table[BM_elem_index_get(v_other)]];
+                                               }
+                                               break;
+                                       }
+                               }
+
+                               if (sv_end) {
+                                       int sv_tot_prev = sv_tot;
+                                       const float *co_src = sv_iter->v->co;
+                                       const float *co_dst = sv_end->v->co;
+                                       const float *dir_src = sv_iter->dir_side[0];
+                                       const float *dir_dst = sv_end->dir_side[0];
+                                       sv_tot = j;
+
+                                       while (j-- != sv_tot_prev) {
+                                               float factor;
+                                               factor = line_point_factor_v3(sv_array[j].v->co, co_src, co_dst);
+                                               interp_v3_v3v3(sv_array[j].dir_side[0], dir_src, dir_dst, factor);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
+
+       sld->sv = sv_array;
+       sld->totsv = sv_tot;
+
+       /* use for visibility checks */
+       if (t->spacetype == SPACE_VIEW3D) {
+               v3d = t->sa ? t->sa->spacedata.first : NULL;
+               rv3d = t->ar ? t->ar->regiondata : NULL;
+               use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
+       }
+
+       calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false);
+
+       /* create copies of faces for customdata projection */
+       bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+       slide_origdata_init_data(t, &sld->orig_data);
+       slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv);
+
+       if (rv3d) {
+               calcEdgeSlide_non_proportional(t, sld, mval);
+       }
+
+       sld->em = em;
+
+       sld->perc = 0.0f;
+
+       t->customData = sld;
+
+       MEM_freeN(sv_table);
+
+       return true;
+}
+
 void projectEdgeSlideData(TransInfo *t, bool is_final)
 {
        EdgeSlideData *sld = t->customData;
 void projectEdgeSlideData(TransInfo *t, bool is_final)
 {
        EdgeSlideData *sld = t->customData;
@@ -6247,15 +6450,23 @@ void freeEdgeSlideVerts(TransInfo *t)
        recalcData(t);
 }
 
        recalcData(t);
 }
 
-static void initEdgeSlide(TransInfo *t)
+static void initEdgeSlide_ex(TransInfo *t, bool use_double_side)
 {
        EdgeSlideData *sld;
 {
        EdgeSlideData *sld;
+       bool ok;
 
        t->mode = TFM_EDGE_SLIDE;
        t->transform = applyEdgeSlide;
        t->handleEvent = handleEventEdgeSlide;
 
 
        t->mode = TFM_EDGE_SLIDE;
        t->transform = applyEdgeSlide;
        t->handleEvent = handleEventEdgeSlide;
 
-       if (!createEdgeSlideVerts(t)) {
+       if (use_double_side) {
+               ok = createEdgeSlideVerts_double_side(t);
+       }
+       else {
+               ok = createEdgeSlideVerts_single_side(t);
+       }
+
+       if (!ok) {
                t->state = TRANS_CANCEL;
                return;
        }
                t->state = TRANS_CANCEL;
                return;
        }
@@ -6284,6 +6495,11 @@ static void initEdgeSlide(TransInfo *t)
        t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 }
 
        t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 }
 
+static void initEdgeSlide(TransInfo *t)
+{
+       initEdgeSlide_ex(t, true);
+}
+
 static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event)
 {
        if (t->mode == TFM_EDGE_SLIDE) {
 static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event)
 {
        if (t->mode == TFM_EDGE_SLIDE) {
index fe06f01e09ce28852350b6bf953817bfb4776aae..e119ec6632106c3cb77d345d420050a465dc78ea 100644 (file)
@@ -837,6 +837,8 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot)
 
 static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
 {
 
 static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
 {
+       PropertyRNA *prop;
+
        /* identifiers */
        ot->name   = "Edge Slide";
        ot->description = "Slide an edge loop along a mesh"; 
        /* identifiers */
        ot->name   = "Edge Slide";
        ot->description = "Slide an edge loop along a mesh"; 
@@ -852,6 +854,9 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot)
 
        RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f);
 
 
        RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f);
 
+       prop = RNA_def_boolean(ot->srna, "single_side", false, "Single Side", "");
+       RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
        Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV);
 }
 
        Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV);
 }