Files
test2/source/blender/bmesh/intern/bmesh_structure.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

570 lines
14 KiB
C++

/* SPDX-FileCopyrightText: 2007 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bmesh
*
* Low level routines for manipulating the BM structure.
*/
#include "BLI_utildefines.h"
#include "bmesh.h"
#include "intern/bmesh_private.h"
/**
* MISC utility functions.
*/
void bmesh_disk_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src)
{
if (e->v1 == v_src) {
e->v1 = v_dst;
e->v1_disk_link.next = e->v1_disk_link.prev = nullptr;
}
else if (e->v2 == v_src) {
e->v2 = v_dst;
e->v2_disk_link.next = e->v2_disk_link.prev = nullptr;
}
else {
BLI_assert(0);
}
}
void bmesh_edge_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src)
{
/* swap out loops */
if (e->l) {
BMLoop *l_iter, *l_first;
l_iter = l_first = e->l;
do {
if (l_iter->v == v_src) {
l_iter->v = v_dst;
}
else if (l_iter->next->v == v_src) {
l_iter->next->v = v_dst;
}
else {
BLI_assert(l_iter->prev->v != v_src);
}
} while ((l_iter = l_iter->radial_next) != l_first);
}
/* swap out edges */
bmesh_disk_vert_replace(e, v_dst, v_src);
}
void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src)
{
BLI_assert(e->v1 == v_src || e->v2 == v_src);
bmesh_disk_edge_remove(e, v_src); /* remove e from tv's disk cycle */
bmesh_disk_vert_swap(e, v_dst, v_src); /* swap out tv for v_new in e */
bmesh_disk_edge_append(e, v_dst); /* add e to v_dst's disk cycle */
BLI_assert(e->v1 != e->v2);
}
/**
* \section bm_cycles BMesh Cycles
*
* NOTE(@joeedh): this is somewhat outdated, though bits of its API are still used.
*
* Cycles are circular doubly linked lists that form the basis of adjacency
* information in the BME modeler. 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 edges 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 completely independent 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_vert_replace
* - #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
* 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_loop_append
* - #bmesh_radial_loop_remove
* - #bmesh_radial_facevert_count
* - #bmesh_radial_facevert_check
* - #bmesh_radial_faceloop_find_first
* - #bmesh_radial_faceloop_find_next
* - #bmesh_radial_validate
* 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 edit-edges.
*
* Functions relating to this cycle:
* - bmesh_cycle_XXX family of functions.
* \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
* advantage is that no intrinsic properties of the data structures are dependent upon the
* cycle order and all non-manifold conditions are represented trivially.
*/
void bmesh_disk_edge_append(BMEdge *e, BMVert *v)
{
if (!v->e) {
BMDiskLink *dl1 = bmesh_disk_edge_link_from_vert(e, v);
v->e = e;
dl1->next = dl1->prev = e;
}
else {
BMDiskLink *dl1, *dl2, *dl3;
dl1 = bmesh_disk_edge_link_from_vert(e, v);
dl2 = bmesh_disk_edge_link_from_vert(v->e, v);
dl3 = dl2->prev ? bmesh_disk_edge_link_from_vert(dl2->prev, v) : nullptr;
dl1->next = v->e;
dl1->prev = dl2->prev;
dl2->prev = e;
if (dl3) {
dl3->next = e;
}
}
}
void bmesh_disk_edge_remove(BMEdge *e, BMVert *v)
{
BMDiskLink *dl1, *dl2;
dl1 = bmesh_disk_edge_link_from_vert(e, v);
if (dl1->prev) {
dl2 = bmesh_disk_edge_link_from_vert(dl1->prev, v);
dl2->next = dl1->next;
}
if (dl1->next) {
dl2 = bmesh_disk_edge_link_from_vert(dl1->next, v);
dl2->prev = dl1->prev;
}
if (v->e == e) {
v->e = (e != dl1->next) ? dl1->next : nullptr;
}
dl1->next = dl1->prev = nullptr;
}
BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2)
{
if (v1->e) {
BMEdge *e_iter, *e_first;
e_first = e_iter = v1->e;
do {
if (BM_verts_in_edge(v1, v2, e_iter)) {
return e_iter;
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v1)) != e_first);
}
return nullptr;
}
int bmesh_disk_count(const BMVert *v)
{
int count = 0;
if (v->e) {
BMEdge *e_first, *e_iter;
e_iter = e_first = v->e;
do {
count++;
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
}
return count;
}
int bmesh_disk_count_at_most(const BMVert *v, const int count_max)
{
int count = 0;
if (v->e) {
BMEdge *e_first, *e_iter;
e_iter = e_first = v->e;
do {
count++;
if (count == count_max) {
break;
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
}
return count;
}
bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
{
BMEdge *e_iter;
if (!BM_vert_in_edge(e, v)) {
return false;
}
if (len == 0 || bmesh_disk_count_at_most(v, len + 1) != len) {
return false;
}
e_iter = e;
do {
if (len != 1 && bmesh_disk_edge_prev(e_iter, v) == e_iter) {
return false;
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
return true;
}
int bmesh_disk_facevert_count(const BMVert *v)
{
/* is there an edge on this vert at all */
int count = 0;
if (v->e) {
BMEdge *e_first, *e_iter;
/* first, loop around edge */
e_first = e_iter = v->e;
do {
if (e_iter->l) {
count += bmesh_radial_facevert_count(e_iter->l, v);
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
}
return count;
}
int bmesh_disk_facevert_count_at_most(const BMVert *v, const int count_max)
{
/* is there an edge on this vert at all */
int count = 0;
if (v->e) {
BMEdge *e_first, *e_iter;
/* first, loop around edge */
e_first = e_iter = v->e;
do {
if (e_iter->l) {
count += bmesh_radial_facevert_count_at_most(e_iter->l, v, count_max - count);
if (count == count_max) {
break;
}
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
}
return count;
}
BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v)
{
const BMEdge *e_iter = e;
do {
if (e_iter->l != nullptr) {
return (BMEdge *)((e_iter->l->v == v) ? e_iter : e_iter->l->next->e);
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
return nullptr;
}
BMLoop *bmesh_disk_faceloop_find_first(const BMEdge *e, const BMVert *v)
{
const BMEdge *e_iter = e;
do {
if (e_iter->l != nullptr) {
return (e_iter->l->v == v) ? e_iter->l : e_iter->l->next;
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
return nullptr;
}
BMLoop *bmesh_disk_faceloop_find_first_visible(const BMEdge *e, const BMVert *v)
{
const BMEdge *e_iter = e;
do {
if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) {
if (e_iter->l != nullptr) {
BMLoop *l_iter, *l_first;
l_iter = l_first = e_iter->l;
do {
if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) {
return (l_iter->v == v) ? l_iter : l_iter->next;
}
} while ((l_iter = l_iter->radial_next) != l_first);
}
}
} while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e);
return nullptr;
}
BMEdge *bmesh_disk_faceedge_find_next(const BMEdge *e, const BMVert *v)
{
BMEdge *e_find;
e_find = bmesh_disk_edge_next(e, v);
do {
if (e_find->l && bmesh_radial_facevert_check(e_find->l, v)) {
return e_find;
}
} while ((e_find = bmesh_disk_edge_next(e_find, v)) != e);
return (BMEdge *)e;
}
bool bmesh_radial_validate(int radlen, BMLoop *l)
{
BMLoop *l_iter = l;
int i = 0;
if (bmesh_radial_length(l) != radlen) {
return false;
}
do {
if (UNLIKELY(!l_iter)) {
BMESH_ASSERT(0);
return false;
}
if (l_iter->e != l->e) {
return false;
}
if (!ELEM(l_iter->v, l->e->v1, l->e->v2)) {
return false;
}
if (UNLIKELY(i > BM_LOOP_RADIAL_MAX)) {
BMESH_ASSERT(0);
return false;
}
i++;
} while ((l_iter = l_iter->radial_next) != l);
return true;
}
void bmesh_radial_loop_append(BMEdge *e, BMLoop *l)
{
if (e->l == nullptr) {
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;
}
if (UNLIKELY(l->e && l->e != e)) {
/* l is already in a radial cycle for a different edge */
BMESH_ASSERT(0);
}
l->e = e;
}
void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l)
{
/* if e is non-nullptr, l must be in the radial cycle of e */
if (UNLIKELY(e != l->e)) {
BMESH_ASSERT(0);
}
if (l->radial_next != l) {
if (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 (l == e->l) {
e->l = nullptr;
}
else {
BMESH_ASSERT(0);
}
}
/* 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 = nullptr;
l->e = nullptr;
}
void bmesh_radial_loop_unlink(BMLoop *l)
{
if (l->radial_next != l) {
l->radial_next->radial_prev = l->radial_prev;
l->radial_prev->radial_next = l->radial_next;
}
/* 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 = nullptr;
l->e = nullptr;
}
BMLoop *bmesh_radial_faceloop_find_first(const BMLoop *l, const BMVert *v)
{
const BMLoop *l_iter;
l_iter = l;
do {
if (l_iter->v == v) {
return (BMLoop *)l_iter;
}
} while ((l_iter = l_iter->radial_next) != l);
return nullptr;
}
BMLoop *bmesh_radial_faceloop_find_next(const BMLoop *l, const BMVert *v)
{
BMLoop *l_iter;
l_iter = l->radial_next;
do {
if (l_iter->v == v) {
return l_iter;
}
} while ((l_iter = l_iter->radial_next) != l);
return (BMLoop *)l;
}
int bmesh_radial_length(const BMLoop *l)
{
const BMLoop *l_iter = l;
int i = 0;
if (!l) {
return 0;
}
do {
if (UNLIKELY(!l_iter)) {
/* Radial cycle is broken (not a circular loop). */
BMESH_ASSERT(0);
return 0;
}
i++;
if (UNLIKELY(i >= BM_LOOP_RADIAL_MAX)) {
BMESH_ASSERT(0);
return -1;
}
} while ((l_iter = l_iter->radial_next) != l);
return i;
}
int bmesh_radial_facevert_count(const BMLoop *l, const BMVert *v)
{
const BMLoop *l_iter;
int count = 0;
l_iter = l;
do {
if (l_iter->v == v) {
count++;
}
} while ((l_iter = l_iter->radial_next) != l);
return count;
}
int bmesh_radial_facevert_count_at_most(const BMLoop *l, const BMVert *v, const int count_max)
{
const BMLoop *l_iter;
int count = 0;
l_iter = l;
do {
if (l_iter->v == v) {
count++;
if (count == count_max) {
break;
}
}
} while ((l_iter = l_iter->radial_next) != l);
return count;
}
bool bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v)
{
const BMLoop *l_iter;
l_iter = l;
do {
if (l_iter->v == v) {
return true;
}
} while ((l_iter = l_iter->radial_next) != l);
return false;
}
bool 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 == nullptr) {
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) {
if ((l_iter->f != f) || (l_iter == l_first)) {
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;
}