BMesh: edge-offset feature (Ctrl+Shift+R)

Ability to quickly add 2x edge loops  on either side of selected loops.
This commit is contained in:
2015-06-15 10:58:07 +10:00
parent 3efc0aca54
commit d8e994b35f
9 changed files with 323 additions and 17 deletions

View 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 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]);
@@ -2229,8 +2230,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
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;
}
case TFM_VERT_SLIDE:
initVertSlide(t);
break;
@@ -5684,8 +5688,11 @@ static void calcEdgeSlide_mval_range(
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_dir[3], dist_best_sq, (*loop_dir)[3], *loop_maxdist;
float mval_dir[3], dist_best_sq;
BMIter iter;
BMEdge *e;
@@ -5715,9 +5722,11 @@ static void calcEdgeSlide_mval_range(
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)) {
@@ -5773,11 +5782,13 @@ static void calcEdgeSlide_mval_range(
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]);
}
}
MEM_freeN(loop_dir);
MEM_freeN(loop_maxdist);
}
/* 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);
}
MEM_freeN(loop_dir);
MEM_freeN(loop_maxdist);
}
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;
@@ -6211,6 +6222,198 @@ static bool createEdgeSlideVerts(TransInfo *t)
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;
@@ -6247,15 +6450,23 @@ void freeEdgeSlideVerts(TransInfo *t)
recalcData(t);
}
static void initEdgeSlide(TransInfo *t)
static void initEdgeSlide_ex(TransInfo *t, bool use_double_side)
{
EdgeSlideData *sld;
bool ok;
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;
}
@@ -6284,6 +6495,11 @@ static void initEdgeSlide(TransInfo *t)
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) {