Got the walker API to work, for safely recursing the mesh.

Used it to implement the dissolve faces operation (previous
incarnation was just a debugging hack).  The code works by
creating one giant new face per region of faces.

The dissolve verts (xkey->collapse, heh need to rename it)
operator now invokes dissolve faces on the faces around verts.
This is less error-prone then a pure topological/euler based
solution.
This commit is contained in:
Joseph Eagar
2009-03-08 15:02:49 +00:00
parent 662230a14c
commit f23b4bc2a1
11 changed files with 397 additions and 148 deletions

View File

@@ -1,7 +1,7 @@
/**
* bmesh.h jan 2007
*
* BM API.
* BMesh API.
*
* $Id: BKE_bmesh.h,v 1.00 2007/01/17 17:42:01 Briggs Exp $
*
@@ -34,8 +34,8 @@
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef BM_H
#define BM_H
#ifndef BMESH_H
#define BMESH_H
#include "DNA_listBase.h"
#include "DNA_customdata_types.h"
@@ -217,4 +217,6 @@ struct EditMesh *bmesh_to_editmesh(BMesh *bm);
#include "bmesh_marking.h"
#include "bmesh_operators.h"
#include "bmesh_queries.h"
#endif
#include "bmesh_walkers.h"
#endif /* BMESH_H */

View File

@@ -26,6 +26,7 @@
#define BM_EDGES_OF_FACE 10
#define BM_LOOPS_OF_FACE 11
#define BM_LOOPS_OF_VERT 12
#define BM_LOOPS_OF_LOOP 13
/*Iterator Structure*/
typedef struct BMIter{

View File

@@ -146,6 +146,7 @@ void BMO_RaiseError(BMesh *bm, BMOperator *owner, int errcode, char *msg);
/*gets the topmost error from the stack.
returns error code or 0 if no error.*/
int BMO_GetError(BMesh *bm, char **msg, BMOperator **op);
int BMO_HasError(BMesh *bm);
/*same as geterror, only pops the error off the stack as well*/
int BMO_PopError(BMesh *bm, char **msg, BMOperator **op);
@@ -168,6 +169,8 @@ int BMO_CatchOpError(BMesh *bm, BMOperator *catchop, int errorcode, char **msg);
#define BMERR_DISSOLVEDISK_FAILED 2
#define BMERR_CONNECTVERT_FAILED 3
#define BMERR_WALKER_FAILED 4
#define BMERR_DISSOLVEFACES_FAILED 5
#define BMERR_DISSOLVEVERTS_FAILED 6
static char *bmop_error_messages[] = {
0,
@@ -175,6 +178,8 @@ static char *bmop_error_messages[] = {
"Could not dissolve vert",
"Could not connect verts",
"Could not traverse mesh",
"Could not dissolve faces",
"Could not dissolve vertices",
};
/*------------begin operator defines (see bmesh_opdefines.c too)------------*/

View File

@@ -32,4 +32,5 @@ float BM_Face_Angle(struct BMesh *bm, struct BMEdge *e);
int BM_Exist_Face_Overlaps(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface);
int BM_Edge_Share_Faces(struct BMEdge *e1, struct BMEdge *e2);
int BM_Validate_Face(BMesh *bm, BMFace *face, FILE *err);
int BM_FacesAroundEdge(BMEdge *e);
#endif

View File

@@ -8,26 +8,28 @@
*/
/*Walkers*/
typedef struct BMWalker{
typedef struct BMWalker {
BLI_mempool *stack;
BMesh *bm;
void *currentstate;
void *(*begin) (struct BMWalker *walker, void *start);
void (*begin) (struct BMWalker *walker, void *start);
void *(*yield)(struct BMWalker *walker);
void *(*step) (struct BMWalker *walker);
int restrictflag;
GHash *visithash;
}BMWalker;
} BMWalker;
void BMWalker_Init(struct BMWalker *walker,BMesh *bm,int type, int searchmask);
void *BMWalker_Step(struct BMWalker *walker);
void BMWalker_End(struct BMWalker *walker);
void BMW_Init(struct BMWalker *walker,BMesh *bm,int type, int searchmask);
void *BMW_Begin(BMWalker *walker, void *start);
void *BMW_Step(struct BMWalker *walker);
void BMW_End(struct BMWalker *walker);
#define BMW_SHELL 0
/*#define BMW_LOOP 1
#define BMW_RING 2
#define BMW_UVISLANDS 3*/
#define BMW_ISLANDBOUND 1
#define BMW_MAXWALKERS 2
#define BMW_ISLAND 2
#define BMW_MAXWALKERS 3
#endif

View File

@@ -317,6 +317,8 @@ BMFace *bmesh_mf(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **elist, int len)
if(edok != (l->e->head.eflag2 + 1)) bmesh_error();
}
}
for(i=0;i<len;i++) elist[i]->head.eflag1=elist[i]->head.eflag2 = 0;
return f;
}

