Fix: Edge Slide sometimes slides edge in wrong direction

Caused by 7f1b70ac50.

The test to find the best direction did not work for boundary edges.
This commit is contained in:
Germano Cavalcante
2024-03-08 12:53:13 -03:00
parent 456c34da1a
commit 930b11b0fe

View File

@@ -2347,6 +2347,7 @@ Array<TransDataEdgeSlideVert> transform_mesh_edge_slide_data_create(const TransD
}
sv->td = td;
sv->loop_nr = -1;
sv->dir_side[0] = float3(0);
sv->dir_side[1] = float3(0);
/* Identify the #TransDataEdgeSlideVert by the vertex index. */
@@ -2411,6 +2412,79 @@ Array<TransDataEdgeSlideVert> transform_mesh_edge_slide_data_create(const TransD
float3 dst;
} fdata[2];
bool vert_is_edge_pair;
int find_best_dir(const BMFace *f_curr,
const BMLoop *l_src,
const BMVert *v_dst,
bool *r_do_isect_curr_dirs) const
{
*r_do_isect_curr_dirs = false;
if (f_curr == this->fdata[0].f || v_dst == this->fdata[0].v_dst) {
return 0;
}
if (f_curr == this->fdata[1].f || v_dst == this->fdata[1].v_dst) {
return 1;
}
if (this->fdata[0].f || this->fdata[1].f) {
/* Find the best direction checking the edges that share faces between them. */
int best_dir = -1;
const BMLoop *l_edge = l_src->next->v == v_dst ? l_src : l_src->prev;
const BMLoop *l_other = l_edge->radial_next;
while (l_other != l_edge) {
if (l_other->f == this->fdata[0].f) {
best_dir = 0;
break;
}
if (l_other->f == this->fdata[1].f) {
best_dir = 1;
break;
}
l_other = (l_other->v == this->v ? l_other->prev : l_other->next)->radial_next;
}
if (best_dir != -1) {
*r_do_isect_curr_dirs = true;
return best_dir;
}
}
if (ELEM(nullptr, this->fdata[0].f, this->fdata[1].f)) {
return int(this->fdata[0].f != nullptr);
}
/* Find the best direction among those already computed.
* Prioritizing in order:
* - Boundary edge that points to the closest direction.
* - Any edge that points to the closest direction. */
*r_do_isect_curr_dirs = true;
BMEdge *e0 = this->fdata[0].v_dst ? BM_edge_exists(this->v, this->fdata[0].v_dst) :
nullptr;
BMEdge *e1 = this->fdata[1].v_dst ? BM_edge_exists(this->v, this->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) {
return 0;
}
if (is_boundary_1 && !is_boundary_0) {
return 1;
}
/* Find the closest direction. */
float3 src = this->v->co;
float3 dst = v_dst->co;
float3 dir_curr = dst - src;
float3 dir0 = math::normalize(this->fdata[0].dst - src);
float3 dir1 = math::normalize(this->fdata[1].dst - src);
float dot0 = math::dot(dir_curr, dir0);
float dot1 = math::dot(dir_curr, dir1);
return int(dot0 < dot1);
}
} prev = {}, curr = {}, next = {}, next_next = {};
next.i = td_connected[i_curr][0] != i_prev ? td_connected[i_curr][0] : td_connected[i_curr][1];
@@ -2444,20 +2518,19 @@ Array<TransDataEdgeSlideVert> transform_mesh_edge_slide_data_create(const TransD
BMVert *v1_dst, *v2_dst;
BMEdge *l_edge_next;
BMLoop *l1_slide, *l1, *l2;
BMLoop *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;
v1_dst = l1->prev->v;
v2_dst = l2->next->v;
}
else {
l1_slide = l1 = l->next;
l1 = l->next;
l2 = l;
l_edge_next = l2->prev->e;
v1_dst = l1_slide->next->v;
v1_dst = l1->next->v;
v2_dst = l2->prev->v;
}
@@ -2468,93 +2541,46 @@ Array<TransDataEdgeSlideVert> transform_mesh_edge_slide_data_create(const TransD
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;
int best_dir = curr.find_best_dir(f_curr, l1, v1_dst, &isect_curr_dirs);
if (curr.fdata[best_dir].f == nullptr) {
curr.fdata[best_dir].f = f_curr;
if (curr.vert_is_edge_pair) {
curr.fdata[best_slide].dst = isect_face_dst(l1);
curr.fdata[best_dir].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);
}
curr.fdata[best_dir].v_dst = v1_dst;
curr.fdata[best_dir].dst = v1_dst->co;
}
}
/* Compute `next`. */
next.fdata[best_slide].f = f_curr;
next.fdata[best_dir].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);
next.fdata[best_dir].v_dst = nullptr;
next.fdata[best_dir].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;
next.fdata[best_dir].v_dst = v2_dst;
next.fdata[best_dir].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;
/* The `best_dir` can only have one direction. */
float3 &dst0 = prev.fdata[best_dir].dst;
float3 &dst1 = curr.fdata[best_dir].dst;
float3 &dst2 = dst;
float3 &dst3 = next.fdata[best_slide].dst;
float3 &dst3 = next.fdata[best_dir].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);
if (isect_line_line_epsilon_v3(dst0, dst1, dst2, dst3, isect0, isect1, FLT_EPSILON) ==
2)
{
curr.fdata[best_dir].dst = math::midpoint(isect0, isect1);
}
else {
curr.fdata[best_dir].dst = math::midpoint(dst1, dst2);
}
}
}