2012-02-19 18:31:04 +00:00
|
|
|
/*
|
|
|
|
|
* ***** 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.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* 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 *****
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** \file blender/bmesh/intern/bmesh_structure.c
|
|
|
|
|
* \ingroup bmesh
|
|
|
|
|
*
|
|
|
|
|
* Low level routines for manipulating the BM structure.
|
|
|
|
|
*/
|
|
|
|
|
|
2012-03-24 01:24:58 +00:00
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
#include "bmesh.h"
|
2012-03-08 03:25:53 +00:00
|
|
|
#include "intern/bmesh_private.h"
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* MISC utility functions.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int bmesh_vert_in_edge(BMEdge *e, BMVert *v)
|
|
|
|
|
{
|
|
|
|
|
if (e->v1 == v || e->v2 == v) return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->v1 == v1 && e->v2 == v2) return TRUE;
|
|
|
|
|
else if (e->v1 == v2 && e->v2 == v1) return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
BMVert *bmesh_edge_other_vert_get(BMEdge *e, BMVert *v)
|
2012-02-25 16:49:59 +00:00
|
|
|
{
|
2012-02-19 18:31:04 +00:00
|
|
|
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->v1_disk_link.next = e->v1_disk_link.prev = NULL;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (e->v2 == orig) {
|
|
|
|
|
e->v2 = newv;
|
|
|
|
|
e->v2_disk_link.next = e->v2_disk_link.prev = NULL;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \section bm_cycles BMesh Cycles
|
2012-02-19 18:31:04 +00:00
|
|
|
* (this is somewhat outdate, though bits of its API are still used) - joeedh
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Cycles are circular doubly linked lists that form the basis of adjacency
|
2012-04-08 07:34:09 +00:00
|
|
|
* information in the BME modeler. Full adjacency relations can be derived
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* The three cycles explicitly stored in the BM data structure are as follows:
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 1: The Disk Cycle - A circle of edges around a vertex
|
|
|
|
|
* Base: vertex->edge pointer.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains
|
2012-04-08 07:34:09 +00:00
|
|
|
* two bmesh_CycleNode structures to keep track of that edges membership in the disk cycle
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-03-01 12:20:18 +00:00
|
|
|
* Note that the disk cycle is completely independent from face data. One advantage of this
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Functions relating to this cycle:
|
|
|
|
|
* - #bmesh_disk_edge_append
|
|
|
|
|
* - #bmesh_disk_edge_remove
|
|
|
|
|
* - #bmesh_disk_edge_next
|
|
|
|
|
* - #bmesh_disk_edge_prev
|
|
|
|
|
* - #bmesh_disk_facevert_count
|
|
|
|
|
* - #bmesh_disk_faceedge_find_first
|
|
|
|
|
* - #bmesh_disk_faceedge_find_next
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge
|
|
|
|
|
* Base: edge->l->radial structure.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Functions relating to this cycle:
|
|
|
|
|
* - #bmesh_radial_append
|
|
|
|
|
* - #bmesh_radial_loop_remove
|
|
|
|
|
* - #bmesh_radial_face_find
|
|
|
|
|
* - #bmesh_radial_facevert_count
|
|
|
|
|
* - #bmesh_radial_faceloop_find_first
|
|
|
|
|
* - #bmesh_radial_faceloop_find_next
|
|
|
|
|
* - #bmesh_radial_validate
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 3: The Loop Cycle - A circle of face edges around a polygon.
|
|
|
|
|
* Base: polygon->lbase.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* 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.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Functions relating to this cycle:
|
|
|
|
|
* - bmesh_cycle_XXX family of functions.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note 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
|
2012-03-01 12:20:18 +00:00
|
|
|
* advantage is that no intrinsic properties of the data structures are dependent upon the
|
2012-02-29 06:55:10 +00:00
|
|
|
* cycle order and all non-manifold conditions are represented trivially.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2012-02-28 19:30:44 +00:00
|
|
|
int bmesh_disk_edge_append(BMEdge *e, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
if (!v->e) {
|
2012-02-28 16:29:48 +00:00
|
|
|
BMDiskLink *dl1 = BM_DISK_EDGE_LINK_GET(e, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
v->e = e;
|
|
|
|
|
dl1->next = dl1->prev = e;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BMDiskLink *dl1, *dl2, *dl3;
|
|
|
|
|
|
2012-02-28 16:29:48 +00:00
|
|
|
dl1 = BM_DISK_EDGE_LINK_GET(e, v);
|
|
|
|
|
dl2 = BM_DISK_EDGE_LINK_GET(v->e, v);
|
|
|
|
|
dl3 = dl2->prev ? BM_DISK_EDGE_LINK_GET(dl2->prev, v) : NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
dl1->next = v->e;
|
|
|
|
|
dl1->prev = dl2->prev;
|
|
|
|
|
|
|
|
|
|
dl2->prev = e;
|
|
|
|
|
if (dl3)
|
|
|
|
|
dl3->next = e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
void bmesh_disk_edge_remove(BMEdge *e, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMDiskLink *dl1, *dl2;
|
|
|
|
|
|
2012-02-28 16:29:48 +00:00
|
|
|
dl1 = BM_DISK_EDGE_LINK_GET(e, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (dl1->prev) {
|
2012-02-28 16:29:48 +00:00
|
|
|
dl2 = BM_DISK_EDGE_LINK_GET(dl1->prev, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
dl2->next = dl1->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dl1->next) {
|
2012-02-28 16:29:48 +00:00
|
|
|
dl2 = BM_DISK_EDGE_LINK_GET(dl1->next, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
dl2->prev = dl1->prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (v->e == e)
|
2012-02-25 20:58:03 +00:00
|
|
|
v->e = (e != dl1->next) ? dl1->next : NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
dl1->next = dl1->prev = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-05 00:50:18 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Next Disk Edge
|
2012-02-23 10:24:20 +00:00
|
|
|
*
|
2012-08-13 15:17:15 +00:00
|
|
|
* Find the next edge in a disk cycle
|
2012-02-23 10:24:20 +00:00
|
|
|
*
|
2012-08-13 15:17:15 +00:00
|
|
|
* \return Pointer to the next edge in the disk cycle for the vertex v.
|
2012-02-23 10:24:20 +00:00
|
|
|
*/
|
2012-02-28 19:30:44 +00:00
|
|
|
BMEdge *bmesh_disk_edge_next(BMEdge *e, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
if (v == e->v1)
|
|
|
|
|
return e->v1_disk_link.next;
|
|
|
|
|
if (v == e->v2)
|
|
|
|
|
return e->v2_disk_link.next;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
BMEdge *bmesh_disk_edge_prev(BMEdge *e, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
if (v == e->v1)
|
|
|
|
|
return e->v1_disk_link.prev;
|
|
|
|
|
if (v == e->v2)
|
|
|
|
|
return e->v2_disk_link.prev;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
BMEdge *bmesh_disk_edge_exists(BMVert *v1, BMVert *v2)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-27 21:33:30 +00:00
|
|
|
BMEdge *e_iter, *e_first;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (v1->e) {
|
2012-03-01 16:04:37 +00:00
|
|
|
e_first = e_iter = v1->e;
|
2012-02-27 21:33:30 +00:00
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2012-02-27 21:33:30 +00:00
|
|
|
if (bmesh_verts_in_edge(v1, v2, e_iter)) {
|
|
|
|
|
return e_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-02-28 19:30:44 +00:00
|
|
|
} while ((e_iter = bmesh_disk_edge_next(e_iter, v1)) != e_first);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
int bmesh_disk_count(BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-27 21:33:30 +00:00
|
|
|
if (v->e) {
|
|
|
|
|
BMEdge *e_first, *e_iter;
|
|
|
|
|
int count = 0;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-27 21:33:30 +00:00
|
|
|
e_iter = e_first = v->e;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-27 21:33:30 +00:00
|
|
|
do {
|
|
|
|
|
if (!e_iter) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-27 21:33:30 +00:00
|
|
|
if (count >= (1 << 20)) {
|
|
|
|
|
printf("bmesh error: infinite loop in disk cycle!\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
count++;
|
2012-02-28 19:30:44 +00:00
|
|
|
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
|
2012-02-27 21:33:30 +00:00
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
|
|
|
|
|
{
|
2012-02-27 21:33:30 +00:00
|
|
|
BMEdge *e_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (!BM_vert_in_edge(e, v))
|
|
|
|
|
return FALSE;
|
|
|
|
|
if (bmesh_disk_count(v) != len || len == 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2012-02-27 21:33:30 +00:00
|
|
|
e_iter = e;
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2012-02-28 19:30:44 +00:00
|
|
|
if (len != 1 && bmesh_disk_edge_prev(e_iter, v) == e_iter) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
2012-02-28 19:30:44 +00:00
|
|
|
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief DISK COUNT FACE VERT
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
* Counts the number of loop users
|
|
|
|
|
* for this vertex. Note that this is
|
|
|
|
|
* equivalent to counting the number of
|
|
|
|
|
* faces incident upon this vertex
|
|
|
|
|
*/
|
2012-02-27 13:47:53 +00:00
|
|
|
int bmesh_disk_facevert_count(BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
/* is there an edge on this vert at all */
|
2012-02-27 21:33:30 +00:00
|
|
|
if (v->e) {
|
|
|
|
|
BMEdge *e_first, *e_iter;
|
|
|
|
|
int count = 0;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-27 21:33:30 +00:00
|
|
|
/* first, loop around edge */
|
|
|
|
|
e_first = e_iter = v->e;
|
|
|
|
|
do {
|
|
|
|
|
if (e_iter->l) {
|
|
|
|
|
count += bmesh_radial_facevert_count(e_iter->l, v);
|
|
|
|
|
}
|
2012-02-28 19:30:44 +00:00
|
|
|
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
|
2012-02-27 21:33:30 +00:00
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief FIND FIRST FACE EDGE
|
2012-02-23 10:24:20 +00:00
|
|
|
*
|
|
|
|
|
* Finds the first edge in a vertices
|
|
|
|
|
* Disk cycle that has one of this
|
|
|
|
|
* vert's loops attached
|
|
|
|
|
* to it.
|
|
|
|
|
*/
|
2012-02-28 19:30:44 +00:00
|
|
|
BMEdge *bmesh_disk_faceedge_find_first(BMEdge *e, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *searchedge = NULL;
|
|
|
|
|
searchedge = e;
|
|
|
|
|
do {
|
2012-02-27 13:47:53 +00:00
|
|
|
if (searchedge->l && bmesh_radial_facevert_count(searchedge->l, v)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return searchedge;
|
|
|
|
|
}
|
2012-02-28 19:30:44 +00:00
|
|
|
} while ((searchedge = bmesh_disk_edge_next(searchedge, v)) != e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
BMEdge *bmesh_disk_faceedge_find_next(BMEdge *e, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *searchedge = NULL;
|
2012-02-28 19:30:44 +00:00
|
|
|
searchedge = bmesh_disk_edge_next(e, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2012-02-27 13:47:53 +00:00
|
|
|
if (searchedge->l && bmesh_radial_facevert_count(searchedge->l, v)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return searchedge;
|
|
|
|
|
}
|
2012-02-28 19:30:44 +00:00
|
|
|
} while ((searchedge = bmesh_disk_edge_next(searchedge, v)) != e);
|
2012-02-19 18:31:04 +00:00
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****radial cycle functions, e.g. loops surrounding edges**** */
|
|
|
|
|
int bmesh_radial_validate(int radlen, BMLoop *l)
|
|
|
|
|
{
|
2012-02-23 09:26:53 +00:00
|
|
|
BMLoop *l_iter = l;
|
2012-02-19 18:31:04 +00:00
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
if (bmesh_radial_length(l) != radlen)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
do {
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(!l_iter)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-23 09:26:53 +00:00
|
|
|
if (l_iter->e != l->e)
|
2012-02-19 18:31:04 +00:00
|
|
|
return FALSE;
|
2012-02-23 09:26:53 +00:00
|
|
|
if (l_iter->v != l->e->v1 && l_iter->v != l->e->v2)
|
2012-02-19 18:31:04 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(i > BM_LOOP_RADIAL_MAX)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i++;
|
2012-03-01 17:13:02 +00:00
|
|
|
} while ((l_iter = l_iter->radial_next) != l);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief BMESH RADIAL REMOVE LOOP
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
* Removes a loop from an radial cycle. If edge e is non-NULL
|
|
|
|
|
* it should contain the radial cycle, and it will also get
|
|
|
|
|
* updated (in the case that the edge's link into the radial
|
|
|
|
|
* cycle was the loop which is being removed from the cycle).
|
|
|
|
|
*/
|
2012-02-27 13:47:53 +00:00
|
|
|
void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
/* if e is non-NULL, l must be in the radial cycle of e */
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(e && e != l->e)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
if (e) {
|
|
|
|
|
if (l == e->l) {
|
|
|
|
|
e->l = NULL;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* l is no longer in a radial cycle; empty the links
|
|
|
|
|
* to the cycle and the link back to an edge */
|
|
|
|
|
l->radial_next = l->radial_prev = NULL;
|
|
|
|
|
l->e = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief BME RADIAL FIND FIRST FACE VERT
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
* Finds the first loop of v around radial
|
|
|
|
|
* cycle
|
|
|
|
|
*/
|
2012-02-27 13:47:53 +00:00
|
|
|
BMLoop *bmesh_radial_faceloop_find_first(BMLoop *l, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-23 09:26:53 +00:00
|
|
|
BMLoop *l_iter;
|
|
|
|
|
l_iter = l;
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2012-02-23 09:26:53 +00:00
|
|
|
if (l_iter->v == v) {
|
|
|
|
|
return l_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-03-01 17:13:02 +00:00
|
|
|
} while ((l_iter = l_iter->radial_next) != l);
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
BMLoop *bmesh_radial_faceloop_find_next(BMLoop *l, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-23 09:26:53 +00:00
|
|
|
BMLoop *l_iter;
|
2012-03-01 17:13:02 +00:00
|
|
|
l_iter = l->radial_next;
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2012-02-23 09:26:53 +00:00
|
|
|
if (l_iter->v == v) {
|
|
|
|
|
return l_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-03-01 17:13:02 +00:00
|
|
|
} while ((l_iter = l_iter->radial_next) != l);
|
2012-02-19 18:31:04 +00:00
|
|
|
return l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bmesh_radial_length(BMLoop *l)
|
|
|
|
|
{
|
2012-02-23 10:24:20 +00:00
|
|
|
BMLoop *l_iter = l;
|
2012-02-19 18:31:04 +00:00
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
if (!l)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
do {
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(!l_iter)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* radial cycle is broken (not a circulat loop) */
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i++;
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(i >= BM_LOOP_RADIAL_MAX)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2012-02-23 10:24:20 +00:00
|
|
|
} while ((l_iter = l_iter->radial_next) != l);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(l->e && l->e != e)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* l is already in a radial cycle for a different edge */
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l->e = e;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
int bmesh_radial_face_find(BMEdge *e, BMFace *f)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-23 10:24:20 +00:00
|
|
|
BMLoop *l_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
int i, len;
|
|
|
|
|
|
|
|
|
|
len = bmesh_radial_length(e->l);
|
2012-02-23 10:24:20 +00:00
|
|
|
for (i = 0, l_iter = e->l; i < len; i++, l_iter = l_iter->radial_next) {
|
|
|
|
|
if (l_iter->f == f)
|
2012-02-19 18:31:04 +00:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief RADIAL COUNT FACE VERT
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
* Returns the number of times a vertex appears
|
|
|
|
|
* in a radial cycle
|
|
|
|
|
*/
|
2012-02-27 13:47:53 +00:00
|
|
|
int bmesh_radial_facevert_count(BMLoop *l, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-02-23 09:26:53 +00:00
|
|
|
BMLoop *l_iter;
|
2012-02-19 18:31:04 +00:00
|
|
|
int count = 0;
|
2012-02-23 09:26:53 +00:00
|
|
|
l_iter = l;
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2012-02-23 09:26:53 +00:00
|
|
|
if (l_iter->v == v) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
2012-03-01 17:13:02 +00:00
|
|
|
} while ((l_iter = l_iter->radial_next) != l);
|
2012-02-23 09:26:53 +00:00
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****loop cycle functions, e.g. loops surrounding a face**** */
|
|
|
|
|
int bmesh_loop_validate(BMFace *f)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int len = f->len;
|
|
|
|
|
BMLoop *l_iter, *l_first;
|
|
|
|
|
|
|
|
|
|
l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
|
|
|
|
|
if (l_first == NULL) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Validate that the face loop cycle is the length specified by f->len */
|
|
|
|
|
for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) {
|
2012-04-08 08:09:37 +00:00
|
|
|
if ((l_iter->f != f) ||
|
|
|
|
|
(l_iter == l_first))
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (l_iter != l_first) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Validate the loop->prev links also form a cycle of length f->len */
|
|
|
|
|
for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) {
|
|
|
|
|
if (l_iter == l_first) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (l_iter != l_first) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|