Cleanup: move the Edge Slide data creation code to the mesh convert file

tmp
tmp
This commit is contained in:
Germano Cavalcante
2024-03-06 13:26:05 -03:00
parent 060878b286
commit 6888d7bc87
3 changed files with 704 additions and 703 deletions

View File

@@ -44,6 +44,26 @@ struct TransConvertTypeInfo {
void (*special_aftertrans_update)(bContext *C, TransInfo *t);
};
/**
* Structure used for Edge Slide operation.
*/
struct TransDataEdgeSlideVert {
/** #TransDataGenericSlideVert (header) */
struct BMVert *v;
struct LinkNode **cd_loop_groups;
float v_co_orig[3];
/* end generic */
float edge_len;
struct BMVert *v_side[2];
/* add origvert.co to get the original locations */
float dir_side[2][3];
int loop_nr;
};
/* `transform_convert.cc` */
/**
@@ -240,6 +260,11 @@ void transform_convert_mesh_crazyspace_transdata_set(const float mtx[3][3],
TransData *r_td);
void transform_convert_mesh_crazyspace_free(TransMeshDataCrazySpace *r_crazyspace_data);
TransDataEdgeSlideVert *transform_mesh_edge_slide_data_create(const TransDataContainer *tc,
const bool use_double_side,
int *r_group_len,
int *r_sv_len);
/* `transform_convert_mesh_edge.cc` */
extern TransConvertTypeInfo TransConvertType_MeshEdge;

View File

