patch [#28518] BMesh: fix 28491 (implement edge tag shortest path)

from Andrew Wiggin (ender79)
This commit is contained in:
Campbell Barton
2011-09-06 03:32:58 +00:00
parent 6d4b46d96f
commit e351082114
3 changed files with 271 additions and 256 deletions

View File

@@ -56,6 +56,7 @@ BMEditMesh_mods.c, UI level access, no geometry changes
#include "BLI_rand.h"
#include "BLI_array.h"
#include "BLI_smallhash.h"
#include "BLI_heap.h"
#include "BKE_context.h"
#include "BKE_displist.h"
@@ -1053,15 +1054,267 @@ void MESH_OT_loop_select(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "");
}
/* ******************* edgetag_shortest_path and helpers ****************** */
static float edgetag_cut_cost(BMEditMesh *UNUSED(em), BMEdge *e1, BMEdge *e2, BMVert* v)
{
BMVert *v1 = (e1->v1 == v) ? e1->v2 : e1->v1;
BMVert *v2 = (e2->v1 == v) ? e2->v2 : e2->v1;
float cost, d1[3], d2[3];
/*The cost is based on the simple sum of the length of the two edgees...*/
sub_v3_v3v3(d1, v->co, v1->co);
sub_v3_v3v3(d2, v2->co, v->co);
cost = len_v3(d1);
cost += len_v3(d2);
/*...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*/
cost = cost + 0.5f*cost*(2.0f - sqrt(fabs(dot_v3v3(d1, d2))));
return cost;
}
static void edgetag_add_adjacent(BMEditMesh *em, SmallHash *visithash, Heap *heap, int mednum, int vertnum,
int *nedges, int *edges, int *prevedge, float *cost)
{
BMEdge *e1 = EDBM_get_edge_for_index(em, mednum);
BMVert *v = EDBM_get_vert_for_index(em, vertnum);
int startadj, endadj = nedges[vertnum+1];
for (startadj = nedges[vertnum]; startadj < endadj; startadj++) {
int adjnum = edges[startadj];
BMEdge *e2 = EDBM_get_edge_for_index(em, adjnum);
float newcost;
float cutcost;
if (BLI_smallhash_haskey(visithash, (uintptr_t)e2))
continue;
cutcost = edgetag_cut_cost(em, e1, e2, v);
newcost = cost[mednum] + cutcost;
if (cost[adjnum] > newcost) {
cost[adjnum] = newcost;
prevedge[adjnum] = mednum;
BLI_heap_insert(heap, newcost, SET_INT_IN_POINTER(adjnum));
}
}
}
static void edgetag_context_set(BMEditMesh *em, Scene *scene, BMEdge *e, int val)
{
switch (scene->toolsettings->edge_mode) {
case EDGE_MODE_SELECT:
BM_Select(em->bm, e, val);
break;
case EDGE_MODE_TAG_SEAM:
if (val) {BM_SetHFlag(e, BM_SEAM);}
else {BM_ClearHFlag(e, BM_SEAM);}
break;
case EDGE_MODE_TAG_SHARP:
if (val) {BM_SetHFlag(e, BM_SEAM);}
else {BM_ClearHFlag(e, BM_SEAM);}
break;
case EDGE_MODE_TAG_CREASE:
{
float *crease = CustomData_bmesh_get(&em->bm->edata, e->head.data, CD_CREASE);
if (val) {*crease = 1.0f;}
else {*crease = 0.0f;}
break;
}
case EDGE_MODE_TAG_BEVEL:
{
float *bweight = CustomData_bmesh_get(&em->bm->edata, e->head.data, CD_BWEIGHT);
if (val) {*bweight = 1.0f;}
else {*bweight = 0.0f;}
break;
}
}
}
static float bm_cdata_get_single_float(BMesh *UNUSED(bm), CustomData *cdata, void *element, int type)
{
BMHeader *ele = element;
float *f;
if (!CustomData_has_layer(cdata, type))
return 0.0f;
f = CustomData_bmesh_get(cdata, ele->data, type);
return *f;
}
static int edgetag_context_check(Scene *scene, BMEditMesh *em, BMEdge *e)
{
switch (scene->toolsettings->edge_mode) {
case EDGE_MODE_SELECT:
return BM_TestHFlag(e, BM_SELECT) ? 1 : 0;
case EDGE_MODE_TAG_SEAM:
return BM_TestHFlag(e, BM_SEAM);
case EDGE_MODE_TAG_SHARP:
return BM_TestHFlag(e, BM_SHARP);
case EDGE_MODE_TAG_CREASE:
return bm_cdata_get_single_float(em->bm, &em->bm->edata, e, CD_CREASE) ? 1 : 0;
case EDGE_MODE_TAG_BEVEL:
return bm_cdata_get_single_float(em->bm, &em->bm->edata, e, CD_BWEIGHT) ? 1 : 0;
}
return 0;
}
static int edgetag_shortest_path(Scene *scene, BMEditMesh *em, BMEdge *source, BMEdge *target)
{
BMEdge *e;
BMVert *v;
BMIter iter;
Heap *heap;
SmallHash visithash;
float *cost;
int i, totvert=0, totedge=0, *nedges, *edges, *prevedge, mednum = -1, nedgeswap = 0;
int targetnum;
BLI_smallhash_init(&visithash);
/* we need the vert */
BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
BM_SetIndex(v, totvert);
totvert++;
}
BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
e->head.flags[0].f = 0;
if (BM_TestHFlag(e, BM_HIDDEN)) {
BLI_smallhash_insert(&visithash, (uintptr_t)e, NULL);
}
BM_SetIndex(e, totedge);
totedge++;
}
/* alloc */
nedges = MEM_callocN(sizeof(*nedges)*totvert+1, "SeamPathNEdges");
edges = MEM_mallocN(sizeof(*edges)*totedge*2, "SeamPathEdges");
prevedge = MEM_mallocN(sizeof(*prevedge)*totedge, "SeamPathPrevious");
cost = MEM_mallocN(sizeof(*cost)*totedge, "SeamPathCost");
/* count edges, compute adjacent edges offsets and fill adjacent */
BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
nedges[BM_GetIndex(e->v1)+1]++;
nedges[BM_GetIndex(e->v2)+1]++;
}
for (i = 1; i < totvert; i++) {
int newswap = nedges[i+1];
nedges[i+1] = nedgeswap + nedges[i];
nedgeswap = newswap;
}
nedges[0] = nedges[1] = 0;
i = 0;
BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
edges[nedges[BM_GetIndex(e->v1)+1]++] = i;
edges[nedges[BM_GetIndex(e->v2)+1]++] = i;
cost[i] = 1e20f;
prevedge[i] = -1;
i++;
}
/*
* Arrays are now filled as follows:
*
* nedges[n] = sum of the # of edges incident to all vertices numbered 0 thru n-1
* edges[edges[n]..edges[n-1]] = the indices of of the edges incident to vertex n
*
* As the search continues, prevedge[n] will be the previous edge on the shortest
* path found so far to edge n. The visitedhash will of course contain entries
* for edges that have been 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 arry, but inverted: it is a worklist of edges prioritized
* by the shortest path found so far to the edge.
*/
for (i = 0; i < totvert; i++) {
int start = nedges[i], end = nedges[i+1], cur;
for (cur = start; cur < end; cur++) {
BMEdge* e = EDBM_get_edge_for_index(em, edges[cur]);
}
}
/* regular dijkstra shortest path, but over edges instead of vertices */
heap = BLI_heap_new();
BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(BM_GetIndex(source)));
cost[BM_GetIndex(source)] = 0.0f;
EDBM_init_index_arrays(em, 1, 1, 0);
targetnum = BM_GetIndex(target);
while (!BLI_heap_empty(heap)) {
mednum = GET_INT_FROM_POINTER(BLI_heap_popmin(heap));
e = EDBM_get_edge_for_index(em, mednum);
if (mednum == targetnum)
break;
if (BLI_smallhash_haskey(&visithash, (uintptr_t)e))
continue;
BLI_smallhash_insert(&visithash, (uintptr_t)e, NULL);
edgetag_add_adjacent(em, &visithash, heap, mednum, BM_GetIndex(e->v1), nedges, edges, prevedge, cost);
edgetag_add_adjacent(em, &visithash, heap, mednum, BM_GetIndex(e->v2), nedges, edges, prevedge, cost);
}
if (mednum == targetnum) {
short allseams = 1;
/*Check whether the path is already completely tagged.
if it is, the tags will be cleared instead of set.*/
mednum = targetnum;
do {
e = EDBM_get_edge_for_index(em, mednum);
if (!edgetag_context_check(scene, em, e)) {
allseams = 0;
break;
}
mednum = prevedge[mednum];
} while (mednum != BM_GetIndex(source));
/*Follow path back and source and add or remove tags*/
mednum = targetnum;
do {
e = EDBM_get_edge_for_index(em, mednum);
if (allseams)
edgetag_context_set(em, scene, e, 0);
else
edgetag_context_set(em, scene, e, 1);
mednum = prevedge[mednum];
} while (mednum != -1);
}
EDBM_free_index_arrays(em);
MEM_freeN(nedges);
MEM_freeN(edges);
MEM_freeN(prevedge);
MEM_freeN(cost);
BLI_heap_free(heap, NULL);
BLI_smallhash_release(&visithash);
return 1;
}
/* ******************* mesh shortest path select, uses prev-selected edge ****************** */
/* since you want to create paths with multiple selects, it doesn't have extend option */
static void mouse_mesh_shortest_path(bContext *UNUSED(C), int UNUSED(mval[2]))
static void mouse_mesh_shortest_path(bContext *C, int mval[2])
{
#if 0 //BMESH_TODO
Object *ob = CTX_data_edit_object(C);
ViewContext vc;
BMEditMesh *em;
BMEdge *eed;
BMEdge *e;
int dist= 50;
em_setup_viewcontext(C, &vc);
@@ -1069,37 +1322,37 @@ static void mouse_mesh_shortest_path(bContext *UNUSED(C), int UNUSED(mval[2]))
vc.mval[1]= mval[1];
em= vc.em;
eed= findnearestedge(&vc, &dist);
if(eed) {
e= EDBM_findnearestedge(&vc, &dist);
if(e) {
Mesh *me= vc.obedit->data;
int path = 0;
if (em->bm->selected.last) {
EditSelection *ese = em->bm->selected.last;
if(ese && ese->type == BMEdge) {
BMEdge *eed_act;
eed_act = (BMEdge*)ese->data;
if (eed_act != eed) {
if (edgetag_shortest_path(vc.scene, em, eed_act, eed)) {
EM_remove_selection(em, eed_act, BMEdge);
if(ese && ese->type == BM_EDGE) {
BMEdge *e_act;
e_act = (BMEdge*)ese->data;
if (e_act != e) {
if (edgetag_shortest_path(vc.scene, em, e_act, e)) {
EDBM_remove_selection(em, e_act);
path = 1;
}
}
}
}
if (path==0) {
int act = (edgetag_context_check(vc.scene, eed)==0);
edgetag_context_set(em, vc.scene, eed, act); /* switch the edge option */
int act = (edgetag_context_check(vc.scene, em, e)==0);
edgetag_context_set(em, vc.scene, e, act); /* switch the edge option */
}
EM_selectmode_flush(em);
EDBM_selectmode_flush(em);
/* even if this is selected it may not be in the selection list */
if(edgetag_context_check(vc.scene, eed)==0)
EDBM_remove_selection(em, eed);
if(edgetag_context_check(vc.scene, em, e)==0)
EDBM_remove_selection(em, e);
else
EDBM_store_selection(em, eed);
EDBM_store_selection(em, e);
/* force drawmode for mesh */
switch (CTX_data_tool_settings(C)->edge_mode) {
@@ -1121,7 +1374,6 @@ static void mouse_mesh_shortest_path(bContext *UNUSED(C), int UNUSED(mval[2]))
DAG_id_tag_update(ob->data, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
}
#endif
}

View File

@@ -39,10 +39,8 @@
#include "BLI_utildefines.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_heap.h"
#include "BLI_edgehash.h"
#include "BLI_editVert.h"
#include "BLI_utildefines.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
@@ -437,239 +435,6 @@ int paintface_minmax(Object *ob, float *min, float *max)
return ok;
}
/* ******************** edge loop shortest path ********************* */
#define ME_SEAM_DONE 2 /* reuse this flag */
static float edgetag_cut_cost(BMEditMesh *em, int e1, int e2, int vert)
{
BMVert *v = EDBM_get_vert_for_index(em, vert);
BMEdge *eed1 = EDBM_get_edge_for_index(em, e1), *eed2 = EDBM_get_edge_for_index(em, e2);
BMVert *v1 = EDBM_get_vert_for_index(em, (BM_GetIndex(eed1->v1) == vert)? BM_GetIndex(eed1->v2): vert );
BMVert *v2 = EDBM_get_vert_for_index(em, (BM_GetIndex(eed2->v1) == vert)? BM_GetIndex(eed2->v2): vert );
float cost, d1[3], d2[3];
cost = len_v3v3(v1->co, v->co);
cost += len_v3v3(v->co, v2->co);
sub_v3_v3v3(d1, v->co, v1->co);
sub_v3_v3v3(d2, v2->co, v->co);
cost = cost + 0.5f*cost*(2.0f - fabsf(d1[0]*d2[0] + d1[1]*d2[1] + d1[2]*d2[2]));
return cost;
}
static void edgetag_add_adjacent(BMEditMesh *em, Heap *heap, int mednum, int vertnum,
int *nedges, int *edges, int *prevedge, float *cost)
{
int startadj, endadj = nedges[vertnum+1];
for (startadj = nedges[vertnum]; startadj < endadj; startadj++) {
int adjnum = edges[startadj];
BMEdge *eedadj = EDBM_get_edge_for_index(em, adjnum);
float newcost;
if (eedadj->head.flags[0].f & ME_SEAM_DONE)
continue;
newcost = cost[mednum] + edgetag_cut_cost(em, mednum, adjnum, vertnum);
if (cost[adjnum] > newcost) {
cost[adjnum] = newcost;
prevedge[adjnum] = mednum;
BLI_heap_insert(heap, newcost, SET_INT_IN_POINTER(adjnum));
}
}
}
void edgetag_context_set(BMEditMesh *em, Scene *scene, BMEdge *eed, int val)
{
switch (scene->toolsettings->edge_mode) {
case EDGE_MODE_SELECT:
BM_Select(em->bm, eed, val);
break;
case EDGE_MODE_TAG_SEAM:
if (val) {BM_SetHFlag(eed, BM_SEAM);}
else {BM_ClearHFlag(eed, BM_SEAM);}
break;
case EDGE_MODE_TAG_SHARP:
if (val) {BM_SetHFlag(eed, BM_SEAM);}
else {BM_ClearHFlag(eed, BM_SEAM);}
break;
case EDGE_MODE_TAG_CREASE:
{
float *crease = CustomData_bmesh_get(&em->bm->edata, eed->head.data, CD_CREASE);
if (val) {*crease = 1.0f;}
else {*crease = 0.0f;}
break;
}
case EDGE_MODE_TAG_BEVEL:
{
float *bweight = CustomData_bmesh_get(&em->bm->edata, eed->head.data, CD_BWEIGHT);
if (val) {*bweight = 1.0f;}
else {*bweight = 0.0f;}
break;
}
}
}
static float bm_cdata_get_single_float(BMesh *UNUSED(bm), CustomData *cdata, void *element, int type)
{
BMHeader *ele = element;
float *f;
if (!CustomData_has_layer(cdata, type))
return 0.0f;
f = CustomData_bmesh_get(cdata, ele->data, type);
return *f;
}
int edgetag_context_check(Scene *scene, BMEditMesh *em, BMEdge *eed)
{
switch (scene->toolsettings->edge_mode) {
case EDGE_MODE_SELECT:
return BM_TestHFlag(eed, BM_SELECT) ? 1 : 0;
case EDGE_MODE_TAG_SEAM:
return BM_TestHFlag(eed, BM_SEAM);
case EDGE_MODE_TAG_SHARP:
return BM_TestHFlag(eed, BM_SHARP);
case EDGE_MODE_TAG_CREASE:
return bm_cdata_get_single_float(em->bm, &em->bm->edata, eed, CD_CREASE) ? 1 : 0;
case EDGE_MODE_TAG_BEVEL:
return bm_cdata_get_single_float(em->bm, &em->bm->edata, eed, CD_BWEIGHT) ? 1 : 0;
}
return 0;
}
int edgetag_shortest_path(Scene *scene, BMEditMesh *em, BMEdge *source, BMEdge *target)
{
BMEdge *eed;
BMVert *ev;
BMIter iter;
Heap *heap;
float *cost;
int a, totvert=0, totedge=0, *nedges, *edges, *prevedge, mednum = -1, nedgeswap = 0;
/* we need the vert */
BM_ITER(ev, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
BM_SetIndex(ev, totvert);
totvert++;
}
BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
eed->head.flags[0].f = 0;
if (BM_TestHFlag(eed, BM_SELECT)) {
eed->head.flags[0].f |= ME_SEAM_DONE;
}
BM_SetIndex(eed, totedge);
totedge++;
}
/* alloc */
nedges = MEM_callocN(sizeof(*nedges)*totvert+1, "SeamPathNEdges");
edges = MEM_mallocN(sizeof(*edges)*totedge*2, "SeamPathEdges");
prevedge = MEM_mallocN(sizeof(*prevedge)*totedge, "SeamPathPrevious");
cost = MEM_mallocN(sizeof(*cost)*totedge, "SeamPathCost");
/* count edges, compute adjacent edges offsets and fill adjacent edges */
BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
nedges[BM_GetIndex(eed->v1)+1]++;
nedges[BM_GetIndex(eed->v2)+1]++;
}
for (a=1; a<totvert; a++) {
int newswap = nedges[a+1];
nedges[a+1] = nedgeswap + nedges[a];
nedgeswap = newswap;
}
nedges[0] = nedges[1] = 0;
BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
edges[nedges[BM_GetIndex(eed->v1)+1]++] = a;
edges[nedges[BM_GetIndex(eed->v2)+1]++] = a;
cost[a] = 1e20f;
prevedge[a] = -1;
a++;
}
/* regular dijkstra shortest path, but over edges instead of vertices */
heap = BLI_heap_new();
BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(BM_GetIndex(source)));
cost[BM_GetIndex(source)] = 0.0f;
EDBM_init_index_arrays(em, 1, 1, 0);
while (!BLI_heap_empty(heap)) {
mednum = GET_INT_FROM_POINTER(BLI_heap_popmin(heap));
eed = EDBM_get_edge_for_index(em, mednum);
if (mednum == BM_GetIndex(target))
break;
if (eed->head.flags[0].f & ME_SEAM_DONE)
continue;
eed->head.flags[0].f |= ME_SEAM_DONE;
edgetag_add_adjacent(em, heap, mednum, BM_GetIndex(eed->v1), nedges, edges, prevedge, cost);
edgetag_add_adjacent(em, heap, mednum, BM_GetIndex(eed->v2), nedges, edges, prevedge, cost);
}
MEM_freeN(nedges);
MEM_freeN(edges);
MEM_freeN(cost);
BLI_heap_free(heap, NULL);
BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
eed->head.flags[0].f &= ~ME_SEAM_DONE;
}
if (mednum != BM_GetIndex(target)) {
MEM_freeN(prevedge);
EDBM_free_index_arrays(em);
return 0;
}
/* follow path back to source and mark as seam */
if (mednum == BM_GetIndex(target)) {
short allseams = 1;
mednum = BM_GetIndex(target);
do {
eed = EDBM_get_edge_for_index(em, mednum);
if (!edgetag_context_check(scene, em, eed)) {
allseams = 0;
break;
}
mednum = prevedge[mednum];
} while (mednum != BM_GetIndex(source));
mednum = BM_GetIndex(target);
do {
eed = EDBM_get_edge_for_index(em, mednum);
if (allseams)
edgetag_context_set(em, scene, eed, 0);
else
edgetag_context_set(em, scene, eed, 1);
mednum = prevedge[mednum];
} while (mednum != -1);
}
MEM_freeN(prevedge);
EDBM_free_index_arrays(em);
return 1;
}
/* *************************************** */
#if 0
static void seam_edgehash_insert_face(EdgeHash *ehash, MPoly *mf, MLoop *loopstart)

View File

@@ -100,9 +100,7 @@ float labda_PdistVL2Dfl(const float v1[3], const float v2[3], const float v3[3])
/* ******************** editface.c */
int edgetag_context_check(Scene *scene, BMEditMesh *em, BMEdge *eed);
void edgetag_context_set(BMEditMesh *em, Scene *scene, BMEdge *eed, int val);
int edgetag_shortest_path(Scene *scene, BMEditMesh *em, BMEdge *source, BMEdge *target);
/* ******************* editmesh.c */