From bfbc9c804ca019b10d70af25221ba52f4c1f5b8b Mon Sep 17 00:00:00 2001 From: "Jason C. Wenger" Date: Sat, 7 Jun 2025 12:15:34 +1000 Subject: [PATCH] Fix bug in dissolve edges that was introduced in 4.4 A bug that was introduced in !131645 where the number of verts eligible for dissolve was reduced, to prevent dissolving unrelated verts. That PR changed the code to only do the dissolve check on verts at the ends of selected edges, which solved bug #109765. However, this didn't properly account for dissolving only one edge in a chain in a face pair. This could result in cases where one of the vertices should be checked but wasn't - if it wasn't selected. Now when an edge is dissolved, each of its verts is checked, and if it's in a chain, the VERT_MARK tag is moved down the chain until it finds its natural endpoint. Ref !139959. --- .../blender/bmesh/operators/bmo_dissolve.cc | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/source/blender/bmesh/operators/bmo_dissolve.cc b/source/blender/bmesh/operators/bmo_dissolve.cc index 5aff8869425..4ba0e0d4645 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.cc +++ b/source/blender/bmesh/operators/bmo_dissolve.cc @@ -355,6 +355,25 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "region.out", BM_FACE, FACE_NEW); } +/** + * Given an edge, and vert that are part of a chain, finds the vert at the far end of the chain. + */ +static BMVert *bmo_find_end_of_chain(BMEdge *e, BMVert *v) +{ + BMVert *v_init = v; + while (BM_vert_is_edge_pair(v)) { + e = BM_DISK_EDGE_NEXT(e, v); + v = BM_edge_other_vert(e, v); + /* While this should never happen in the context this function is called. + * Avoid an eternal loop even in the case of degenerate geometry. */ + BLI_assert(v != v_init); + if (UNLIKELY(v == v_init)) { + break; + } + } + return v; +} + void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op) { // BMOperator fop; @@ -441,6 +460,21 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op) BMO_ITER (e, &eiter, op->slots_in, "edges", BM_EDGE) { BMLoop *l_a, *l_b; if (BM_edge_loop_pair(e, &l_a, &l_b)) { + + /* When #VERT_MARK is set on a vert in the middle of a chain, the flag needs to be moved to + * the end of the chain, because when all the chain edges between the two faces get cleaned + * up as part of #BM_faces_join_pair, the flagged vert would otherwise be lost. + * Find the end of the chain, where the dissolve test should be done, move the flag there. */ + for (int i = 0; i < 2; i++) { + BMVert *v_edge = *((&e->v1) + i); + if (BMO_vert_flag_test(bm, v_edge, VERT_MARK)) { + BMVert *v_edge_chain_end = bmo_find_end_of_chain(e, v_edge); + if (v_edge != v_edge_chain_end) { + BMO_vert_flag_enable(bm, v_edge_chain_end, VERT_MARK); + } + } + } + BM_faces_join_pair(bm, l_a, l_b, false, nullptr); } }