Files
test/source/blender/bmesh/intern/bmesh_structure.c
Joseph Eagar 5d1c1c6421 [note: do not test quite yet]
Phase 1 of restructuring done.  There are now two
distinct subclass systems within the bmesh API;
one is compile-time, and forms the backend of what will
eventually be a "lite" bmesh API for modifiers (the
ones that use bmesh are simply too slow right now). 
The other is dynamic, and will be used to implement
multires reprojection.

The idea was to solve as many serious problems with
memory, speed, etc, at once as possible and set up others 
to be solved more easily later.

I've also added holes into the data structure, but not
the api; I don't plan to finish implementing that until
after bmesh gets into trunk.  I simply wanted to lessen
how much code I'll have to rewrite, since I was doing a
fairly major restructuring anyway.

In addition, I've added iteration support to mempool, to
avoid having to store linked list pointers (though this
has caveats).

Next step: merge in trunk changes. . .oh, what fun :P
2010-07-15 00:55:31 +00:00

939 lines
20 KiB
C

/**
* bmesh_structure.c jan 2007
*
* Low level routines for manipulating the BM structure.
*
* $Id: bmesh_structure.c,v 1.00 2007/01/17 17:42:01 Briggs Exp $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
* about this.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2007 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Geoffrey Bantle.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "BKE_utildefines.h"
#include "bmesh.h"
#include "bmesh_private.h"
#include "BLI_blenlib.h"
#include "BLI_linklist.h"
#include "BLI_ghash.h"
/**
* MISC utility functions.
*
*/
int bmesh_vert_in_edge(BMEdge *e, BMVert *v){
if(e->v1 == v || e->v2 == v) return 1;
return 0;
}
int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e){
if(e->v1 == v1 && e->v2 == v2) return 1;
else if(e->v1 == v2 && e->v2 == v1) return 1;
return 0;
}
BMVert *bmesh_edge_getothervert(BMEdge *e, BMVert *v){
if(e->v1 == v) return e->v2;
else if(e->v2 == v) return e->v1;
return NULL;
}
int bmesh_edge_swapverts(BMEdge *e, BMVert *orig, BMVert *newv){
if(e->v1 == orig){
e->v1 = newv;
e->dlink1.next = e->dlink1.prev = NULL;
return 1;
}
else if(e->v2 == orig){
e->v2 = newv;
e->dlink2.next = e->dlink2.prev = NULL;
return 1;
}
return 0;
}
/**
* BMESH CYCLES
* (this is somewhat outdate, though bits of its API are still used) - joeedh
*
* Cycles are circular doubly linked lists that form the basis of adjacency
* information in the BME modeller. Full adjacency relations can be derived
* from examining these cycles very quickly. Although each cycle is a double
* circular linked list, each one is considered to have a 'base' or 'head',
* and care must be taken by Euler code when modifying the contents of a cycle.
*
* The contents of this file are split into two parts. First there are the
* bmesh_cycle family of functions which are generic circular double linked list
* procedures. The second part contains higher level procedures for supporting
* modification of specific cycle types.
*
* The three cycles explicitly stored in the BM data structure are as follows:
*
* 1: The Disk Cycle - A circle of edges around a vertex
* Base: vertex->edge pointer.
*
* This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains
* two bmesh_CycleNode structures to keep track of that edge's membership in the disk cycle
* of each of its vertices. However for any given vertex it may be the first in some edges
* in its disk cycle and the second for others. The bmesh_disk_XXX family of functions contain
* some nice utilities for navigating disk cycles in a way that hides this detail from the
* tool writer.
*
* Note that the disk cycle is completley independant from face data. One advantage of this
* is that wire edges are fully integrated into the topology database. Another is that the
* the disk cycle has no problems dealing with non-manifold conditions involving faces.
*
* Functions relating to this cycle:
*
* bmesh_disk_append_edge
* bmesh_disk_remove_edge
* bmesh_disk_nextedge
* bmesh_disk_getpointer
*
* 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge
* Base: edge->l->radial structure.
*
* The radial cycle is similar to the radial cycle in the radial edge data structure.*
* Unlike the radial edge however, the radial cycle does not require a large amount of memory
* to store non-manifold conditions since BM does not keep track of region/shell
* information.
*
* Functions relating to this cycle:
*
* bmesh_radial_append
* bmesh_radial_remove_loop
* bmesh_radial_nextloop
* bmesh_radial_find_face
*
*
* 3: The Loop Cycle - A circle of face edges around a polygon.
* Base: polygon->lbase.
*
* The loop cycle keeps track of a faces vertices and edges. It should be noted that the
* direction of a loop cycle is either CW or CCW depending on the face normal, and is
* not oriented to the faces editedges.
*
* Functions relating to this cycle:
*
* bmesh_cycle_XXX family of functions.
*
*
* Note that the order of elements in all cycles except the loop cycle is undefined. This
* leads to slightly increased seek time for deriving some adjacency relations, however the
* advantage is that no intrinsic properties of the data structures are dependant upon the
* cycle order and all non-manifold conditions are represented trivially.
*
*/
int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v)
{
if (!v->e) {
Link *e1 = (Link*)bm_get_edge_link(e, v);
v->e = e;
e1->next = e1->prev = (Link*)e;
} else {
Link *e1, *e2, *e3;
e1 = bm_get_edge_link(e, v);
e2 = bm_get_edge_link(v->e, v);
e3 = e2->prev ? bm_get_edge_link(e2->prev, v) : NULL;
e1->next = (Link*)v->e;
e1->prev = e2->prev;
e2->prev = (Link*)e;
if (e3)
e3->next = (Link*)e;
}
return 1;
}
void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v)
{
Link *e1, *e2;
e1 = bm_get_edge_link(e, v);
if (e1->prev) {
e2 = bm_get_edge_link(e1->prev, v);
e2->next = e1->next;
}
if (e1->next) {
e2 = bm_get_edge_link(e1->next, v);
e2->prev = e1->prev;
}
if (v->e == e)
v->e = e!=e1->next ? (BMEdge*)e1->next : NULL;
e1->next = e1->prev = NULL;
}
struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v)
{
if (v == e->v1)
return e->dlink1.next;
if (v == e->v2)
return e->dlink2.next;
return NULL;
}
BMEdge *bmesh_disk_prevedge(BMEdge *e, BMVert *v)
{
if (v == e->v1)
return e->dlink1.prev;
if (v == e->v2)
return e->dlink2.prev;
return NULL;
}
int bmesh_disk_count(struct BMVert *v)
{
BMEdge *e = v->e;
int i=0;
if (!e)
return 0;
do {
if (!e)
return 0;
e = bmesh_disk_nextedge(e, v);
if (i >= (1<<20)) {
printf("bmesh error: infinite loop in disk cycle!\n");
return 0;
}
i += 1;
} while (e != v->e);
return i;
}
int bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
{
BMEdge *e2;
if (!BM_Vert_In_Edge(e, v))
return 0;
if (bmesh_disk_count(v) != len || len == 0)
return 0;
e2 = e;
do {
if (len!=1 && bmesh_disk_prevedge(e2, v) == e2)
return 0;
e2 = bmesh_disk_nextedge(e2, v);
} while (e2 != e);
return 1;
}
/*
* BME DISK COUNT FACE VERT
*
* Counts the number of loop users
* for this vertex. Note that this is
* equivalent to counting the number of
* faces incident upon this vertex
*
*/
int bmesh_disk_count_facevert(BMVert *v)
{
BMEdge *curedge;
int count = 0;
/*is there an edge on this vert at all?*/
if(!v->e)
return count;
/*first, loop around edges*/
curedge = v->e;
do{
if(curedge->l) count += bmesh_radial_count_facevert(curedge->l, v);
curedge = bmesh_disk_nextedge(curedge, v);
}while(curedge != v->e);
return count;
}
struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = e;
do{
if(searchedge->l && bmesh_radial_count_facevert(searchedge->l,v)) return searchedge;
searchedge = bmesh_disk_nextedge(searchedge,v);
}while(searchedge != e);
return NULL;
}
struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = bmesh_disk_nextedge(e,v);
do{
if(searchedge->l && bmesh_radial_count_facevert(searchedge->l,v)) return searchedge;
searchedge = bmesh_disk_nextedge(searchedge,v);
}while(searchedge !=e);
return e;
}
/*****radial cycle functions, e.g. loops surrounding edges******/
int bmesh_radial_validate(int radlen, BMLoop *l)
{
BMLoop *l2 = l;
if (bmesh_radial_length(l) != radlen)
return 0;
do {
if (l2->e != l->e)
return 0;
if (l2->v != l->e->v1 && l2->v != l->e->v2)
return 0;
l2 = l2->radial_next;
} while (l2 != l);
return 1;
}
void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e)
{
if (l->radial_next != l) {
if (e && l == e->l)
e->l = l->radial_next;
l->radial_next->radial_prev = l->radial_prev;
l->radial_prev->radial_next = l->radial_next;
} else {
l->radial_next = l->radial_prev = NULL;
if (e && l == e->l)
e->l = NULL;
else if (e)
bmesh_error();
}
}
/*
* BME RADIAL FIND FIRST FACE VERT
*
* Finds the first loop of v around radial
* cycle
*
*/
BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = l;
do{
if(curloop->v == v) return curloop;
curloop = bmesh_radial_nextloop(curloop);
}while(curloop != l);
return NULL;
}
BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = bmesh_radial_nextloop(l);
do{
if(curloop->v == v) return curloop;
curloop = bmesh_radial_nextloop(curloop);
}while(curloop !=l);
return l;
}
BMLoop *bmesh_radial_nextloop(BMLoop *l)
{
return l->radial_next;
}
int bmesh_radial_length(BMLoop *l)
{
BMLoop *l2 = l;
int i = 0;
if (!l)
return 0;
do {
i++;
l2 = l2->radial_next;
if (i >= 555555)
return -1;
} while (l2 != l);
return i;
}
void bmesh_radial_append(BMEdge *e, BMLoop *l){
if(e->l == NULL) {
e->l = l;
l->radial_next = l->radial_prev = l;
} else {
l->radial_prev = e->l;
l->radial_next = e->l->radial_next;
e->l->radial_next->radial_prev = l;
e->l->radial_next = l;
e->l = l;
}
}
int bmesh_radial_find_face(BMEdge *e, BMFace *f)
{
BMLoop *curloop;
int i, len;
len = bmesh_radial_length(e->l);
for(i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) {
if(curloop->f == f)
return 1;
}
return 0;
}
/*
* BME RADIAL COUNT FACE VERT
*
* Returns the number of times a vertex appears
* in a radial cycle
*
*/
int bmesh_radial_count_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
int count = 0;
curloop = l;
do{
if(curloop->v == v) count++;
curloop = bmesh_radial_nextloop(curloop);
}while(curloop != l);
return count;
}
#if 0
void bmesh_cycle_append(void *h, void *nt)
{
BMNode *oldtail, *head, *newtail;
head = (BMNode*)h;
newtail = (BMNode*)nt;
if(head->next == NULL){
head->next = newtail;
head->prev = newtail;
newtail->next = head;
newtail->prev = head;
}
else{
oldtail = head->prev;
oldtail->next = newtail;
newtail->next = head;
newtail->prev = oldtail;
head->prev = newtail;
}
}
/**
* bmesh_cycle_length
*
* Count the nodes in a cycle.
*
* Returns -
* Integer
*/
int bmesh_cycle_length(BMEdge *e, BMVert *v) {
BMEdge *next, *prev, *cur;
int len, vi = v == e->v1 ? 0 : 1;
/*should skip 2 forward if v is 1, happily reduces to
v*2*/
prev = *(&e->v1_prev + vi*2);
cur = e;
len = 1;
while (cur != prev) {
vi = cur->v1 == v ? 0 : 1;
len++;
cur = *(&cur->v1_next + vi*2);
}
return len;
}
/**
* bmesh_cycle_remove
*
* Removes a node from a cycle.
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_cycle_remove(void *h, void *remn)
{
int i, len;
BMNode *head, *remnode, *curnode;
head = (BMNode*)h;
remnode = (BMNode*)remn;
len = bmesh_cycle_length(h);
if(len == 1 && head == remnode){
head->next = NULL;
head->prev = NULL;
return 1;
}
else{
for(i=0, curnode = head; i < len; curnode = curnode->next){
if(curnode == remnode){
remnode->prev->next = remnode->next;
remnode->next->prev = remnode->prev;
/*zero out remnode pointers, important!*/
//remnode->next = NULL;
//remnode->prev = NULL;
return 1;
}
}
}
return 0;
}
/**
* bmesh_cycle_validate
*
* Validates a cycle. Takes as an argument the expected length of the cycle and
* a pointer to the cycle head or base.
*
*
* Returns -
* 1 for success, 0 for failure.
*/
int bmesh_cycle_validate(int len, void *h){
int i;
BMNode *curnode, *head;
head = (BMNode*)h;
/*forward validation*/
for(i = 0, curnode = head; i < len; i++, curnode = curnode->next);
if(curnode != head) return 0;
/*reverse validation*/
for(i = 0, curnode = head; i < len; i++, curnode = curnode->prev);
if(curnode != head) return 0;
return 1;
}
/*Begin Disk Cycle routines*/
/**
* bmesh_disk_nextedge
*
* Find the next edge in a disk cycle
*
* Returns -
* Pointer to the next edge in the disk cycle for the vertex v.
*/
BMEdge *bmesh_disk_nextedge(BMEdge *e, BMVert *v)
{
if(bmesh_vert_in_edge(e, v)){
if(e->v1 == v) return e->d1.next->data;
else if(e->v2 == v) return e->d2.next->data;
}
return NULL;
}
/**
* bmesh_disk_getpointer
*
* Given an edge and one of its vertices, find the apporpriate CycleNode
*
* Returns -
* Pointer to bmesh_CycleNode.
*/
BMNode *bmesh_disk_getpointer(BMEdge *e, BMVert *v){
/*returns pointer to the cycle node for the appropriate vertex in this disk*/
if(e->v1 == v) return &(e->d1);
else if (e->v2 == v) return &(e->d2);
return NULL;
}
/**
* bmesh_disk_append_edge
*
* Appends edge to the end of a vertex disk cycle.
*
* Returns -
* 1 for success, 0 for failure
*/
int bmesh_disk_append_edge(BMEdge *e, BMVert *v)
{
BMNode *base, *tail;
if(bmesh_vert_in_edge(e, v) == 0) return 0; /*check to make sure v is in e*/
/*check for loose vert first*/
if(v->e == NULL){
v->e = e;
base = tail = bmesh_disk_getpointer(e, v);
bmesh_cycle_append(base, tail); /*circular reference is ok!*/
return 1;
}
/*insert e at the end of disk cycle and make it the new v->e*/
base = bmesh_disk_getpointer(v->e, v);
tail = bmesh_disk_getpointer(e, v);
bmesh_cycle_append(base, tail);
return 1;
}
/**
* bmesh_disk_remove_edge
*
* Removes an edge from a disk cycle. If the edge to be removed is
* at the base of the cycle, the next edge becomes the new base.
*
*
* Returns -
* Nothing
*/
void bmesh_disk_remove_edge(BMEdge *e, BMVert *v)
{
BMNode *base, *remnode;
BMEdge *newbase;
int len;
base = bmesh_disk_getpointer(v->e, v);
remnode = bmesh_disk_getpointer(e, v);
/*first deal with v->e pointer...*/
len = bmesh_cycle_length(base);
if(len == 1) newbase = NULL;
else if(v->e == e) newbase = base->next-> data;
else newbase = v->e;
/*remove and rebase*/
bmesh_cycle_remove(base, remnode);
v->e = newbase;
}
/**
* bmesh_disk_next_edgeflag
*
* Searches the disk cycle of v, starting with e, for the
* next edge that has either eflag or tflag.
*
* bmesh_Edge pointer.
*/
BMEdge *bmesh_disk_next_edgeflag(BMEdge *e, BMVert *v, int eflag, int tflag)
{
BMNode *diskbase;
BMEdge *curedge;
int len, ok;
if(eflag && tflag) return NULL;
ok = bmesh_vert_in_edge(e,v);
if(ok){
diskbase = bmesh_disk_getpointer(e, v);
len = bmesh_cycle_length(diskbase);
curedge = bmesh_disk_nextedge(e,v);
while(curedge != e){
if(eflag){
if(curedge->head.eflag1 == eflag) return curedge;
}
curedge = bmesh_disk_nextedge(curedge, v);
}
}
return NULL;
}
/**
* bmesh_disk_count_edgeflag
*
* Counts number of edges in this verts disk cycle which have
* either eflag or tflag (but not both!)
*
* Returns -
* Integer.
*/
int bmesh_disk_count_edgeflag(BMVert *v, int eflag, int tflag)
{
BMNode *diskbase;
BMEdge *curedge;
int i, len=0, count=0;
if(v->e){
if(eflag && tflag) return 0; /*tflag and eflag are reserved for different functions!*/
diskbase = bmesh_disk_getpointer(v->e, v);
len = bmesh_cycle_length(diskbase);
for(i = 0, curedge=v->e; i<len; i++){
if(eflag){
if(curedge->head.eflag1 == eflag) count++;
}
curedge = bmesh_disk_nextedge(curedge, v);
}
}
return count;
}
int bmesh_disk_hasedge(BMVert *v, BMEdge *e){
BMNode *diskbase;
BMEdge *curedge;
int i, len=0;
if(v->e){
diskbase = bmesh_disk_getpointer(v->e,v);
len = bmesh_cycle_length(diskbase);
for(i = 0, curedge=v->e; i<len; i++){
if(curedge == e) return 1;
else curedge=bmesh_disk_nextedge(curedge, v);
}
}
return 0;
}
BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2){
BMNode *diskbase;
BMEdge *curedge;
int i, len=0;
if(v1->e){
diskbase = bmesh_disk_getpointer(v1->e,v1);
len = bmesh_cycle_length(diskbase);
for(i=0,curedge=v1->e;i<len;i++,curedge = bmesh_disk_nextedge(curedge,v1)){
if(bmesh_verts_in_edge(v1,v2,curedge)) return curedge;
}
}
return NULL;
}
/*end disk cycle routines*/
BMLoop *bmesh_radial_nextloop(BMLoop *l){
return (BMLoop*)(l->radial_next);
}
void bmesh_radial_append(BMEdge *e, BMLoop *l){
if(e->l == NULL) e->l = l;
bmesh_cycle_append(&(e->l->radial), &(l->radial));
}
void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e)
{
BMLoop *newbase;
int len;
/*deal with edge->l pointer*/
len = bmesh_cycle_length(&(e->l->radial));
if(len == 1) newbase = NULL;
else if(e->l == l) newbase = e->l->radial_next;
else newbase = e->l;
/*remove and rebase*/
bmesh_cycle_remove(&(e->l->radial), &(l->radial));
e->l = newbase;
}
int bmesh_radial_find_face(BMEdge *e,BMFace *f)
{
BMLoop *curloop;
int i, len;
len = bmesh_cycle_length(&(e->l->radial));
for(i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next){
if(curloop->f == f) return 1;
}
return 0;
}
/*
* BME RADIAL COUNT FACE VERT
*
* Returns the number of times a vertex appears
* in a radial cycle
*
*/
int bmesh_radial_count_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
int count = 0;
curloop = l;
do{
if(curloop->v == v) count++;
curloop = bmesh_radial_nextloop(curloop);
}while(curloop != l);
return count;
}
/*
* BME DISK COUNT FACE VERT
*
* Counts the number of loop users
* for this vertex. Note that this is
* equivalent to counting the number of
* faces incident upon this vertex
*
*/
int bmesh_disk_count_facevert(BMVert *v)
{
BMEdge *curedge;
int count = 0;
/*is there an edge on this vert at all?*/
if(!v->e)
return count;
/*first, loop around edges*/
curedge = v->e;
do{
if(curedge->l) count += bmesh_radial_count_facevert(curedge->l, v);
curedge = bmesh_disk_nextedge(curedge, v);
}while(curedge != v->e);
return count;
}
/*
* BME RADIAL FIND FIRST FACE VERT
*
* Finds the first loop of v around radial
* cycle
*
*/
BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = l;
do{
if(curloop->v == v) return curloop;
curloop = bmesh_radial_nextloop(curloop);
}while(curloop != l);
return NULL;
}
BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v)
{
BMLoop *curloop;
curloop = bmesh_radial_nextloop(l);
do{
if(curloop->v == v) return curloop;
curloop = bmesh_radial_nextloop(curloop);
}while(curloop !=l);
return l;
}
/*
* BME FIND FIRST FACE EDGE
*
* Finds the first edge in a vertices
* Disk cycle that has one of this
* vert's loops attached
* to it.
*
*
*/
BMEdge *bmesh_disk_find_first_faceedge(BMEdge *e, BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = e;
do{
if(searchedge->l && bmesh_radial_count_facevert(searchedge->l,v)) return searchedge;
searchedge = bmesh_disk_nextedge(searchedge,v);
}while(searchedge != e);
return NULL;
}
BMEdge *bmesh_disk_find_next_faceedge(BMEdge *e, BMVert *v)
{
BMEdge *searchedge = NULL;
searchedge = bmesh_disk_nextedge(e,v);
do{
if(searchedge->l && bmesh_radial_count_facevert(searchedge->l,v)) return searchedge;
searchedge = bmesh_disk_nextedge(searchedge,v);
}while(searchedge !=e);
return e;
}
struct BMLoop *bmesh_loop_find_loop(struct BMFace *f, struct BMVert *v) {
BMLoop *l;
int i, len;
len = bmesh_cycle_length(f->lbase);
for (i = 0, l=f->loopbase; i < len; i++, l=((BMLoop*)(l->next)) ) {
if (l->v == v) return l;
}
return NULL;
}
#endif