264 lines
6.9 KiB
C
264 lines
6.9 KiB
C
/*
|
|
* 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
|
|
*
|
|
* BMesh Walker API.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h> /* for memcpy */
|
|
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "bmesh.h"
|
|
|
|
#include "bmesh_walkers_private.h"
|
|
|
|
/**
|
|
* - joeedh -
|
|
* design notes:
|
|
*
|
|
* original design: walkers directly emulation recursive functions.
|
|
* functions save their state onto a worklist, and also add new states
|
|
* to implement recursive or looping behavior. 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 choice is the flagged as visited using
|
|
* the ghash. each step may push multiple new states onto the worklist at once.
|
|
*
|
|
* - 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 necessary, in the far future :) .
|
|
* - tools should ALWAYS have necessary error handling
|
|
* for if walkers fail.
|
|
*/
|
|
|
|
void *BMW_begin(BMWalker *walker, void *start)
|
|
{
|
|
BLI_assert(((BMHeader *)start)->htype & walker->begin_htype);
|
|
|
|
walker->begin(walker, start);
|
|
|
|
return BMW_current_state(walker) ? walker->step(walker) : NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Init Walker
|
|
*
|
|
* Allocates and returns a new mesh walker of a given type.
|
|
* The elements visited are filtered by the bitmask 'searchmask'.
|
|
*/
|
|
void BMW_init(BMWalker *walker,
|
|
BMesh *bm,
|
|
int type,
|
|
short mask_vert,
|
|
short mask_edge,
|
|
short mask_face,
|
|
BMWFlag flag,
|
|
int layer)
|
|
{
|
|
memset(walker, 0, sizeof(BMWalker));
|
|
|
|
walker->layer = layer;
|
|
walker->flag = flag;
|
|
walker->bm = bm;
|
|
|
|
walker->mask_vert = mask_vert;
|
|
walker->mask_edge = mask_edge;
|
|
walker->mask_face = mask_face;
|
|
|
|
walker->visit_set = BLI_gset_ptr_new("bmesh walkers");
|
|
walker->visit_set_alt = BLI_gset_ptr_new("bmesh walkers sec");
|
|
|
|
if (UNLIKELY(type >= BMW_MAXWALKERS || type < 0)) {
|
|
fprintf(stderr,
|
|
"%s: Invalid walker type in BMW_init; type: %d, "
|
|
"searchmask: (v:%d, e:%d, f:%d), flag: %u, layer: %d\n",
|
|
__func__,
|
|
type,
|
|
mask_vert,
|
|
mask_edge,
|
|
mask_face,
|
|
flag,
|
|
layer);
|
|
BLI_assert(0);
|
|
return;
|
|
}
|
|
|
|
if (type != BMW_CUSTOM) {
|
|
walker->begin_htype = bm_walker_types[type]->begin_htype;
|
|
walker->begin = bm_walker_types[type]->begin;
|
|
walker->yield = bm_walker_types[type]->yield;
|
|
walker->step = bm_walker_types[type]->step;
|
|
walker->structsize = bm_walker_types[type]->structsize;
|
|
walker->order = bm_walker_types[type]->order;
|
|
walker->valid_mask = bm_walker_types[type]->valid_mask;
|
|
|
|
/* safety checks */
|
|
/* if this raises an error either the caller is wrong or
|
|
* 'bm_walker_types' needs updating */
|
|
BLI_assert(mask_vert == 0 || (walker->valid_mask & BM_VERT));
|
|
BLI_assert(mask_edge == 0 || (walker->valid_mask & BM_EDGE));
|
|
BLI_assert(mask_face == 0 || (walker->valid_mask & BM_FACE));
|
|
}
|
|
|
|
walker->worklist = BLI_mempool_create(walker->structsize, 0, 128, BLI_MEMPOOL_NOP);
|
|
BLI_listbase_clear(&walker->states);
|
|
}
|
|
|
|
/**
|
|
* \brief End Walker
|
|
*
|
|
* Frees a walker's worklist.
|
|
*/
|
|
void BMW_end(BMWalker *walker)
|
|
{
|
|
BLI_mempool_destroy(walker->worklist);
|
|
BLI_gset_free(walker->visit_set, NULL);
|
|
BLI_gset_free(walker->visit_set_alt, NULL);
|
|
}
|
|
|
|
/**
|
|
* \brief Step Walker
|
|
*/
|
|
void *BMW_step(BMWalker *walker)
|
|
{
|
|
BMHeader *head;
|
|
|
|
head = BMW_walk(walker);
|
|
|
|
return head;
|
|
}
|
|
|
|
/**
|
|
* \brief Walker Current Depth
|
|
*
|
|
* Returns the current depth of the walker.
|
|
*/
|
|
|
|
int BMW_current_depth(BMWalker *walker)
|
|
{
|
|
return walker->depth;
|
|
}
|
|
|
|
/**
|
|
* \brief Main Walking Function
|
|
*
|
|
* Steps a mesh walker forward by one element
|
|
*/
|
|
void *BMW_walk(BMWalker *walker)
|
|
{
|
|
void *current = NULL;
|
|
|
|
while (BMW_current_state(walker)) {
|
|
current = walker->step(walker);
|
|
if (current) {
|
|
return current;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Current Walker State
|
|
*
|
|
* Returns the first state from the walker state
|
|
* worklist. This state is the next in the
|
|
* worklist for processing.
|
|
*/
|
|
void *BMW_current_state(BMWalker *walker)
|
|
{
|
|
BMwGenericWalker *currentstate = walker->states.first;
|
|
if (currentstate) {
|
|
/* Automatic update of depth. For most walkers that
|
|
* follow the standard "Step" pattern of:
|
|
* - read current state
|
|
* - remove current state
|
|
* - push new states
|
|
* - return walk result from just-removed current state
|
|
* this simple automatic update should keep track of depth
|
|
* just fine. Walkers that deviate from that pattern may
|
|
* need to manually update the depth if they care about
|
|
* keeping it correct. */
|
|
walker->depth = currentstate->depth + 1;
|
|
}
|
|
return currentstate;
|
|
}
|
|
|
|
/**
|
|
* \brief Remove Current Walker State
|
|
*
|
|
* Remove and free an item from the end of the walker state
|
|
* worklist.
|
|
*/
|
|
void BMW_state_remove(BMWalker *walker)
|
|
{
|
|
void *oldstate;
|
|
oldstate = BMW_current_state(walker);
|
|
BLI_remlink(&walker->states, oldstate);
|
|
BLI_mempool_free(walker->worklist, oldstate);
|
|
}
|
|
|
|
/**
|
|
* \brief Add a new Walker State
|
|
*
|
|
* Allocate a new empty state and put it on the worklist.
|
|
* A pointer to the new state is returned so that the caller
|
|
* can fill in the state data. The new state will be inserted
|
|
* at the front for depth-first walks, and at the end for
|
|
* breadth-first walks.
|
|
*/
|
|
void *BMW_state_add(BMWalker *walker)
|
|
{
|
|
BMwGenericWalker *newstate;
|
|
newstate = BLI_mempool_alloc(walker->worklist);
|
|
newstate->depth = walker->depth;
|
|
switch (walker->order) {
|
|
case BMW_DEPTH_FIRST:
|
|
BLI_addhead(&walker->states, newstate);
|
|
break;
|
|
case BMW_BREADTH_FIRST:
|
|
BLI_addtail(&walker->states, newstate);
|
|
break;
|
|
default:
|
|
BLI_assert(0);
|
|
break;
|
|
}
|
|
return newstate;
|
|
}
|
|
|
|
/**
|
|
* \brief Reset Walker
|
|
*
|
|
* Frees all states from the worklist, resetting the walker
|
|
* for reuse in a new walk.
|
|
*/
|
|
void BMW_reset(BMWalker *walker)
|
|
{
|
|
while (BMW_current_state(walker)) {
|
|
BMW_state_remove(walker);
|
|
}
|
|
walker->depth = 0;
|
|
BLI_gset_clear(walker->visit_set, NULL);
|
|
BLI_gset_clear(walker->visit_set_alt, NULL);
|
|
}
|