Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/bmesh/tools')
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c5
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect_edges.c24
-rw-r--r--source/blender/bmesh/tools/bmesh_path.c126
-rw-r--r--source/blender/bmesh/tools/bmesh_path_uv.c433
-rw-r--r--source/blender/bmesh/tools/bmesh_path_uv.h47
-rw-r--r--source/blender/bmesh/tools/bmesh_region_match.c4
6 files changed, 579 insertions, 60 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index b109dc3199a..626d58f75f8 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -1353,8 +1353,9 @@ static void offset_meet(EdgeHalf *e1,
/**
* Calculate the meeting point between e1 and e2 (one of which should have zero offsets),
- * where e1 precedes e2 in CCW order around their common vertex v (viewed from normal side).
- * If r_angle is provided, return the angle between e and emeet in *r_angle.
+ * where \a e1 precedes \a e2 in CCW order around their common vertex \a v
+ * (viewed from normal side).
+ * If \a r_angle is provided, return the angle between \a e and \a meetco in `*r_angle`.
* If the angle is 0, or it is 180 degrees or larger, there will be no meeting point;
* return false in that case, else true.
*/
diff --git a/source/blender/bmesh/tools/bmesh_intersect_edges.c b/source/blender/bmesh/tools/bmesh_intersect_edges.c
index 99a5a9edb57..52231033fd3 100644
--- a/source/blender/bmesh/tools/bmesh_intersect_edges.c
+++ b/source/blender/bmesh/tools/bmesh_intersect_edges.c
@@ -833,15 +833,37 @@ bool BM_mesh_intersect_edges(
}
if (pair_array) {
+ BMVert *v_key, *v_val;
pair_iter = &pair_array[0];
for (i = 0; i < pair_len; i++, pair_iter++) {
BLI_assert((*pair_iter)[0].elem->head.htype == BM_VERT);
BLI_assert((*pair_iter)[1].elem->head.htype == BM_VERT);
BLI_assert((*pair_iter)[0].elem != (*pair_iter)[1].elem);
- BMVert *v_key, *v_val;
v_key = (*pair_iter)[0].vert;
v_val = (*pair_iter)[1].vert;
BLI_ghash_insert(r_targetmap, v_key, v_val);
+ }
+
+ /**
+ * The weld_verts operator works best when all keys in the same group of
+ * collapsed vertices point to the same vertex.
+ * That is, if the pairs of vertices are:
+ * [1, 2], [2, 3] and [3, 4],
+ * They are better adjusted to:
+ * [1, 4], [2, 4] and [3, 4].
+ */
+ pair_iter = &pair_array[0];
+ for (i = 0; i < pair_len; i++, pair_iter++) {
+ v_key = (*pair_iter)[0].vert;
+ v_val = (*pair_iter)[1].vert;
+ BMVert *v_target;
+ while ((v_target = BLI_ghash_lookup(r_targetmap, v_val))) {
+ v_val = v_target;
+ }
+ if (v_val != (*pair_iter)[1].vert) {
+ BMVert **v_val_p = (BMVert **)BLI_ghash_lookup_p(r_targetmap, v_key);
+ *v_val_p = (*pair_iter)[1].vert = v_val;
+ }
if (split_faces) {
/* The vertex index indicates its position in the pair_array flat. */
BM_elem_index_set(v_key, i * 2);
diff --git a/source/blender/bmesh/tools/bmesh_path.c b/source/blender/bmesh/tools/bmesh_path.c
index 713a68969e5..cb75f47acf3 100644
--- a/source/blender/bmesh/tools/bmesh_path.c
+++ b/source/blender/bmesh/tools/bmesh_path.c
@@ -18,6 +18,8 @@
* \ingroup bmesh
*
* Find a path between 2 elements.
+ *
+ * \note All 3 functions are similar, changes to one most likely apply to another.
*/
#include "MEM_guardedalloc.h"
@@ -29,8 +31,11 @@
#include "bmesh.h"
#include "bmesh_path.h" /* own include */
+#define COST_INIT_MAX FLT_MAX
+
/* -------------------------------------------------------------------- */
-/* Generic Helpers */
+/** \name Generic Helpers
+ * \{ */
/**
* Use skip options when we want to start measuring from a boundary.
@@ -40,16 +45,15 @@ static float step_cost_3_v3_ex(
{
float d1[3], d2[3];
- /* The cost is based on the simple sum of the length of the two edgees... */
+ /* The cost is based on the simple sum of the length of the two edges. */
sub_v3_v3v3(d1, v2, v1);
sub_v3_v3v3(d2, v3, v2);
const float cost_12 = normalize_v3(d1);
const float cost_23 = normalize_v3(d2);
const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23));
- /* but is biased to give higher values to sharp turns, so that it will take
- * paths with fewer "turns" when selecting between equal-weighted paths between
- * the two edges */
+ /* But is biased to give higher values to sharp turns, so that it will take paths with
+ * fewer "turns" when selecting between equal-weighted paths between the two edges. */
return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2)))));
}
@@ -58,8 +62,11 @@ static float step_cost_3_v3(const float v1[3], const float v2[3], const float v3
return step_cost_3_v3_ex(v1, v2, v3, false, false);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BM_mesh_calc_path_vert */
+/** \name BM_mesh_calc_path_vert
+ * \{ */
static void verttag_add_adjacent(HeapSimple *heap,
BMVert *v_a,
@@ -72,11 +79,11 @@ static void verttag_add_adjacent(HeapSimple *heap,
{
BMIter eiter;
BMEdge *e;
- /* loop over faces of face, but do so by first looping over loops */
+ /* Loop over faces of face, but do so by first looping over loops. */
BM_ITER_ELEM (e, &eiter, v_a, BM_EDGES_OF_VERT) {
BMVert *v_b = BM_edge_other_vert(e, v_a);
if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
- /* we know 'v_b' is not visited, check it out! */
+ /* We know 'v_b' is not visited, check it out! */
const int v_b_index = BM_elem_index_get(v_b);
const float cost_cut = params->use_topology_distance ? 1.0f : len_v3v3(v_a->co, v_b->co);
const float cost_new = cost[v_a_index] + cost_cut;
@@ -93,15 +100,15 @@ static void verttag_add_adjacent(HeapSimple *heap,
if (params->use_step_face) {
BMIter liter;
BMLoop *l;
- /* loop over faces of face, but do so by first looping over loops */
+ /* Loop over faces of face, but do so by first looping over loops. */
BM_ITER_ELEM (l, &liter, v_a, BM_LOOPS_OF_VERT) {
if (l->f->len > 3) {
- /* skip loops on adjacent edges */
+ /* Skip loops on adjacent edges. */
BMLoop *l_iter = l->next->next;
do {
BMVert *v_b = l_iter->v;
if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
- /* we know 'v_b' is not visited, check it out! */
+ /* We know 'v_b' is not visited, check it out! */
const int v_b_index = BM_elem_index_get(v_b);
const float cost_cut = params->use_topology_distance ? 1.0f :
len_v3v3(v_a->co, v_b->co);
@@ -127,7 +134,7 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
void *user_data)
{
LinkNode *path = NULL;
- /* BM_ELEM_TAG flag is used to store visited edges */
+ /* #BM_ELEM_TAG flag is used to store visited edges. */
BMVert *v;
BMIter viter;
HeapSimple *heap;
@@ -135,7 +142,7 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
BMVert **verts_prev;
int i, totvert;
- /* note, would pass BM_EDGE except we are looping over all faces anyway */
+ /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */
// BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
@@ -144,25 +151,25 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
}
bm->elem_index_dirty &= ~BM_VERT;
- /* alloc */
+ /* Allocate. */
totvert = bm->totvert;
verts_prev = MEM_callocN(sizeof(*verts_prev) * totvert, __func__);
cost = MEM_mallocN(sizeof(*cost) * totvert, __func__);
- copy_vn_fl(cost, totvert, 1e20f);
+ copy_vn_fl(cost, totvert, COST_INIT_MAX);
/*
* Arrays are now filled as follows:
*
- * As the search continues, verts_prev[n] will be the previous verts on the shortest
- * path found so far to face n. BM_ELEM_TAG is used to tag elements we have visited,
- * cost[n] will contain the length of the shortest
+ * As the search continues, `verts_prev[n]` will be the previous verts on the shortest
+ * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
+ * `cost[n]` will contain the length of the shortest
* path to face n found so far, Finally, heap is a priority heap which is built on the
- * the same data as the cost array, but inverted: it is a worklist of faces prioritized
+ * the same data as the cost array, but inverted: it is a work-list of faces prioritized
* by the shortest path found so far to the face.
*/
- /* regular dijkstra shortest path, but over faces instead of vertices */
+ /* Regular dijkstra shortest path, but over faces instead of vertices. */
heap = BLI_heapsimple_new();
BLI_heapsimple_insert(heap, 0.0f, v_src);
cost[BM_elem_index_get(v_src)] = 0.0f;
@@ -193,8 +200,11 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm,
return path;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BM_mesh_calc_path_edge */
+/** \name BM_mesh_calc_path_edge
+ * \{ */
static float edgetag_cut_cost_vert(BMEdge *e_a, BMEdge *e_b, BMVert *v)
{
@@ -223,8 +233,8 @@ static void edgetag_add_adjacent(HeapSimple *heap,
{
const int e_a_index = BM_elem_index_get(e_a);
- /* unlike vert/face, stepping faces disables scanning connected edges
- * and only steps over faces (selecting a ring of edges instead of a loop) */
+ /* Unlike vert/face, stepping faces disables scanning connected edges
+ * and only steps over faces (selecting a ring of edges instead of a loop). */
if (params->use_step_face == false || e_a->l == NULL) {
BMIter viter;
BMVert *v;
@@ -234,14 +244,14 @@ static void edgetag_add_adjacent(HeapSimple *heap,
BM_ITER_ELEM (v, &viter, e_a, BM_VERTS_OF_EDGE) {
- /* don't walk over previous vertex */
+ /* Don't walk over previous vertex. */
if ((edges_prev[e_a_index]) && (BM_vert_in_edge(edges_prev[e_a_index], v))) {
continue;
}
BM_ITER_ELEM (e_b, &eiter, v, BM_EDGES_OF_VERT) {
if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
- /* we know 'e_b' is not visited, check it out! */
+ /* We know 'e_b' is not visited, check it out! */
const int e_b_index = BM_elem_index_get(e_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -267,7 +277,7 @@ static void edgetag_add_adjacent(HeapSimple *heap,
l_cycle_iter = l_iter->next;
l_cycle_end = l_iter;
- /* good, but we need to allow this otherwise paths may fail to connect at all */
+ /* Good, but we need to allow this otherwise paths may fail to connect at all. */
#if 0
if (l_iter->f->len > 3) {
l_cycle_iter = l_cycle_iter->next;
@@ -278,7 +288,7 @@ static void edgetag_add_adjacent(HeapSimple *heap,
do {
BMEdge *e_b = l_cycle_iter->e;
if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) {
- /* we know 'e_b' is not visited, check it out! */
+ /* We know 'e_b' is not visited, check it out! */
const int e_b_index = BM_elem_index_get(e_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -304,7 +314,7 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
void *user_data)
{
LinkNode *path = NULL;
- /* BM_ELEM_TAG flag is used to store visited edges */
+ /* #BM_ELEM_TAG flag is used to store visited edges. */
BMEdge *e;
BMIter eiter;
HeapSimple *heap;
@@ -312,7 +322,7 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
BMEdge **edges_prev;
int i, totedge;
- /* note, would pass BM_EDGE except we are looping over all edges anyway */
+ /* Note, would pass #BM_EDGE except we are looping over all edges anyway. */
BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */);
BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
@@ -321,25 +331,25 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
}
bm->elem_index_dirty &= ~BM_EDGE;
- /* alloc */
+ /* Allocate. */
totedge = bm->totedge;
- edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, "SeamPathPrevious");
- cost = MEM_mallocN(sizeof(*cost) * totedge, "SeamPathCost");
+ edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totedge, __func__);
- copy_vn_fl(cost, totedge, 1e20f);
+ copy_vn_fl(cost, totedge, COST_INIT_MAX);
/*
* Arrays are now filled as follows:
*
- * As the search continues, prevedge[n] will be the previous edge on the shortest
- * path found so far to edge n. BM_ELEM_TAG is used to tag elements we have visited,
- * cost[n] will contain the length of the shortest
+ * As the search continues, `edges_prev[n]` will be the previous edge on the shortest
+ * path found so far to edge `n`. #BM_ELEM_TAG is used to tag elements we have visited,
+ * `cost[n]` will contain the length of the shortest
* path to edge n found so far, Finally, heap is a priority heap which is built on the
- * the same data as the cost array, but inverted: it is a worklist of edges prioritized
+ * the same data as the cost array, but inverted: it is a work-list of edges prioritized
* by the shortest path found so far to the edge.
*/
- /* regular dijkstra shortest path, but over edges instead of vertices */
+ /* Regular dijkstra shortest path, but over edges instead of vertices. */
heap = BLI_heapsimple_new();
BLI_heapsimple_insert(heap, 0.0f, e_src);
cost[BM_elem_index_get(e_src)] = 0.0f;
@@ -370,8 +380,11 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm,
return path;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BM_mesh_calc_path_face */
+/** \name BM_mesh_calc_path_face
+ * \{ */
static float facetag_cut_cost_edge(BMFace *f_a,
BMFace *f_b,
@@ -387,15 +400,15 @@ static float facetag_cut_cost_edge(BMFace *f_a,
#if 0
mid_v3_v3v3(e_cent, e->v1->co, e->v2->co);
#else
- /* for triangle fans it gives better results to pick a point on the edge */
+ /* For triangle fans it gives better results to pick a point on the edge. */
{
- float ix_e[3], ix_f[3], f;
+ float ix_e[3], ix_f[3];
isect_line_line_v3(e->v1->co, e->v2->co, f_a_cent, f_b_cent, ix_e, ix_f);
- f = line_point_factor_v3(ix_e, e->v1->co, e->v2->co);
- if (f < 0.0f) {
+ const float factor = line_point_factor_v3(ix_e, e->v1->co, e->v2->co);
+ if (factor < 0.0f) {
copy_v3_v3(e_cent, e->v1->co);
}
- else if (f > 1.0f) {
+ else if (factor > 1.0f) {
copy_v3_v3(e_cent, e->v2->co);
}
else {
@@ -432,7 +445,7 @@ static void facetag_add_adjacent(HeapSimple *heap,
{
const int f_a_index = BM_elem_index_get(f_a);
- /* loop over faces of face, but do so by first looping over loops */
+ /* Loop over faces of face, but do so by first looping over loops. */
{
BMIter liter;
BMLoop *l_a;
@@ -444,7 +457,7 @@ static void facetag_add_adjacent(HeapSimple *heap,
do {
BMFace *f_b = l_iter->f;
if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
- /* we know 'f_b' is not visited, check it out! */
+ /* We know 'f_b' is not visited, check it out! */
const int f_b_index = BM_elem_index_get(f_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -472,7 +485,7 @@ static void facetag_add_adjacent(HeapSimple *heap,
if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
BMFace *f_b = l_b->f;
if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
- /* we know 'f_b' is not visited, check it out! */
+ /* We know 'f_b' is not visited, check it out! */
const int f_b_index = BM_elem_index_get(f_b);
const float cost_cut = params->use_topology_distance ?
1.0f :
@@ -499,7 +512,7 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
void *user_data)
{
LinkNode *path = NULL;
- /* BM_ELEM_TAG flag is used to store visited edges */
+ /* #BM_ELEM_TAG flag is used to store visited edges. */
BMFace *f;
BMIter fiter;
HeapSimple *heap;
@@ -510,7 +523,7 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
/* Start measuring face path at the face edges, ignoring their centers. */
const void *const f_endpoints[2] = {f_src, f_dst};
- /* note, would pass BM_EDGE except we are looping over all faces anyway */
+ /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */
// BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG
BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
@@ -519,25 +532,25 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
}
bm->elem_index_dirty &= ~BM_FACE;
- /* alloc */
+ /* Allocate. */
totface = bm->totface;
faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__);
cost = MEM_mallocN(sizeof(*cost) * totface, __func__);
- copy_vn_fl(cost, totface, 1e20f);
+ copy_vn_fl(cost, totface, COST_INIT_MAX);
/*
* Arrays are now filled as follows:
*
- * As the search continues, faces_prev[n] will be the previous face on the shortest
- * path found so far to face n. BM_ELEM_TAG is used to tag elements we have visited,
- * cost[n] will contain the length of the shortest
+ * As the search continues, `faces_prev[n]` will be the previous face on the shortest
+ * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited,
+ * `cost[n]` will contain the length of the shortest
* path to face n found so far, Finally, heap is a priority heap which is built on the
- * the same data as the cost array, but inverted: it is a worklist of faces prioritized
+ * the same data as the cost array, but inverted: it is a work-list of faces prioritized
* by the shortest path found so far to the face.
*/
- /* regular dijkstra shortest path, but over faces instead of vertices */
+ /* Regular dijkstra shortest path, but over faces instead of vertices. */
heap = BLI_heapsimple_new();
BLI_heapsimple_insert(heap, 0.0f, f_src);
cost[BM_elem_index_get(f_src)] = 0.0f;
@@ -567,3 +580,4 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm,
return path;
}
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.c b/source/blender/bmesh/tools/bmesh_path_uv.c
new file mode 100644
index 00000000000..57a70645187
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_path_uv.c
@@ -0,0 +1,433 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * Find a path between 2 elements in UV space.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_heap_simple.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "bmesh.h"
+#include "bmesh_path_uv.h" /* own include */
+#include "intern/bmesh_query.h"
+#include "intern/bmesh_query_uv.h"
+
+#define COST_INIT_MAX FLT_MAX
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Helpers
+ * \{ */
+
+/**
+ * Use skip options when we want to start measuring from a boundary.
+ *
+ * See #step_cost_3_v3_ex in bmesh_path.c which follows the same logic.
+ */
+static float step_cost_3_v2_ex(
+ const float v1[2], const float v2[2], const float v3[2], bool skip_12, bool skip_23)
+{
+ float d1[2], d2[2];
+
+ /* The cost is based on the simple sum of the length of the two edges. */
+ sub_v2_v2v2(d1, v2, v1);
+ sub_v2_v2v2(d2, v3, v2);
+ const float cost_12 = normalize_v2(d1);
+ const float cost_23 = normalize_v2(d2);
+ const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23));
+
+ /* But is biased to give higher values to sharp turns, so that it will take paths with
+ * fewer "turns" when selecting between equal-weighted paths between the two edges. */
+ return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v2v2(d1, d2)))));
+}
+
+static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2],
+ const float v2[2],
+ const float v3[2])
+{
+ return step_cost_3_v2_ex(v1, v2, v3, false, false);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BM_mesh_calc_path_uv_vert
+ * \{ */
+
+static void looptag_add_adjacent_uv(HeapSimple *heap,
+ BMLoop *l_a,
+ BMLoop **loops_prev,
+ float *cost,
+ const struct BMCalcPathUVParams *params)
+{
+ BLI_assert(params->aspect_y != 0.0f);
+ const uint cd_loop_uv_offset = params->cd_loop_uv_offset;
+ const int l_a_index = BM_elem_index_get(l_a);
+ const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
+ const float uv_a[2] = {luv_a->uv[0], luv_a->uv[1] / params->aspect_y};
+
+ {
+ BMIter liter;
+ BMLoop *l;
+ /* Loop over faces of face, but do so by first looping over loops. */
+ BM_ITER_ELEM (l, &liter, l_a->v, BM_LOOPS_OF_VERT) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_a->uv, luv->uv)) {
+ /* 'l_a' is already tagged, tag all adjacent. */
+ BM_elem_flag_enable(l, BM_ELEM_TAG);
+ BMLoop *l_b = l->next;
+ do {
+ if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
+ const MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
+ const float uv_b[2] = {luv_b->uv[0], luv_b->uv[1] / params->aspect_y};
+ /* We know 'l_b' is not visited, check it out! */
+ const int l_b_index = BM_elem_index_get(l_b);
+ const float cost_cut = params->use_topology_distance ? 1.0f : len_v2v2(uv_a, uv_b);
+ const float cost_new = cost[l_a_index] + cost_cut;
+
+ if (cost[l_b_index] > cost_new) {
+ cost[l_b_index] = cost_new;
+ loops_prev[l_b_index] = l_a;
+ BLI_heapsimple_insert(heap, cost_new, l_b);
+ }
+ }
+ /* This means we only step onto `l->prev` & `l->next`. */
+ if (params->use_step_face == false) {
+ if (l_b == l->next) {
+ l_b = l->prev->prev;
+ }
+ }
+ } while ((l_b = l_b->next) != l);
+ }
+ }
+ }
+}
+
+struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMLoop *, void *),
+ void *user_data)
+{
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited edges */
+ BMIter viter;
+ HeapSimple *heap;
+ float *cost;
+ BMLoop **loops_prev;
+ int i = 0, totloop;
+ BMFace *f;
+
+ /* Note, would pass BM_EDGE except we are looping over all faces anyway. */
+ // BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG
+
+ BM_ITER_MESH (f, &viter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter = l_first;
+ do {
+ BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data));
+ BM_elem_index_set(l_iter, i); /* set_inline */
+ i += 1;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~BM_LOOP;
+
+ /* Allocate. */
+ totloop = bm->totloop;
+ loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totloop, __func__);
+
+ copy_vn_fl(cost, totloop, COST_INIT_MAX);
+
+ /* Regular dijkstra shortest path, but over UV loops instead of vertices. */
+ heap = BLI_heapsimple_new();
+ BLI_heapsimple_insert(heap, 0.0f, l_src);
+ cost[BM_elem_index_get(l_src)] = 0.0f;
+
+ BMLoop *l = NULL;
+ while (!BLI_heapsimple_is_empty(heap)) {
+ l = BLI_heapsimple_pop_min(heap);
+
+ if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) {
+ break;
+ }
+
+ if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
+ /* Adjacent loops are tagged while stepping to avoid 2x loops. */
+ BM_elem_flag_enable(l, BM_ELEM_TAG);
+ looptag_add_adjacent_uv(heap, l, loops_prev, cost, params);
+ }
+ }
+
+ if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) {
+ do {
+ BLI_linklist_prepend(&path, l);
+ } while ((l = loops_prev[BM_elem_index_get(l)]));
+ }
+
+ MEM_freeN(loops_prev);
+ MEM_freeN(cost);
+ BLI_heapsimple_free(heap, NULL);
+
+ return path;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BM_mesh_calc_path_uv_edge
+ * \{ */
+/* TODO(campbell): not very urgent, since the operator fakes this using vertex path. */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BM_mesh_calc_path_uv_face
+ * \{ */
+
+static float facetag_cut_cost_edge_uv(BMFace *f_a,
+ BMFace *f_b,
+ BMLoop *l_edge,
+ const void *const f_endpoints[2],
+ const float aspect_v2[2],
+ const int cd_loop_uv_offset)
+{
+ float f_a_cent[2];
+ float f_b_cent[2];
+ float e_cent[2];
+
+ BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent);
+ BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent);
+
+ const float *co_v1 = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_edge, cd_loop_uv_offset))->uv;
+ const float *co_v2 =
+ ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_edge->next, cd_loop_uv_offset))->uv;
+
+#if 0
+ mid_v2_v2v2(e_cent, co_v1, co_v2);
+#else
+ /* For triangle fans it gives better results to pick a point on the edge. */
+ {
+ float ix_e[2];
+ isect_line_line_v2_point(co_v1, co_v2, f_a_cent, f_b_cent, ix_e);
+ const float factor = line_point_factor_v2(ix_e, co_v1, co_v2);
+ if (factor < 0.0f) {
+ copy_v2_v2(e_cent, co_v1);
+ }
+ else if (factor > 1.0f) {
+ copy_v2_v2(e_cent, co_v2);
+ }
+ else {
+ copy_v2_v2(e_cent, ix_e);
+ }
+ }
+#endif
+
+ /* Apply aspect before calculating cost. */
+ mul_v2_v2(f_a_cent, aspect_v2);
+ mul_v2_v2(f_b_cent, aspect_v2);
+ mul_v2_v2(e_cent, aspect_v2);
+
+ return step_cost_3_v2_ex(
+ f_a_cent, e_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
+}
+
+static float facetag_cut_cost_vert_uv(BMFace *f_a,
+ BMFace *f_b,
+ BMLoop *l_vert,
+ const void *const f_endpoints[2],
+ const float aspect_v2[2],
+ const int cd_loop_uv_offset)
+{
+ float f_a_cent[2];
+ float f_b_cent[2];
+ float v_cent[2];
+
+ BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent);
+ BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent);
+
+ copy_v2_v2(v_cent, ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_vert, cd_loop_uv_offset))->uv);
+
+ mul_v2_v2(f_a_cent, aspect_v2);
+ mul_v2_v2(f_b_cent, aspect_v2);
+ mul_v2_v2(v_cent, aspect_v2);
+
+ return step_cost_3_v2_ex(
+ f_a_cent, v_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
+}
+
+static void facetag_add_adjacent_uv(HeapSimple *heap,
+ BMFace *f_a,
+ BMFace **faces_prev,
+ float *cost,
+ const void *const f_endpoints[2],
+ const float aspect_v2[2],
+ const struct BMCalcPathUVParams *params)
+{
+ const uint cd_loop_uv_offset = params->cd_loop_uv_offset;
+ const int f_a_index = BM_elem_index_get(f_a);
+
+ /* Loop over faces of face, but do so by first looping over loops. */
+ {
+ BMIter liter;
+ BMLoop *l_a;
+
+ BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
+ BMLoop *l_first, *l_iter;
+
+ /* Check there is an adjacent face to loop over. */
+ if (l_a != l_a->radial_next) {
+ l_iter = l_first = l_a->radial_next;
+ do {
+ BMFace *f_b = l_iter->f;
+ if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
+ if (BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) {
+ /* We know 'f_b' is not visited, check it out! */
+ const int f_b_index = BM_elem_index_get(f_b);
+ const float cost_cut =
+ params->use_topology_distance ?
+ 1.0f :
+ facetag_cut_cost_edge_uv(
+ f_a, f_b, l_iter, f_endpoints, aspect_v2, cd_loop_uv_offset);
+ const float cost_new = cost[f_a_index] + cost_cut;
+
+ if (cost[f_b_index] > cost_new) {
+ cost[f_b_index] = cost_new;
+ faces_prev[f_b_index] = f_a;
+ BLI_heapsimple_insert(heap, cost_new, f_b);
+ }
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ }
+ }
+
+ if (params->use_step_face) {
+ BMIter liter;
+ BMLoop *l_a;
+
+ BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
+ BMIter litersub;
+ BMLoop *l_b;
+ BM_ITER_ELEM (l_b, &litersub, l_a->v, BM_LOOPS_OF_VERT) {
+ if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
+ BMFace *f_b = l_b->f;
+ if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
+ if (BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) {
+ /* We know 'f_b' is not visited, check it out! */
+ const int f_b_index = BM_elem_index_get(f_b);
+ const float cost_cut =
+ params->use_topology_distance ?
+ 1.0f :
+ facetag_cut_cost_vert_uv(
+ f_a, f_b, l_a, f_endpoints, aspect_v2, cd_loop_uv_offset);
+ const float cost_new = cost[f_a_index] + cost_cut;
+
+ if (cost[f_b_index] > cost_new) {
+ cost[f_b_index] = cost_new;
+ faces_prev[f_b_index] = f_a;
+ BLI_heapsimple_insert(heap, cost_new, f_b);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm,
+ BMFace *f_src,
+ BMFace *f_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMFace *, void *),
+ void *user_data)
+{
+ const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y};
+ LinkNode *path = NULL;
+ /* BM_ELEM_TAG flag is used to store visited edges */
+ BMIter fiter;
+ HeapSimple *heap;
+ float *cost;
+ BMFace **faces_prev;
+ int i = 0, totface;
+
+ /* Start measuring face path at the face edges, ignoring their centers. */
+ const void *const f_endpoints[2] = {f_src, f_dst};
+
+ /* Note, would pass BM_EDGE except we are looping over all faces anyway. */
+ // BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG
+
+ {
+ BMFace *f;
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_set(f, BM_ELEM_TAG, !filter_fn(f, user_data));
+ BM_elem_index_set(f, i); /* set_inline */
+ i += 1;
+ }
+ bm->elem_index_dirty &= ~BM_FACE;
+ }
+
+ /* Allocate. */
+ totface = bm->totface;
+ faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totface, __func__);
+
+ copy_vn_fl(cost, totface, COST_INIT_MAX);
+
+ /* Regular dijkstra shortest path, but over UV faces instead of vertices. */
+ heap = BLI_heapsimple_new();
+ BLI_heapsimple_insert(heap, 0.0f, f_src);
+ cost[BM_elem_index_get(f_src)] = 0.0f;
+
+ BMFace *f = NULL;
+ while (!BLI_heapsimple_is_empty(heap)) {
+ f = BLI_heapsimple_pop_min(heap);
+
+ if (f == f_dst) {
+ break;
+ }
+
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ /* Adjacent loops are tagged while stepping to avoid 2x loops. */
+ BM_elem_flag_enable(f, BM_ELEM_TAG);
+ facetag_add_adjacent_uv(heap, f, faces_prev, cost, f_endpoints, aspect_v2, params);
+ }
+ }
+
+ if (f == f_dst) {
+ do {
+ BLI_linklist_prepend(&path, f);
+ } while ((f = faces_prev[BM_elem_index_get(f)]));
+ }
+
+ MEM_freeN(faces_prev);
+ MEM_freeN(cost);
+ BLI_heapsimple_free(heap, NULL);
+
+ return path;
+}
+
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.h b/source/blender/bmesh/tools/bmesh_path_uv.h
new file mode 100644
index 00000000000..c7c5768f7d0
--- /dev/null
+++ b/source/blender/bmesh/tools/bmesh_path_uv.h
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BMESH_PATH_UV_H__
+#define __BMESH_PATH_UV_H__
+
+/** \file
+ * \ingroup bmesh
+ */
+
+struct BMCalcPathUVParams {
+ uint use_topology_distance : 1;
+ uint use_step_face : 1;
+ uint cd_loop_uv_offset;
+ float aspect_y;
+};
+
+struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMLoop *, void *),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3, 5);
+
+struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm,
+ BMFace *f_src,
+ BMFace *f_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMFace *, void *),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3, 5);
+
+#endif /* __BMESH_PATH_UV_H__ */
diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c
index 8de23b696bf..d222ea214c4 100644
--- a/source/blender/bmesh/tools/bmesh_region_match.c
+++ b/source/blender/bmesh/tools/bmesh_region_match.c
@@ -1299,7 +1299,9 @@ static UUIDFashMatch *bm_vert_fasthash_create(BMesh *bm, const uint depth)
return id_curr;
}
-static void bm_vert_fasthash_edge_order(UUIDFashMatch *fm, const BMEdge *e, UUIDFashMatch e_fm[2])
+static void bm_vert_fasthash_edge_order(const UUIDFashMatch *fm,
+ const BMEdge *e,
+ UUIDFashMatch e_fm[2])
{
e_fm[0] = fm[BM_elem_index_get(e->v1)];
e_fm[1] = fm[BM_elem_index_get(e->v2)];