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.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
|
|
|
|
|
*
|
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
|
*/
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/** \file blender/bmesh/intern/bmesh_core.c
|
2012-02-19 18:31:04 +00:00
|
|
|
* \ingroup bmesh
|
|
|
|
|
*
|
2014-01-19 23:14:24 +11:00
|
|
|
* Core BMesh functions for adding, removing BMesh elements.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
#include "BLI_math_vector.h"
|
|
|
|
|
#include "BLI_array.h"
|
2013-07-28 10:38:25 +00:00
|
|
|
#include "BLI_alloca.h"
|
2013-05-09 12:46:35 +00:00
|
|
|
#include "BLI_smallhash.h"
|
2014-06-25 00:01:10 +10:00
|
|
|
#include "BLI_stackdefines.h"
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-10-27 11:12:09 +00:00
|
|
|
#include "BLF_translation.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_DerivedMesh.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
|
|
|
|
|
|
|
|
/* use so valgrinds memcheck alerts us when undefined index is used.
|
|
|
|
|
* TESTING ONLY! */
|
|
|
|
|
// #define USE_DEBUG_INDEX_MEMCHECK
|
|
|
|
|
|
|
|
|
|
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
2012-05-27 20:13:59 +00:00
|
|
|
#define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \
|
|
|
|
|
{ \
|
|
|
|
|
int undef_idx; \
|
|
|
|
|
BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \
|
|
|
|
|
} (void)0
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Main function for creating a new vertex.
|
|
|
|
|
*/
|
2013-08-21 07:51:47 +00:00
|
|
|
BMVert *BM_vert_create(BMesh *bm, const float co[3],
|
|
|
|
|
const BMVert *v_example, const eBMCreateFlag create_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2014-04-08 12:51:00 +10:00
|
|
|
BMVert *v = BLI_mempool_alloc(bm->vpool);
|
|
|
|
|
|
2014-08-21 13:04:56 +10:00
|
|
|
BLI_assert((v_example == NULL) || (v_example->head.htype == BM_VERT));
|
|
|
|
|
BLI_assert(!(create_flag & 1));
|
2014-04-08 12:51:00 +10:00
|
|
|
|
|
|
|
|
/* --- assign all members --- */
|
|
|
|
|
v->head.data = NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
|
|
|
|
DEBUG_MEMCHECK_INDEX_INVALIDATE(v)
|
|
|
|
|
#else
|
|
|
|
|
BM_elem_index_set(v, -1); /* set_ok_invalid */
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
v->head.htype = BM_VERT;
|
|
|
|
|
v->head.hflag = 0;
|
|
|
|
|
v->head.api_flag = 0;
|
|
|
|
|
|
|
|
|
|
/* allocate flags */
|
|
|
|
|
v->oflags = bm->vtoolflagpool ? BLI_mempool_calloc(bm->vtoolflagpool) : NULL;
|
|
|
|
|
|
|
|
|
|
/* 'v->no' is handled by BM_elem_attrs_copy */
|
|
|
|
|
if (co) {
|
|
|
|
|
copy_v3_v3(v->co, co);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
zero_v3(v->co);
|
|
|
|
|
}
|
|
|
|
|
zero_v3(v->no);
|
|
|
|
|
|
|
|
|
|
v->e = NULL;
|
|
|
|
|
/* --- done --- */
|
|
|
|
|
|
|
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
/* disallow this flag for verts - its meaningless */
|
|
|
|
|
BLI_assert((create_flag & BM_CREATE_NO_DOUBLE) == 0);
|
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
/* may add to middle of the pool */
|
|
|
|
|
bm->elem_index_dirty |= BM_VERT;
|
|
|
|
|
bm->elem_table_dirty |= BM_VERT;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
bm->totvert++;
|
|
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
if (!(create_flag & BM_CREATE_SKIP_CD)) {
|
2013-08-21 07:51:47 +00:00
|
|
|
if (v_example) {
|
2012-11-29 16:26:39 +00:00
|
|
|
int *keyi;
|
2012-08-23 13:54:30 +00:00
|
|
|
|
2013-08-21 07:51:47 +00:00
|
|
|
BM_elem_attrs_copy(bm, bm, v_example, v);
|
2012-08-23 13:54:30 +00:00
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
/* exception: don't copy the original shapekey index */
|
|
|
|
|
keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX);
|
|
|
|
|
if (keyi) {
|
|
|
|
|
*keyi = ORIGINDEX_NONE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
|
2012-09-08 06:40:03 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(v);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
return v;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Main function for creating a new edge.
|
|
|
|
|
*
|
|
|
|
|
* \note Duplicate edges are supported by the API however users should _never_ see them.
|
2013-01-14 16:42:43 +00:00
|
|
|
* so unless you need a unique edge or know the edge won't exist, you should call with \a no_double = true
|
2012-04-26 08:27:50 +00:00
|
|
|
*/
|
2013-08-21 07:51:47 +00:00
|
|
|
BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2,
|
|
|
|
|
const BMEdge *e_example, const eBMCreateFlag create_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *e;
|
2014-05-10 08:46:35 +10:00
|
|
|
|
|
|
|
|
BLI_assert(v1 != v2);
|
|
|
|
|
BLI_assert(v1->head.htype == BM_VERT && v2->head.htype == BM_VERT);
|
2014-08-21 13:04:56 +10:00
|
|
|
BLI_assert((e_example == NULL) || (e_example->head.htype == BM_EDGE));
|
|
|
|
|
BLI_assert(!(create_flag & 1));
|
2014-05-10 08:46:35 +10:00
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
if ((create_flag & BM_CREATE_NO_DOUBLE) && (e = BM_edge_exists(v1, v2)))
|
2012-02-25 20:58:03 +00:00
|
|
|
return e;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
e = BLI_mempool_alloc(bm->epool);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* --- assign all members --- */
|
|
|
|
|
e->head.data = NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
|
|
|
|
DEBUG_MEMCHECK_INDEX_INVALIDATE(e)
|
|
|
|
|
#else
|
|
|
|
|
BM_elem_index_set(e, -1); /* set_ok_invalid */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
e->head.htype = BM_EDGE;
|
2014-04-08 12:51:00 +10:00
|
|
|
e->head.hflag = BM_ELEM_SMOOTH | BM_ELEM_DRAW;
|
|
|
|
|
e->head.api_flag = 0;
|
|
|
|
|
|
2012-12-11 14:24:27 +00:00
|
|
|
/* allocate flags */
|
2014-04-08 12:51:00 +10:00
|
|
|
e->oflags = bm->etoolflagpool ? BLI_mempool_calloc(bm->etoolflagpool) : NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
e->v1 = v1;
|
|
|
|
|
e->v2 = v2;
|
2014-04-08 12:51:00 +10:00
|
|
|
e->l = NULL;
|
|
|
|
|
|
|
|
|
|
memset(&e->v1_disk_link, 0, sizeof(BMDiskLink) * 2);
|
|
|
|
|
/* --- done --- */
|
|
|
|
|
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_disk_edge_append(e, e->v1);
|
|
|
|
|
bmesh_disk_edge_append(e, e->v2);
|
2014-04-08 12:51:00 +10:00
|
|
|
|
|
|
|
|
/* may add to middle of the pool */
|
|
|
|
|
bm->elem_index_dirty |= BM_EDGE;
|
|
|
|
|
bm->elem_table_dirty |= BM_EDGE;
|
|
|
|
|
|
|
|
|
|
bm->totedge++;
|
|
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
if (!(create_flag & BM_CREATE_SKIP_CD)) {
|
2013-08-21 07:51:47 +00:00
|
|
|
if (e_example) {
|
|
|
|
|
BM_elem_attrs_copy(bm, bm, e_example, e);
|
2012-11-29 16:26:39 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CustomData_bmesh_set_default(&bm->edata, &e->head.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
return e;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f,
|
2014-08-21 13:04:56 +10:00
|
|
|
const BMLoop *l_example, const eBMCreateFlag create_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMLoop *l = NULL;
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
l = BLI_mempool_alloc(bm->lpool);
|
|
|
|
|
|
2014-08-21 13:04:56 +10:00
|
|
|
BLI_assert((l_example == NULL) || (l_example->head.htype == BM_LOOP));
|
|
|
|
|
BLI_assert(!(create_flag & 1));
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
/* --- assign all members --- */
|
|
|
|
|
l->head.data = NULL;
|
2014-05-21 22:35:08 +02:00
|
|
|
|
|
|
|
|
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
|
|
|
|
DEBUG_MEMCHECK_INDEX_INVALIDATE(l)
|
|
|
|
|
#else
|
|
|
|
|
BM_elem_index_set(l, -1); /* set_ok_invalid */
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
l->head.htype = BM_LOOP;
|
2015-04-14 18:24:40 +10:00
|
|
|
l->head.hflag = 0;
|
2014-04-08 12:51:00 +10:00
|
|
|
l->head.api_flag = 0;
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
l->v = v;
|
|
|
|
|
l->e = e;
|
|
|
|
|
l->f = f;
|
2014-04-08 12:51:00 +10:00
|
|
|
|
|
|
|
|
l->radial_next = NULL;
|
|
|
|
|
l->radial_prev = NULL;
|
|
|
|
|
l->next = NULL;
|
|
|
|
|
l->prev = NULL;
|
|
|
|
|
/* --- done --- */
|
|
|
|
|
|
2014-04-13 12:25:02 +02:00
|
|
|
/* may add to middle of the pool */
|
|
|
|
|
bm->elem_index_dirty |= BM_LOOP;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
bm->totloop++;
|
|
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
if (!(create_flag & BM_CREATE_SKIP_CD)) {
|
2014-08-21 13:04:56 +10:00
|
|
|
if (l_example) {
|
|
|
|
|
CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_example->head.data, &l->head.data);
|
2012-11-29 16:26:39 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CustomData_bmesh_set_default(&bm->ldata, &l->head.data);
|
|
|
|
|
}
|
2012-03-06 17:23:26 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return l;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-21 07:51:47 +00:00
|
|
|
static BMLoop *bm_face_boundary_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte,
|
|
|
|
|
const eBMCreateFlag create_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool);
|
|
|
|
|
#endif
|
2012-11-29 16:26:39 +00:00
|
|
|
BMLoop *l = bm_loop_create(bm, startv, starte, f, starte->l, create_flag);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
bmesh_radial_append(starte, l);
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
lst->first = lst->last = l;
|
|
|
|
|
BLI_addtail(&f->loops, lst);
|
|
|
|
|
#else
|
|
|
|
|
f->l_first = l;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
l->f = f;
|
|
|
|
|
|
|
|
|
|
return l;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-11 04:24:36 +00:00
|
|
|
BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f,
|
2013-01-14 16:42:43 +00:00
|
|
|
const bool copy_verts, const bool copy_edges)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-12-11 15:10:19 +00:00
|
|
|
BMVert **verts = BLI_array_alloca(verts, f->len);
|
|
|
|
|
BMEdge **edges = BLI_array_alloca(edges, f->len);
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l_iter;
|
|
|
|
|
BMLoop *l_first;
|
2012-04-26 08:27:50 +00:00
|
|
|
BMLoop *l_copy;
|
|
|
|
|
BMFace *f_copy;
|
2012-02-19 18:31:04 +00:00
|
|
|
int i;
|
|
|
|
|
|
2013-07-11 04:24:36 +00:00
|
|
|
BLI_assert((bm_dst == bm_src) || (copy_verts && copy_edges));
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
2012-04-28 07:43:21 +00:00
|
|
|
i = 0;
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2013-01-14 16:42:43 +00:00
|
|
|
if (copy_verts) {
|
2013-08-21 05:39:46 +00:00
|
|
|
verts[i] = BM_vert_create(bm_dst, l_iter->v->co, l_iter->v, BM_CREATE_NOP);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-04-28 07:43:21 +00:00
|
|
|
verts[i] = l_iter->v;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-04-28 07:43:21 +00:00
|
|
|
i++;
|
2012-02-19 18:31:04 +00:00
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
i = 0;
|
|
|
|
|
do {
|
2013-01-14 16:42:43 +00:00
|
|
|
if (copy_edges) {
|
2012-02-19 18:31:04 +00:00
|
|
|
BMVert *v1, *v2;
|
|
|
|
|
|
|
|
|
|
if (l_iter->e->v1 == verts[i]) {
|
|
|
|
|
v1 = verts[i];
|
|
|
|
|
v2 = verts[(i + 1) % f->len];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
v2 = verts[i];
|
|
|
|
|
v1 = verts[(i + 1) % f->len];
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-21 07:51:47 +00:00
|
|
|
edges[i] = BM_edge_create(bm_dst, v1, v2, l_iter->e, BM_CREATE_NOP);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-04-28 07:43:21 +00:00
|
|
|
edges[i] = l_iter->e;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
2013-08-21 07:51:47 +00:00
|
|
|
f_copy = BM_face_create(bm_dst, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-07-11 04:24:36 +00:00
|
|
|
BM_elem_attrs_copy(bm_src, bm_dst, f, f_copy);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
2012-04-26 08:27:50 +00:00
|
|
|
l_copy = BM_FACE_FIRST_LOOP(f_copy);
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2013-07-11 04:24:36 +00:00
|
|
|
BM_elem_attrs_copy(bm_src, bm_dst, l_iter, l_copy);
|
2012-04-26 08:27:50 +00:00
|
|
|
l_copy = l_copy->next;
|
2012-02-19 18:31:04 +00:00
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
2012-04-28 07:43:21 +00:00
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
return f_copy;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
/**
|
|
|
|
|
* only create the face, since this calloc's the length is initialized to 0,
|
|
|
|
|
* leave adding loops to the caller.
|
2014-04-08 12:51:00 +10:00
|
|
|
*
|
|
|
|
|
* \note, caller needs to handle customdata.
|
2012-02-28 19:30:44 +00:00
|
|
|
*/
|
2014-04-08 12:51:00 +10:00
|
|
|
BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm)
|
2012-02-28 19:30:44 +00:00
|
|
|
{
|
|
|
|
|
BMFace *f;
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
f = BLI_mempool_alloc(bm->fpool);
|
2012-02-28 19:30:44 +00:00
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
|
|
|
|
|
/* --- assign all members --- */
|
|
|
|
|
f->head.data = NULL;
|
2012-02-28 19:30:44 +00:00
|
|
|
#ifdef USE_DEBUG_INDEX_MEMCHECK
|
|
|
|
|
DEBUG_MEMCHECK_INDEX_INVALIDATE(f)
|
|
|
|
|
#else
|
|
|
|
|
BM_elem_index_set(f, -1); /* set_ok_invalid */
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
f->head.htype = BM_FACE;
|
|
|
|
|
f->head.hflag = 0;
|
|
|
|
|
f->head.api_flag = 0;
|
|
|
|
|
|
|
|
|
|
/* allocate flags */
|
|
|
|
|
f->oflags = bm->ftoolflagpool ? BLI_mempool_calloc(bm->ftoolflagpool) : NULL;
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BLI_listbase_clear(&f->loops);
|
|
|
|
|
#else
|
|
|
|
|
f->l_first = NULL;
|
|
|
|
|
#endif
|
|
|
|
|
f->len = 0;
|
|
|
|
|
zero_v3(f->no);
|
|
|
|
|
f->mat_nr = 0;
|
|
|
|
|
/* --- done --- */
|
|
|
|
|
|
|
|
|
|
|
2013-10-28 02:05:33 +00:00
|
|
|
/* may add to middle of the pool */
|
|
|
|
|
bm->elem_index_dirty |= BM_FACE;
|
|
|
|
|
bm->elem_table_dirty |= BM_FACE;
|
2012-02-28 19:30:44 +00:00
|
|
|
|
|
|
|
|
bm->totface++;
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
f->totbounds = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
2013-03-27 05:52:28 +00:00
|
|
|
* Main face creation function
|
|
|
|
|
*
|
|
|
|
|
* \param bm The mesh
|
|
|
|
|
* \param verts A sorted array of verts size of len
|
|
|
|
|
* \param edges A sorted array of edges size of len
|
|
|
|
|
* \param len Length of the face
|
|
|
|
|
* \param create_flag Options for creating the face
|
2012-04-26 08:27:50 +00:00
|
|
|
*/
|
2013-08-21 07:51:47 +00:00
|
|
|
BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len,
|
|
|
|
|
const BMFace *f_example, const eBMCreateFlag create_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMFace *f = NULL;
|
|
|
|
|
BMLoop *l, *startl, *lastl;
|
2013-08-21 07:51:47 +00:00
|
|
|
int i;
|
2014-08-21 13:04:56 +10:00
|
|
|
|
|
|
|
|
BLI_assert((f_example == NULL) || (f_example->head.htype == BM_FACE));
|
|
|
|
|
BLI_assert(!(create_flag & 1));
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
if (len == 0) {
|
2012-12-11 14:24:27 +00:00
|
|
|
/* just return NULL for now */
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
if (create_flag & BM_CREATE_NO_DOUBLE) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* Check if face already exists */
|
2013-08-21 07:51:47 +00:00
|
|
|
const bool is_overlap = BM_face_exists(verts, len, &f);
|
|
|
|
|
if (is_overlap) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BLI_assert(f == NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
f = bm_face_create__internal(bm);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-11-29 16:26:39 +00:00
|
|
|
startl = lastl = bm_face_boundary_add(bm, f, verts[0], edges[0], create_flag);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
startl->v = verts[0];
|
|
|
|
|
startl->e = edges[0];
|
2012-02-19 18:31:04 +00:00
|
|
|
for (i = 1; i < len; i++) {
|
2012-11-29 16:26:39 +00:00
|
|
|
l = bm_loop_create(bm, verts[i], edges[i], f, edges[i]->l, create_flag);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
l->f = f;
|
2012-02-19 18:31:04 +00:00
|
|
|
bmesh_radial_append(edges[i], l);
|
|
|
|
|
|
|
|
|
|
l->prev = lastl;
|
|
|
|
|
lastl->next = l;
|
|
|
|
|
lastl = l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startl->prev = lastl;
|
|
|
|
|
lastl->next = startl;
|
|
|
|
|
|
|
|
|
|
f->len = len;
|
|
|
|
|
|
2013-08-21 07:51:47 +00:00
|
|
|
if (!(create_flag & BM_CREATE_SKIP_CD)) {
|
|
|
|
|
if (f_example) {
|
|
|
|
|
BM_elem_attrs_copy(bm, bm, f_example, f);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CustomData_bmesh_set_default(&bm->pdata, &f->head.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(f);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
return f;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2013-08-21 05:11:11 +00:00
|
|
|
/**
|
|
|
|
|
* Wrapper for #BM_face_create when you don't have an edge array
|
|
|
|
|
*/
|
2013-08-21 07:51:47 +00:00
|
|
|
BMFace *BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len,
|
|
|
|
|
const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges)
|
2013-08-21 05:11:11 +00:00
|
|
|
{
|
|
|
|
|
BMEdge **edge_arr = BLI_array_alloca(edge_arr, len);
|
|
|
|
|
int i, i_prev = len - 1;
|
|
|
|
|
|
|
|
|
|
if (create_edges) {
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
|
edge_arr[i_prev] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE);
|
|
|
|
|
i_prev = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-08-21 16:00:53 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
|
edge_arr[i_prev] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]);
|
|
|
|
|
if (edge_arr[i_prev] == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
i_prev = i;
|
2013-08-21 05:11:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-21 07:51:47 +00:00
|
|
|
return BM_face_create(bm, vert_arr, edge_arr, len, f_example, create_flag);
|
2013-08-21 05:11:11 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-12 04:53:49 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* Check the element is valid.
|
|
|
|
|
*
|
|
|
|
|
* BMESH_TODO, when this raises an error the output is incredible confusing.
|
|
|
|
|
* need to have some nice way to print/debug what the hecks going on.
|
|
|
|
|
*/
|
2012-04-19 14:38:09 +00:00
|
|
|
int bmesh_elem_check(void *element, const char htype)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMHeader *head = element;
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
if (!element)
|
2015-02-24 13:04:00 +11:00
|
|
|
return (1 << 0);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (head->htype != htype)
|
2015-02-24 13:04:00 +11:00
|
|
|
return (1 << 1);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
switch (htype) {
|
2012-09-08 08:59:47 +00:00
|
|
|
case BM_VERT:
|
|
|
|
|
{
|
2012-02-19 18:31:04 +00:00
|
|
|
BMVert *v = element;
|
|
|
|
|
if (v->e && v->e->head.htype != BM_EDGE) {
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 2);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-09-08 08:59:47 +00:00
|
|
|
case BM_EDGE:
|
|
|
|
|
{
|
2012-02-19 18:31:04 +00:00
|
|
|
BMEdge *e = element;
|
|
|
|
|
if (e->l && e->l->head.htype != BM_LOOP)
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 3);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (e->l && e->l->f->head.htype != BM_FACE)
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 4);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (e->v1_disk_link.prev == NULL ||
|
|
|
|
|
e->v2_disk_link.prev == NULL ||
|
|
|
|
|
e->v1_disk_link.next == NULL ||
|
|
|
|
|
e->v2_disk_link.next == NULL)
|
|
|
|
|
{
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 5);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL))
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 6);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (e->l && e->l->f->len <= 0)
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 7);
|
2012-02-19 18:31:04 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2012-09-08 08:59:47 +00:00
|
|
|
case BM_LOOP:
|
|
|
|
|
{
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l = element, *l2;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (l->f->head.htype != BM_FACE)
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 8);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (l->e->head.htype != BM_EDGE)
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 9);
|
2012-06-23 23:22:19 +00:00
|
|
|
if (l->v->head.htype != BM_VERT)
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 10);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (!BM_vert_in_edge(l->e, l->v)) {
|
|
|
|
|
fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__);
|
2015-02-24 13:04:00 +11:00
|
|
|
err |= (1 << 11);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (l->radial_next == NULL || l->radial_prev == NULL)
|
|
|
|
|
err |= (1 << 12);
|
|
|
|
|
if (l->f->len <= 0)
|
|
|
|
|
err |= (1 << 13);
|
|
|
|
|
|
2012-03-03 20:19:11 +00:00
|
|
|
/* validate boundary loop -- invalid for hole loops, of course,
|
|
|
|
|
* but we won't be allowing those for a while yet */
|
2012-02-19 18:31:04 +00:00
|
|
|
l2 = l;
|
|
|
|
|
i = 0;
|
|
|
|
|
do {
|
|
|
|
|
if (i >= BM_NGON_MAX) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
} while ((l2 = l2->next) != l);
|
|
|
|
|
|
|
|
|
|
if (i != l->f->len || l2 != l)
|
|
|
|
|
err |= (1 << 14);
|
|
|
|
|
|
|
|
|
|
if (!bmesh_radial_validate(bmesh_radial_length(l), l))
|
|
|
|
|
err |= (1 << 15);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-09-08 08:59:47 +00:00
|
|
|
case BM_FACE:
|
|
|
|
|
{
|
2012-02-19 18:31:04 +00:00
|
|
|
BMFace *f = element;
|
|
|
|
|
BMLoop *l_iter;
|
|
|
|
|
BMLoop *l_first;
|
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
if (!f->loops.first)
|
|
|
|
|
#else
|
|
|
|
|
if (!f->l_first)
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
err |= (1 << 16);
|
|
|
|
|
}
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
do {
|
|
|
|
|
if (l_iter->f != f) {
|
|
|
|
|
fprintf(stderr, "%s: loop inside one face points to another! (bmesh internal error)\n", __func__);
|
|
|
|
|
err |= (1 << 17);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!l_iter->e)
|
|
|
|
|
err |= (1 << 18);
|
|
|
|
|
if (!l_iter->v)
|
|
|
|
|
err |= (1 << 19);
|
|
|
|
|
if (!BM_vert_in_edge(l_iter->e, l_iter->v) || !BM_vert_in_edge(l_iter->e, l_iter->next->v)) {
|
|
|
|
|
err |= (1 << 20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter))
|
|
|
|
|
err |= (1 << 21);
|
|
|
|
|
|
|
|
|
|
if (!bmesh_disk_count(l_iter->v) || !bmesh_disk_count(l_iter->next->v))
|
|
|
|
|
err |= (1 << 22);
|
|
|
|
|
|
|
|
|
|
len++;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
if (len != f->len)
|
|
|
|
|
err |= (1 << 23);
|
2013-04-14 12:01:12 +00:00
|
|
|
break;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-04-14 12:01:12 +00:00
|
|
|
default:
|
|
|
|
|
BLI_assert(0);
|
2013-07-21 08:16:37 +00:00
|
|
|
break;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(err == 0);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-12 04:53:49 +00:00
|
|
|
#endif /* NDEBUG */
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
2012-04-26 08:27:50 +00:00
|
|
|
* low level function, only frees the vert,
|
|
|
|
|
* doesn't change or adjust surrounding geometry
|
|
|
|
|
*/
|
2012-02-28 19:10:53 +00:00
|
|
|
static void bm_kill_only_vert(BMesh *bm, BMVert *v)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
bm->totvert--;
|
|
|
|
|
bm->elem_index_dirty |= BM_VERT;
|
2013-10-28 02:05:33 +00:00
|
|
|
bm->elem_table_dirty |= BM_VERT;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-24 21:19:18 +00:00
|
|
|
BM_select_history_remove(bm, v);
|
2012-04-26 08:27:50 +00:00
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
if (v->head.data)
|
|
|
|
|
CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
|
|
|
|
|
|
2012-12-12 05:04:01 +00:00
|
|
|
if (bm->vtoolflagpool) {
|
|
|
|
|
BLI_mempool_free(bm->vtoolflagpool, v->oflags);
|
2012-11-18 12:14:22 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_mempool_free(bm->vpool, v);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* low level function, only frees the edge,
|
|
|
|
|
* doesn't change or adjust surrounding geometry
|
|
|
|
|
*/
|
2012-02-28 19:10:53 +00:00
|
|
|
static void bm_kill_only_edge(BMesh *bm, BMEdge *e)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
bm->totedge--;
|
|
|
|
|
bm->elem_index_dirty |= BM_EDGE;
|
2013-10-28 02:05:33 +00:00
|
|
|
bm->elem_table_dirty |= BM_EDGE;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 22:23:40 +00:00
|
|
|
BM_select_history_remove(bm, (BMElem *)e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (e->head.data)
|
|
|
|
|
CustomData_bmesh_free_block(&bm->edata, &e->head.data);
|
|
|
|
|
|
2012-12-12 05:04:01 +00:00
|
|
|
if (bm->etoolflagpool) {
|
|
|
|
|
BLI_mempool_free(bm->etoolflagpool, e->oflags);
|
2012-11-18 12:14:22 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_mempool_free(bm->epool, e);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* low level function, only frees the face,
|
|
|
|
|
* doesn't change or adjust surrounding geometry
|
|
|
|
|
*/
|
2012-02-28 19:10:53 +00:00
|
|
|
static void bm_kill_only_face(BMesh *bm, BMFace *f)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
if (bm->act_face == f)
|
|
|
|
|
bm->act_face = NULL;
|
|
|
|
|
|
|
|
|
|
bm->totface--;
|
|
|
|
|
bm->elem_index_dirty |= BM_FACE;
|
2013-10-28 02:05:33 +00:00
|
|
|
bm->elem_table_dirty |= BM_FACE;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-25 22:23:40 +00:00
|
|
|
BM_select_history_remove(bm, (BMElem *)f);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (f->head.data)
|
|
|
|
|
CustomData_bmesh_free_block(&bm->pdata, &f->head.data);
|
|
|
|
|
|
2012-12-12 05:04:01 +00:00
|
|
|
if (bm->ftoolflagpool) {
|
|
|
|
|
BLI_mempool_free(bm->ftoolflagpool, f->oflags);
|
2012-11-18 12:14:22 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_mempool_free(bm->fpool, f);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* low level function, only frees the loop,
|
|
|
|
|
* doesn't change or adjust surrounding geometry
|
|
|
|
|
*/
|
2012-02-28 19:10:53 +00:00
|
|
|
static void bm_kill_only_loop(BMesh *bm, BMLoop *l)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
bm->totloop--;
|
2014-04-15 16:15:43 +02:00
|
|
|
bm->elem_index_dirty |= BM_LOOP;
|
2012-02-19 18:31:04 +00:00
|
|
|
if (l->head.data)
|
|
|
|
|
CustomData_bmesh_free_block(&bm->ldata, &l->head.data);
|
|
|
|
|
|
|
|
|
|
BLI_mempool_free(bm->lpool, l);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/**
|
2012-04-26 08:27:50 +00:00
|
|
|
* kills all edges associated with \a f, along with any other faces containing
|
2012-02-28 18:28:30 +00:00
|
|
|
* those edges
|
|
|
|
|
*/
|
2012-02-19 18:31:04 +00:00
|
|
|
void BM_face_edges_kill(BMesh *bm, BMFace *f)
|
|
|
|
|
{
|
2013-07-28 10:38:25 +00:00
|
|
|
BMEdge **edges = BLI_array_alloca(edges, f->len);
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l_iter;
|
|
|
|
|
BMLoop *l_first;
|
2012-12-11 15:10:19 +00:00
|
|
|
int i = 0;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
do {
|
2012-12-11 15:10:19 +00:00
|
|
|
edges[i++] = l_iter->e;
|
2012-02-19 18:31:04 +00:00
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
2013-07-28 10:38:25 +00:00
|
|
|
for (i = 0; i < f->len; i++) {
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_edge_kill(bm, edges[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/**
|
2012-04-26 08:27:50 +00:00
|
|
|
* kills all verts associated with \a f, along with any other faces containing
|
2012-02-28 18:28:30 +00:00
|
|
|
* those vertices
|
|
|
|
|
*/
|
2012-02-19 18:31:04 +00:00
|
|
|
void BM_face_verts_kill(BMesh *bm, BMFace *f)
|
|
|
|
|
{
|
2013-07-28 10:38:25 +00:00
|
|
|
BMVert **verts = BLI_array_alloca(verts, f->len);
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l_iter;
|
|
|
|
|
BMLoop *l_first;
|
2012-12-11 15:10:19 +00:00
|
|
|
int i = 0;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
do {
|
2012-12-11 15:10:19 +00:00
|
|
|
verts[i++] = l_iter->v;
|
2012-02-19 18:31:04 +00:00
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
2013-07-28 10:38:25 +00:00
|
|
|
for (i = 0; i < f->len; i++) {
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_vert_kill(bm, verts[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-02 11:10:54 +00:00
|
|
|
/**
|
|
|
|
|
* Kills \a f and its loops.
|
|
|
|
|
*/
|
2012-02-19 18:31:04 +00:00
|
|
|
void BM_face_kill(BMesh *bm, BMFace *f)
|
|
|
|
|
{
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BMLoopList *ls, *ls_next;
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(f);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
for (ls = f->loops.first; ls; ls = ls_next)
|
|
|
|
|
#else
|
|
|
|
|
if (f->l_first)
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
BMLoop *l_iter, *l_next, *l_first;
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
ls_next = ls->next;
|
|
|
|
|
l_iter = l_first = ls->first;
|
|
|
|
|
#else
|
|
|
|
|
l_iter = l_first = f->l_first;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
l_next = l_iter->next;
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_radial_loop_remove(l_iter, l_iter->e);
|
2012-02-28 19:10:53 +00:00
|
|
|
bm_kill_only_loop(bm, l_iter);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
} while ((l_iter = l_next) != l_first);
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BLI_mempool_free(bm->looplistpool, ls);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:10:53 +00:00
|
|
|
bm_kill_only_face(bm, f);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* kills \a e and all faces that use it.
|
|
|
|
|
*/
|
2012-02-19 18:31:04 +00:00
|
|
|
void BM_edge_kill(BMesh *bm, BMEdge *e)
|
|
|
|
|
{
|
|
|
|
|
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_disk_edge_remove(e, e->v1);
|
|
|
|
|
bmesh_disk_edge_remove(e, e->v2);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (e->l) {
|
|
|
|
|
BMLoop *l = e->l, *lnext, *startl = e->l;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
lnext = l->radial_next;
|
|
|
|
|
if (lnext->f == l->f) {
|
|
|
|
|
BM_face_kill(bm, l->f);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BM_face_kill(bm, l->f);
|
|
|
|
|
|
|
|
|
|
if (l == lnext)
|
|
|
|
|
break;
|
|
|
|
|
l = lnext;
|
|
|
|
|
} while (l != startl);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:10:53 +00:00
|
|
|
bm_kill_only_edge(bm, e);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* kills \a v and all edges that use it.
|
|
|
|
|
*/
|
2012-02-19 18:31:04 +00:00
|
|
|
void BM_vert_kill(BMesh *bm, BMVert *v)
|
|
|
|
|
{
|
|
|
|
|
if (v->e) {
|
2014-04-11 20:28:31 +10:00
|
|
|
BMEdge *e, *e_next;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
e = v->e;
|
|
|
|
|
while (v->e) {
|
2014-04-11 20:28:31 +10:00
|
|
|
e_next = bmesh_disk_edge_next(e, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_edge_kill(bm, e);
|
2014-04-11 20:28:31 +10:00
|
|
|
e = e_next;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 19:10:53 +00:00
|
|
|
bm_kill_only_vert(bm, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/********** private disk and radial cycle functions ********** */
|
|
|
|
|
|
2012-05-02 11:10:54 +00:00
|
|
|
/**
|
|
|
|
|
* return the length of the face, should always equal \a l->f->len
|
|
|
|
|
*/
|
|
|
|
|
static int UNUSED_FUNCTION(bm_loop_length)(BMLoop *l)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMLoop *l_first = l;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
i++;
|
|
|
|
|
} while ((l = l->next) != l_first);
|
|
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Loop Reverse
|
|
|
|
|
*
|
|
|
|
|
* Changes the winding order of a face from CW to CCW or vice versa.
|
2012-03-03 11:45:08 +00:00
|
|
|
* This euler is a bit peculiar in comparison to others as it is its
|
2012-02-29 06:55:10 +00:00
|
|
|
* own inverse.
|
|
|
|
|
*
|
|
|
|
|
* BMESH_TODO: reinsert validation code.
|
|
|
|
|
*
|
|
|
|
|
* \return Success
|
|
|
|
|
*/
|
2013-01-14 16:42:43 +00:00
|
|
|
static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f
|
2012-02-19 18:31:04 +00:00
|
|
|
#ifdef USE_BMESH_HOLES
|
2012-04-20 13:45:38 +00:00
|
|
|
, BMLoopList *lst
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
2012-04-20 13:45:38 +00:00
|
|
|
)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BMLoop *l_first = lst->first;
|
|
|
|
|
#else
|
|
|
|
|
BMLoop *l_first = f->l_first;
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-05-02 11:10:54 +00:00
|
|
|
const int len = f->len;
|
2013-01-14 16:42:43 +00:00
|
|
|
const bool do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
|
2012-02-19 18:31:04 +00:00
|
|
|
BMLoop *l_iter, *oldprev, *oldnext;
|
2012-12-11 15:10:19 +00:00
|
|
|
BMEdge **edar = BLI_array_alloca(edar, len);
|
2012-05-02 11:10:54 +00:00
|
|
|
int i, j, edok;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
|
2012-05-02 11:10:54 +00:00
|
|
|
bmesh_radial_loop_remove(l_iter, (edar[i] = l_iter->e));
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* actually reverse the loop */
|
|
|
|
|
for (i = 0, l_iter = l_first; i < len; i++) {
|
|
|
|
|
oldnext = l_iter->next;
|
|
|
|
|
oldprev = l_iter->prev;
|
|
|
|
|
l_iter->next = oldprev;
|
|
|
|
|
l_iter->prev = oldnext;
|
|
|
|
|
l_iter = oldnext;
|
|
|
|
|
|
|
|
|
|
if (do_disps) {
|
|
|
|
|
float (*co)[3];
|
|
|
|
|
int x, y, sides;
|
2012-05-02 11:10:54 +00:00
|
|
|
MDisps *md;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
md = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS);
|
|
|
|
|
if (!md->totdisp || !md->disps)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
sides = (int)sqrt(md->totdisp);
|
|
|
|
|
co = md->disps;
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < sides; x++) {
|
|
|
|
|
for (y = 0; y < x; y++) {
|
|
|
|
|
swap_v3_v3(co[y * sides + x], co[sides * x + y]);
|
2013-06-11 08:06:59 +00:00
|
|
|
SWAP(float, co[y * sides + x][0], co[y * sides + x][1]);
|
|
|
|
|
SWAP(float, co[x * sides + y][0], co[x * sides + y][1]);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-06-11 08:06:59 +00:00
|
|
|
SWAP(float, co[x * sides + x][0], co[x * sides + x][1]);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len == 2) { /* two edged face */
|
|
|
|
|
/* do some verification here! */
|
|
|
|
|
l_first->e = edar[1];
|
|
|
|
|
l_first->next->e = edar[0];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
|
|
|
|
|
edok = 0;
|
|
|
|
|
for (j = 0; j < len; j++) {
|
2013-12-23 16:03:07 +11:00
|
|
|
edok = BM_verts_in_edge(l_iter->v, l_iter->next->v, edar[j]);
|
2012-02-19 18:31:04 +00:00
|
|
|
if (edok) {
|
|
|
|
|
l_iter->e = edar[j];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-12-11 14:24:27 +00:00
|
|
|
/* rebuild radial */
|
2012-02-19 18:31:04 +00:00
|
|
|
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next)
|
|
|
|
|
bmesh_radial_append(l_iter->e, l_iter);
|
|
|
|
|
|
2012-12-11 14:24:27 +00:00
|
|
|
/* validate radial */
|
2012-02-19 18:31:04 +00:00
|
|
|
for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) {
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(l_iter);
|
|
|
|
|
BM_CHECK_ELEMENT(l_iter->e);
|
|
|
|
|
BM_CHECK_ELEMENT(l_iter->v);
|
|
|
|
|
BM_CHECK_ELEMENT(l_iter->f);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(f);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2014-05-28 18:34:26 +02:00
|
|
|
/* Loop indices are no more valid! */
|
|
|
|
|
bm->elem_index_dirty |= BM_LOOP;
|
|
|
|
|
|
2013-01-14 16:42:43 +00:00
|
|
|
return true;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-26 08:27:50 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Flip the faces direction
|
|
|
|
|
*/
|
2013-01-14 16:42:43 +00:00
|
|
|
bool bmesh_loop_reverse(BMesh *bm, BMFace *f)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
2012-10-15 09:11:17 +00:00
|
|
|
return bm_loop_reverse_loop(bm, f, f->loops.first);
|
2012-02-19 18:31:04 +00:00
|
|
|
#else
|
2012-02-28 19:10:53 +00:00
|
|
|
return bm_loop_reverse_loop(bm, f);
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 12:55:53 +10:00
|
|
|
static void bm_elements_systag_enable(void *veles, int tot, const char api_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMHeader **eles = veles;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tot; i++) {
|
2014-07-13 12:55:53 +10:00
|
|
|
BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], api_flag);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 12:55:53 +10:00
|
|
|
static void bm_elements_systag_disable(void *veles, int tot, const char api_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMHeader **eles = veles;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tot; i++) {
|
2014-07-13 12:55:53 +10:00
|
|
|
BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], api_flag);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 12:55:53 +10:00
|
|
|
static int bm_loop_systag_count_radial(BMLoop *l, const char api_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2014-07-13 12:55:53 +10:00
|
|
|
BMLoop *l_iter = l;
|
|
|
|
|
int i = 0;
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
2014-07-13 12:55:53 +10:00
|
|
|
i += BM_ELEM_API_FLAG_TEST(l_iter->f, api_flag) ? 1 : 0;
|
|
|
|
|
} while ((l_iter = l_iter->radial_next) != l);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 12:55:53 +10:00
|
|
|
static int UNUSED_FUNCTION(bm_vert_systag_count_disk)(BMVert *v, const char api_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *e = v->e;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
if (!e)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
do {
|
2014-07-13 12:55:53 +10:00
|
|
|
i += BM_ELEM_API_FLAG_TEST(e, api_flag) ? 1 : 0;
|
2012-04-19 21:47:32 +00:00
|
|
|
} while ((e = bmesh_disk_edge_next(e, v)) != v->e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 12:55:53 +10:00
|
|
|
static bool disk_is_flagged(BMVert *v, const char api_flag)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMEdge *e = v->e;
|
|
|
|
|
|
|
|
|
|
if (!e)
|
2013-01-14 16:42:43 +00:00
|
|
|
return false;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
BMLoop *l = e->l;
|
|
|
|
|
|
|
|
|
|
if (!l) {
|
2013-01-14 16:42:43 +00:00
|
|
|
return false;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bmesh_radial_length(l) == 1)
|
2013-01-14 16:42:43 +00:00
|
|
|
return false;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
do {
|
2014-07-13 12:55:53 +10:00
|
|
|
if (!BM_ELEM_API_FLAG_TEST(l->f, api_flag))
|
2013-01-14 16:42:43 +00:00
|
|
|
return false;
|
2014-04-11 20:28:31 +10:00
|
|
|
} while ((l = l->radial_next) != e->l);
|
|
|
|
|
} while ((e = bmesh_disk_edge_next(e, v)) != v->e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-01-14 16:42:43 +00:00
|
|
|
return true;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* Mid-level Topology Manipulation Functions */
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-28 18:28:30 +00:00
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Join Connected Faces
|
|
|
|
|
*
|
2012-02-19 18:31:04 +00:00
|
|
|
* Joins a collected group of faces into one. Only restriction on
|
|
|
|
|
* the input data is that the faces must be connected to each other.
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The newly created combine BMFace.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note If a pair of faces share multiple edges,
|
|
|
|
|
* the pair of faces will be joined at every edge.
|
2012-02-28 18:28:30 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note this is a generic, flexible join faces function,
|
|
|
|
|
* almost everything uses this, including #BM_faces_join_pair
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-01-14 16:42:43 +00:00
|
|
|
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2013-03-09 17:12:24 +00:00
|
|
|
BMFace *f, *f_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BMLoopList *lst;
|
|
|
|
|
ListBase holes = {NULL, NULL};
|
|
|
|
|
#endif
|
|
|
|
|
BMLoop *l_iter;
|
|
|
|
|
BMLoop *l_first;
|
|
|
|
|
BMEdge **edges = NULL;
|
|
|
|
|
BMEdge **deledges = NULL;
|
|
|
|
|
BMVert **delverts = NULL;
|
2012-11-12 05:53:43 +00:00
|
|
|
BLI_array_staticdeclare(edges, BM_DEFAULT_NGON_STACK_SIZE);
|
|
|
|
|
BLI_array_staticdeclare(deledges, BM_DEFAULT_NGON_STACK_SIZE);
|
|
|
|
|
BLI_array_staticdeclare(delverts, BM_DEFAULT_NGON_STACK_SIZE);
|
2012-02-19 18:31:04 +00:00
|
|
|
BMVert *v1 = NULL, *v2 = NULL;
|
|
|
|
|
const char *err = NULL;
|
|
|
|
|
int i, tote = 0;
|
|
|
|
|
|
2012-02-26 16:39:21 +00:00
|
|
|
if (UNLIKELY(!totface)) {
|
|
|
|
|
BMESH_ASSERT(0);
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (totface == 1)
|
|
|
|
|
return faces[0];
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
bm_elements_systag_enable(faces, totface, _FLAG_JF);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
for (i = 0; i < totface; i++) {
|
|
|
|
|
f = faces[i];
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
do {
|
2014-07-13 12:55:53 +10:00
|
|
|
int rlen = bm_loop_systag_count_radial(l_iter, _FLAG_JF);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (rlen > 2) {
|
2012-10-27 11:12:09 +00:00
|
|
|
err = N_("Input faces do not form a contiguous manifold region");
|
2012-02-19 18:31:04 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
else if (rlen == 1) {
|
|
|
|
|
BLI_array_append(edges, l_iter->e);
|
|
|
|
|
|
|
|
|
|
if (!v1) {
|
|
|
|
|
v1 = l_iter->v;
|
|
|
|
|
v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
|
|
|
|
|
}
|
|
|
|
|
tote++;
|
|
|
|
|
}
|
|
|
|
|
else if (rlen == 2) {
|
|
|
|
|
int d1, d2;
|
|
|
|
|
|
|
|
|
|
d1 = disk_is_flagged(l_iter->e->v1, _FLAG_JF);
|
|
|
|
|
d2 = disk_is_flagged(l_iter->e->v2, _FLAG_JF);
|
|
|
|
|
|
|
|
|
|
if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
|
|
|
|
|
/* don't remove an edge it makes up the side of another face
|
|
|
|
|
* else this will remove the face as well - campbell */
|
2015-04-12 15:21:40 +10:00
|
|
|
if (!BM_edge_face_count_is_over(l_iter->e, 3)) {
|
2012-04-04 14:48:10 +00:00
|
|
|
if (do_del) {
|
|
|
|
|
BLI_array_append(deledges, l_iter->e);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_ELEM_API_FLAG_ENABLE(l_iter->e, _FLAG_JF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) {
|
2012-04-04 14:48:10 +00:00
|
|
|
if (do_del) {
|
|
|
|
|
BLI_array_append(delverts, l_iter->e->v1);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_ELEM_API_FLAG_ENABLE(l_iter->e->v1, _FLAG_JF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) {
|
2012-04-04 14:48:10 +00:00
|
|
|
if (do_del) {
|
|
|
|
|
BLI_array_append(delverts, l_iter->e->v2);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_ELEM_API_FLAG_ENABLE(l_iter->e->v2, _FLAG_JF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
for (lst = f->loops.first; lst; lst = lst->next) {
|
2012-03-01 13:13:08 +00:00
|
|
|
if (lst == f->loops.first) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_remlink(&f->loops, lst);
|
|
|
|
|
BLI_addtail(&holes, lst);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-05 01:53:30 +00:00
|
|
|
/* create region face */
|
2013-08-21 07:51:47 +00:00
|
|
|
f_new = tote ? BM_face_create_ngon(bm, v1, v2, edges, tote, faces[0], BM_CREATE_NOP) : NULL;
|
2013-03-09 17:12:24 +00:00
|
|
|
if (UNLIKELY(!f_new || BMO_error_occurred(bm))) {
|
2012-02-19 18:31:04 +00:00
|
|
|
if (!BMO_error_occurred(bm))
|
2012-10-27 11:12:09 +00:00
|
|
|
err = N_("Invalid boundary region to join faces");
|
2012-02-19 18:31:04 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* copy over loop data */
|
2013-03-09 17:12:24 +00:00
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
|
|
|
|
BMLoop *l2 = l_iter->radial_next;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF))
|
|
|
|
|
break;
|
|
|
|
|
l2 = l2->radial_next;
|
|
|
|
|
} while (l2 != l_iter);
|
|
|
|
|
|
|
|
|
|
if (l2 != l_iter) {
|
2012-12-11 14:24:27 +00:00
|
|
|
/* I think this is correct? */
|
2012-02-19 18:31:04 +00:00
|
|
|
if (l2->v != l_iter->v) {
|
|
|
|
|
l2 = l2->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BM_elem_attrs_copy(bm, bm, l2, l_iter);
|
|
|
|
|
}
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
2012-12-11 14:24:27 +00:00
|
|
|
/* add holes */
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_movelisttolist(&f_new->loops, &holes);
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* update loop face pointer */
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
2013-03-09 17:12:24 +00:00
|
|
|
for (lst = f_new->loops.first; lst; lst = lst->next)
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
l_iter = l_first = lst->first;
|
|
|
|
|
#else
|
2013-03-09 17:12:24 +00:00
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
do {
|
2013-03-09 17:12:24 +00:00
|
|
|
l_iter->f = f_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
bm_elements_systag_disable(faces, totface, _FLAG_JF);
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_ELEM_API_FLAG_DISABLE(f_new, _FLAG_JF);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* handle multi-res data */
|
2012-02-19 18:31:04 +00:00
|
|
|
if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
|
2013-03-09 17:12:24 +00:00
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
do {
|
|
|
|
|
for (i = 0; i < totface; i++) {
|
|
|
|
|
BM_loop_interp_multires(bm, l_iter, faces[i]);
|
|
|
|
|
}
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-04 14:48:10 +00:00
|
|
|
/* delete old geometry */
|
|
|
|
|
if (do_del) {
|
|
|
|
|
for (i = 0; i < BLI_array_count(deledges); i++) {
|
|
|
|
|
BM_edge_kill(bm, deledges[i]);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-04 14:48:10 +00:00
|
|
|
for (i = 0; i < BLI_array_count(delverts); i++) {
|
|
|
|
|
BM_vert_kill(bm, delverts[i]);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-04-04 15:10:20 +00:00
|
|
|
else {
|
|
|
|
|
/* otherwise we get both old and new faces */
|
|
|
|
|
for (i = 0; i < totface; i++) {
|
2012-04-05 01:20:32 +00:00
|
|
|
BM_face_kill(bm, faces[i]);
|
2012-04-04 15:10:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
BLI_array_free(edges);
|
|
|
|
|
BLI_array_free(deledges);
|
|
|
|
|
BLI_array_free(delverts);
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_CHECK_ELEMENT(f_new);
|
|
|
|
|
return f_new;
|
2012-03-05 01:53:30 +00:00
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
error:
|
2012-04-19 14:38:09 +00:00
|
|
|
bm_elements_systag_disable(faces, totface, _FLAG_JF);
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_array_free(edges);
|
|
|
|
|
BLI_array_free(deledges);
|
|
|
|
|
BLI_array_free(delverts);
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
BMO_error_raise(bm, bm->currentop, BMERR_DISSOLVEFACES_FAILED, err);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-08 12:54:29 +10:00
|
|
|
static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *f_example)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMFace *f;
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BMLoopList *lst;
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-08 12:51:00 +10:00
|
|
|
f = bm_face_create__internal(bm);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
2012-02-28 19:30:44 +00:00
|
|
|
lst = BLI_mempool_calloc(bm->looplistpool);
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_addtail(&f->loops, lst);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
f->totbounds = 1;
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-09 22:15:25 +02:00
|
|
|
BM_elem_attrs_copy(bm, bm, f_example, f);
|
2014-04-08 12:51:00 +10:00
|
|
|
|
2012-02-25 20:58:03 +00:00
|
|
|
return f;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Split Face Make Edge (SFME)
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-10-16 16:04:12 +00:00
|
|
|
* \warning this is a low level function, most likely you want to use #BM_face_split()
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Takes as input two vertices in a single face. An edge is created which divides the original face
|
|
|
|
|
* into two distinct regions. One of the regions is assigned to the original face and it is closed off.
|
|
|
|
|
* The second region has a new face assigned to it.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \par Examples:
|
2012-08-13 15:17:15 +00:00
|
|
|
* <pre>
|
2012-02-19 18:31:04 +00:00
|
|
|
* Before: After:
|
2012-04-19 14:38:09 +00:00
|
|
|
* +--------+ +--------+
|
2012-02-29 06:55:10 +00:00
|
|
|
* | | | |
|
|
|
|
|
* | | | f1 |
|
|
|
|
|
* v1 f1 v2 v1======v2
|
|
|
|
|
* | | | f2 |
|
|
|
|
|
* | | | |
|
2012-04-19 14:38:09 +00:00
|
|
|
* +--------+ +--------+
|
2012-08-13 15:17:15 +00:00
|
|
|
* </pre>
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note the input vertices can be part of the same edge. This will
|
|
|
|
|
* result in a two edged face. This is desirable for advanced construction
|
|
|
|
|
* tools and particularly essential for edge bevel. Because of this it is
|
|
|
|
|
* up to the caller to decide what to do with the extra edge.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note If \a holes is NULL, then both faces will lose
|
|
|
|
|
* all holes from the original face. Also, you cannot split between
|
|
|
|
|
* a hole vert and a boundary vert; that case is handled by higher-
|
|
|
|
|
* level wrapping functions (when holes are fully implemented, anyway).
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note that holes represents which holes goes to the new face, and of
|
2012-03-03 11:45:08 +00:00
|
|
|
* course this requires removing them from the existing face first, since
|
2012-02-29 06:55:10 +00:00
|
|
|
* you cannot have linked list links inside multiple lists.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return A BMFace pointer
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-12-24 11:04:03 +11:00
|
|
|
BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2,
|
2012-02-27 21:33:30 +00:00
|
|
|
BMLoop **r_l,
|
2012-02-19 18:31:04 +00:00
|
|
|
#ifdef USE_BMESH_HOLES
|
2012-02-23 04:26:24 +00:00
|
|
|
ListBase *holes,
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
2014-08-21 13:04:56 +10:00
|
|
|
BMEdge *e_example,
|
2013-01-14 16:42:43 +00:00
|
|
|
const bool no_double
|
2012-02-19 18:31:04 +00:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
BMLoopList *lst, *lst2;
|
2012-10-15 09:11:17 +00:00
|
|
|
#else
|
|
|
|
|
int first_loop_f1;
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
BMFace *f2;
|
|
|
|
|
BMLoop *l_iter, *l_first;
|
2013-12-24 11:04:03 +11:00
|
|
|
BMLoop *l_f1 = NULL, *l_f2 = NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
BMEdge *e;
|
2013-12-24 11:04:03 +11:00
|
|
|
BMVert *v1 = l_v1->v, *v2 = l_v2->v;
|
|
|
|
|
int f1len, f2len;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-12-24 11:04:03 +11:00
|
|
|
BLI_assert(f == l_v1->f && f == l_v2->f);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* allocate new edge between v1 and v2 */
|
2014-08-21 13:04:56 +10:00
|
|
|
e = BM_edge_create(bm, v1, v2, e_example, no_double ? BM_CREATE_NO_DOUBLE : BM_CREATE_NOP);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-28 19:30:44 +00:00
|
|
|
f2 = bm_face_create__sfme(bm, f);
|
2013-03-09 17:12:24 +00:00
|
|
|
l_f1 = bm_loop_create(bm, v2, e, f, l_v2, 0);
|
|
|
|
|
l_f2 = bm_loop_create(bm, v1, e, f2, l_v1, 0);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
l_f1->prev = l_v2->prev;
|
|
|
|
|
l_f2->prev = l_v1->prev;
|
|
|
|
|
l_v2->prev->next = l_f1;
|
|
|
|
|
l_v1->prev->next = l_f2;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
l_f1->next = l_v1;
|
|
|
|
|
l_f2->next = l_v2;
|
|
|
|
|
l_v1->prev = l_f1;
|
|
|
|
|
l_v2->prev = l_f2;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
lst = f->loops.first;
|
|
|
|
|
lst2 = f2->loops.first;
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
lst2->first = lst2->last = l_f2;
|
|
|
|
|
lst->first = lst->last = l_f1;
|
2012-02-19 18:31:04 +00:00
|
|
|
#else
|
2012-04-10 11:07:02 +00:00
|
|
|
/* find which of the faces the original first loop is in */
|
2013-03-09 17:12:24 +00:00
|
|
|
l_iter = l_first = l_f1;
|
2012-04-10 11:07:02 +00:00
|
|
|
first_loop_f1 = 0;
|
|
|
|
|
do {
|
2012-04-20 13:45:38 +00:00
|
|
|
if (l_iter == f->l_first)
|
2012-04-10 11:07:02 +00:00
|
|
|
first_loop_f1 = 1;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
2012-04-20 13:45:38 +00:00
|
|
|
if (first_loop_f1) {
|
2012-04-10 11:07:02 +00:00
|
|
|
/* original first loop was in f1, find a suitable first loop for f2
|
2012-04-22 11:54:53 +00:00
|
|
|
* which is as similar as possible to f1. the order matters for tools
|
|
|
|
|
* such as duplifaces. */
|
2013-03-09 17:12:24 +00:00
|
|
|
if (f->l_first->prev == l_f1)
|
|
|
|
|
f2->l_first = l_f2->prev;
|
|
|
|
|
else if (f->l_first->next == l_f1)
|
|
|
|
|
f2->l_first = l_f2->next;
|
2012-04-10 11:07:02 +00:00
|
|
|
else
|
2013-03-09 17:12:24 +00:00
|
|
|
f2->l_first = l_f2;
|
2012-04-10 11:07:02 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* original first loop was in f2, further do same as above */
|
|
|
|
|
f2->l_first = f->l_first;
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
if (f->l_first->prev == l_f2)
|
|
|
|
|
f->l_first = l_f1->prev;
|
|
|
|
|
else if (f->l_first->next == l_f2)
|
|
|
|
|
f->l_first = l_f1->next;
|
2012-04-10 11:07:02 +00:00
|
|
|
else
|
2013-03-09 17:12:24 +00:00
|
|
|
f->l_first = l_f1;
|
2012-04-10 11:07:02 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* validate both loop */
|
2012-03-18 07:38:51 +00:00
|
|
|
/* I don't know how many loops are supposed to be in each face at this point! FIXME */
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* go through all of f2's loops and make sure they point to it properly */
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f2);
|
|
|
|
|
f2len = 0;
|
|
|
|
|
do {
|
|
|
|
|
l_iter->f = f2;
|
|
|
|
|
f2len++;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* link up the new loops into the new edges radial */
|
2013-03-09 17:12:24 +00:00
|
|
|
bmesh_radial_append(e, l_f1);
|
|
|
|
|
bmesh_radial_append(e, l_f2);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
f2->len = f2len;
|
|
|
|
|
|
|
|
|
|
f1len = 0;
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
|
|
|
do {
|
|
|
|
|
f1len++;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
f->len = f1len;
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
if (r_l) *r_l = l_f2;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
#ifdef USE_BMESH_HOLES
|
|
|
|
|
if (holes) {
|
|
|
|
|
BLI_movelisttolist(&f2->loops, holes);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* this code is not significant until holes actually work */
|
|
|
|
|
//printf("warning: call to split face euler without holes argument; holes will be tossed.\n");
|
|
|
|
|
for (lst = f->loops.last; lst != f->loops.first; lst = lst2) {
|
|
|
|
|
lst2 = lst->prev;
|
|
|
|
|
BLI_mempool_free(bm->looplistpool, lst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(e);
|
|
|
|
|
BM_CHECK_ELEMENT(f);
|
|
|
|
|
BM_CHECK_ELEMENT(f2);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return f2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Split Edge Make Vert (SEMV)
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Takes \a e edge and splits it into two, creating a new vert.
|
2012-03-06 12:09:35 +00:00
|
|
|
* \a tv should be one end of \a e : the newly created edge
|
|
|
|
|
* will be attached to that end and is returned in \a r_e.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \par Examples:
|
2012-11-26 00:59:11 +00:00
|
|
|
*
|
2012-08-13 15:17:15 +00:00
|
|
|
* <pre>
|
2012-03-06 12:09:35 +00:00
|
|
|
* E
|
|
|
|
|
* Before: OV-------------TV
|
|
|
|
|
* E RE
|
|
|
|
|
* After: OV------NV-----TV
|
2012-08-13 15:17:15 +00:00
|
|
|
* </pre>
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The newly created BMVert pointer.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2012-02-27 21:33:30 +00:00
|
|
|
BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2013-03-09 17:12:24 +00:00
|
|
|
BMLoop *l_next;
|
|
|
|
|
BMEdge *e_new;
|
|
|
|
|
BMVert *v_new, *v_old;
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
int valence1, valence2;
|
2013-01-14 16:42:43 +00:00
|
|
|
bool edok;
|
2013-07-28 09:05:27 +00:00
|
|
|
int i;
|
|
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-12-23 16:03:07 +11:00
|
|
|
BLI_assert(BM_vert_in_edge(e, tv) != false);
|
2012-03-03 14:48:50 +00:00
|
|
|
|
2013-12-23 16:03:07 +11:00
|
|
|
v_old = BM_edge_other_vert(e, tv);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
2013-03-09 17:12:24 +00:00
|
|
|
valence1 = bmesh_disk_count(v_old);
|
2012-02-19 18:31:04 +00:00
|
|
|
valence2 = bmesh_disk_count(tv);
|
2013-07-28 09:05:27 +00:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2014-09-18 13:39:10 +10:00
|
|
|
/* order of 'e_new' verts should match 'e'
|
|
|
|
|
* (so extruded faces don't flip) */
|
2013-08-21 05:39:46 +00:00
|
|
|
v_new = BM_vert_create(bm, tv->co, tv, BM_CREATE_NOP);
|
2014-09-18 13:39:10 +10:00
|
|
|
e_new = BM_edge_create(bm, tv, v_new, e, BM_CREATE_NOP);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
bmesh_disk_edge_remove(e_new, tv);
|
|
|
|
|
bmesh_disk_edge_remove(e_new, v_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-03-06 12:09:35 +00:00
|
|
|
/* remove e from tv's disk cycle */
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_disk_edge_remove(e, tv);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* swap out tv for v_new in e */
|
|
|
|
|
bmesh_edge_swapverts(e, tv, v_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* add e to v_new's disk cycle */
|
|
|
|
|
bmesh_disk_edge_append(e, v_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* add e_new to v_new's disk cycle */
|
|
|
|
|
bmesh_disk_edge_append(e_new, v_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* add e_new to tv's disk cycle */
|
|
|
|
|
bmesh_disk_edge_append(e_new, tv);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
2012-12-11 14:24:27 +00:00
|
|
|
/* verify disk cycles */
|
2013-03-09 17:12:24 +00:00
|
|
|
edok = bmesh_disk_validate(valence1, v_old->e, v_old);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
edok = bmesh_disk_validate(valence2, tv->e, tv);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2013-03-09 17:12:24 +00:00
|
|
|
edok = bmesh_disk_validate(2, v_new->e, v_new);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2013-07-28 09:05:27 +00:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* Split the radial cycle if present */
|
2013-03-09 17:12:24 +00:00
|
|
|
l_next = e->l;
|
2012-02-19 18:31:04 +00:00
|
|
|
e->l = NULL;
|
2013-03-09 17:12:24 +00:00
|
|
|
if (l_next) {
|
|
|
|
|
BMLoop *l_new, *l;
|
2013-08-05 12:49:13 +00:00
|
|
|
#ifndef NDEBUG
|
2013-03-09 17:12:24 +00:00
|
|
|
int radlen = bmesh_radial_length(l_next);
|
2013-08-05 12:49:13 +00:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
int first1 = 0, first2 = 0;
|
|
|
|
|
|
|
|
|
|
/* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
|
2013-03-09 17:12:24 +00:00
|
|
|
while (l_next) {
|
|
|
|
|
l = l_next;
|
2012-02-19 18:31:04 +00:00
|
|
|
l->f->len++;
|
2013-03-09 17:12:24 +00:00
|
|
|
l_next = l_next != l_next->radial_next ? l_next->radial_next : NULL;
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_radial_loop_remove(l, NULL);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
l_new = bm_loop_create(bm, NULL, NULL, l->f, l, 0);
|
|
|
|
|
l_new->prev = l;
|
|
|
|
|
l_new->next = (l->next);
|
|
|
|
|
l_new->prev->next = l_new;
|
|
|
|
|
l_new->next->prev = l_new;
|
|
|
|
|
l_new->v = v_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* assign the correct edge to the correct loop */
|
2013-12-23 16:03:07 +11:00
|
|
|
if (BM_verts_in_edge(l_new->v, l_new->next->v, e)) {
|
2013-03-09 17:12:24 +00:00
|
|
|
l_new->e = e;
|
|
|
|
|
l->e = e_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* append l into e_new's rad cycle */
|
2012-02-19 18:31:04 +00:00
|
|
|
if (!first1) {
|
|
|
|
|
first1 = 1;
|
|
|
|
|
l->radial_next = l->radial_prev = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!first2) {
|
|
|
|
|
first2 = 1;
|
|
|
|
|
l->radial_next = l->radial_prev = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
bmesh_radial_append(l_new->e, l_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
bmesh_radial_append(l->e, l);
|
|
|
|
|
}
|
2013-12-23 16:03:07 +11:00
|
|
|
else if (BM_verts_in_edge(l_new->v, l_new->next->v, e_new)) {
|
2013-03-09 17:12:24 +00:00
|
|
|
l_new->e = e_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
l->e = e;
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* append l into e_new's rad cycle */
|
2012-02-19 18:31:04 +00:00
|
|
|
if (!first1) {
|
|
|
|
|
first1 = 1;
|
|
|
|
|
l->radial_next = l->radial_prev = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!first2) {
|
|
|
|
|
first2 = 1;
|
|
|
|
|
l->radial_next = l->radial_prev = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
bmesh_radial_append(l_new->e, l_new);
|
2012-02-19 18:31:04 +00:00
|
|
|
bmesh_radial_append(l->e, l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
2012-04-07 12:37:15 +00:00
|
|
|
/* verify length of radial cycle */
|
2012-02-19 18:31:04 +00:00
|
|
|
edok = bmesh_radial_validate(radlen, e->l);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2013-03-09 17:12:24 +00:00
|
|
|
edok = bmesh_radial_validate(radlen, e_new->l);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* verify loop->v and loop->next->v pointers for e */
|
2012-02-19 18:31:04 +00:00
|
|
|
for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) {
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(l->e == e);
|
|
|
|
|
//BMESH_ASSERT(l->radial_next == l);
|
2013-03-09 17:12:24 +00:00
|
|
|
BMESH_ASSERT(!(l->prev->e != e_new && l->next->e != e_new));
|
2012-02-26 16:39:21 +00:00
|
|
|
|
2013-12-23 16:03:07 +11:00
|
|
|
edok = BM_verts_in_edge(l->v, l->next->v, e);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(l->v != l->next->v);
|
|
|
|
|
BMESH_ASSERT(l->e != l->next->e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-12-11 14:24:27 +00:00
|
|
|
/* verify loop cycle for kloop->f */
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(l);
|
|
|
|
|
BM_CHECK_ELEMENT(l->v);
|
|
|
|
|
BM_CHECK_ELEMENT(l->e);
|
|
|
|
|
BM_CHECK_ELEMENT(l->f);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-03-09 17:12:24 +00:00
|
|
|
/* verify loop->v and loop->next->v pointers for e_new */
|
|
|
|
|
for (i = 0, l = e_new->l; i < radlen; i++, l = l->radial_next) {
|
|
|
|
|
BMESH_ASSERT(l->e == e_new);
|
2012-02-26 16:39:21 +00:00
|
|
|
// BMESH_ASSERT(l->radial_next == l);
|
|
|
|
|
BMESH_ASSERT(!(l->prev->e != e && l->next->e != e));
|
2013-12-23 16:03:07 +11:00
|
|
|
edok = BM_verts_in_edge(l->v, l->next->v, e_new);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-26 16:39:21 +00:00
|
|
|
BMESH_ASSERT(l->v != l->next->v);
|
|
|
|
|
BMESH_ASSERT(l->e != l->next->e);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(l);
|
|
|
|
|
BM_CHECK_ELEMENT(l->v);
|
|
|
|
|
BM_CHECK_ELEMENT(l->e);
|
|
|
|
|
BM_CHECK_ELEMENT(l->f);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-07-28 09:05:27 +00:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_CHECK_ELEMENT(e_new);
|
|
|
|
|
BM_CHECK_ELEMENT(v_new);
|
|
|
|
|
BM_CHECK_ELEMENT(v_old);
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(e);
|
|
|
|
|
BM_CHECK_ELEMENT(tv);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
if (r_e) *r_e = e_new;
|
|
|
|
|
return v_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Join Edge Kill Vert (JEKV)
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2013-03-09 17:12:24 +00:00
|
|
|
* Takes an edge \a e_kill and pointer to one of its vertices \a v_kill
|
2012-02-29 06:55:10 +00:00
|
|
|
* and collapses the edge on that vertex.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \par Examples:
|
2012-09-06 02:10:09 +00:00
|
|
|
*
|
2012-08-13 15:17:15 +00:00
|
|
|
* <pre>
|
2012-02-29 06:55:10 +00:00
|
|
|
* Before: OE KE
|
|
|
|
|
* ------- -------
|
|
|
|
|
* | || |
|
|
|
|
|
* OV KV TV
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
|
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* After: OE
|
|
|
|
|
* ---------------
|
|
|
|
|
* | |
|
|
|
|
|
* OV TV
|
2012-08-13 15:17:15 +00:00
|
|
|
* </pre>
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \par Restrictions:
|
|
|
|
|
* KV is a vertex that must have a valance of exactly two. Furthermore
|
|
|
|
|
* both edges in KV's disk cycle (OE and KE) must be unique (no double edges).
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The resulting edge, NULL for failure.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note This euler has the possibility of creating
|
|
|
|
|
* faces with just 2 edges. It is up to the caller to decide what to do with
|
|
|
|
|
* these faces.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2014-06-27 20:11:23 +10:00
|
|
|
BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill,
|
|
|
|
|
const bool do_del, const bool check_edge_double)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2013-03-09 17:12:24 +00:00
|
|
|
BMEdge *e_old;
|
|
|
|
|
BMVert *v_old, *tv;
|
2013-07-28 09:05:27 +00:00
|
|
|
BMLoop *l_kill;
|
|
|
|
|
int len, radlen = 0, i;
|
2014-03-27 19:14:20 +06:00
|
|
|
bool halt = false;
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
bool edok;
|
|
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2014-06-27 20:11:23 +10:00
|
|
|
BLI_assert(BM_vert_in_edge(e_kill, v_kill));
|
|
|
|
|
|
2013-12-23 16:03:07 +11:00
|
|
|
if (BM_vert_in_edge(e_kill, v_kill) == 0) {
|
2012-02-26 04:38:37 +00:00
|
|
|
return NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
len = bmesh_disk_count(v_kill);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (len == 2) {
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
int valence1, valence2;
|
|
|
|
|
BMLoop *l;
|
|
|
|
|
#endif
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
e_old = bmesh_disk_edge_next(e_kill, v_kill);
|
2013-12-23 16:03:07 +11:00
|
|
|
tv = BM_edge_other_vert(e_kill, v_kill);
|
|
|
|
|
v_old = BM_edge_other_vert(e_old, v_kill);
|
|
|
|
|
halt = BM_verts_in_edge(v_kill, tv, e_old); /* check for double edges */
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (halt) {
|
2012-02-26 04:38:37 +00:00
|
|
|
return NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-02-26 04:38:37 +00:00
|
|
|
BMEdge *e_splice;
|
|
|
|
|
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
2013-03-09 17:12:24 +00:00
|
|
|
/* For verification later, count valence of v_old and tv */
|
|
|
|
|
valence1 = bmesh_disk_count(v_old);
|
2012-02-19 18:31:04 +00:00
|
|
|
valence2 = bmesh_disk_count(tv);
|
2013-07-28 09:05:27 +00:00
|
|
|
#endif
|
2012-02-26 04:38:37 +00:00
|
|
|
|
|
|
|
|
if (check_edge_double) {
|
2013-03-09 17:12:24 +00:00
|
|
|
e_splice = BM_edge_exists(tv, v_old);
|
2012-02-26 04:38:37 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* remove e_old from v_kill's disk cycle */
|
|
|
|
|
bmesh_disk_edge_remove(e_old, v_kill);
|
|
|
|
|
/* relink e_old->v_kill to be e_old->tv */
|
|
|
|
|
bmesh_edge_swapverts(e_old, v_kill, tv);
|
|
|
|
|
/* append e_old to tv's disk cycle */
|
|
|
|
|
bmesh_disk_edge_append(e_old, tv);
|
|
|
|
|
/* remove e_kill from tv's disk cycle */
|
|
|
|
|
bmesh_disk_edge_remove(e_kill, tv);
|
|
|
|
|
|
|
|
|
|
/* deal with radial cycle of e_kill */
|
|
|
|
|
radlen = bmesh_radial_length(e_kill->l);
|
|
|
|
|
if (e_kill->l) {
|
|
|
|
|
/* first step, fix the neighboring loops of all loops in e_kill's radial cycle */
|
|
|
|
|
for (i = 0, l_kill = e_kill->l; i < radlen; i++, l_kill = l_kill->radial_next) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* relink loops and fix vertex pointer */
|
2013-03-09 17:12:24 +00:00
|
|
|
if (l_kill->next->v == v_kill) {
|
|
|
|
|
l_kill->next->v = tv;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
l_kill->next->prev = l_kill->prev;
|
|
|
|
|
l_kill->prev->next = l_kill->next;
|
|
|
|
|
if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
|
|
|
|
|
BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-03-09 17:12:24 +00:00
|
|
|
l_kill->next = NULL;
|
|
|
|
|
l_kill->prev = NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* fix len attribute of face */
|
2013-03-09 17:12:24 +00:00
|
|
|
l_kill->f->len--;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-03-09 17:12:24 +00:00
|
|
|
/* second step, remove all the hanging loops attached to e_kill */
|
|
|
|
|
radlen = bmesh_radial_length(e_kill->l);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
if (LIKELY(radlen)) {
|
2012-12-11 15:10:19 +00:00
|
|
|
BMLoop **loops = BLI_array_alloca(loops, radlen);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
l_kill = e_kill->l;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-07 12:37:15 +00:00
|
|
|
/* this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well... */
|
2012-02-19 18:31:04 +00:00
|
|
|
for (i = 0; i < radlen; i++) {
|
2013-03-09 17:12:24 +00:00
|
|
|
loops[i] = l_kill;
|
|
|
|
|
l_kill = l_kill->radial_next;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
for (i = 0; i < radlen; i++) {
|
2014-07-24 09:15:38 +02:00
|
|
|
bm_kill_only_loop(bm, loops[i]);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
2014-03-25 07:52:22 +11:00
|
|
|
#ifndef NDEBUG
|
2013-03-09 17:12:24 +00:00
|
|
|
/* Validate radial cycle of e_old */
|
|
|
|
|
edok = bmesh_radial_validate(radlen, e_old->l);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2014-03-25 07:52:22 +11:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-10-19 03:07:58 +00:00
|
|
|
/* deallocate edge */
|
2013-03-09 17:12:24 +00:00
|
|
|
bm_kill_only_edge(bm, e_kill);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-10-19 03:07:58 +00:00
|
|
|
/* deallocate vertex */
|
2014-06-27 20:11:23 +10:00
|
|
|
if (do_del) {
|
|
|
|
|
bm_kill_only_vert(bm, v_kill);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
v_kill->e = NULL;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-07-28 09:05:27 +00:00
|
|
|
#ifndef NDEBUG
|
2013-03-09 17:12:24 +00:00
|
|
|
/* Validate disk cycle lengths of v_old, tv are unchanged */
|
|
|
|
|
edok = bmesh_disk_validate(valence1, v_old->e, v_old);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
edok = bmesh_disk_validate(valence2, tv->e, tv);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* Validate loop cycle of all faces attached to 'e_old' */
|
|
|
|
|
for (i = 0, l = e_old->l; i < radlen; i++, l = l->radial_next) {
|
|
|
|
|
BMESH_ASSERT(l->e == e_old);
|
2013-12-23 16:03:07 +11:00
|
|
|
edok = BM_verts_in_edge(l->v, l->next->v, e_old);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
edok = bmesh_loop_validate(l->f);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(l);
|
|
|
|
|
BM_CHECK_ELEMENT(l->v);
|
|
|
|
|
BM_CHECK_ELEMENT(l->e);
|
|
|
|
|
BM_CHECK_ELEMENT(l->f);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-07-28 09:05:27 +00:00
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-26 04:38:37 +00:00
|
|
|
if (check_edge_double) {
|
|
|
|
|
if (e_splice) {
|
|
|
|
|
/* removes e_splice */
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_edge_splice(bm, e_splice, e_old);
|
2012-02-26 04:38:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_CHECK_ELEMENT(v_old);
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(tv);
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_CHECK_ELEMENT(e_old);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
return e_old;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
2012-02-26 04:38:37 +00:00
|
|
|
return NULL;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2012-02-29 06:55:10 +00:00
|
|
|
* \brief Join Face Kill Edge (JFKE)
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-04-07 12:37:15 +00:00
|
|
|
* Takes two faces joined by a single 2-manifold edge and fuses them together.
|
2012-02-29 06:55:10 +00:00
|
|
|
* The edge shared by the faces must not be connected to any other edges which have
|
|
|
|
|
* Both faces in its radial cycle
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \par Examples:
|
2012-08-13 15:17:15 +00:00
|
|
|
* <pre>
|
2012-02-29 06:55:10 +00:00
|
|
|
* A B
|
2012-04-18 04:44:50 +00:00
|
|
|
* +--------+ +--------+
|
2012-02-29 06:55:10 +00:00
|
|
|
* | | | |
|
|
|
|
|
* | f1 | | f1 |
|
|
|
|
|
* v1========v2 = Ok! v1==V2==v3 == Wrong!
|
|
|
|
|
* | f2 | | f2 |
|
|
|
|
|
* | | | |
|
2012-04-18 04:44:50 +00:00
|
|
|
* +--------+ +--------+
|
2012-08-13 15:17:15 +00:00
|
|
|
* </pre>
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* In the example A, faces \a f1 and \a f2 are joined by a single edge,
|
|
|
|
|
* and the euler can safely be used.
|
|
|
|
|
* In example B however, \a f1 and \a f2 are joined by multiple edges and will produce an error.
|
|
|
|
|
* The caller in this case should call #bmesh_jekv on the extra edges
|
|
|
|
|
* before attempting to fuse \a f1 and \a f2.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \note The order of arguments decides whether or not certain per-face attributes are present
|
2012-04-07 12:37:15 +00:00
|
|
|
* in the resultant face. For instance vertex winding, material index, smooth flags, etc are inherited
|
2012-02-29 06:55:10 +00:00
|
|
|
* from \a f1, not \a f2.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return A BMFace pointer
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
|
|
|
|
BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
|
|
|
|
|
{
|
2013-03-09 17:12:24 +00:00
|
|
|
BMLoop *l_iter, *l_f1 = NULL, *l_f2 = NULL;
|
2013-03-17 10:26:23 +00:00
|
|
|
int newlen = 0, i, f1len = 0, f2len = 0;
|
|
|
|
|
bool edok;
|
2012-04-18 04:44:50 +00:00
|
|
|
/* can't join a face to itself */
|
2012-02-19 18:31:04 +00:00
|
|
|
if (f1 == f2) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 04:44:50 +00:00
|
|
|
/* validate that edge is 2-manifold edge */
|
|
|
|
|
if (!BM_edge_is_manifold(e)) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
/* verify that e is in both f1 and f2 */
|
|
|
|
|
f1len = f1->len;
|
|
|
|
|
f2len = f2->len;
|
2012-04-18 04:32:55 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
if (!((l_f1 = BM_face_edge_share_loop(f1, e)) &&
|
|
|
|
|
(l_f2 = BM_face_edge_share_loop(f2, e))))
|
2012-04-18 04:32:55 +00:00
|
|
|
{
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* validate direction of f2's loop cycle is compatible */
|
2013-03-09 17:12:24 +00:00
|
|
|
if (l_f1->v == l_f2->v) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* validate that for each face, each vertex has another edge in its disk cycle that is
|
|
|
|
|
* not e, and not shared. */
|
2013-08-03 15:30:57 +00:00
|
|
|
if (BM_edge_in_face(l_f1->next->e, f2) ||
|
|
|
|
|
BM_edge_in_face(l_f1->prev->e, f2) ||
|
|
|
|
|
BM_edge_in_face(l_f2->next->e, f1) ||
|
|
|
|
|
BM_edge_in_face(l_f2->prev->e, f1) )
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 04:44:50 +00:00
|
|
|
/* validate only one shared edge */
|
|
|
|
|
if (BM_face_share_edge_count(f1, f2) > 1) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* validate no internal join */
|
|
|
|
|
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
|
2012-04-18 04:32:55 +00:00
|
|
|
BM_elem_flag_disable(l_iter->v, BM_ELEM_INTERNAL_TAG);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
|
2012-04-18 04:32:55 +00:00
|
|
|
BM_elem_flag_disable(l_iter->v, BM_ELEM_INTERNAL_TAG);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
|
2013-03-09 17:12:24 +00:00
|
|
|
if (l_iter != l_f1) {
|
2012-04-18 04:32:55 +00:00
|
|
|
BM_elem_flag_enable(l_iter->v, BM_ELEM_INTERNAL_TAG);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
|
2013-03-09 17:12:24 +00:00
|
|
|
if (l_iter != l_f2) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* as soon as a duplicate is found, bail out */
|
2012-04-18 04:32:55 +00:00
|
|
|
if (BM_elem_flag_test(l_iter->v, BM_ELEM_INTERNAL_TAG)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* join the two loop */
|
2013-03-09 17:12:24 +00:00
|
|
|
l_f1->prev->next = l_f2->next;
|
|
|
|
|
l_f2->next->prev = l_f1->prev;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
l_f1->next->prev = l_f2->prev;
|
|
|
|
|
l_f2->prev->next = l_f1->next;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
/* if l_f1 was baseloop, make l_f1->next the base. */
|
|
|
|
|
if (BM_FACE_FIRST_LOOP(f1) == l_f1)
|
|
|
|
|
BM_FACE_FIRST_LOOP(f1) = l_f1->next;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* increase length of f1 */
|
|
|
|
|
f1->len += (f2->len - 2);
|
|
|
|
|
|
2012-04-18 04:44:50 +00:00
|
|
|
/* make sure each loop points to the proper face */
|
2012-02-19 18:31:04 +00:00
|
|
|
newlen = f1->len;
|
|
|
|
|
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next)
|
|
|
|
|
l_iter->f = f1;
|
|
|
|
|
|
|
|
|
|
/* remove edge from the disk cycle of its two vertices */
|
2013-03-09 17:12:24 +00:00
|
|
|
bmesh_disk_edge_remove(l_f1->e, l_f1->e->v1);
|
|
|
|
|
bmesh_disk_edge_remove(l_f1->e, l_f1->e->v2);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* deallocate edge and its two loops as well as f2 */
|
2012-12-12 05:04:01 +00:00
|
|
|
if (bm->etoolflagpool) {
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_mempool_free(bm->etoolflagpool, l_f1->e->oflags);
|
2012-11-18 12:14:22 +00:00
|
|
|
}
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_mempool_free(bm->epool, l_f1->e);
|
2012-02-19 18:31:04 +00:00
|
|
|
bm->totedge--;
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_mempool_free(bm->lpool, l_f1);
|
2012-02-19 18:31:04 +00:00
|
|
|
bm->totloop--;
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_mempool_free(bm->lpool, l_f2);
|
2012-02-19 18:31:04 +00:00
|
|
|
bm->totloop--;
|
2012-12-12 05:04:01 +00:00
|
|
|
if (bm->ftoolflagpool) {
|
|
|
|
|
BLI_mempool_free(bm->ftoolflagpool, f2->oflags);
|
2012-11-18 12:14:22 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_mempool_free(bm->fpool, f2);
|
|
|
|
|
bm->totface--;
|
|
|
|
|
/* account for both above */
|
2014-05-21 22:35:08 +02:00
|
|
|
bm->elem_index_dirty |= BM_EDGE | BM_LOOP | BM_FACE;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(f1);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* validate the new loop cycle */
|
|
|
|
|
edok = bmesh_loop_validate(f1);
|
2013-01-14 16:42:43 +00:00
|
|
|
BMESH_ASSERT(edok != false);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
return f1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-18 15:56:34 +10:00
|
|
|
/**
|
|
|
|
|
* Check if splicing vertices would create any double edges.
|
|
|
|
|
*
|
|
|
|
|
* \note assume caller will handle case where verts share an edge.
|
|
|
|
|
*/
|
|
|
|
|
bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b)
|
|
|
|
|
{
|
|
|
|
|
bool is_double = false;
|
|
|
|
|
|
|
|
|
|
BLI_assert(BM_edge_exists(v_a, v_b) == false);
|
|
|
|
|
|
|
|
|
|
if (v_a->e && v_b->e) {
|
|
|
|
|
SmallHash visit;
|
|
|
|
|
BMEdge *e, *e_first;
|
|
|
|
|
|
|
|
|
|
BLI_smallhash_init(&visit);
|
|
|
|
|
|
|
|
|
|
e = e_first = v_a->e;
|
|
|
|
|
do {
|
|
|
|
|
BMVert *v_other = BM_edge_other_vert(e, v_a);
|
|
|
|
|
BLI_smallhash_insert(&visit, (uintptr_t)v_other, NULL);
|
|
|
|
|
} while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
|
|
|
|
|
|
|
|
|
|
e = e_first = v_b->e;
|
|
|
|
|
do {
|
|
|
|
|
BMVert *v_other = BM_edge_other_vert(e, v_b);
|
|
|
|
|
if (BLI_smallhash_haskey(&visit, (uintptr_t)v_other)) {
|
|
|
|
|
is_double = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while ((e = BM_DISK_EDGE_NEXT(e, v_b)) != e_first);
|
|
|
|
|
|
|
|
|
|
BLI_smallhash_release(&visit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return is_double;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Splice Vert
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Merges two verts into one (\a v into \a vtarget).
|
|
|
|
|
*
|
|
|
|
|
* \return Success
|
2012-10-19 03:07:58 +00:00
|
|
|
*
|
|
|
|
|
* \warning This does't work for collapsing edges,
|
|
|
|
|
* where \a v and \a vtarget are connected by an edge
|
|
|
|
|
* (assert checks for this case).
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-01-14 16:42:43 +00:00
|
|
|
bool BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2012-11-12 05:53:43 +00:00
|
|
|
BMEdge *e;
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
/* verts already spliced */
|
2012-10-20 09:56:40 +00:00
|
|
|
if (v == v_target) {
|
2013-01-14 16:42:43 +00:00
|
|
|
return false;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2014-07-16 17:12:08 +10:00
|
|
|
BLI_assert(BM_vert_pair_share_face_check(v, v_target) == false);
|
|
|
|
|
|
2014-07-16 16:38:41 +10:00
|
|
|
/* move all the edges from v's disk to vtarget's disk */
|
|
|
|
|
while ((e = v->e)) {
|
2012-11-12 05:53:43 +00:00
|
|
|
|
2014-07-16 16:38:41 +10:00
|
|
|
/* loop */
|
|
|
|
|
BMLoop *l_first;
|
|
|
|
|
if ((l_first = e->l)) {
|
|
|
|
|
BMLoop *l_iter = l_first;
|
|
|
|
|
do {
|
|
|
|
|
if (l_iter->v == v) {
|
|
|
|
|
l_iter->v = v_target;
|
|
|
|
|
}
|
|
|
|
|
/* else if (l_iter->prev->v == v) {...}
|
|
|
|
|
* (this case will be handled by a different edge) */
|
|
|
|
|
} while ((l_iter = l_iter->radial_next) != l_first);
|
2012-11-12 05:53:43 +00:00
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2014-07-16 16:38:41 +10:00
|
|
|
/* disk */
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_disk_edge_remove(e, v);
|
2012-10-20 09:56:40 +00:00
|
|
|
bmesh_edge_swapverts(e, v, v_target);
|
|
|
|
|
bmesh_disk_edge_append(e, v_target);
|
2012-10-19 03:07:58 +00:00
|
|
|
BLI_assert(e->v1 != e->v2);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(v);
|
2012-10-20 09:56:40 +00:00
|
|
|
BM_CHECK_ELEMENT(v_target);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* v is unused now, and can be killed */
|
|
|
|
|
BM_vert_kill(bm, v);
|
|
|
|
|
|
2013-01-14 16:42:43 +00:00
|
|
|
return true;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
2012-03-09 00:01:38 +00:00
|
|
|
* \brief Separate Vert
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-03-09 00:01:38 +00:00
|
|
|
* Separates all disjoint fans that meet at a vertex, making a unique
|
2012-02-29 06:55:10 +00:00
|
|
|
* vertex for each region. returns an array of all resulting vertices.
|
|
|
|
|
*
|
2012-03-09 00:01:38 +00:00
|
|
|
* \note this is a low level function, bm_edge_separate needs to run on edges first
|
2012-03-23 07:08:28 +00:00
|
|
|
* or, the faces sharing verts must not be sharing edges for them to split at least.
|
2012-03-09 00:01:38 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return Success
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-08-13 01:00:07 +00:00
|
|
|
void bmesh_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len,
|
|
|
|
|
const bool copy_select)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2013-05-09 12:46:35 +00:00
|
|
|
const int v_edgetot = BM_vert_face_count(v);
|
|
|
|
|
BMEdge **stack = BLI_array_alloca(stack, v_edgetot);
|
2013-05-12 12:23:44 +00:00
|
|
|
STACK_DECLARE(stack);
|
2013-05-09 12:46:35 +00:00
|
|
|
|
|
|
|
|
SmallHash visithash;
|
2012-02-19 18:31:04 +00:00
|
|
|
BMVert **verts = NULL;
|
|
|
|
|
BMIter eiter, liter;
|
|
|
|
|
BMLoop *l;
|
|
|
|
|
BMEdge *e;
|
|
|
|
|
int i, maxindex;
|
2013-03-09 17:12:24 +00:00
|
|
|
BMLoop *l_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2014-02-02 17:08:26 +11:00
|
|
|
BLI_smallhash_init_ex(&visithash, v_edgetot);
|
2013-05-09 12:46:35 +00:00
|
|
|
|
2014-06-29 05:06:22 +10:00
|
|
|
STACK_INIT(stack, v_edgetot);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
maxindex = 0;
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
|
2013-05-09 12:46:35 +00:00
|
|
|
if (BLI_smallhash_haskey(&visithash, (uintptr_t)e)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Considering only edges and faces incident on vertex v, walk
|
|
|
|
|
* the edges & faces and assign an index to each connected set */
|
2014-07-17 17:46:55 +10:00
|
|
|
BLI_smallhash_insert(&visithash, (uintptr_t)e, SET_INT_IN_POINTER(maxindex));
|
2013-05-09 12:46:35 +00:00
|
|
|
do {
|
|
|
|
|
if (e->l) {
|
|
|
|
|
BMLoop *l_iter, *l_first;
|
|
|
|
|
l_iter = l_first = e->l;
|
|
|
|
|
do {
|
|
|
|
|
l_new = (l_iter->v == v) ? l_iter->prev : l_iter->next;
|
2014-07-17 17:46:55 +10:00
|
|
|
BLI_assert(BM_vert_in_edge(l_new->e, v));
|
2013-05-09 12:46:35 +00:00
|
|
|
if (!BLI_smallhash_haskey(&visithash, (uintptr_t)l_new->e)) {
|
2014-07-17 17:46:55 +10:00
|
|
|
BLI_smallhash_insert(&visithash, (uintptr_t)l_new->e, SET_INT_IN_POINTER(maxindex));
|
2013-05-09 12:46:35 +00:00
|
|
|
STACK_PUSH(stack, l_new->e);
|
|
|
|
|
}
|
|
|
|
|
} while ((l_iter = l_iter->radial_next) != l_first);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-05-09 12:46:35 +00:00
|
|
|
} while ((e = STACK_POP(stack)));
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
maxindex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make enough verts to split v for each group */
|
2013-05-09 12:46:35 +00:00
|
|
|
if (r_vout != NULL) {
|
|
|
|
|
verts = MEM_callocN(sizeof(BMVert *) * maxindex, __func__);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
verts = BLI_array_alloca(verts, maxindex);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
verts[0] = v;
|
|
|
|
|
for (i = 1; i < maxindex; i++) {
|
2013-08-21 05:39:46 +00:00
|
|
|
verts[i] = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
|
2013-08-13 01:00:07 +00:00
|
|
|
if (copy_select) {
|
|
|
|
|
BM_elem_select_copy(bm, bm, verts[i], v);
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Replace v with the new verts in each group */
|
2012-03-23 05:43:56 +00:00
|
|
|
#if 0
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
|
2012-03-23 05:18:03 +00:00
|
|
|
/* call first since its faster then a hash lookup */
|
|
|
|
|
if (l->v != v) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-02-19 18:31:04 +00:00
|
|
|
i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e));
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-01 12:20:18 +00:00
|
|
|
/* Loops here should always refer to an edge that has v as an
|
2012-02-19 18:31:04 +00:00
|
|
|
* endpoint. For each appearance of this vert in a face, there
|
|
|
|
|
* will actually be two iterations: one for the loop heading
|
|
|
|
|
* towards vertex v, and another for the loop heading out from
|
|
|
|
|
* vertex v. Only need to swap the vertex on one of those times,
|
|
|
|
|
* on the outgoing loop. */
|
2012-03-23 05:43:56 +00:00
|
|
|
|
|
|
|
|
/* XXX - because this clobbers the iterator, this *whole* block is commented, see below */
|
2012-03-23 05:18:03 +00:00
|
|
|
l->v = verts[i];
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2012-03-23 05:43:56 +00:00
|
|
|
#else
|
2012-04-07 12:37:15 +00:00
|
|
|
/* note: this is the same as the commented code above *except* that it doesn't break iterator
|
2012-03-23 05:43:56 +00:00
|
|
|
* by modifying data it loops over [#30632], this re-uses the 'stack' variable which is a bit
|
|
|
|
|
* bad practice but save alloc'ing a new array - note, the comment above is useful, keep it
|
|
|
|
|
* if you are tidying up code - campbell */
|
2014-06-29 05:06:22 +10:00
|
|
|
STACK_INIT(stack, v_edgetot);
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
|
2012-04-17 15:44:19 +00:00
|
|
|
if (l->v == v) {
|
2013-05-09 12:46:35 +00:00
|
|
|
STACK_PUSH(stack, (BMEdge *)l);
|
2012-03-23 05:43:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-05-09 12:46:35 +00:00
|
|
|
while ((l = (BMLoop *)(STACK_POP(stack)))) {
|
|
|
|
|
if ((i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)l->e)))) {
|
2012-04-17 15:44:19 +00:00
|
|
|
l->v = verts[i];
|
|
|
|
|
}
|
2012-03-23 05:43:56 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-04-19 13:47:58 +00:00
|
|
|
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
|
2013-05-09 12:46:35 +00:00
|
|
|
i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)e));
|
2012-02-19 18:31:04 +00:00
|
|
|
if (i == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_assert(e->v1 == v || e->v2 == v);
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_disk_edge_remove(e, v);
|
2012-02-19 18:31:04 +00:00
|
|
|
bmesh_edge_swapverts(e, v, verts[i]);
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_disk_edge_append(e, verts[i]);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2013-05-09 12:46:35 +00:00
|
|
|
BLI_smallhash_release(&visithash);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
for (i = 0; i < maxindex; i++) {
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(verts[i]);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-03-09 00:01:38 +00:00
|
|
|
if (r_vout_len != NULL) {
|
|
|
|
|
*r_vout_len = maxindex;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-03-09 00:01:38 +00:00
|
|
|
if (r_vout != NULL) {
|
|
|
|
|
*r_vout = verts;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-09 00:01:38 +00:00
|
|
|
/**
|
2012-08-17 14:43:20 +00:00
|
|
|
* High level function which wraps both #bmesh_vert_separate and #bmesh_edge_separate
|
2012-03-09 00:01:38 +00:00
|
|
|
*/
|
2013-08-12 23:49:56 +00:00
|
|
|
void BM_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len,
|
2012-03-09 00:01:38 +00:00
|
|
|
BMEdge **e_in, int e_in_len)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < e_in_len; i++) {
|
|
|
|
|
BMEdge *e = e_in[i];
|
|
|
|
|
if (e->l && BM_vert_in_edge(e, v)) {
|
2013-08-13 01:00:07 +00:00
|
|
|
bmesh_edge_separate(bm, e, e->l, false);
|
2012-03-09 00:01:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-13 01:00:07 +00:00
|
|
|
bmesh_vert_separate(bm, v, r_vout, r_vout_len, false);
|
2012-03-09 00:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Splice Edge
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* Splice two unique edges which share the same two vertices into one edge.
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return Success
|
|
|
|
|
*
|
|
|
|
|
* \note Edges must already have the same vertices.
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-01-14 16:42:43 +00:00
|
|
|
bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMLoop *l;
|
|
|
|
|
|
2012-10-20 09:56:40 +00:00
|
|
|
if (!BM_vert_in_edge(e, e_target->v1) || !BM_vert_in_edge(e, e_target->v2)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* not the same vertices can't splice */
|
2012-10-20 09:56:40 +00:00
|
|
|
|
|
|
|
|
/* the caller should really make sure this doesn't happen ever
|
|
|
|
|
* so assert on release builds */
|
|
|
|
|
BLI_assert(0);
|
|
|
|
|
|
2013-01-14 16:42:43 +00:00
|
|
|
return false;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (e->l) {
|
|
|
|
|
l = e->l;
|
2012-10-20 09:56:40 +00:00
|
|
|
BLI_assert(BM_vert_in_edge(e_target, l->v));
|
|
|
|
|
BLI_assert(BM_vert_in_edge(e_target, l->next->v));
|
2012-02-27 13:47:53 +00:00
|
|
|
bmesh_radial_loop_remove(l, e);
|
2012-10-20 09:56:40 +00:00
|
|
|
bmesh_radial_append(e_target, l);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_assert(bmesh_radial_length(e->l) == 0);
|
|
|
|
|
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(e);
|
2012-10-20 09:56:40 +00:00
|
|
|
BM_CHECK_ELEMENT(e_target);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-02-26 04:38:37 +00:00
|
|
|
/* removes from disks too */
|
2012-02-19 18:31:04 +00:00
|
|
|
BM_edge_kill(bm, e);
|
|
|
|
|
|
2013-01-14 16:42:43 +00:00
|
|
|
return true;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
2012-03-09 00:01:38 +00:00
|
|
|
* \brief Separate Edge
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-03-09 00:01:38 +00:00
|
|
|
* Separates a single edge into two edge: the original edge and
|
|
|
|
|
* a new edge that has only \a l_sep in its radial.
|
2012-02-29 06:55:10 +00:00
|
|
|
*
|
|
|
|
|
* \return Success
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-03-09 00:01:38 +00:00
|
|
|
* \note Does nothing if \a l_sep is already the only loop in the
|
2012-02-19 18:31:04 +00:00
|
|
|
* edge radial.
|
|
|
|
|
*/
|
2013-08-13 01:00:07 +00:00
|
|
|
void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep,
|
|
|
|
|
const bool copy_select)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2013-03-09 17:12:24 +00:00
|
|
|
BMEdge *e_new;
|
2013-08-12 22:36:46 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
const int radlen = bmesh_radial_length(e->l);
|
|
|
|
|
#endif
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2012-03-09 00:01:38 +00:00
|
|
|
BLI_assert(l_sep->e == e);
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_assert(e->l);
|
|
|
|
|
|
2013-08-12 22:36:46 +00:00
|
|
|
if (BM_edge_is_boundary(e)) {
|
2012-02-19 18:31:04 +00:00
|
|
|
/* no cut required */
|
2013-08-12 22:36:46 +00:00
|
|
|
return;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-03-09 00:01:38 +00:00
|
|
|
if (l_sep == e->l) {
|
|
|
|
|
e->l = l_sep->radial_next;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2013-08-21 05:39:46 +00:00
|
|
|
e_new = BM_edge_create(bm, e->v1, e->v2, e, BM_CREATE_NOP);
|
2012-03-09 00:01:38 +00:00
|
|
|
bmesh_radial_loop_remove(l_sep, e);
|
2013-03-09 17:12:24 +00:00
|
|
|
bmesh_radial_append(e_new, l_sep);
|
|
|
|
|
l_sep->e = e_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-08-13 01:00:07 +00:00
|
|
|
if (copy_select) {
|
|
|
|
|
BM_elem_select_copy(bm, bm, e_new, e);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-19 18:31:04 +00:00
|
|
|
BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_assert(bmesh_radial_length(e_new->l) == 1);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
BM_CHECK_ELEMENT(e_new);
|
2012-04-19 14:38:09 +00:00
|
|
|
BM_CHECK_ELEMENT(e);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
2013-02-11 00:49:00 +00:00
|
|
|
* \brief Un-glue Region Make Vert (URMV)
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2013-03-09 17:12:24 +00:00
|
|
|
* Disconnects a face from its vertex fan at loop \a l_sep
|
2012-02-29 06:55:10 +00:00
|
|
|
*
|
|
|
|
|
* \return The newly created BMVert
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-03-09 17:12:24 +00:00
|
|
|
BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *l_sep)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
|
|
|
|
BMVert **vtar;
|
|
|
|
|
int len, i;
|
2013-03-09 17:12:24 +00:00
|
|
|
BMVert *v_new = NULL;
|
|
|
|
|
BMVert *v_sep = l_sep->v;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* peel the face from the edge radials on both sides of the
|
|
|
|
|
* loop vert, disconnecting the face from its fan */
|
2013-08-13 01:00:07 +00:00
|
|
|
bmesh_edge_separate(bm, l_sep->e, l_sep, false);
|
|
|
|
|
bmesh_edge_separate(bm, l_sep->prev->e, l_sep->prev, false);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
if (bmesh_disk_count(v_sep) == 2) {
|
|
|
|
|
/* If there are still only two edges out of v_sep, then
|
2012-02-19 18:31:04 +00:00
|
|
|
* this whole URMV was just a no-op, so exit now. */
|
2013-03-09 17:12:24 +00:00
|
|
|
return v_sep;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update the disk start, so that v->e points to an edge
|
2012-03-09 00:01:38 +00:00
|
|
|
* not touching the split loop. This is so that BM_vert_split
|
2013-03-09 17:12:24 +00:00
|
|
|
* will leave the original v_sep on some *other* fan (not the
|
2012-02-19 18:31:04 +00:00
|
|
|
* one-face fan that holds the unglue face). */
|
2013-03-09 17:12:24 +00:00
|
|
|
while (v_sep->e == l_sep->e || v_sep->e == l_sep->prev->e) {
|
|
|
|
|
v_sep->e = bmesh_disk_edge_next(v_sep->e, v_sep);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Split all fans connected to the vert, duplicating it for
|
|
|
|
|
* each fans. */
|
2013-08-13 01:00:07 +00:00
|
|
|
bmesh_vert_separate(bm, v_sep, &vtar, &len, false);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* There should have been at least two fans cut apart here,
|
|
|
|
|
* otherwise the early exit would have kicked in. */
|
|
|
|
|
BLI_assert(len >= 2);
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
v_new = l_sep->v;
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* Desired result here is that a new vert should always be
|
|
|
|
|
* created for the unglue face. This is so we can glue any
|
|
|
|
|
* extras back into the original vert. */
|
2013-03-09 17:12:24 +00:00
|
|
|
BLI_assert(v_new != v_sep);
|
|
|
|
|
BLI_assert(v_sep == vtar[0]);
|
2012-02-19 18:31:04 +00:00
|
|
|
|
|
|
|
|
/* If there are more than two verts as a result, glue together
|
|
|
|
|
* all the verts except the one this URMV intended to create */
|
|
|
|
|
if (len > 2) {
|
|
|
|
|
for (i = 0; i < len; i++) {
|
2013-03-09 17:12:24 +00:00
|
|
|
if (vtar[i] == v_new) {
|
2012-02-19 18:31:04 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i != len) {
|
|
|
|
|
/* Swap the single vert that was needed for the
|
|
|
|
|
* unglue into the last array slot */
|
|
|
|
|
SWAP(BMVert *, vtar[i], vtar[len - 1]);
|
|
|
|
|
|
|
|
|
|
/* And then glue the rest back together */
|
|
|
|
|
for (i = 1; i < len - 1; i++) {
|
2012-03-19 09:47:32 +00:00
|
|
|
BM_vert_splice(bm, vtar[i], vtar[0]);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MEM_freeN(vtar);
|
|
|
|
|
|
2013-03-09 17:12:24 +00:00
|
|
|
return v_new;
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
2012-02-29 06:55:10 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Unglue Region Make Vert (URMV)
|
|
|
|
|
*
|
2013-03-09 17:12:24 +00:00
|
|
|
* Disconnects f_sep from the vertex fan at \a v_sep
|
2012-02-19 18:31:04 +00:00
|
|
|
*
|
2012-02-29 06:55:10 +00:00
|
|
|
* \return The newly created BMVert
|
2012-02-19 18:31:04 +00:00
|
|
|
*/
|
2013-03-09 17:12:24 +00:00
|
|
|
BMVert *bmesh_urmv(BMesh *bm, BMFace *f_sep, BMVert *v_sep)
|
2012-02-19 18:31:04 +00:00
|
|
|
{
|
2013-03-09 17:12:24 +00:00
|
|
|
BMLoop *l = BM_face_vert_share_loop(f_sep, v_sep);
|
2012-03-08 20:00:37 +00:00
|
|
|
return bmesh_urmv_loop(bm, l);
|
2012-02-19 18:31:04 +00:00
|
|
|
}
|
2013-10-08 19:28:11 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Avoid calling this where possible,
|
2013-10-08 20:18:38 +00:00
|
|
|
* low level function so both face pointers remain intact but point to swapped data.
|
|
|
|
|
* \note must be from the same bmesh.
|
2013-10-08 19:28:11 +00:00
|
|
|
*/
|
2013-11-27 08:08:00 +11:00
|
|
|
void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b)
|
2013-10-08 19:28:11 +00:00
|
|
|
{
|
|
|
|
|
BMLoop *l_iter, *l_first;
|
|
|
|
|
|
|
|
|
|
BLI_assert(f_a != f_b);
|
|
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
|
|
|
|
|
do {
|
|
|
|
|
l_iter->f = f_b;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f_b);
|
|
|
|
|
do {
|
|
|
|
|
l_iter->f = f_a;
|
|
|
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
|
|
|
|
|
|
|
|
SWAP(BMFace, (*f_a), (*f_b));
|
2013-11-27 08:08:00 +11:00
|
|
|
|
|
|
|
|
/* swap back */
|
|
|
|
|
SWAP(void *, f_a->head.data, f_b->head.data);
|
|
|
|
|
SWAP(int, f_a->head.index, f_b->head.index);
|
2013-10-08 19:28:11 +00:00
|
|
|
}
|