@@ -18,6 +18,7 @@
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
#include "BLI_memarena.h"
#include "BLI_utildefines_stack.h"
#include "BKE_context.hh"
#include "BKE_crazyspace.hh"
@@ -2158,6 +2159,669 @@ static void special_aftertrans_update__mesh(bContext * /*C*/, TransInfo *t)
/** \} */
/* -------------------------------------------------------------------- */
/** \name API for Vert Slide
* \{ */
static BMEdge *get_other_edge(BMVert *v, BMEdge *e)
{
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;
}
/**
* 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])
{
/* skip adjacent edges */
BMLoop *l_first = l_tmp->next;
BMLoop *l_last = l_tmp->prev;
BMLoop *l_iter;
float dist = FLT_MAX;
bool found = false;
l_iter = l_first;
do {
float tvec[3];
if (isect_line_plane_v3(tvec, l_iter->v->co, l_iter->next->v->co, l_tmp->v->co, plane_no)) {
const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
/* allow some overlap to avoid missing the intersection because of float precision */
if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
/* likelihood of multiple intersections per ngon is quite low,
* it would have to loop back on itself, but better support it
* so check for the closest opposite edge */
const float tdist = len_v3v3(l_tmp->v->co, tvec);
if (tdist < dist) {
copy_v3_v3(r_co, tvec);
dist = tdist;
found = true;
}
}
}
} while ((l_iter = l_iter->next) != l_last);
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])
{
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));
}
copy_v3_v3(r_slide_vec, vec_accum);
return nullptr;
}
static TransDataEdgeSlideVert *createEdgeSlideVerts_double_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;
BMVert *v;
TransDataEdgeSlideVert *sv_array;
int sv_tot;
int *sv_table; /* BMVert -> sv_array index */
int numsel, i, loop_nr;
/* 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<int *>(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<TransDataEdgeSlideVert *>(
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) {
break;
}
if (!v->e) {
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);
}
}
}
/* 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;
TransDataEdgeSlideVert *sv;
BMVert *v_prev;
BMEdge *e_prev;
/* 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;
if (l_a || l_a_prev) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_a ? l_a : l_a_prev, v);
sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v);
copy_v3_v3(sv->dir_side[0], vec_a);
}
if (l_b || l_b_prev) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_b ? l_b : l_b_prev, v);
sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v);
copy_v3_v3(sv->dir_side[1], vec_b);
}
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);
sv->v_side[0] = 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], sv->v_side[0]->co, v->co);
}
}
if (l_b) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
sv->v_side[1] = 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]);
}
else {
sub_v3_v3v3(sv->dir_side[1], sv->v_side[1]->co, v->co);
}
}
BM_elem_flag_disable(v, BM_ELEM_TAG);
BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
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
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<TransDataEdgeSlideVert *>(
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<int *>(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);
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;
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)
{
if (use_double_side) {
return createEdgeSlideVerts_double_side(tc, r_group_len, r_sv_len);
}
return createEdgeSlideVerts_single_side(tc, r_group_len, r_sv_len);
}
/** \} */
TransConvertTypeInfo TransConvertType_Mesh = {
/*flags*/ (T_EDIT | T_POINTS),
/*create_trans_data*/ createTransEditVerts,

View File

@@ -6,14 +6,8 @@
* \ingroup edtransform
*/
#include <cstdlib>
#include "MEM_guardedalloc.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector_types.hh"
#include "BLI_string.h"
#include "BLI_utildefines_stack.h"
#include "BKE_editmesh.hh"
#include "BKE_editmesh_bvh.h"
@@ -21,18 +15,15 @@
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "ED_mesh.hh"
#include "ED_screen.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "BLT_translation.hh"
@@ -46,23 +37,6 @@
/** \name Transform (Edge Slide)
* \{ */
struct TransDataEdgeSlideVert {
/** #TransDataGenericSlideVert (header) */
BMVert *v;
LinkNode **cd_loop_groups;
float v_co_orig[3];
/* end generic */
float edge_len;
BMVert *v_side[2];
/* add origvert.co to get the original locations */
float dir_side[2][3];
int loop_nr;
};
struct EdgeSlideData {
TransDataEdgeSlideVert *sv;
int totsv;
@@ -116,20 +90,6 @@ static void calcEdgeSlideCustomPoints(TransInfo *t)
applyMouseInput(t, &t->mouse, t->mval, t->values);
}
static BMEdge *get_other_edge(BMVert *v, BMEdge *e)
{
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;
}
/* Interpolates along a line made up of 2 segments (used for edge slide). */
static void interp_line_v3_v3v3v3(
float p[3], const float v1[3], const float v2[3], const float v3[3], float t)
@@ -161,140 +121,6 @@ static void interp_line_v3_v3v3v3(
}
}
/**
* 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])
{
/* skip adjacent edges */
BMLoop *l_first = l_tmp->next;
BMLoop *l_last = l_tmp->prev;
BMLoop *l_iter;
float dist = FLT_MAX;
bool found = false;
l_iter = l_first;
do {
float tvec[3];
if (isect_line_plane_v3(tvec, l_iter->v->co, l_iter->next->v->co, l_tmp->v->co, plane_no)) {
const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
/* allow some overlap to avoid missing the intersection because of float precision */
if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
/* likelihood of multiple intersections per ngon is quite low,
* it would have to loop back on itself, but better support it
* so check for the closest opposite edge */
const float tdist = len_v3v3(l_tmp->v->co, tvec);
if (tdist < dist) {
copy_v3_v3(r_co, tvec);
dist = tdist;
found = true;
}
}
}
} while ((l_iter = l_iter->next) != l_last);
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])
{
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));
}
copy_v3_v3(r_slide_vec, vec_accum);
return nullptr;
}
static blender::float4x4 edge_slide_projmat_get(TransInfo *t, TransDataContainer *tc)
{
RegionView3D *rv3d = nullptr;
@@ -366,7 +192,6 @@ static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float
static void calcEdgeSlide_mval_range(TransInfo *t,
TransDataContainer *tc,
EdgeSlideData *sld,
const int *sv_table,
const int loop_nr,
const blender::float2 &mval,
const bool use_occlude_geometry,
@@ -414,9 +239,6 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
BMEdge *e;
BMVert *v = sv->v;
UNUSED_VARS_NDEBUG(sv_table); /* silence warning */
BLI_assert(i == sv_table[BM_elem_index_get(v)]);
/* Search cross edges for visible edge to the mouse cursor,
* then use the shared vertex to calculate screen vector. */
BM_ITER_ELEM (e, &iter_other, v, BM_EDGES_OF_VERT) {
@@ -518,532 +340,25 @@ static void calcEdgeSlide_even(TransInfo *t,
}
}
static EdgeSlideData *createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc)
static EdgeSlideData *createEdgeSlideVerts(TransInfo *t,
TransDataContainer *tc,
const bool use_double_side)
{
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 */
EdgeSlideData *sld = static_cast<EdgeSlideData *>(MEM_callocN(sizeof(*sld), "sld"));
int numsel, i, loop_nr;
bool use_occlude_geometry = false;
View3D *v3d = nullptr;
RegionView3D *rv3d = nullptr;
sld->curr_sv_index = 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. */
MEM_freeN(sld);
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 */
MEM_freeN(sld);
return nullptr;
}
}
}
sv_table = static_cast<int *>(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(sld);
MEM_freeN(sv_table);
return nullptr;
}
sv_tot = j;
}
sv_array = static_cast<TransDataEdgeSlideVert *>(
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) {
break;
}
if (!v->e) {
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);
}
}
}
/* 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;
TransDataEdgeSlideVert *sv;
BMVert *v_prev;
BMEdge *e_prev;
/* 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;
if (l_a || l_a_prev) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_a ? l_a : l_a_prev, v);
sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v);
copy_v3_v3(sv->dir_side[0], vec_a);
}
if (l_b || l_b_prev) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_b ? l_b : l_b_prev, v);
sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v);
copy_v3_v3(sv->dir_side[1], vec_b);
}
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);
sv->v_side[0] = 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], sv->v_side[0]->co, v->co);
}
}
if (l_b) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
sv->v_side[1] = 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]);
}
else {
sub_v3_v3v3(sv->dir_side[1], sv->v_side[1]->co, v->co);
}
}
BM_elem_flag_disable(v, BM_ELEM_TAG);
BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
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
loop_nr++;
#undef EDGESLIDE_VERT_IS_INNER
}
// EDBM_flag_disable_all(em, BM_ELEM_SELECT);
BLI_assert(STACK_SIZE(sv_array) == uint(sv_tot));
sld->sv = sv_array;
sld->totsv = sv_tot;
/* use for visibility checks */
if (t->spacetype == SPACE_VIEW3D) {
v3d = static_cast<View3D *>(t->area ? t->area->spacedata.first : nullptr);
rv3d = static_cast<RegionView3D *>(t->region ? t->region->regiondata : nullptr);
use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE &&
!XRAY_ENABLED(v3d));
}
calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, t->mval, use_occlude_geometry, true);
if (rv3d) {
calcEdgeSlide_even(t, tc, sld, t->mval);
}
MEM_freeN(sv_table);
return sld;
}
/**
* A simple version of #createEdgeSlideVerts_double_side
* Which assumes the longest unselected.
*/
static EdgeSlideData *createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc)
{
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 */
EdgeSlideData *sld = static_cast<EdgeSlideData *>(MEM_callocN(sizeof(*sld), "sld"));
int loop_nr;
EdgeSlideData *sld = static_cast<EdgeSlideData *>(MEM_callocN(sizeof(*sld), "sld"));
sld->curr_sv_index = 0;
sld->sv = transform_mesh_edge_slide_data_create(tc, use_double_side, &loop_nr, &sld->totsv);
if (sld->sv == nullptr) {
MEM_freeN(sld);
return nullptr;
}
bool use_occlude_geometry = false;
View3D *v3d = nullptr;
RegionView3D *rv3d = nullptr;
if (t->spacetype == SPACE_VIEW3D) {
/* background mode support */
v3d = static_cast<View3D *>(t->area ? t->area->spacedata.first : nullptr);
rv3d = static_cast<RegionView3D *>(t->region ? t->region->regiondata : nullptr);
}
sld->curr_sv_index = 0;
/* 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) {
MEM_freeN(sld);
return nullptr;
}
sv_tot = j;
}
BLI_assert(sv_tot != 0);
/* over alloc */
sv_array = static_cast<TransDataEdgeSlideVert *>(
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<int *>(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);
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;
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);
sld->sv = sv_array;
sld->totsv = sv_tot;
/* use for visibility checks */
if (t->spacetype == SPACE_VIEW3D) {
v3d = static_cast<View3D *>(t->area ? t->area->spacedata.first : nullptr);
@@ -1052,14 +367,12 @@ static EdgeSlideData *createEdgeSlideVerts_single_side(TransInfo *t, TransDataCo
!XRAY_ENABLED(v3d));
}
calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, t->mval, use_occlude_geometry, false);
calcEdgeSlide_mval_range(t, tc, sld, loop_nr, t->mval, use_occlude_geometry, false);
if (rv3d) {
calcEdgeSlide_even(t, tc, sld, t->mval);
}
MEM_freeN(sv_table);
return sld;
}
@@ -1541,8 +854,7 @@ static void initEdgeSlide_ex(
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
sld = use_double_side ? createEdgeSlideVerts_double_side(t, tc) :
createEdgeSlideVerts_single_side(t, tc);
sld = createEdgeSlideVerts(t, tc, use_double_side);
if (sld) {
tc->custom.mode.data = sld;
tc->custom.mode.free_cb = freeEdgeSlideVerts;