From 7f1b70ac505486d428304263eb155439c9dc2cdc Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 22 Feb 2024 01:31:30 -0300 Subject: [PATCH] Fix: errors in the Edge Slide computed direction The Edge Slide code was practically redone to correct errors and to enable this operator to become a generic transform operator. The errors are; 1. Unpredictable slide direction when the vertex "slides" over multiple edges. 2. Incorrect direction when the sliding edge has disconnected faces. The fixes were: - When the sliding direction splits into multiple directions, the chosen target is now the intersection of the destination edge lines (instead of the average point or a randon point) - If the edge faces disconnect, the vertex slides in different directions on each face. Other changes: - Use C++ for Array e Vector types. - Create methods to access or edit struct parameters. - Use `TransData` to access the initial position of the vertex. - Acess slide vertice by reference. --- .../editors/transform/transform_convert.hh | 20 +- .../transform/transform_convert_mesh.cc | 864 ++++++------------ .../transform/transform_mode_edge_slide.cc | 288 +++--- 3 files changed, 446 insertions(+), 726 deletions(-) diff --git a/source/blender/editors/transform/transform_convert.hh b/source/blender/editors/transform/transform_convert.hh index 7a7daca1b7d..1455c1659bf 100644 --- a/source/blender/editors/transform/transform_convert.hh +++ b/source/blender/editors/transform/transform_convert.hh @@ -48,11 +48,15 @@ struct TransConvertTypeInfo { * Structure used for Edge Slide operation. */ struct TransDataEdgeSlideVert { - struct BMVert *v; - blender::float3 v_co_orig; - blender::float3 dir_side[2]; - float edge_len; - int loop_nr; + TransData *td; + blender::float3 dir_side[2]; /* Directional vectors on the sides.*/ + float edge_len; /* Distance between vectors. */ + int loop_nr; /* Number that identifies the group of connected edges. */ + + const float *v_co_orig() const + { + return this->td->iloc; + } }; /** @@ -273,10 +277,8 @@ void transform_convert_mesh_crazyspace_free(TransMeshDataCrazySpace *r_crazyspac blender::Array transform_mesh_vert_slide_data_create( const TransDataContainer *tc, blender::Vector &r_loc_dst_buffer); -TransDataEdgeSlideVert *transform_mesh_edge_slide_data_create(const TransDataContainer *tc, - const bool use_double_side, - int *r_group_len, - int *r_sv_len); +blender::Array transform_mesh_edge_slide_data_create( + const TransDataContainer *tc, int *r_group_len); /* `transform_convert_mesh_edge.cc` */ diff --git a/source/blender/editors/transform/transform_convert_mesh.cc b/source/blender/editors/transform/transform_convert_mesh.cc index 82817ffeffe..d0af24a6aff 100644 --- a/source/blender/editors/transform/transform_convert_mesh.cc +++ b/source/blender/editors/transform/transform_convert_mesh.cc @@ -2231,25 +2231,16 @@ Array transform_mesh_vert_slide_data_create( /** \name API for Edge Slide * \{ */ -static BMEdge *get_other_edge(BMVert *v, BMEdge *e) +static bool mesh_vert_is_inner(BMVert *v) { - BMIter iter; - BMEdge *e_iter; - - BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT) && e_iter != e) { - return e_iter; - } - } - - return nullptr; + return BM_vert_is_edge_pair(v) && !BM_vert_is_boundary(v); } /** * Find the closest point on the ngon on the opposite side. * used to set the edge slide distance for ngons. */ -static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], float r_co[3]) +static bool bm_loop_calc_opposite_co(const BMLoop *l_tmp, const float plane_no[3], float r_co[3]) { /* skip adjacent edges */ BMLoop *l_first = l_tmp->next; @@ -2281,616 +2272,321 @@ static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], flo return found; } -/** - * Given 2 edges and a loop, step over the loops - * and calculate a direction to slide along. - * - * \param r_slide_vec: the direction to slide, - * the length of the vector defines the slide distance. - */ -static BMLoop *get_next_loop( - BMVert *v, BMLoop *l, BMEdge *e_prev, BMEdge *e_next, float r_slide_vec[3]) +static float3 isect_face_dst(const BMLoop *l) { - BMLoop *l_first; - float vec_accum[3] = {0.0f, 0.0f, 0.0f}; - float vec_accum_len = 0.0f; - int i = 0; - - BLI_assert(BM_edge_share_vert(e_prev, e_next) == v); - BLI_assert(BM_vert_in_edge(l->e, v)); - - l_first = l; - do { - l = BM_loop_other_edge_loop(l, v); - - if (l->e == e_next) { - if (i) { - normalize_v3_length(vec_accum, vec_accum_len / float(i)); - } - else { - /* When there is no edge to slide along, - * we must slide along the vector defined by the face we're attach to */ - BMLoop *l_tmp = BM_face_vert_share_loop(l_first->f, v); - - BLI_assert(ELEM(l_tmp->e, e_prev, e_next) && ELEM(l_tmp->prev->e, e_prev, e_next)); - - if (l_tmp->f->len == 4) { - /* we could use code below, but in this case - * sliding diagonally across the quad works well */ - sub_v3_v3v3(vec_accum, l_tmp->next->next->v->co, v->co); - } - else { - float tdir[3]; - BM_loop_calc_face_direction(l_tmp, tdir); - cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir); -#if 0 - /* Rough guess, we can do better! */ - normalize_v3_length(vec_accum, - (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f); -#else - /* be clever, check the opposite ngon edge to slide into. - * this gives best results */ - { - float tvec[3]; - float dist; - - if (bm_loop_calc_opposite_co(l_tmp, tdir, tvec)) { - dist = len_v3v3(l_tmp->v->co, tvec); - } - else { - dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f; - } - - normalize_v3_length(vec_accum, dist); - } -#endif - } - } - - copy_v3_v3(r_slide_vec, vec_accum); - return l; - } - - /* accumulate the normalized edge vector, - * normalize so some edges don't skew the result */ - float tvec[3]; - sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co); - vec_accum_len += normalize_v3(tvec); - add_v3_v3(vec_accum, tvec); - i += 1; - - if (BM_loop_other_edge_loop(l, v)->e == e_next) { - if (i) { - normalize_v3_length(vec_accum, vec_accum_len / float(i)); - } - - copy_v3_v3(r_slide_vec, vec_accum); - return BM_loop_other_edge_loop(l, v); - } - - } while ((l != l->radial_next) && ((l = l->radial_next) != l_first)); - - if (i) { - normalize_v3_length(vec_accum, vec_accum_len / float(i)); + BMFace *f = l->f; + BMLoop *l_next = l->next; + if (f->len == 4) { + /* we could use code below, but in this case + * sliding diagonally across the quad works well */ + return l_next->next->v->co; } - copy_v3_v3(r_slide_vec, vec_accum); + float3 plane_no; + BM_loop_calc_face_direction(l, plane_no); - return nullptr; + float3 isect_co; + if (!bm_loop_calc_opposite_co(l, plane_no, isect_co)) { + /* Rare case. */ + mid_v3_v3v3(isect_co, l->prev->v->co, l_next->v->co); + } + return isect_co; } -static TransDataEdgeSlideVert *createEdgeSlideVerts_double_side(const TransDataContainer *tc, - int *r_group_len, - int *r_sv_len) +Array transform_mesh_edge_slide_data_create(const TransDataContainer *tc, + int *r_group_len) { BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; - BMIter iter; - BMEdge *e; - BMVert *v; - TransDataEdgeSlideVert *sv_array; - int sv_tot; - int *sv_table; /* BMVert -> sv_array index */ - int numsel, i, loop_nr; + + int td_selected_len = 0; /* Ensure valid selection. */ - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - BMIter iter2; - numsel = 0; - BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - /* BMESH_TODO: this is probably very evil, - * set `v->e` to a selected edge. */ - v->e = e; - - numsel++; - } - } - - if (numsel == 0 || numsel > 2) { - /* Invalid edge selection. */ - return nullptr; - } - } - } - - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - /* NOTE: any edge with loops can work, but we won't get predictable results, so bail out. */ - if (!BM_edge_is_manifold(e) && !BM_edge_is_boundary(e)) { - /* can edges with at least once face user */ - return nullptr; - } - } - } - - sv_table = static_cast(MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__)); - -#define INDEX_UNSET -1 -#define INDEX_INVALID -2 - - { - int j = 0; - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - BM_elem_flag_enable(v, BM_ELEM_TAG); - sv_table[i] = INDEX_UNSET; - j += 1; - } - else { - BM_elem_flag_disable(v, BM_ELEM_TAG); - sv_table[i] = INDEX_INVALID; - } - BM_elem_index_set(v, i); /* set_inline */ - } - bm->elem_index_dirty &= ~BM_VERT; - - if (!j) { - MEM_freeN(sv_table); - return nullptr; - } - sv_tot = j; - } - - sv_array = static_cast( - MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array")); - loop_nr = 0; - - STACK_DECLARE(sv_array); - STACK_INIT(sv_array, sv_tot); - - while (true) { - float vec_a[3], vec_b[3]; - BMLoop *l_a, *l_b; - BMLoop *l_a_prev, *l_b_prev; - BMVert *v_first; -/* If this succeeds call get_next_loop() - * which calculates the direction to slide based on clever checks. - * - * otherwise we simply use 'e_dir' as an edge-rail. - * (which is better when the attached edge is a boundary, see: #40422) - */ -#define EDGESLIDE_VERT_IS_INNER(v, e_dir) \ -\ - ((BM_edge_is_boundary(e_dir) == false) && (BM_vert_edge_count_nonwire(v) == 2)) - - v = nullptr; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_TAG)) { - break; - } - } - - if (!v) { + BMIter iter; + BMVert *v; + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + if (!(td->flag & TD_SELECTED)) { + /* The selected ones are sorted at the beginning. */ break; } + v = static_cast(td->extra); + int numsel = BM_iter_elem_count_flag(BM_EDGES_OF_VERT, v, BM_ELEM_SELECT, true); + if (numsel == 0 || numsel > 2) { + /* Invalid edge selection. */ + return {}; + } + td_selected_len++; + } - if (!v->e) { + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + continue; + } + if (!BM_edge_is_manifold(e) && !BM_edge_is_boundary(e)) { + /* Can edges with at least once face user. */ + return {}; + } + } + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_elem_index_set(v, -1); + } + bm->elem_index_dirty |= BM_VERT; + + /* Alloc and initialize the #TransDataEdgeSlideVert. */ + + Array r_sv(td_selected_len); + TransDataEdgeSlideVert *sv = &r_sv[0]; + int sv_index = 0; + td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + if (!(td->flag & TD_SELECTED)) { + continue; + } + sv->td = td; + sv->loop_nr = -1; + sv->dir_side[1] = float3(0); + + /* Identify the #TransDataEdgeSlideVert by the vertex index. */ + v = static_cast(td->extra); + BM_elem_index_set(v, sv_index); + sv_index++; + sv++; + } + + /* Map indicating the indexes of #TransData connected by edge. */ + Array td_connected(tc->data_len, int2(-1, -1)); + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + continue; + } + int td_index_1 = BM_elem_index_get(e->v1); + int td_index_2 = BM_elem_index_get(e->v2); + + int slot_1 = int(td_connected[td_index_1][0] != -1); + int slot_2 = int(td_connected[td_index_2][0] != -1); + + td_connected[td_index_1][slot_1] = td_index_2; + td_connected[td_index_2][slot_2] = td_index_1; + } + + /* Compute the sliding groups. */ + int loop_nr = 0; + for (int i : r_sv.index_range()) { + TransDataEdgeSlideVert *sv = &r_sv[i]; + if (sv->loop_nr != -1) { + /* This vertex has already been computed. */ continue; } - v_first = v; - - /* Walk along the edge loop. */ - e = v->e; - - /* First, rewind. */ - do { - e = get_other_edge(v, e); - if (!e) { - e = v->e; - break; - } - - if (!BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG)) { - break; - } - - v = BM_edge_other_vert(e, v); - } while (e != v_first->e); - - BM_elem_flag_disable(v, BM_ELEM_TAG); - - l_a = e->l; - l_b = e->l->radial_next; - - /* regarding e_next, use get_next_loop()'s improved interpolation where possible */ - { - BMEdge *e_next = get_other_edge(v, e); - if (e_next) { - get_next_loop(v, l_a, e, e_next, vec_a); - } - else { - BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v); - if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { - get_next_loop(v, l_a, e, l_tmp->e, vec_a); - } - else { - sub_v3_v3v3(vec_a, BM_edge_other_vert(l_tmp->e, v)->co, v->co); - } - } + /* Start from a vertex connected to just a single edge or any if it doesn't exist. */ + int i_curr = i; + int i_prev = td_connected[i][1]; + while (!ELEM(i_prev, -1, i)) { + int tmp = td_connected[i_prev][0] != i_curr ? td_connected[i_prev][0] : + td_connected[i_prev][1]; + i_curr = i_prev; + i_prev = tmp; } - /* Equivalent to `!BM_edge_is_boundary(e)`. */ - if (l_b != l_a) { - BMEdge *e_next = get_other_edge(v, e); - if (e_next) { - get_next_loop(v, l_b, e, e_next, vec_b); - } - else { - BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v); - if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { - get_next_loop(v, l_b, e, l_tmp->e, vec_b); - } - else { - sub_v3_v3v3(vec_b, BM_edge_other_vert(l_tmp->e, v)->co, v->co); - } - } - } - else { - l_b = nullptr; - } - - l_a_prev = nullptr; - l_b_prev = nullptr; - -#define SV_FROM_VERT(v) \ -\ - ((sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \ - ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), \ -\ - STACK_PUSH_RET_PTR(sv_array)) : \ -\ - (&sv_array[sv_table[BM_elem_index_get(v)]])) - - /* Iterate over the loop. */ - v_first = v; - do { - bool l_a_ok_prev; - bool l_b_ok_prev; + /** + * We need at least 3 points to calculate the intersection of + * `prev`-`curr` and `next`-`curr` destinations. + * `next_next` is only required to identify the edge in `next.e`. + * + * | | | | + * | prev.e | curr.e | next.e | + * prev.v ---- curr.v ---- next.v ---- next_next.v + */ + struct { + int i; /* The #TransDataEdgeSlideVert index. */ TransDataEdgeSlideVert *sv; - BMVert *v_prev; - BMEdge *e_prev; + BMVert *v; + BMEdge *e; + struct { + BMFace *f; + BMVert *v_dst; + float3 dst; + } fdata[2]; + bool vert_is_edge_pair; + } prev = {}, curr = {}, next = {}, next_next = {}; - /* XXX, 'sv' will initialize multiple times, this is suspicious. see #34024. */ - BLI_assert(v != nullptr); - BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID); - sv = SV_FROM_VERT(v); - sv->v = v; - copy_v3_v3(sv->v_co_orig, v->co); - sv->loop_nr = loop_nr; + next.i = td_connected[i_curr][0] != i_prev ? td_connected[i_curr][0] : td_connected[i_curr][1]; + next.sv = &r_sv[next.i]; + next.v = static_cast(next.sv->td->extra); + next.vert_is_edge_pair = mesh_vert_is_inner(next.v); - if (l_a || l_a_prev) { - copy_v3_v3(sv->dir_side[0], vec_a); - } + curr.i = i_curr; + curr.sv = &r_sv[curr.i]; + curr.v = static_cast(curr.sv->td->extra); + curr.vert_is_edge_pair = mesh_vert_is_inner(curr.v); + curr.e = BM_edge_exists(curr.v, next.v); - if (l_b || l_b_prev) { - copy_v3_v3(sv->dir_side[1], vec_b); - } + /* Do not compute `prev` for now. Let the loop calculate `curr` twice. */ + prev.i = -1; - v_prev = v; - v = BM_edge_other_vert(e, v); - - e_prev = e; - e = get_other_edge(v, e); - - if (!e) { - BLI_assert(v != nullptr); - - BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID); - sv = SV_FROM_VERT(v); - - sv->v = v; - copy_v3_v3(sv->v_co_orig, v->co); - sv->loop_nr = loop_nr; - - if (l_a) { - BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v); - BMVert *v_other = BM_edge_other_vert(l_tmp->e, v); - if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { - get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_side[0]); - } - else { - sub_v3_v3v3(sv->dir_side[0], v_other->co, v->co); - } + while (curr.i != -1) { + if (next.i != -1) { + next_next.i = td_connected[next.i][0] != curr.i ? td_connected[next.i][0] : + td_connected[next.i][1]; + if (next_next.i != -1) { + next_next.sv = &r_sv[next_next.i]; + next_next.v = static_cast(next_next.sv->td->extra); + next_next.vert_is_edge_pair = mesh_vert_is_inner(next_next.v); + next.e = BM_edge_exists(next.v, next_next.v); } - if (l_b) { - BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v); - BMVert *v_other = BM_edge_other_vert(l_tmp->e, v); - if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { - get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_side[1]); + BMLoop *l; + BM_ITER_ELEM (l, &iter, curr.e, BM_LOOPS_OF_EDGE) { + BMFace *f_curr = l->f; + + BMVert *v1_dst, *v2_dst; + BMEdge *l_edge_next; + BMLoop *l1_slide, *l1, *l2; + if (l->v == curr.v) { + l1 = l; + l1_slide = l->prev; + l2 = l->next; + l_edge_next = l2->e; + v1_dst = l1_slide->v; + v2_dst = l2->next->v; } else { - sub_v3_v3v3(sv->dir_side[1], v_other->co, v->co); + l1_slide = l1 = l->next; + l2 = l; + l_edge_next = l2->prev->e; + v1_dst = l1_slide->next->v; + v2_dst = l2->prev->v; + } + + float3 dst = v1_dst->co; + + /* Sometimes the sliding direction may fork (`isect_curr_dirs` is `true`). + * In this case, the resulting direction is the intersection of the destinations. */ + bool isect_curr_dirs = false; + + /* Identify the slot to slide according to the directions already computed in `curr`. */ + int best_slide = -1; + if (f_curr == curr.fdata[0].f || v1_dst == curr.fdata[0].v_dst) { + best_slide = 0; + } + else if (f_curr == curr.fdata[1].f || v1_dst == curr.fdata[1].v_dst) { + best_slide = 1; + } + else if (ELEM(nullptr, curr.fdata[0].f, curr.fdata[1].f)) { + best_slide = int(curr.fdata[0].f != nullptr); + curr.fdata[best_slide].f = f_curr; + if (curr.vert_is_edge_pair) { + curr.fdata[best_slide].dst = isect_face_dst(l1); + } + else { + curr.fdata[best_slide].v_dst = v1_dst; + curr.fdata[best_slide].dst = v1_dst->co; + } + } + else { + isect_curr_dirs = true; + + /* Find the best direction among those already computed. + * Prioritizing in order: + * - Edge that share faces between them. + * - Boundary edge that points to the closest direction. + * - Any edge that points to the closest direction. */ + BMLoop *l_other = l1_slide->radial_next; + while (l_other != l1_slide) { + if (l_other->f == curr.fdata[0].f) { + best_slide = 0; + break; + } + if (l_other->f == curr.fdata[1].f) { + best_slide = 1; + break; + } + l_other = (l_other->v == curr.v ? l_other->prev : l_other->next)->radial_next; + } + + if (best_slide == -1) { + BMEdge *e0 = curr.fdata[0].v_dst ? BM_edge_exists(curr.v, curr.fdata[0].v_dst) : + nullptr; + BMEdge *e1 = curr.fdata[1].v_dst ? BM_edge_exists(curr.v, curr.fdata[1].v_dst) : + nullptr; + const bool is_boundary_0 = e0 && BM_edge_is_boundary(e0); + const bool is_boundary_1 = e1 && BM_edge_is_boundary(e1); + if (is_boundary_0 && !is_boundary_1) { + best_slide = 0; + } + else if (is_boundary_1 && !is_boundary_0) { + best_slide = 1; + } + else { + /* Find the closest direction. */ + float3 src = curr.v->co; + float3 dir_curr = dst - src; + float3 dir0 = math::normalize(curr.fdata[0].dst - src); + float3 dir1 = math::normalize(curr.fdata[1].dst - src); + float dot0 = math::dot(dir_curr, dir0); + float dot1 = math::dot(dir_curr, dir1); + best_slide = int(dot0 < dot1); + } + } + } + + /* Compute `next`. */ + next.fdata[best_slide].f = f_curr; + if (l_edge_next == next.e || next.vert_is_edge_pair) { + /* Case where the vertex slides over the face. */ + next.fdata[best_slide].v_dst = nullptr; + next.fdata[best_slide].dst = isect_face_dst(l2); + } + else { + /* Case where the vertex slides over an edge. */ + next.fdata[best_slide].v_dst = v2_dst; + next.fdata[best_slide].dst = v2_dst->co; + } + + if (isect_curr_dirs) { + /* The `best_slide` can only have one direction. */ + float3 &dst0 = prev.fdata[best_slide].dst; + float3 &dst1 = curr.fdata[best_slide].dst; + float3 &dst2 = dst; + float3 &dst3 = next.fdata[best_slide].dst; + float3 isect0, isect1; + if (isect_line_line_epsilon_v3(dst0, dst1, dst2, dst3, isect0, isect1, FLT_EPSILON)) { + curr.fdata[best_slide].dst = math::midpoint(isect0, isect1); + } } } + } - BM_elem_flag_disable(v, BM_ELEM_TAG); - BM_elem_flag_disable(v_prev, BM_ELEM_TAG); + /* The data in `curr` is computed. Use to compute the #TransDataEdgeSlideVert. */ + float3 iloc = curr.sv->td->iloc; + if (curr.fdata[0].f) { + curr.sv->dir_side[0] = curr.fdata[0].dst - iloc; + } + if (curr.fdata[1].f) { + curr.sv->dir_side[1] = curr.fdata[1].dst - iloc; + } + curr.sv->edge_len = math::distance(curr.sv->dir_side[0], curr.sv->dir_side[1]); + curr.sv->loop_nr = loop_nr; + if (i_prev != -1 && prev.i == i_prev) { + /* Cycle returned to the beginning. + * The data with index `i_curr` was computed twice to make sure the directions are correct + * the second time. */ break; } - l_a_ok_prev = (l_a != nullptr); - l_b_ok_prev = (l_b != nullptr); - - l_a_prev = l_a; - l_b_prev = l_b; - - if (l_a) { - l_a = get_next_loop(v, l_a, e_prev, e, vec_a); - } - else { - zero_v3(vec_a); - } - - if (l_b) { - l_b = get_next_loop(v, l_b, e_prev, e, vec_b); - } - else { - zero_v3(vec_b); - } - - if (l_a && l_b) { - /* pass */ - } - else { - if (l_a || l_b) { - /* find the opposite loop if it was missing previously */ - if (l_a == nullptr && l_b && (l_b->radial_next != l_b)) { - l_a = l_b->radial_next; - } - else if (l_b == nullptr && l_a && (l_a->radial_next != l_a)) { - l_b = l_a->radial_next; - } - } - else if (e->l != nullptr) { - /* if there are non-contiguous faces, we can still recover - * the loops of the new edges faces */ - - /* NOTE:, the behavior in this case means edges may move in opposite directions, - * this could be made to work more usefully. */ - - if (l_a_ok_prev) { - l_a = e->l; - l_b = (l_a->radial_next != l_a) ? l_a->radial_next : nullptr; - } - else if (l_b_ok_prev) { - l_b = e->l; - l_a = (l_b->radial_next != l_b) ? l_b->radial_next : nullptr; - } - } - - if (!l_a_ok_prev && l_a) { - get_next_loop(v, l_a, e, e_prev, vec_a); - } - if (!l_b_ok_prev && l_b) { - get_next_loop(v, l_b, e, e_prev, vec_b); - } - } - - BM_elem_flag_disable(v, BM_ELEM_TAG); - BM_elem_flag_disable(v_prev, BM_ELEM_TAG); - } while ((e != v_first->e) && (l_a || l_b)); - -#undef SV_FROM_VERT -#undef INDEX_UNSET -#undef INDEX_INVALID + /* Move forward. */ + prev = curr; + curr = next; + next = next_next; + } loop_nr++; - -#undef EDGESLIDE_VERT_IS_INNER } - - // EDBM_flag_disable_all(em, BM_ELEM_SELECT); - - BLI_assert(STACK_SIZE(sv_array) == uint(sv_tot)); - - MEM_freeN(sv_table); - *r_group_len = loop_nr; - *r_sv_len = sv_tot; - return sv_array; -} - -/** - * A simple version of #createEdgeSlideVerts_double_side - * Which assumes the longest unselected. - */ -static TransDataEdgeSlideVert *createEdgeSlideVerts_single_side(const TransDataContainer *tc, - int *r_group_len, - int *r_sv_len) -{ - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - BMIter iter; - BMEdge *e; - TransDataEdgeSlideVert *sv_array; - int sv_tot; - int *sv_table; /* BMVert -> sv_array index */ - int loop_nr; - - /* ensure valid selection */ - { - int i = 0, j = 0; - BMVert *v; - - 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 nullptr; - } - - sv_tot = j; - } - - BLI_assert(sv_tot != 0); - /* over alloc */ - sv_array = static_cast( - MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array")); - - /* Same loop for all loops, weak but we don't connect loops in this case. */ - loop_nr = 1; - - sv_table = static_cast(MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__)); - - { - int i = 0, j = 0; - BMVert *v; - - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - sv_table[i] = -1; - if ((v->e != nullptr) && 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); - BMVert *v_other = BM_edge_other_vert(v->e, v); - sub_v3_v3v3(sv->dir_side[0], v_other->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; - - for (int 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 = nullptr; - BMEdge *e_step = e; - BMVert *v = sv_iter->v; - int j; - - j = sv_tot; - - while (true) { - 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); - - MEM_freeN(sv_table); - - *r_group_len = loop_nr; - *r_sv_len = sv_tot; - return sv_array; -} - -TransDataEdgeSlideVert *transform_mesh_edge_slide_data_create(const TransDataContainer *tc, - const bool use_double_side, - int *r_group_len, - int *r_sv_len) -{ - TransDataEdgeSlideVert *sv_array = - use_double_side ? createEdgeSlideVerts_double_side(tc, r_group_len, r_sv_len) : - createEdgeSlideVerts_single_side(tc, r_group_len, r_sv_len); - - if (sv_array) { - TransDataEdgeSlideVert *sv = sv_array; - for (int i = 0; i < *r_sv_len; i++, sv++) { - /* Set length */ - sv->edge_len = len_v3v3(sv->dir_side[0], sv->dir_side[1]); - } - } - - return sv_array; + return r_sv; } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_edge_slide.cc b/source/blender/editors/transform/transform_mode_edge_slide.cc index 786f000dadd..542c0a3503a 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.cc +++ b/source/blender/editors/transform/transform_mode_edge_slide.cc @@ -9,14 +9,11 @@ #include "BLI_math_matrix.h" #include "BLI_string.h" -#include "BKE_editmesh.hh" -#include "BKE_editmesh_bvh.h" #include "BKE_unit.hh" #include "GPU_immediate.h" #include "GPU_matrix.h" -#include "ED_mesh.hh" #include "ED_screen.hh" #include "ED_transform_snap_object_context.hh" @@ -25,6 +22,7 @@ #include "RNA_access.hh" #include "UI_interface.hh" +#include "UI_view2d.hh" #include "BLT_translation.hh" @@ -41,11 +39,44 @@ using namespace blender; * \{ */ struct EdgeSlideData { - TransDataEdgeSlideVert *sv; - int totsv; + Array sv; int mval_start[2], mval_end[2]; int curr_sv_index; + + private: + float4x4 proj_mat; + float2 win_half; + + public: + void update_proj_mat(TransInfo *t, const TransDataContainer *tc) + { + ARegion *region = t->region; + this->win_half = {region->winx / 2.0f, region->winy / 2.0f}; + + if (t->spacetype == SPACE_VIEW3D) { + RegionView3D *rv3d = static_cast(region->regiondata); + this->proj_mat = ED_view3d_ob_project_mat_get(rv3d, tc->obedit); + + for (int i = 0; i < 4; i++) { + this->proj_mat[i][0] *= this->win_half[0]; + this->proj_mat[i][1] *= this->win_half[1]; + } + } + else { + const View2D *v2d = static_cast(t->view); + UI_view2d_view_to_region_m4(v2d, this->proj_mat.ptr()); + this->proj_mat.location()[0] -= this->win_half[0]; + this->proj_mat.location()[1] -= this->win_half[1]; + } + } + + void project(const TransDataEdgeSlideVert *sv, float2 &r_sco_a, float2 &r_sco_b) + { + float3 iloc = sv->v_co_orig(); + r_sco_a = math::project_point(this->proj_mat, iloc + sv->dir_side[0]).xy() + this->win_half; + r_sco_b = math::project_point(this->proj_mat, iloc + sv->dir_side[1]).xy() + this->win_half; + } }; struct EdgeSlideParams { @@ -124,37 +155,6 @@ static void interp_line_v3_v3v3v3( } } -static blender::float4x4 edge_slide_projmat_get(TransInfo *t, TransDataContainer *tc) -{ - RegionView3D *rv3d = nullptr; - - if (t->spacetype == SPACE_VIEW3D) { - /* Background mode support. */ - rv3d = static_cast(t->region ? t->region->regiondata : nullptr); - } - - if (!rv3d) { - /* Ok, let's try to survive this. */ - return blender::float4x4::identity(); - } - return ED_view3d_ob_project_mat_get(rv3d, tc->obedit); -} - -static void edge_slide_pair_project(TransDataEdgeSlideVert *sv, - ARegion *region, - const float projectMat[4][4], - float r_sco_a[3], - float r_sco_b[3]) -{ - BMVert *v = sv->v; - - add_v3_v3v3(r_sco_b, v->co, sv->dir_side[1]); - ED_view3d_project_float_v3_m4(region, r_sco_b, r_sco_b, projectMat); - - add_v3_v3v3(r_sco_a, v->co, sv->dir_side[0]); - ED_view3d_project_float_v3_m4(region, r_sco_a, r_sco_a, projectMat); -} - static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir) { /* Possible all of the edge loops are pointing directly at the view. */ @@ -184,10 +184,11 @@ static bool is_vert_slide_visible(TransInfo *t, TransDataEdgeSlideVert *sv, const float4 &plane_near) { + const float3 &v_co_orig = sv->v_co_orig(); float3 points[3] = { - sv->v_co_orig, - sv->v_co_orig + sv->dir_side[0] * 0.9f, - sv->v_co_orig + sv->dir_side[1] * 0.9f, + v_co_orig, + v_co_orig + sv->dir_side[0] * 0.9f, + v_co_orig + sv->dir_side[1] * 0.9f, }; float3 hit_loc; @@ -239,7 +240,6 @@ static bool is_vert_slide_visible(TransInfo *t, * Calculate screen-space `mval_start` / `mval_end`, optionally slide direction. */ static void calcEdgeSlide_mval_range(TransInfo *t, - TransDataContainer *tc, EdgeSlideData *sld, const int loop_nr, const blender::float2 &mval, @@ -248,9 +248,9 @@ static void calcEdgeSlide_mval_range(TransInfo *t, /* only for use_calc_direction */ float(*loop_dir)[3] = nullptr, *loop_maxdist = nullptr; - float mval_dir[3], dist_best_sq; + float mval_dir[3], dist_best_sq = FLT_MAX; - /* Use for visibility checks. */ + /* use for visibility checks */ SnapObjectContext *snap_context = nullptr; bool use_occlude_geometry = false; float4 plane_near; @@ -262,21 +262,18 @@ static void calcEdgeSlide_mval_range(TransInfo *t, snap_context = ED_transform_snap_object_context_create(t->scene, 0); } - const blender::float4x4 projection = edge_slide_projmat_get(t, tc); - /* find mouse vectors, the global one, and one per loop in case we have * multiple loops selected, in case they are oriented different */ zero_v3(mval_dir); - dist_best_sq = -1.0f; if (use_calc_direction) { loop_dir = static_cast(MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir")); loop_maxdist = static_cast(MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist")); - copy_vn_fl(loop_maxdist, loop_nr, -1.0f); + copy_vn_fl(loop_maxdist, loop_nr, FLT_MAX); } - TransDataEdgeSlideVert *sv = &sld->sv[0]; - for (int i = 0; i < sld->totsv; i++, sv++) { + for (int i : sld->sv.index_range()) { + TransDataEdgeSlideVert *sv = &sld->sv[i]; bool is_visible = !use_occlude_geometry || is_vert_slide_visible(t, snap_context, sv, plane_near); @@ -285,20 +282,16 @@ static void calcEdgeSlide_mval_range(TransInfo *t, continue; } + /* Search cross edges for visible edge to the mouse cursor, + * then use the shared vertex to calculate screen vector. */ /* screen-space coords */ - float sco_a[3], sco_b[3]; - float dist_sq; - int l_nr; - - edge_slide_pair_project(sv, t->region, projection.ptr(), sco_a, sco_b); + float2 sco_a, sco_b; + sld->project(sv, sco_a, sco_b); /* global direction */ - dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a); + float dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a); if (is_visible) { - if ((dist_best_sq == -1.0f) || - /* intentionally use 2d size on 3d vector */ - (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f))) - { + if (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f)) { dist_best_sq = dist_sq; sub_v3_v3v3(mval_dir, sco_b, sco_a); sld->curr_sv_index = i; @@ -307,8 +300,8 @@ static void calcEdgeSlide_mval_range(TransInfo *t, if (use_calc_direction) { /* per loop direction */ - l_nr = sv->loop_nr; - if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) { + int l_nr = sv->loop_nr; + if (dist_sq < loop_maxdist[l_nr]) { loop_maxdist[l_nr] = dist_sq; sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a); } @@ -316,13 +309,11 @@ static void calcEdgeSlide_mval_range(TransInfo *t, } if (use_calc_direction) { - int i; - sv = &sld->sv[0]; - for (i = 0; i < sld->totsv; i++, sv++) { + for (TransDataEdgeSlideVert &sv : sld->sv) { /* switch a/b if loop direction is different from global direction */ - int l_nr = sv->loop_nr; + int l_nr = sv.loop_nr; if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) { - swap_v3_v3(sv->dir_side[0], sv->dir_side[1]); + swap_v3_v3(sv.dir_side[0], sv.dir_side[1]); } } @@ -341,18 +332,48 @@ static EdgeSlideData *createEdgeSlideVerts(TransInfo *t, TransDataContainer *tc, const bool use_double_side) { - int loop_nr; - EdgeSlideData *sld = static_cast(MEM_callocN(sizeof(*sld), "sld")); - sld->curr_sv_index = 0; + int group_len; + EdgeSlideData *sld = MEM_new("sld"); + sld->sv = transform_mesh_edge_slide_data_create(tc, &group_len); - sld->sv = transform_mesh_edge_slide_data_create(tc, use_double_side, &loop_nr, &sld->totsv); - - if (sld->sv == nullptr) { - MEM_freeN(sld); + if (sld->sv.is_empty()) { + MEM_delete(sld); return nullptr; } - calcEdgeSlide_mval_range(t, tc, sld, loop_nr, t->mval, use_double_side); + if (!use_double_side) { + /* Single Side Case. + * Used by #MESH_OT_offset_edge_loops_slide. + * It only slides to the side with the longest length. */ + struct TMP { + float2 accum; + int count; + } zero{}; + + Array array_len(group_len, zero); + for (TransDataEdgeSlideVert &sv : sld->sv) { + array_len[sv.loop_nr].accum += float2(math::length(sv.dir_side[0]), + math::length(sv.dir_side[1])); + array_len[sv.loop_nr].count++; + } + + for (TMP &accum : array_len) { + accum.accum /= accum.count; + } + + for (TransDataEdgeSlideVert &sv : sld->sv) { + if (array_len[sv.loop_nr].accum[1] > array_len[sv.loop_nr].accum[0]) { + sv.dir_side[0] = sv.dir_side[1]; + } + sv.dir_side[1] = float3(0); + sv.edge_len = math::length(sv.dir_side[0]); + } + } + + sld->curr_sv_index = 0; + sld->update_proj_mat(t, tc); + + calcEdgeSlide_mval_range(t, sld, group_len, t->mval, use_double_side); return sld; } @@ -367,8 +388,7 @@ static void freeEdgeSlideVerts(TransInfo * /*t*/, return; } - MEM_freeN(sld->sv); - MEM_freeN(sld); + MEM_delete(sld); custom_data->data = nullptr; } @@ -427,47 +447,51 @@ static void drawEdgeSlide(TransInfo *t) GPU_blend(GPU_BLEND_ALPHA); - GPU_matrix_push(); - GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->object_to_world().ptr()); + if (t->spacetype == SPACE_VIEW3D) { + GPU_matrix_push(); + GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->object_to_world().ptr()); + } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; + const float3 &curr_sv_co_orig = curr_sv->v_co_orig(); + if (slp->use_even == true) { /* Even mode */ float co_a[3], co_b[3], co_mark[3]; - TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; const float fac = (slp->perc + 1.0f) / 2.0f; const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f; const float guide_size = ctrl_size - 0.5f; const int alpha_shade = -30; - add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]); - add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]); + add_v3_v3v3(co_a, curr_sv_co_orig, curr_sv->dir_side[0]); + add_v3_v3v3(co_b, curr_sv_co_orig, curr_sv->dir_side[1]); GPU_line_width(line_size); immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); immBeginAtMost(GPU_PRIM_LINES, 4); - if (!is_zero_v3(curr_sv->dir_side[0])) { + if (!math::is_zero(curr_sv->dir_side[0])) { immVertex3fv(pos, co_a); - immVertex3fv(pos, curr_sv->v_co_orig); + immVertex3fv(pos, curr_sv_co_orig); } - if (!is_zero_v3(curr_sv->dir_side[1])) { + if (!math::is_zero(curr_sv->dir_side[1])) { immVertex3fv(pos, co_b); - immVertex3fv(pos, curr_sv->v_co_orig); + immVertex3fv(pos, curr_sv_co_orig); } immEnd(); { float *co_test = nullptr; if (slp->flipped) { - if (!is_zero_v3(curr_sv->dir_side[1])) { + if (!math::is_zero(curr_sv->dir_side[1])) { co_test = co_b; } } else { - if (!is_zero_v3(curr_sv->dir_side[0])) { + if (!math::is_zero(curr_sv->dir_side[0])) { co_test = co_a; } } @@ -484,36 +508,35 @@ static void drawEdgeSlide(TransInfo *t) immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade); GPU_point_size(guide_size); immBegin(GPU_PRIM_POINTS, 1); - interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac); + interp_line_v3_v3v3v3(co_mark, co_b, curr_sv_co_orig, co_a, fac); immVertex3fv(pos, co_mark); immEnd(); } else if (is_clamp == false) { const int side_index = slp->curr_side_unclamp; - TransDataEdgeSlideVert *sv; - int i; const int alpha_shade = -160; GPU_line_width(line_size); immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); - immBegin(GPU_PRIM_LINES, sld->totsv * 2); + immBegin(GPU_PRIM_LINES, sld->sv.size() * 2); /* TODO(@ideasman42): Loop over all verts. */ - sv = sld->sv; - for (i = 0; i < sld->totsv; i++, sv++) { + for (TransDataEdgeSlideVert &sv : sld->sv) { float a[3], b[3]; - if (!is_zero_v3(sv->dir_side[side_index])) { - copy_v3_v3(a, sv->dir_side[side_index]); + if (!is_zero_v3(sv.dir_side[side_index])) { + copy_v3_v3(a, sv.dir_side[side_index]); } else { - copy_v3_v3(a, sv->dir_side[!side_index]); + copy_v3_v3(a, sv.dir_side[!side_index]); } mul_v3_fl(a, 100.0f); negate_v3_v3(b, a); - add_v3_v3(a, sv->v_co_orig); - add_v3_v3(b, sv->v_co_orig); + + const float3 &sv_co_orig = sv.v_co_orig(); + add_v3_v3(a, sv_co_orig); + add_v3_v3(b, sv_co_orig); immVertex3fv(pos, a); immVertex3fv(pos, b); @@ -522,27 +545,27 @@ static void drawEdgeSlide(TransInfo *t) } else { /* Common case. */ - TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; const int alpha_shade = -160; float co_dir[3]; - add_v3_v3v3(co_dir, curr_sv->v_co_orig, curr_sv->dir_side[slp->curr_side_unclamp]); + add_v3_v3v3(co_dir, curr_sv_co_orig, curr_sv->dir_side[slp->curr_side_unclamp]); GPU_line_width(line_size); immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); immBeginAtMost(GPU_PRIM_LINES, 2); - immVertex3fv(pos, curr_sv->v_co_orig); + immVertex3fv(pos, curr_sv_co_orig); immVertex3fv(pos, co_dir); immEnd(); } immUnbindProgram(); - GPU_matrix_pop(); + if (t->spacetype == SPACE_VIEW3D) { + GPU_matrix_pop(); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + } GPU_blend(GPU_BLEND_NONE); - - GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void edge_slide_snap_apply(TransInfo *t, float *value) @@ -551,11 +574,11 @@ static void edge_slide_snap_apply(TransInfo *t, float *value) EdgeSlideParams *slp = static_cast(t->custom.mode.data); EdgeSlideData *sld_active = static_cast(tc->custom.mode.data); TransDataEdgeSlideVert *sv = &sld_active->sv[sld_active->curr_sv_index]; - float snap_point[3], co_orig[3], co_dest[2][3], dvec[3]; + float3 co_orig, co_dest[2], dvec, snap_point; + co_orig = sv->v_co_orig(); + co_dest[0] = co_orig + sv->dir_side[0]; + co_dest[1] = co_orig + sv->dir_side[1]; - copy_v3_v3(co_orig, sv->v_co_orig); - add_v3_v3v3(co_dest[0], co_orig, sv->dir_side[0]); - add_v3_v3v3(co_dest[1], co_orig, sv->dir_side[1]); if (tc->use_local_mat) { mul_m4_v3(tc->mat, co_orig); mul_m4_v3(tc->mat, co_dest[0]); @@ -575,7 +598,10 @@ static void edge_slide_snap_apply(TransInfo *t, float *value) side_index = perc < 0.0f; } else { - side_index = slp->curr_side_unclamp; + /* Use the side indicated in `EdgeSlideParams::curr_side_unclamp` as long as that side is not + * zero length. */ + side_index = int(slp->curr_side_unclamp == + !math::is_zero(sv->dir_side[slp->curr_side_unclamp])); } } else { @@ -628,7 +654,7 @@ static void edge_slide_snap_apply(TransInfo *t, float *value) *value = perc; } -static void edge_slide_apply_elem(const TransDataEdgeSlideVert *sv, +static void edge_slide_apply_elem(const TransDataEdgeSlideVert &sv, const float fac, const float curr_length_fac, const int curr_side_unclamp, @@ -637,21 +663,21 @@ static void edge_slide_apply_elem(const TransDataEdgeSlideVert *sv, const bool use_flip, float r_co[3]) { - copy_v3_v3(r_co, sv->v_co_orig); + copy_v3_v3(r_co, sv.v_co_orig()); if (use_even == false) { if (use_clamp) { const int side_index = (fac < 0.0f); const float fac_final = fabsf(fac); - madd_v3_v3fl(r_co, sv->dir_side[side_index], fac_final); + madd_v3_v3fl(r_co, sv.dir_side[side_index], fac_final); } else { int side_index = curr_side_unclamp; - if (is_zero_v3(sv->dir_side[side_index])) { - side_index = !side_index; + if (is_zero_v3(sv.dir_side[side_index])) { + side_index = int(!side_index); } const float fac_final = (side_index == (fac < 0.0f) ? fabsf(fac) : -fabsf(fac)); - madd_v3_v3fl(r_co, sv->dir_side[side_index], fac_final); + madd_v3_v3fl(r_co, sv.dir_side[side_index], fac_final); } } else { @@ -665,18 +691,18 @@ static void edge_slide_apply_elem(const TransDataEdgeSlideVert *sv, * is the same as the distance between the original vert locations, * same goes for the lines below. */ - if (sv->edge_len > FLT_EPSILON) { + if (sv.edge_len > FLT_EPSILON) { float co_a[3], co_b[3]; - const float fac_final = min_ff(sv->edge_len, curr_length_fac) / sv->edge_len; + const float fac_final = min_ff(sv.edge_len, curr_length_fac) / sv.edge_len; - add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]); - add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]); + add_v3_v3v3(co_a, r_co, sv.dir_side[0]); + add_v3_v3v3(co_b, r_co, sv.dir_side[1]); if (use_flip) { - interp_line_v3_v3v3v3(r_co, co_b, sv->v_co_orig, co_a, fac_final); + interp_line_v3_v3v3v3(r_co, co_b, r_co, co_a, fac_final); } else { - interp_line_v3_v3v3v3(r_co, co_a, sv->v_co_orig, co_b, fac_final); + interp_line_v3_v3v3v3(r_co, co_a, r_co, co_b, fac_final); } } } @@ -710,10 +736,9 @@ static void doEdgeSlide(TransInfo *t, float perc) continue; } - TransDataEdgeSlideVert *sv = sld->sv; - for (int i = 0; i < sld->totsv; i++, sv++) { + for (TransDataEdgeSlideVert &sv : sld->sv) { edge_slide_apply_elem( - sv, perc, curr_length_fac, curr_side_unclamp, use_clamp, use_even, use_flip, sv->v->co); + sv, perc, curr_length_fac, curr_side_unclamp, use_clamp, use_even, use_flip, sv.td->loc); } } } @@ -780,14 +805,14 @@ static void edge_slide_transform_matrix_fn(TransInfo *t, float mat_xform[4][4]) EdgeSlideParams *slp = static_cast(t->custom.mode.data); TransDataContainer *tc = edge_slide_container_first_ok(t); EdgeSlideData *sld_active = static_cast(tc->custom.mode.data); - TransDataEdgeSlideVert *sv_active = &sld_active->sv[sld_active->curr_sv_index]; + TransDataEdgeSlideVert &sv_active = sld_active->sv[sld_active->curr_sv_index]; - copy_v3_v3(orig_co, sv_active->v_co_orig); + copy_v3_v3(orig_co, sv_active.v_co_orig()); const float fac = t->values_final[0]; float curr_length_fac = 0.0f; if (slp->use_even) { - curr_length_fac = sv_active->edge_len * (((slp->flipped ? fac : -fac) + 1.0f) / 2.0f); + curr_length_fac = sv_active.edge_len * (((slp->flipped ? fac : -fac) + 1.0f) / 2.0f); } edge_slide_apply_elem(sv_active, @@ -885,18 +910,15 @@ static void initEdgeSlide(TransInfo *t, wmOperator *op) void transform_mode_edge_slide_reproject_input(TransInfo *t) { - ARegion *region = t->region; - FOREACH_TRANS_DATA_CONTAINER (t, tc) { EdgeSlideData *sld = static_cast(tc->custom.mode.data); if (sld) { - const blender::float4x4 projection = edge_slide_projmat_get(t, tc); - + sld->update_proj_mat(t, tc); TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; - float mval_dir[3], sco_a[3], sco_b[3]; - edge_slide_pair_project(curr_sv, region, projection.ptr(), sco_a, sco_b); - sub_v3_v3v3(mval_dir, sco_b, sco_a); + float2 sco_a, sco_b; + sld->project(curr_sv, sco_a, sco_b); + float2 mval_dir = sco_b - sco_a; edge_slide_data_init_mval(&t->mouse, sld, mval_dir); } }