View File

@@ -182,6 +182,30 @@ static void *face_of_vert_step(BMIter *iter)
return NULL;
}
static void loops_of_loop_begin(BMIter *iter)
{
BMLoop *l;
l = iter->ldata;
/*note sure why this sets ldata. . .*/
init_iterator(iter);
iter->firstloop = l;
iter->nextloop = bmesh_radial_nextloop(iter->firstloop);
}
static void *loops_of_loop_step(BMIter *iter)
{
BMLoop *current = iter->nextloop;
if(iter->nextloop) iter->nextloop = bmesh_radial_nextloop(iter->nextloop);
if(iter->nextloop == iter->firstloop) iter->nextloop = NULL;
if(current) return current;
return NULL;
}
/*
* FACE OF EDGE CALLBACKS
*
@@ -384,6 +408,11 @@ void *BMIter_New(BMIter *iter, BMesh *bm, int type, void *data)
iter->step = loop_of_vert_step;
iter->vdata = data;
break;
case BM_LOOPS_OF_LOOP:
iter->begin = loops_of_loop_begin;
iter->step = loops_of_loop_step;
iter->ldata = data;
break;
default:
break;
}

View File

@@ -809,6 +809,11 @@ void BMO_RaiseError(BMesh *bm, BMOperator *owner, int errcode, char *msg)
BLI_addhead(&bm->errorstack, err);
}
int BMO_HasError(BMesh *bm)
{
return bm->errorstack.first != NULL;
}
/*returns error code or 0 if no error*/
int BMO_GetError(BMesh *bm, char **msg, BMOperator **op)
{

View File

@@ -62,12 +62,19 @@ BMLoop *BM_OtherFaceLoop(BMEdge *e, BMFace *f, BMVert *v)
int found = 0;
do {
if (l->v == v) break;
if (l->e == e) break;
found = 1;
l = l->head.next;
} while (l != f->loopbase);
return l->head.prev;
return l->v == v ? l->head.prev : l->head.next;
}
int BM_FacesAroundEdge(BMEdge *e)
{
if (!e->loop) return 0;
return bmesh_cycle_length(&(e->loop->radial));
}
/*

View File

@@ -11,13 +11,20 @@
- joeedh -
design notes:
* walkers should use tool flags, not header flags
* walkers now use ghash rather then stealing flags.
ghash can be rewritten to be faster if necassary.
* walkers should always raise BMERR_WALKER_FAILED,
with a custom error message. This message will
probably be replaced by operator-specific messages
in most cases.
original desing: walkers directly emulation recursive functions.
functions save their state onto a stack, and also push new states
to implement recursive or looping behaviour. generally only one
state push per call with a specific state is desired.
basic design pattern: the walker step function goes through it's
list of possible choices for recursion, and recurses (by pushing a new state)
using the first non-visited one. this choise is the flagged as visited using
the ghash. each time this happens, only one state is pushed.
* walkers use tool flags, not header flags
* walkers now use ghash for storing visited elements,
rather then stealing flags. ghash can be rewritten
to be faster if necassary, in the far future :) .
* tools should ALWAYS have necassary error handling
for if walkers fail.
*/
@@ -34,11 +41,16 @@ typedef struct shellWalker{
typedef struct islandboundWalker {
struct islandboundWalker *prev;
BMEdge *base;
BMLoop *base;
BMVert *lastv;
BMEdge *curedge;
BMLoop *curloop;
} islandboundWalker;
typedef struct islandWalker {
struct islandWalker * prev;
BMFace *cur;
} islandWalker;
/* NOTE: this comment is out of date, update it - joeedh
* BMWalker - change this to use the filters functions.
*
@@ -46,34 +58,39 @@ typedef struct islandboundWalker {
* the surface of a mesh. An example of usage:
*
* BMEdge *edge;
* BMWalker *walker = BMWalker_create(BM_SHELLWALKER, BM_SELECT);
* BMWalker *walker = BMW_create(BM_SHELLWALKER, BM_SELECT);
* walker->begin(walker, vert);
* for(edge = BMWalker_walk(walker); edge; edge = bmeshWwalker_walk(walker)){
* for(edge = BMW_walk(walker); edge; edge = bmeshWwalker_walk(walker)){
* bmesh_select_edge(edge);
* }
* BMWalker_free(walker);
* BMW_free(walker);
*
* The above example creates a walker that walks over the surface a mesh by starting at
* a vertex and traveling across its edges to other vertices, and repeating the process
* over and over again until it has visited each vertex in the shell. An additional restriction
* is passed into the BMWalker_create function stating that we are only interested
* is passed into the BMW_create function stating that we are only interested
* in walking over edges that have been flagged with the bitmask 'BM_SELECT'.
*
*
*/
/*Forward declerations*/
static void *BMWalker_walk(struct BMWalker *walker);
static void BMWalker_popstate(struct BMWalker *walker);
static void BMWalker_pushstate(struct BMWalker *walker);
static void *BMW_walk(struct BMWalker *walker);
static void BMW_popstate(struct BMWalker *walker);
static void BMW_pushstate(struct BMWalker *walker);
static void *shellWalker_Begin(struct BMWalker *walker, void *data);
static void *shellWalker_Yield(struct BMWalker *walker);
static void *shellWalker_Step(struct BMWalker *walker);
static void shellWalker_begin(struct BMWalker *walker, void *data);
static void *shellWalker_yield(struct BMWalker *walker);
static void *shellWalker_step(struct BMWalker *walker);
static void *islandboundWalker_Begin(BMWalker *walker, void *data);
static void *islandboundWalker_Yield(BMWalker *walker);
static void *islandboundWalker_Step(BMWalker *walker);
static void islandboundWalker_begin(BMWalker *walker, void *data);
static void *islandboundWalker_yield(BMWalker *walker);
static void *islandboundWalker_step(BMWalker *walker);
static void islandWalker_begin(BMWalker *walker, void *data);
static void *islandWalker_yield(BMWalker *walker);
static void *islandWalker_step(BMWalker *walker);
struct shellWalker;
@@ -83,8 +100,14 @@ typedef struct bmesh_walkerGeneric{
} bmesh_walkerGeneric;
void *BMW_Begin(BMWalker *walker, void *start) {
walker->begin(walker, start);
return walker->step(walker);
}
/*
* BMWalker_CREATE
* BMW_CREATE
*
* Allocates and returns a new mesh walker of
* a given type. The elements visited are filtered
@@ -92,7 +115,7 @@ typedef struct bmesh_walkerGeneric{
*
*/
void BMWalker_Init(BMWalker *walker, BMesh *bm, int type, int searchmask)
void BMW_Init(BMWalker *walker, BMesh *bm, int type, int searchmask)
{
int size = 0;
@@ -103,17 +126,24 @@ void BMWalker_Init(BMWalker *walker, BMesh *bm, int type, int searchmask)
switch(type){
case BMW_SHELL:
walker->begin = shellWalker_Begin;
walker->step = shellWalker_Step;
walker->yield = shellWalker_Yield;
walker->begin = shellWalker_begin;
walker->step = shellWalker_step;
walker->yield = shellWalker_yield;
size = sizeof(shellWalker);
break;
case BMW_ISLANDBOUND:
walker->begin = islandboundWalker_Begin;
walker->step = islandboundWalker_Step;
walker->yield = islandboundWalker_Yield;
walker->begin = islandboundWalker_begin;
walker->step = islandboundWalker_step;
walker->yield = islandboundWalker_yield;
size = sizeof(islandboundWalker);
break;
case BMW_ISLAND:
walker->begin = islandWalker_begin;
walker->step = islandWalker_step;
walker->yield = islandWalker_yield;
size = sizeof(islandWalker);
break;
//case BMW_LOOP:
// walker->begin = loopwalker_Begin;
// walker->step = loopwalker_Step;
@@ -134,34 +164,35 @@ void BMWalker_Init(BMWalker *walker, BMesh *bm, int type, int searchmask)
}
/*
* BMWalker_End
* BMW_End
*
* Frees a walker's stack.
*
*/
void BMWalker_End(BMWalker *walker)
void BMW_End(BMWalker *walker)
{
BLI_mempool_destroy(walker->stack);
BLI_ghash_free(walker->visithash, NULL, NULL);
}
/*
* BMWalker_Step
* BMW_Step
*
*/
void *BMWalker_Step(BMWalker *walker)
void *BMW_Step(BMWalker *walker)
{
BMHeader *head;
head = BMWalker_walk(walker);
head = BMW_walk(walker);
return head;
}
/*
* BMWalker_WALK
* BMW_WALK
*
* Steps a mesh walker forward by one element
*
@@ -170,29 +201,27 @@ void *BMWalker_Step(BMWalker *walker)
*
*/
static void *BMWalker_walk(BMWalker *walker)
static void *BMW_walk(BMWalker *walker)
{
void *current = NULL;
while(walker->currentstate){
walker->step(walker);
current = walker->yield(walker);
current = walker->step(walker);
if(current) return current;
else BMWalker_popstate(walker);
else BMW_popstate(walker);
}
return NULL;
}
/*
* BMWalker_popstate
* BMW_popstate
*
* Pops the current walker state off the stack
* and makes the previous state current
*
*/
static void BMWalker_popstate(BMWalker *walker)
static void BMW_popstate(BMWalker *walker)
{
void *oldstate;
oldstate = walker->currentstate;
@@ -202,14 +231,14 @@ static void BMWalker_popstate(BMWalker *walker)
}
/*
* BMWalker_pushstate
* BMW_pushstate
*
* Pushes the current state down the stack and allocates
* a new one.
*
*/
static void BMWalker_pushstate(BMWalker *walker)
static void BMW_pushstate(BMWalker *walker)
{
bmesh_walkerGeneric *newstate;
newstate = BLI_mempool_alloc(walker->stack);
@@ -217,9 +246,9 @@ static void BMWalker_pushstate(BMWalker *walker)
walker->currentstate = newstate;
}
void BMWalker_reset(BMWalker *walker) {
void BMW_reset(BMWalker *walker) {
while (walker->currentstate) {
BMWalker_popstate(walker);
BMW_popstate(walker);
}
}
@@ -234,27 +263,28 @@ void BMWalker_reset(BMWalker *walker) {
*
*/
static void *shellWalker_Begin(BMWalker *walker, void *data){
static void shellWalker_begin(BMWalker *walker, void *data){
BMVert *v = data;
shellWalker *shellWalk = NULL;
BMWalker_pushstate(walker);
BMW_pushstate(walker);
shellWalk = walker->currentstate;
shellWalk->base = NULL;
shellWalk->curedge = NULL;
if(v->edge){
shellWalk->base = v;
shellWalk->curedge = v->edge;
}
return v->edge;
}
static void *shellWalker_Yield(BMWalker *walker)
static void *shellWalker_yield(BMWalker *walker)
{
shellWalker *shellWalk = walker->currentstate;
return shellWalk->curedge;
}
static void *shellWalker_Step(BMWalker *walker)
static void *shellWalker_step(BMWalker *walker)
{
BMEdge *curedge, *next = NULL;
BMVert *ov = NULL;
@@ -269,14 +299,19 @@ static void *shellWalker_Step(BMWalker *walker)
do{
if (!BLI_ghash_lookup(walker->visithash, curedge)) {
BLI_ghash_insert(walker->visithash, curedge, NULL);
if(walker->restrictflag && (!BMO_TestFlag(walker->bm, curedge, walker->restrictflag))) restrictpass = 0;
if(walker->restrictflag &&
(!BMO_TestFlag(walker->bm, curedge, walker->restrictflag)))
{
restrictpass = 0;
}
if(restrictpass) {
ov = BM_OtherEdgeVert(curedge, shellWalk->base);
/*save current state*/
shellWalk->curedge = curedge;
/*push a new state onto the stack*/
BMWalker_pushstate(walker);
BMW_pushstate(walker);
/*populate the new state*/
((shellWalker*)walker->currentstate)->base = ov;
@@ -286,12 +321,12 @@ static void *shellWalker_Step(BMWalker *walker)
next = curedge;
break;
}
curedge = bmesh_disk_nextedge(curedge, shellWalk->base);
}
curedge = bmesh_disk_nextedge(curedge, shellWalk->base);
}while(curedge != shellWalk->curedge);
shellWalk->current = next;
return shellWalk->current;
return next;
}
/* Island Boundary Walker:
@@ -305,82 +340,143 @@ static void *shellWalker_Step(BMWalker *walker)
*
*/
static void *islandboundWalker_Begin(BMWalker *walker, void *data){
BMEdge *e = data;
static void islandboundWalker_begin(BMWalker *walker, void *data){
BMLoop *l = data;
islandboundWalker *iwalk = NULL;
BMWalker_pushstate(walker);
BMW_pushstate(walker);
iwalk = walker->currentstate;
iwalk->base = iwalk->curedge = e;
return e;
iwalk->base = iwalk->curloop = l;
iwalk->lastv = l->v;
BLI_ghash_insert(walker->visithash, data, NULL);
}
static void *islandboundWalker_Yield(BMWalker *walker)
static void *islandboundWalker_yield(BMWalker *walker)
{
islandboundWalker *iwalk = walker->currentstate;
return iwalk->curedge;
return iwalk->curloop;
}
static void *islandboundWalker_Step(BMWalker *walker)
static void *islandboundWalker_step(BMWalker *walker)
{
islandboundWalker *iwalk = walker->currentstate, *owalk;
BMIter iter, liter;
islandboundWalker *iwalk = walker->currentstate, owalk;
BMVert *v;
BMEdge *e = iwalk->curedge;
BMEdge *e = iwalk->curloop->e;
BMFace *f;
BMLoop *l;
int found=0, radlen, sellen;;
BMLoop *l = iwalk->curloop;
int found=0;
owalk = iwalk;
owalk = *iwalk;
if (iwalk->lastv == e->v1) v = e->v2;
else v = e->v1;
if (BM_Nonmanifold_Vert(v)) {
BMWalker_reset(walker);
if (BM_Nonmanifold_Vert(walker->bm, v)) {
BMW_reset(walker);
BMO_RaiseError(walker->bm, NULL,BMERR_WALKER_FAILED,
"Non-manifold vert"
"while searching region boundary");
return NULL;
}
BMWalker_popstate(walker);
/*find start face*/
l = BMIter_New(&liter, walker->bm, BM_LOOPS_OF_EDGE; e);
for (; l; l=BMIter_Step(&liter)) {
if (BMO_TestFlag(walker->bm, l->f, walker->restrictflag)) {
f = l->f;
break;
}
}
/*pop off current state*/
BMW_popstate(walker);
f = l->f;
while (1) {
l = BM_OtherFaceLoop(e, v, f);
if (l) {
l = l->radial.next->data;
l = BM_OtherFaceLoop(e, f, v);
if (bmesh_radial_nextloop(l) != l) {
l = bmesh_radial_nextloop(l);
f = l->f;
e = l->e;
if(!BMO_TestFlag(walker->bm,l->f,walker->restrictflag))
if(!BMO_TestFlag(walker->bm, f, walker->restrictflag)){
l = l->radial.next->data;
break;
}
} else {
f = l->f;
e = l->e;
break;
}
}
if (e == iwalk->curedge) return NULL;
if (BLI_ghash_haskey(walker->visithash, e)) return NULL;
if (l == owalk.curloop) return NULL;
if (BLI_ghash_haskey(walker->visithash, l)) return owalk.curloop;
BLI_ghash_insert(walker->visithash, e, NULL);
BMWalker_pushstate(walker);
BLI_ghash_insert(walker->visithash, l, NULL);
BMW_pushstate(walker);
iwalk = walker->currentstate;
iwalk->base = owalk->base;
iwalk->curedge = e;
iwalk->base = owalk.base;
//if (!BMO_TestFlag(walker->bm, l->f, walker->restrictflag))
// iwalk->curloop = l->radial.next->data;
iwalk->curloop = l; //else iwalk->curloop = l;
iwalk->lastv = v;
return iwalk->curedge;
return owalk.curloop;
}
/* Island Walker:
*
* Starts at a tool flagged-face and walks over the face region
*
* TODO:
*
* Add restriction flag/callback for wire edges.
*
*/
static void islandWalker_begin(BMWalker *walker, void *data){
islandWalker *iwalk = NULL;
BMW_pushstate(walker);
iwalk = walker->currentstate;
BLI_ghash_insert(walker->visithash, data, NULL);
iwalk->cur = data;
}
static void *islandWalker_yield(BMWalker *walker)
{
islandWalker *iwalk = walker->currentstate;
return iwalk->cur;
}
static void *islandWalker_step(BMWalker *walker)
{
islandWalker *iwalk = walker->currentstate, *owalk;
BMIter iter, liter;
BMFace *f, *curf = iwalk->cur;
BMLoop *l;
owalk = iwalk;
BMW_popstate(walker);
l = BMIter_New(&liter, walker->bm, BM_LOOPS_OF_FACE, iwalk->cur);
for (; l; l=BMIter_Step(&liter)) {
f = BMIter_New(&iter, walker->bm, BM_FACES_OF_EDGE, l->e);
for (; f; f=BMIter_Step(&iter)) {
if (!BMO_TestFlag(walker->bm, f, walker->restrictflag))
continue;
if (BLI_ghash_haskey(walker->visithash, f)) continue;
BMW_pushstate(walker);
iwalk = walker->currentstate;
iwalk->cur = f;
BLI_ghash_insert(walker->visithash, f, NULL);
break;
}
}
return curf;
}

View File

@@ -8,42 +8,143 @@
#include "BLI_arithb.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FACE_MARK 1
#define FACE_ORIG 2
#define VERT_MARK 1
#define FACE_NEW 4
void dissolvefaces_exec(BMesh *bm, BMOperator *op)
{
BMOIter iter;
BMIter liter;
BMLoop *l;
BMOIter oiter;
BMIter liter, liter2;
BMLoop *l, *l2;
BMFace *f, *f2, *nf = NULL;
V_DECLARE(region);
V_DECLARE(regions);
BMLoop ***regions = NULL;
BMLoop **region = NULL;
BMWalker walker, regwalker;
int i, j, fcopied;
//BMO_Flag_Buffer(bm, op, BMOP_DISFACES_FACEIN, FACE_MARK);
BMO_Flag_Buffer(bm, op, BMOP_DISFACES_FACEIN, FACE_MARK);
/*collect regions*/
f = BMO_IterNew(&oiter, bm, op, BMOP_DISFACES_FACEIN);
for (; f; f=BMO_IterStep(&oiter)) {
if (!BMO_TestFlag(bm, f, FACE_MARK)) continue;
/*TODO: need to discuss with Briggs how best to implement this, seems this would be
a great time to use the walker api, get it up to snuff. perhaps have a walker
that goes over inner vertices of a contiguously-flagged region? then you
could just use dissolve disk on them.*/
if (BMO_GetSlot(op, BMOP_DISFACES_FACEIN)->len != 2) return;
l = BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f);
for (; l; l=BMIter_Step(&liter)) {
l2 = bmesh_radial_nextloop(l);
if (l2!=l && BMO_TestFlag(bm, l2->f, FACE_MARK))
continue;
if (BM_FacesAroundEdge(l->e) <= 2) {
V_RESET(region);
region = NULL; /*forces different allocation*/
/*HACK: for debugging purposes, handle cases of two adjacent faces*/
f = BMO_IterNew(&iter, bm, op, BMOP_DISFACES_FACEIN);
f2 = BMO_IterStep(&iter);
/*yay, walk!*/
BMW_Init(&walker, bm, BMW_ISLANDBOUND, FACE_MARK);
l = BMW_Begin(&walker, l);
for (; l; l=BMW_Step(&walker)) {
V_GROW(region);
region[V_COUNT(region)-1] = l;
}
BMW_End(&walker);
for (l=BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f);l;l=BMIter_Step(&liter)) {
if (!l->radial.next) continue;
if (((BMLoop*)l->radial.next->data)->f == f2) {
nf = BM_Join_Faces(bm, f, f2, l->e, 1, 0);
break;
if (BMO_HasError(bm)) {
BMO_ClearStack(bm);
BMO_RaiseError(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
goto cleanup;
}
if (region == NULL) continue;
BMW_Init(&regwalker, bm, BMW_ISLAND, FACE_MARK);
f2 = BMW_Begin(&regwalker, region[0]->f);
for (; f2; f2=BMW_Step(&regwalker)) {
BMO_ClearFlag(bm, f2, FACE_MARK);
BMO_SetFlag(bm, f2, FACE_ORIG);
}
BMW_End(&regwalker);
if (BMO_HasError(bm)) {
BMO_ClearStack(bm);
BMO_RaiseError(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
goto cleanup;
}
V_GROW(region);
V_GROW(regions);
regions[V_COUNT(regions)-1] = region;
region[V_COUNT(region)-1] = NULL;
break;
}
}
}
for (i=0; i<V_COUNT(regions); i++) {
BMEdge **edges = NULL;
V_DECLARE(edges);
region = regions[i];
for (j=0; region[j]; j++) {
V_GROW(edges);
edges[V_COUNT(edges)-1] = region[j]->e;
}
f= BM_Make_Ngon(bm, edges[0]->v1, edges[0]->v2, edges, j, 0);
if (!f) {
/*raise error*/
BMO_RaiseError(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
goto cleanup;
}
BMO_SetFlag(bm, f, FACE_NEW);
fcopied = 0;
l=BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f);
for (; l; l=BMIter_Step(&liter)) {
/*ensure winding is identical*/
l2 = BMIter_New(&liter2, bm, BM_LOOPS_OF_LOOP, l);
for (; l2; l2=BMIter_Step(&liter2)) {
if (BMO_TestFlag(bm, l2->f, FACE_ORIG)) {
if (l2->v != l->v)
bmesh_loop_reverse(bm, l2->f);
break;
}
}
/*copy over attributes*/
l2 = BMIter_New(&liter2, bm, BM_LOOPS_OF_LOOP, l);
for (; l2; l2=BMIter_Step(&liter2)) {
if (BMO_TestFlag(bm, l2->f, FACE_ORIG)) {
if (!fcopied) {
BM_Copy_Attributes(bm, bm, l2->f, f);
fcopied = 1;
}
BM_Copy_Attributes(bm, bm, l2, l);
break;
}
}
}
}
if (nf) {
BMO_SetFlag(bm, nf, 1);
BMO_Flag_To_Slot(bm, op, BMOP_DISFACES_REGIONOUT, 1, BM_FACE);
BMO_CallOpf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES);
if (BMO_HasError(bm)) return;
BMO_Flag_To_Slot(bm, op, BMOP_DISFACES_REGIONOUT, FACE_NEW, BM_FACE);
cleanup:
/*free/cleanup*/
for (i=0; i<V_COUNT(regions); i++) {
if (regions[i]) V_FREE(regions[i]);
}
V_FREE(regions);
}
/*returns 1 if any faces were dissolved*/
@@ -67,34 +168,33 @@ int BM_DissolveFaces(EditMesh *em, int flag) {
void dissolveverts_exec(BMesh *bm, BMOperator *op)
{
BMOpSlot *vinput;
BMIter iter, liter, fiter;
BMIter iter, fiter;
BMVert *v;
BMFace *f, *f2;
BMEdge *fe;
BMLoop *l;
int found, found2, found3, len, oldlen=0;
BMFace *f;
vinput = BMO_GetSlot(op, BMOP_DISVERTS_VERTIN);
BMO_Flag_Buffer(bm, op, BMOP_DISVERTS_VERTIN, VERT_MARK);
found = 1;
while (found) {
found = 0;
len = 0;
for (v=BMIter_New(&iter, bm, BM_VERTS, NULL); v; v=BMIter_Step(&iter)) {
if (BMO_TestFlag(bm, v, VERT_MARK)) {
if (!BM_Dissolve_Vert(bm, v)) {
BMO_RaiseError(bm, op,
BMERR_DISSOLVEDISK_FAILED, NULL);
return;
}
found = 1;
len++;
for (v=BMIter_New(&iter, bm, BM_VERTS, NULL); v; v=BMIter_Step(&iter)) {
if (BMO_TestFlag(bm, v, VERT_MARK)) {
f=BMIter_New(&fiter, bm, BM_FACES_OF_VERT, v);
for (; f; f=BMIter_Step(&fiter)) {
BMO_SetFlag(bm, f, FACE_MARK);
}
}
}
BMO_CallOpf(bm, "dissolvefaces faces=%ff", FACE_MARK);
if (BMO_HasError(bm)) {
BMO_ClearStack(bm);
BMO_RaiseError(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL);
}
}
/*this code is for cleaning up two-edged faces, it shall become
it's own function one day.*/
#if 0
/*clean up two-edged faces*/
/*basic idea is to keep joining 2-edged faces until their
gone. this however relies on joining two 2-edged faces
@@ -155,6 +255,5 @@ void dissolveverts_exec(BMesh *bm, BMOperator *op)
}
if (oldlen == len) break;
oldlen = len;
}
}
#endif