diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-02-19 22:31:04 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-02-19 22:31:04 +0400 |
commit | afc56a0b10b52d2c8bdfd44257624d776db72f79 (patch) | |
tree | 25d194e29b2708ac1143b3dde6dfbeec7ef1d876 /source/blender/bmesh/intern | |
parent | d6deca4e9d6bc7faff98644286571c7934324a9d (diff) |
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh/intern')
22 files changed, 15354 insertions, 0 deletions
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c new file mode 100644 index 00000000000..ac37041b073 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -0,0 +1,773 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_construct.c + * \ingroup bmesh + * + * BM construction functions. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "BKE_customdata.h" + +#include "DNA_meshdata_types.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#define SELECT 1 + +/* prototypes */ +static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMLoop *source_loop, BMLoop *target_loop); + +/* + * BMESH MAKE QUADTRIANGLE + * + * Creates a new quad or triangle from + * a list of 3 or 4 vertices. If nodouble + * equals 1, then a check is done to see + * if a face with these vertices already + * exists and returns it instead. If a pointer + * to an example face is provided, it's custom + * data and properties will be copied to the new + * face. + * + * Note that the winding of the face is determined + * by the order of the vertices in the vertex array + */ + +BMFace *BM_face_create_quad_tri(BMesh *bm, + BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, + const BMFace *example, const int nodouble) +{ + BMVert *vtar[4] = {v1, v2, v3, v4}; + return BM_face_create_quad_tri_v(bm, vtar, v4 ? 4 : 3, example, nodouble); +} + +/* remove the edge array bits from this. Its not really needed? */ +BMFace *BM_face_create_quad_tri_v(BMesh *bm, BMVert **verts, int len, const BMFace *example, const int nodouble) +{ + BMEdge *edar[4] = {NULL}; + BMFace *f = NULL; + int overlap = 0; + + edar[0] = BM_edge_exists(verts[0], verts[1]); + edar[1] = BM_edge_exists(verts[1], verts[2]); + if (len == 4) { + edar[2] = BM_edge_exists(verts[2], verts[3]); + edar[3] = BM_edge_exists(verts[3], verts[0]); + } + else { + edar[2] = BM_edge_exists(verts[2], verts[0]); + } + + if (nodouble) { + /* check if face exists or overlaps */ + if (len == 4) { + overlap = BM_face_exists_overlap(bm, verts, len, &f); + } + else { + overlap = BM_face_exists_overlap(bm, verts, len, &f); + } + } + + /* make new face */ + if ((!f) && (!overlap)) { + if (!edar[0]) edar[0] = BM_edge_create(bm, verts[0], verts[1], NULL, FALSE); + if (!edar[1]) edar[1] = BM_edge_create(bm, verts[1], verts[2], NULL, FALSE); + if (len == 4) { + if (!edar[2]) edar[2] = BM_edge_create(bm, verts[2], verts[3], NULL, FALSE); + if (!edar[3]) edar[3] = BM_edge_create(bm, verts[3], verts[0], NULL, FALSE); + } + else { + if (!edar[2]) edar[2] = BM_edge_create(bm, verts[2], verts[0], NULL, FALSE); + } + + f = BM_face_create(bm, verts, edar, len, FALSE); + + if (example && f) { + BM_elem_attrs_copy(bm, bm, example, f); + } + } + + return f; +} + + +/* copies face data from shared adjacent faces */ +void BM_face_copy_shared(BMesh *bm, BMFace *f) +{ + BMIter iter; + BMLoop *l, *l2; + + if (!f) return; + + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&iter)) { + l2 = l->radial_next; + + if (l2 && l2 != l) { + if (l2->v == l->v) { + bm_loop_attrs_copy(bm, bm, l2, l); + } + else { + l2 = l2->next; + bm_loop_attrs_copy(bm, bm, l2, l); + } + } + } +} + +/* + * BMESH MAKE NGON + * + * Attempts to make a new Ngon from a list of edges. + * If nodouble equals one, a check for overlaps or existing + * + * The edges are not required to be ordered, simply to to form + * a single closed loop as a whole + * + * Note that while this function will work fine when the edges + * are already sorted, if the edges are always going to be sorted, + * BM_face_create should be considered over this function as it + * avoids some unnecessary work. + */ +BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble) +{ + BMEdge **edges2 = NULL; + BLI_array_staticdeclare(edges2, BM_NGON_STACK_SIZE); + BMVert **verts = NULL, *v; + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + BMFace *f = NULL; + BMEdge *e; + BMVert *ev1, *ev2; + int i, /* j, */ v1found, reverse; + + /* this code is hideous, yeek. I'll have to think about ways of + * cleaning it up. basically, it now combines the old BM_face_create_ngon + * _and_ the old bmesh_mf functions, so its kindof smashed together + * - joeedh */ + + if (!len || !v1 || !v2 || !edges || !bm) + return NULL; + + /* put edges in correct order */ + for (i = 0; i < len; i++) { + BM_ELEM_API_FLAG_ENABLE(edges[i], _FLAG_MF); + } + + ev1 = edges[0]->v1; + ev2 = edges[0]->v2; + + if (v1 == ev2) { + /* Swapping here improves performance and consistency of face + * structure in the special case that the edges are already in + * the correct order and winding */ + SWAP(BMVert *, ev1, ev2); + } + + BLI_array_append(verts, ev1); + v = ev2; + e = edges[0]; + do { + BMEdge *e2 = e; + + BLI_array_append(verts, v); + BLI_array_append(edges2, e); + + do { + e2 = bmesh_disk_nextedge(e2, v); + if (e2 != e && BM_ELEM_API_FLAG_TEST(e2, _FLAG_MF)) { + v = BM_edge_other_vert(e2, v); + break; + } + } while (e2 != e); + + if (e2 == e) + goto err; /* the edges do not form a closed loop */ + + e = e2; + } while (e != edges[0]); + + if (BLI_array_count(edges2) != len) { + goto err; /* we didn't use all edges in forming the boundary loop */ + } + + /* ok, edges are in correct order, now ensure they are going + * in the correct direction */ + v1found = reverse = FALSE; + for (i = 0; i < len; i++) { + if (BM_vert_in_edge(edges2[i], v1)) { + /* see if v1 and v2 are in the same edge */ + if (BM_vert_in_edge(edges2[i], v2)) { + /* if v1 is shared by the *next* edge, then the winding + * is incorrect */ + if (BM_vert_in_edge(edges2[(i + 1) % len], v1)) { + reverse = TRUE; + break; + } + } + + v1found = TRUE; + } + + if ((v1found == FALSE) && BM_vert_in_edge(edges2[i], v2)) { + reverse = TRUE; + break; + } + } + + if (reverse) { + for (i = 0; i < len / 2; i++) { + v = verts[i]; + verts[i] = verts[len - i - 1]; + verts[len - i - 1] = v; + } + } + + for (i = 0; i < len; i++) { + edges2[i] = BM_edge_exists(verts[i], verts[(i + 1) % len]); + if (!edges2[i]) { + goto err; + } + } + + f = BM_face_create(bm, verts, edges2, len, nodouble); + + /* clean up flags */ + for (i = 0; i < len; i++) { + BM_ELEM_API_FLAG_DISABLE(edges2[i], _FLAG_MF); + } + + BLI_array_free(verts); + BLI_array_free(edges2); + + return f; + +err: + for (i = 0; i < len; i++) { + BM_ELEM_API_FLAG_DISABLE(edges[i], _FLAG_MF); + } + + BLI_array_free(verts); + BLI_array_free(edges2); + + return NULL; +} + + +/* bmesh_make_face_from_face(BMesh *bm, BMFace *source, BMFace *target) */ + + +/* + * REMOVE TAGGED XXX + * + * Called by operators to remove elements that they have marked for + * removal. + * + */ + +void BMO_remove_tagged_faces(BMesh *bm, const short oflag) +{ + BMFace *f; + BMIter iter; + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, f, oflag)) { + BM_face_kill(bm, f); + } + } +} + +void BMO_remove_tagged_edges(BMesh *bm, const short oflag) +{ + BMEdge *e; + BMIter iter; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, oflag)) { + BM_edge_kill(bm, e); + } + } +} + +void BMO_remove_tagged_verts(BMesh *bm, const short oflag) +{ + BMVert *v; + BMIter iter; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, oflag)) { + BM_vert_kill(bm, v); + } + } +} + +/*************************************************************/ +/* you need to make remove tagged verts/edges/faces + * api functions that take a filter callback..... + * and this new filter type will be for opstack flags. + * This is because the BM_remove_taggedXXX functions bypass iterator API. + * - Ops dont care about 'UI' considerations like selection state, hide state, ect. + * If you want to work on unhidden selections for instance, + * copy output from a 'select context' operator to another operator.... + */ + +static void bmo_remove_tagged_context_verts(BMesh *bm, const short oflag) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) { + if (BMO_elem_flag_test(bm, v, oflag)) { + /* Visit edge */ + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_VERT, v); e; e = BM_iter_step(&edges)) + BMO_elem_flag_enable(bm, e, oflag); + /* Visit face */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_VERT, v); f; f = BM_iter_step(&faces)) + BMO_elem_flag_enable(bm, f, oflag); + } + } + + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); + BMO_remove_tagged_verts(bm, oflag); +} + +static void bmo_remove_tagged_context_edges(BMesh *bm, const short oflag) +{ + BMEdge *e; + BMFace *f; + + BMIter edges; + BMIter faces; + + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BMO_elem_flag_test(bm, e, oflag)) { + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_EDGE, e); f; f = BM_iter_step(&faces)) { + BMO_elem_flag_enable(bm, f, oflag); + } + } + } + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); +} + +#define DEL_WIREVERT (1 << 10) + +/* warning, oflag applies to different types in some contexts, + * not just the type being removed */ +void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + switch (type) { + case DEL_VERTS: + { + bmo_remove_tagged_context_verts(bm, oflag); + + break; + } + case DEL_EDGES: + { + /* flush down to vert */ + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BMO_elem_flag_test(bm, e, oflag)) { + BMO_elem_flag_enable(bm, e->v1, oflag); + BMO_elem_flag_enable(bm, e->v2, oflag); + } + } + bmo_remove_tagged_context_edges(bm, oflag); + /* remove loose vertice */ + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) { + if (BMO_elem_flag_test(bm, v, oflag) && (!(v->e))) + BMO_elem_flag_enable(bm, v, DEL_WIREVERT); + } + BMO_remove_tagged_verts(bm, DEL_WIREVERT); + + break; + } + case DEL_EDGESFACES: + { + bmo_remove_tagged_context_edges(bm, oflag); + + break; + } + case DEL_ONLYFACES: + { + BMO_remove_tagged_faces(bm, oflag); + + break; + } + case DEL_ONLYTAGGED: + { + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); + BMO_remove_tagged_verts(bm, oflag); + + break; + } + case DEL_FACES: + { + /* go through and mark all edges and all verts of all faces for delet */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + if (BMO_elem_flag_test(bm, f, oflag)) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) + BMO_elem_flag_enable(bm, e, oflag); + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) + BMO_elem_flag_enable(bm, v, oflag); + } + } + /* now go through and mark all remaining faces all edges for keeping */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + if (!BMO_elem_flag_test(bm, f, oflag)) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) { + BMO_elem_flag_disable(bm, e, oflag); + } + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) { + BMO_elem_flag_disable(bm, v, oflag); + } + } + } + /* also mark all the vertices of remaining edges for keeping */ + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (!BMO_elem_flag_test(bm, e, oflag)) { + BMO_elem_flag_disable(bm, e->v1, oflag); + BMO_elem_flag_disable(bm, e->v2, oflag); + } + } + /* now delete marked face */ + BMO_remove_tagged_faces(bm, oflag); + /* delete marked edge */ + BMO_remove_tagged_edges(bm, oflag); + /* remove loose vertice */ + BMO_remove_tagged_verts(bm, oflag); + + break; + } + case DEL_ALL: + { + /* does this option even belong in here? */ + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) + BMO_elem_flag_enable(bm, f, oflag); + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) + BMO_elem_flag_enable(bm, e, oflag); + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) + BMO_elem_flag_enable(bm, v, oflag); + + BMO_remove_tagged_faces(bm, oflag); + BMO_remove_tagged_edges(bm, oflag); + BMO_remove_tagged_verts(bm, oflag); + + break; + } + } +} +/*************************************************************/ + + +static void bm_vert_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMVert *source_vertex, BMVert *target_vertex) +{ + if ((source_mesh == target_mesh) && (source_vertex == target_vertex)) { + return; + } + copy_v3_v3(target_vertex->no, source_vertex->no); + CustomData_bmesh_free_block(&target_mesh->vdata, &target_vertex->head.data); + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, + source_vertex->head.data, &target_vertex->head.data); +} + +static void bm_edge_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMEdge *source_edge, BMEdge *target_edge) +{ + if ((source_mesh == target_mesh) && (source_edge == target_edge)) { + return; + } + CustomData_bmesh_free_block(&target_mesh->edata, &target_edge->head.data); + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, + source_edge->head.data, &target_edge->head.data); +} + +static void bm_loop_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMLoop *source_loop, BMLoop *target_loop) +{ + if ((source_mesh == target_mesh) && (source_loop == target_loop)) { + return; + } + CustomData_bmesh_free_block(&target_mesh->ldata, &target_loop->head.data); + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, + source_loop->head.data, &target_loop->head.data); +} + +static void bm_face_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, + const BMFace *source_face, BMFace *target_face) +{ + if ((source_mesh == target_mesh) && (source_face == target_face)) { + return; + } + copy_v3_v3(target_face->no, source_face->no); + CustomData_bmesh_free_block(&target_mesh->pdata, &target_face->head.data); + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, + source_face->head.data, &target_face->head.data); + target_face->mat_nr = source_face->mat_nr; +} + +/* BMESH_TODO: Special handling for hide flags? */ + +void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target) +{ + const BMHeader *sheader = source; + BMHeader *theader = target; + + if (sheader->htype != theader->htype) + return; + + /* First we copy select */ + if (BM_elem_flag_test(source, BM_ELEM_SELECT)) BM_elem_select_set(target_mesh, target, TRUE); + + /* Now we copy flags */ + theader->hflag = sheader->hflag; + + /* Copy specific attributes */ + if (theader->htype == BM_VERT) + bm_vert_attrs_copy(source_mesh, target_mesh, (const BMVert *)source, (BMVert *)target); + else if (theader->htype == BM_EDGE) + bm_edge_attrs_copy(source_mesh, target_mesh, (const BMEdge *)source, (BMEdge *)target); + else if (theader->htype == BM_LOOP) + bm_loop_attrs_copy(source_mesh, target_mesh, (const BMLoop *)source, (BMLoop *)target); + else if (theader->htype == BM_FACE) + bm_face_attrs_copy(source_mesh, target_mesh, (const BMFace *)source, (BMFace *)target); +} + +BMesh *BM_mesh_copy(BMesh *bmold) +{ + BMesh *bm; + BMVert *v, *v2, **vtable = NULL; + BMEdge *e, *e2, **edges = NULL, **etable = NULL; + BLI_array_declare(edges); + BMLoop *l, /* *l2, */ **loops = NULL; + BLI_array_declare(loops); + BMFace *f, *f2, **ftable = NULL; + BMEditSelection *ese; + BMIter iter, liter; + int i, j; + + /* allocate a bmesh */ + bm = BM_mesh_create(bmold->ob, bm_mesh_allocsize_default); + + CustomData_copy(&bmold->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bmold->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bmold->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bmold->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]); + CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]); + CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]); + CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]); + + vtable = MEM_mallocN(sizeof(BMVert *) * bmold->totvert, "BM_mesh_copy vtable"); + etable = MEM_mallocN(sizeof(BMEdge *) * bmold->totedge, "BM_mesh_copy etable"); + ftable = MEM_mallocN(sizeof(BMFace *) * bmold->totface, "BM_mesh_copy ftable"); + + v = BM_iter_new(&iter, bmold, BM_VERTS_OF_MESH, NULL); + for (i = 0; v; v = BM_iter_step(&iter), i++) { + v2 = BM_vert_create(bm, v->co, NULL); /* copy between meshes so cant use 'example' argument */ + BM_elem_attrs_copy(bmold, bm, v, v2); + vtable[i] = v2; + BM_elem_index_set(v, i); /* set_inline */ + BM_elem_index_set(v2, i); /* set_inline */ + } + bmold->elem_index_dirty &= ~BM_VERT; + bm->elem_index_dirty &= ~BM_VERT; + + /* safety check */ + BLI_assert(i == bmold->totvert); + + e = BM_iter_new(&iter, bmold, BM_EDGES_OF_MESH, NULL); + for (i = 0; e; e = BM_iter_step(&iter), i++) { + e2 = BM_edge_create(bm, + vtable[BM_elem_index_get(e->v1)], + vtable[BM_elem_index_get(e->v2)], + e, FALSE); + + BM_elem_attrs_copy(bmold, bm, e, e2); + etable[i] = e2; + BM_elem_index_set(e, i); /* set_inline */ + BM_elem_index_set(e2, i); /* set_inline */ + } + bmold->elem_index_dirty &= ~BM_EDGE; + bm->elem_index_dirty &= ~BM_EDGE; + + /* safety check */ + BLI_assert(i == bmold->totedge); + + f = BM_iter_new(&iter, bmold, BM_FACES_OF_MESH, NULL); + for (i = 0; f; f = BM_iter_step(&iter), i++) { + BM_elem_index_set(f, i); /* set_inline */ + + BLI_array_empty(loops); + BLI_array_empty(edges); + BLI_array_growitems(loops, f->len); + BLI_array_growitems(edges, f->len); + + l = BM_iter_new(&liter, bmold, BM_LOOPS_OF_FACE, f); + for (j = 0; j < f->len; j++, l = BM_iter_step(&liter)) { + loops[j] = l; + edges[j] = etable[BM_elem_index_get(l->e)]; + } + + v = vtable[BM_elem_index_get(loops[0]->v)]; + v2 = vtable[BM_elem_index_get(loops[1]->v)]; + + if (!bmesh_verts_in_edge(v, v2, edges[0])) { + v = vtable[BM_elem_index_get(loops[BLI_array_count(loops) - 1]->v)]; + v2 = vtable[BM_elem_index_get(loops[0]->v)]; + } + + f2 = BM_face_create_ngon(bm, v, v2, edges, f->len, FALSE); + if (!f2) + continue; + /* use totface incase adding some faces fails */ + BM_elem_index_set(f2, (bm->totface - 1)); /* set_inline */ + + ftable[i] = f2; + + BM_elem_attrs_copy(bmold, bm, f, f2); + copy_v3_v3(f2->no, f->no); + + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f2); + for (j = 0; j < f->len; j++, l = BM_iter_step(&liter)) { + BM_elem_attrs_copy(bmold, bm, loops[j], l); + } + + if (f == bmold->act_face) bm->act_face = f2; + } + bmold->elem_index_dirty &= ~BM_FACE; + bm->elem_index_dirty &= ~BM_FACE; + + /* safety check */ + BLI_assert(i == bmold->totface); + + /* copy over edit selection history */ + for (ese = bmold->selected.first; ese; ese = ese->next) { + void *ele = NULL; + + if (ese->htype == BM_VERT) + ele = vtable[BM_elem_index_get(ese->data)]; + else if (ese->htype == BM_EDGE) + ele = etable[BM_elem_index_get(ese->data)]; + else if (ese->htype == BM_FACE) { + ele = ftable[BM_elem_index_get(ese->data)]; + } + else { + BLI_assert(0); + } + + if (ele) + BM_select_history_store(bm, ele); + } + + MEM_freeN(etable); + MEM_freeN(vtable); + MEM_freeN(ftable); + + BLI_array_free(loops); + BLI_array_free(edges); + + return bm; +} + +/* ME -> BM */ +char BM_vert_flag_from_mflag(const char meflag) +{ + return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) | + ((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0) + ); +} +char BM_edge_flag_from_mflag(const short meflag) +{ + return ( ((meflag & SELECT) ? BM_ELEM_SELECT : 0) | + ((meflag & ME_SEAM) ? BM_ELEM_SEAM : 0) | + ((meflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */ + ((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0) + ); +} +char BM_face_flag_from_mflag(const char meflag) +{ + return ( ((meflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) | + ((meflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) | + ((meflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0) + ); +} + +/* BM -> ME */ +char BM_vert_flag_to_mflag(BMVert *eve) +{ + const char hflag = eve->head.hflag; + + return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) | + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) + ); +} +short BM_edge_flag_to_mflag(BMEdge *eed) +{ + const char hflag = eed->head.hflag; + + return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) | + ((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) | + ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) | + ((BM_edge_is_wire(NULL, eed)) ? ME_LOOSEEDGE : 0) | /* not typical */ + (ME_EDGEDRAW | ME_EDGERENDER) + ); +} +char BM_face_flag_to_mflag(BMFace *efa) +{ + const char hflag = efa->head.hflag; + + return ( ((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) | + ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) + ); +} diff --git a/source/blender/bmesh/intern/bmesh_eulers.c b/source/blender/bmesh/intern/bmesh_eulers.c new file mode 100644 index 00000000000..b1ba4ca3176 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_eulers.c @@ -0,0 +1,1215 @@ +/*some of this may come back, such as split face or split edge, if necassary for speed*/ + +#if 0 +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_eulers.c + * \ingroup bmesh + * + * BM Euler construction API. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_customdata.h" +#include "BKE_utildefines.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" + +/********************************************************* + * "Euler API" * + * * + * * + * Primitive construction operators for mesh tools. * + * * + **********************************************************/ + + +/* + The functions in this file represent the 'primitive' or 'atomic' operators that + mesh tools use to manipulate the topology of the structure.* The purpose of these + functions is to provide a trusted set of operators to manipulate the mesh topology + and which can also be combined together like building blocks to create more + sophisticated tools. It needs to be stressed that NO manipulation of an existing + mesh structure should be done outside of these functions. + + In the BM system, each euler is named by an ancronym which describes what it actually does. + Furthermore each Euler has a logical inverse. An important design criteria of all Eulers is that + through a Euler's logical inverse you can 'undo' an operation. (Special note should + be taken of bmesh_loop_reverse, which is its own inverse). + + bmesh_MF/KF: Make Face and Kill Face + bmesh_ME/KE: Make Edge and Kill Edge + bmesh_MV/KV: Make Vert and Kill Vert + bmesh_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert + bmesh_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge + bmesh_loop_reverse: Reverse a Polygon's loop cycle. (used for flip normals for one) + + Using a combination of these eleven eulers any non-manifold modelling operation can be achieved. + Each Euler operator has a detailed explanation of what is does in the comments preceding its + code. + + *The term "Euler Operator" is actually a misnomer when referring to a non-manifold + data structure. Its use is in keeping with the convention established by others. + + BMESH_TODO: + -Make seperate 'debug levels' of validation + -Add in the UnglueFaceRegionMakeVert and GlueFaceRegionKillVert eulers. + + NOTE: + -The functions in this file are notoriously difficult to debug and even understand sometimes. + better code comments would be nice.... + +*/ + + +/*MAKE Eulers*/ + +/** + * bmesh_MV + * + * MAKE VERT EULER: + * + * Makes a single loose vertex. + * + * Returns - + * A BMVert pointer. + */ + +BMVert *bmesh_mv(BMesh *bm, const float vec[3]) +{ + BMVert *v = bmesh_addvertlist(bm, NULL); + copy_v3_v3(v->co,vec); + return v; +} + +/** + * bmesh_ME + * + * MAKE EDGE EULER: + * + * Makes a single wire edge between two vertices. + * If the caller does not want there to be duplicate + * edges between the vertices, it is up to them to check + * for this condition beforehand. + * + * Returns - + * A BMEdge pointer. + */ + +BMEdge *bmesh_me(BMesh *bm, BMVert *v1, BMVert *v2) +{ + BMEdge *e=NULL; + BMNode *d1=NULL, *d2=NULL; + int valance1=0, valance2=0, edok; + + /*edge must be between two distinct vertices...*/ + if(v1 == v2) return NULL; + + #ifndef bmesh_FASTEULER + /*count valance of v1*/ + if(v1->e) { + d1 = bmesh_disk_getpointer(v1->e,v1); + if(d1) valance1 = bmesh_cycle_length(d1); + else bmesh_error(); + } + if(v2->e) { + d2 = bmesh_disk_getpointer(v2->e,v2); + if(d2) valance2 = bmesh_cycle_length(d2); + else bmesh_error(); + } + #endif + + /*go ahead and add*/ + e = bmesh_addedgelist(bm, v1, v2, NULL); + bmesh_disk_append_edge(e, e->v1); + bmesh_disk_append_edge(e, e->v2); + + #ifndef bmesh_FASTEULER + /*verify disk cycle lengths*/ + d1 = bmesh_disk_getpointer(e, e->v1); + edok = bmesh_cycle_validate(valance1+1, d1); + if(!edok) bmesh_error(); + d2 = bmesh_disk_getpointer(e, e->v2); + edok = bmesh_cycle_validate(valance2+1, d2); + if(!edok) bmesh_error(); + + /*verify that edge actually made it into the cycle*/ + edok = bmesh_disk_hasedge(v1, e); + if(!edok) bmesh_error(); + edok = bmesh_disk_hasedge(v2, e); + if(!edok) bmesh_error(); + #endif + return e; +} + + + +/** + * bmesh_MF + * + * MAKE FACE EULER: + * Takes a list of edge pointers which form a closed loop and makes a face + * from them. The first edge in elist is considered to be the start of the + * polygon, and v1 and v2 are its vertices and determine the winding of the face + * Other than the first edge, no other assumptions are made about the order of edges + * in the elist array. To verify that it is a single closed loop and derive the correct + * order a simple series of verifications is done and all elements are visited. + * + * Returns - + * A BMFace pointer + */ + +#define MF_CANDIDATE 1 +#define MF_VISITED 2 +#define MF_TAKEN 4 + +BMFace *bmesh_mf(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **elist, int len) +{ + BMFace *f = NULL; + BMEdge *curedge; + BMVert *curvert, *tv, **vlist; + int i, j, done, cont, edok; + + if(len < 2) return NULL; + + /*make sure that v1 and v2 are in elist[0]*/ + //if(bmesh_verts_in_edge(v1,v2,elist[0]) == 0) + // return NULL; + + /*clear euler flags*/ + for(i=0;i<len;i++) { + BMNode *diskbase; + BMEdge *curedge; + BMVert *v1; + int j; + + for (j=0; j<2; j++) { + int a, len=0; + + v1 = j ? elist[i]->v2 : elist[i]->v1; + diskbase = bmesh_disk_getpointer(v1->e, v1); + len = bmesh_cycle_length(diskbase); + + for(a=0,curedge=v1->e;a<len;a++,curedge = bmesh_disk_nextedge(curedge,v1)) { + curedge->head.eflag1 = curedge->head.eflag2 = 0; + } + } + } + + for(i=0;i<len;i++) { + elist[i]->head.eflag1 |= MF_CANDIDATE; + + /*if elist[i] has a loop, count its radial length*/ + if(elist[i]->loop) elist[i]->head.eflag2 = bmesh_cycle_length(&(elist[i]->l->radial)); + else elist[i]->head.eflag2 = 0; + } + + /* For each vertex in each edge, it must have exactly two MF_CANDIDATE edges attached to it + Note that this does not gauruntee that face is a single closed loop. At best it gauruntees + that elist contains a finite number of seperate closed loops. + */ +// for(i=0; i<len; i++) { +// edok = bmesh_disk_count_edgeflag(elist[i]->v1, MF_CANDIDATE, 0); +// if(edok != 2) return NULL; +// edok = bmesh_disk_count_edgeflag(elist[i]->v2, MF_CANDIDATE, 0); +// if(edok != 2) return NULL; +// } + + /*set start edge, start vert and target vert for our loop traversal*/ + curedge = elist[0]; + tv = v1; + curvert = v2; + + if(bm->vtarlen < len) { + if (bm->vtar) MEM_freeN(bm->vtar); + bm->vtar = MEM_callocN(sizeof(BMVert *)* len, "BM Vert pointer array"); + bm->vtarlen = len; + } + /*insert tv into vlist since its the first vertex in face*/ + + i=0; + vlist=bm->vtar; + vlist[i] = tv; + + /* Basic procedure: Starting with curv we find the edge in it's disk cycle which hasn't + been visited yet. When we do, we put curv in a linked list and find the next MF_CANDIDATE + edge, loop until we find TV. We know TV is reachable because of test we did earlier. + */ + done=0; + while(!done) { + /*add curvert to vlist*/ + /*insert some error cheking here for overflows*/ + i++; + vlist[i] = curvert; + + /*mark curedge as visited*/ + curedge->head.eflag1 |= MF_VISITED; + + /*find next edge and vert*/ + curedge = bmesh_disk_next_edgeflag(curedge, curvert, MF_CANDIDATE, 0); + curvert = bmesh_edge_getothervert(curedge, curvert); + if(curvert == tv) { + curedge->head.eflag1 |= MF_VISITED; + done=1; + } + } + + /* Verify that all edges have been visited It's possible that we did reach tv + from sv, but that several unconnected loops were passed in via elist. + */ + cont=1; +// for(i=0; i<len; i++) { +// if((elist[i]->head.eflag1 & MF_VISITED) == 0) cont = 0; +// } + + /*if we get this far, its ok to allocate the face and add the loops*/ + if(cont) { + BMLoop *l; + BMEdge *e; + f = bmesh_addpolylist(bm, NULL); + f->len = len; + for(i=0;i<len;i++) { + curvert = vlist[i]; + l = bmesh_create_loop(bm,curvert,NULL,f,NULL); + if(!(f->loopbase)) f->lbase = l; + bmesh_cycle_append(f->lbase, l); + } + + /*take care of edge pointers and radial cycle*/ + for(i=0, l = f->loopbase; i<len; i++, l= l->next) { + e = NULL; + if(l == f->loopbase) e = elist[0]; /*first edge*/ + + else {/*search elist for others*/ + for(j=1; j<len; j++) { + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, elist[j]); + if(edok) { + e = elist[j]; + break; + } + } + } + l->e = e; /*set pointer*/ + bmesh_radial_append(e, l); /*append into radial*/ + } + + f->len = len; + + /*Validation Loop cycle*/ + edok = bmesh_cycle_validate(len, f->lbase); + if(!edok) bmesh_error(); + for(i=0, l = f->loopbase; i<len; i++, l=((l->next))) { + /*validate loop vert pointers*/ + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, l->e); + if(!edok) bmesh_error(); + /*validate the radial cycle of each edge*/ + edok = bmesh_cycle_length(&(l->radial)); + if(edok != (l->e->head.eflag2 + 1)) bmesh_error(); + } + } + + for(i=0;i<len;i++) elist[i]->head.eflag1=elist[i]->head.eflag2 = 0; + return f; +} + +/* KILL Eulers */ + +/** + * bmesh_KV + * + * KILL VERT EULER: + * + * Kills a single loose vertex. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_kv(BMesh *bm, BMVert *v) +{ + if(v->e == NULL) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) bm->totvertsel--; + + BLI_remlink(&(bm->verts), &(v->head)); + bmesh_free_vert(bm,v); + return 1; + } + return 0; +} + +/** + * bmesh_KE + * + * KILL EDGE EULER: + * + * Kills a wire edge. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_ke(BMesh *bm, BMEdge *e) +{ + int edok; + + /*Make sure that no faces!*/ + if(e->l == NULL) { + bmesh_disk_remove_edge(e, e->v1); + bmesh_disk_remove_edge(e, e->v2); + + /*verify that edge out of disk*/ + edok = bmesh_disk_hasedge(e->v1, e); + if(edok) bmesh_error(); + edok = bmesh_disk_hasedge(e->v2, e); + if(edok) bmesh_error(); + + /*remove and deallocate*/ + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel--; + BLI_remlink(&(bm->edges), &(e->head)); + bmesh_free_edge(bm, e); + return 1; + } + return 0; +} + +/** + * bmesh_KF + * + * KILL FACE EULER: + * + * The logical inverse of bmesh_MF. + * Kills a face and removes each of its loops from the radial that it belongs to. + * + * Returns - + * 1 for success, 0 for failure. +*/ + +int bmesh_kf(BMesh *bm, BMFace *bply) +{ + BMLoop *newbase,*oldbase, *curloop; + int i,len=0; + + /*add validation to make sure that radial cycle is cleaned up ok*/ + /*deal with radial cycle first*/ + len = bmesh_cycle_length(bply->lbase); + for(i=0, curloop=bply->loopbase; i < len; i++, curloop = ((curloop->next))) + bmesh_radial_remove_loop(curloop, curloop->e); + + /*now deallocate the editloops*/ + for(i=0; i < len; i++) { + newbase = ((bply->lbase->next)); + oldbase = bply->lbase; + bmesh_cycle_remove(oldbase, oldbase); + bmesh_free_loop(bm, oldbase); + bply->loopbase = newbase; + } + + if (BM_elem_flag_test(bply, BM_ELEM_SELECT)) bm->totfacesel--; + BLI_remlink(&(bm->polys), &(bply->head)); + bmesh_free_poly(bm, bply); + return 1; +} + +/*SPLIT Eulers*/ + +/** + * bmesh_SEMV + * + * SPLIT EDGE MAKE VERT: + * Takes a given edge and splits it into two, creating a new vert. + * + * + * Before: OV---------TV + * After: OV----NV---TV + * + * Returns - + * BMVert pointer. + * +*/ + +BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re) +{ + BMVert *nv, *ov; + BMNode *diskbase; + BMEdge *ne; + int i, edok, valance1=0, valance2=0; + + if(bmesh_vert_in_edge(e,tv) == 0) return NULL; + ov = bmesh_edge_getothervert(e,tv); + //v2 = tv; + + /*count valance of v1*/ + diskbase = bmesh_disk_getpointer(e, ov); + valance1 = bmesh_cycle_length(diskbase); + /*count valance of v2*/ + diskbase = bmesh_disk_getpointer(e, tv); + valance2 = bmesh_cycle_length(diskbase); + + nv = bmesh_addvertlist(bm, tv); + ne = bmesh_addedgelist(bm, nv, tv, e); + + //e->v2 = nv; + /*remove e from v2's disk cycle*/ + bmesh_disk_remove_edge(e, tv); + /*swap out tv for nv in e*/ + bmesh_edge_swapverts(e, tv, nv); + /*add e to nv's disk cycle*/ + bmesh_disk_append_edge(e, nv); + /*add ne to nv's disk cycle*/ + bmesh_disk_append_edge(ne, nv); + /*add ne to tv's disk cycle*/ + bmesh_disk_append_edge(ne, tv); + /*verify disk cycles*/ + diskbase = bmesh_disk_getpointer(ov->e,ov); + edok = bmesh_cycle_validate(valance1, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(tv->e,tv); + edok = bmesh_cycle_validate(valance2, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(nv->e,nv); + edok = bmesh_cycle_validate(2, diskbase); + if(!edok) bmesh_error(); + + /*Split the radial cycle if present*/ + if(e->l) { + BMLoop *nl,*l; + BMNode *radEBase=NULL, *radNEBase=NULL; + int radlen = bmesh_cycle_length(&(e->l->radial)); + /*Take the next loop. Remove it from radial. Split it. Append to appropriate radials.*/ + while(e->l) { + l=e->l; + l->f->len++; + bmesh_radial_remove_loop(l,e); + + nl = bmesh_create_loop(bm,NULL,NULL,l->f,l); + nl->prev = (BMHeader*)l; + nl->next = (BMHeader*)(l->next); + nl->prev->next = (BMHeader*)nl; + nl->next->prev = (BMHeader*)nl; + nl->v = nv; + + /*assign the correct edge to the correct loop*/ + if(bmesh_verts_in_edge(nl->v, ((nl->next))->v, e)) { + nl->e = e; + l->e = ne; + + /*append l into ne's rad cycle*/ + if(!radNEBase) { + radNEBase = &(l->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + + if(!radEBase) { + radEBase = &(nl->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + + bmesh_cycle_append(radEBase,&(nl->radial)); + bmesh_cycle_append(radNEBase,&(l->radial)); + + } + else if(bmesh_verts_in_edge(nl->v,((nl->next))->v,ne)) { + nl->e = ne; + l->e = e; + + if(!radNEBase) { + radNEBase = &(nl->radial); + radNEBase->next = NULL; + radNEBase->prev = NULL; + } + if(!radEBase) { + radEBase = &(l->radial); + radEBase->next = NULL; + radEBase->prev = NULL; + } + bmesh_cycle_append(radEBase,&(l->radial)); + bmesh_cycle_append(radNEBase,&(nl->radial)); + } + + } + + e->l = radEBase->data; + ne->l = radNEBase->data; + + /*verify length of radial cycle*/ + edok = bmesh_cycle_validate(radlen,&(e->l->radial)); + if(!edok) bmesh_error(); + edok = bmesh_cycle_validate(radlen,&(ne->l->radial)); + if(!edok) bmesh_error(); + + /*verify loop->v and loop->next->v pointers for e*/ + for(i=0,l=e->l; i < radlen; i++, l = l->radial_next) { + if(!(l->e == e)) bmesh_error(); + if(!(l->radial.data == l)) bmesh_error(); + if( ((l->prev))->e != ne && ((l->next))->e != ne) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, e); + if(!edok) bmesh_error(); + if(l->v == ((l->next))->v) bmesh_error(); + if(l->e == ((l->next))->e) bmesh_error(); + /*verify loop cycle for kloop->f*/ + edok = bmesh_cycle_validate(l->f->len, l->f->lbase); + if(!edok) bmesh_error(); + } + /*verify loop->v and loop->next->v pointers for ne*/ + for(i=0,l=ne->l; i < radlen; i++, l = l->radial_next) { + if(!(l->e == ne)) bmesh_error(); + if(!(l->radial.data == l)) bmesh_error(); + if( ((l->prev))->e != e && ((l->next))->e != e) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, ((l->next))->v, ne); + if(!edok) bmesh_error(); + if(l->v == ((l->next))->v) bmesh_error(); + if(l->e == ((l->next))->e) bmesh_error(); + /*verify loop cycle for kloop->f. Redundant*/ + edok = bmesh_cycle_validate(l->f->len, l->f->lbase); + if(!edok) bmesh_error(); + } + } + + if(re) *re = ne; + return nv; +} + +/** + * bmesh_SFME + * + * SPLIT FACE MAKE EDGE: + * + * 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. + * + * Examples: + * + * Before: After: + * ---------- ---------- + * | | | | + * | | | f1 | + * v1 f1 v2 v1======v2 + * | | | f2 | + * | | | | + * ---------- ---------- + * + * Note that 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. + * + * Note that the tesselator abuses eflag2 while using this euler! (don't ever ever do this....) + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **rl) +{ + + BMFace *f2; + BMLoop *v1loop = NULL, *v2loop = NULL, *curloop, *f1loop=NULL, *f2loop=NULL; + BMEdge *e; + int i, len, f1len, f2len; + + + /*verify that v1 and v2 are in face.*/ + len = bmesh_cycle_length(f->lbase); + for(i = 0, curloop = f->loopbase; i < len; i++, curloop = ((curloop->next)) ) { + if(curloop->v == v1) v1loop = curloop; + else if(curloop->v == v2) v2loop = curloop; + } + + if(!v1loop || !v2loop) return NULL; + + /*allocate new edge between v1 and v2*/ + e = bmesh_addedgelist(bm, v1, v2,NULL); + bmesh_disk_append_edge(e, v1); + bmesh_disk_append_edge(e, v2); + + f2 = bmesh_addpolylist(bm,f); + f1loop = bmesh_create_loop(bm,v2,e,f,v2loop); + f2loop = bmesh_create_loop(bm,v1,e,f2,v1loop); + + f1loop->prev = v2loop->prev; + f2loop->prev = v1loop->prev; + v2loop->prev->next = (BMHeader*)f1loop; + v1loop->prev->next = (BMHeader*)f2loop; + + f1loop->next = (BMHeader*)v1loop; + f2loop->next = (BMHeader*)v2loop; + v1loop->prev = (BMHeader*)f1loop; + v2loop->prev = (BMHeader*)f2loop; + + f2->loopbase = f2loop; + f->loopbase = f1loop; + + /*validate both loops*/ + /*I dont know how many loops are supposed to be in each face at this point! FIXME!*/ + + /*go through all of f2's loops and make sure they point to it properly.*/ + f2len = bmesh_cycle_length(f2->lbase); + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->f = f2; + + /*link up the new loops into the new edges radial*/ + bmesh_radial_append(e, f1loop); + bmesh_radial_append(e, f2loop); + + + f2->len = f2len; + + f1len = bmesh_cycle_length(f->lbase); + f->len = f1len; + + if(rl) *rl = f2loop; + return f2; +} + + +/** + * bmesh_JEKV + * + * JOIN EDGE KILL VERT: + * Takes a an edge and pointer to one of its vertices and collapses + * the edge on that vertex. + * + * Before: OE KE + * ------- ------- + * | || | + * OV KV TV + * + * + * After: OE + * --------------- + * | | + * OV TV + * + * + * 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). + * + * It should also be noted that 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. + * + * Returns - + * 1 for success, 0 for failure. + */ +int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + BMEdge *oe; + BMVert *ov, *tv; + BMNode *diskbase; + BMLoop *killoop,*nextl; + int len,radlen=0, halt = 0, i, valance1, valance2,edok; + + if(bmesh_vert_in_edge(ke,kv) == 0) return 0; + diskbase = bmesh_disk_getpointer(kv->e, kv); + len = bmesh_cycle_length(diskbase); + + if(len == 2) { + oe = bmesh_disk_nextedge(ke, kv); + tv = bmesh_edge_getothervert(ke, kv); + ov = bmesh_edge_getothervert(oe, kv); + halt = bmesh_verts_in_edge(kv, tv, oe); //check for double edges + + if(halt) return 0; + else { + + /*For verification later, count valance of ov and tv*/ + diskbase = bmesh_disk_getpointer(ov->e, ov); + valance1 = bmesh_cycle_length(diskbase); + diskbase = bmesh_disk_getpointer(tv->e, tv); + valance2 = bmesh_cycle_length(diskbase); + + /*remove oe from kv's disk cycle*/ + bmesh_disk_remove_edge(oe,kv); + /*relink oe->kv to be oe->tv*/ + bmesh_edge_swapverts(oe, kv, tv); + /*append oe to tv's disk cycle*/ + bmesh_disk_append_edge(oe, tv); + /*remove ke from tv's disk cycle*/ + bmesh_disk_remove_edge(ke, tv); + + + + /*deal with radial cycle of ke*/ + if(ke->l) { + /*first step, fix the neighboring loops of all loops in ke's radial cycle*/ + radlen = bmesh_cycle_length(&(ke->l->radial)); + for(i=0,killoop = ke->l; i<radlen; i++, killoop = bmesh_radial_nextloop(killoop)) { + /*relink loops and fix vertex pointer*/ + killoop->next->prev = killoop->prev; + killoop->prev->next = killoop->next; + if( ((killoop->next))->v == kv) ((killoop->next))->v = tv; + + /*fix len attribute of face*/ + killoop->f->len--; + if(killoop->f->loopbase == killoop) killoop->f->lbase = ((killoop->next)); + } + /*second step, remove all the hanging loops attached to ke*/ + killoop = ke->l; + radlen = bmesh_cycle_length(&(ke->l->radial)); + /*make sure we have enough room in bm->lpar*/ + if(bm->lparlen < radlen) { + MEM_freeN(bm->lpar); + bm->lpar = MEM_callocN(sizeof(BMLoop *)* radlen, "BM Loop pointer array"); + bm->lparlen = bm->lparlen * radlen; + } + /*this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well...*/ + i=0; + while(i<radlen) { + bm->lpar[i] = killoop; + killoop = killoop->radial_next; + i++; + } + i=0; + while(i<radlen) { + bmesh_free_loop(bm,bm->lpar[i]); + i++; + } + /*Validate radial cycle of oe*/ + edok = bmesh_cycle_validate(radlen,&(oe->l->radial)); + + } + + + /*Validate disk cycles*/ + diskbase = bmesh_disk_getpointer(ov->e,ov); + edok = bmesh_cycle_validate(valance1, diskbase); + if(!edok) bmesh_error(); + diskbase = bmesh_disk_getpointer(tv->e,tv); + edok = bmesh_cycle_validate(valance2, diskbase); + if(!edok) bmesh_error(); + + /*Validate loop cycle of all faces attached to oe*/ + for(i=0,nextl = oe->l; i<radlen; i++, nextl = bmesh_radial_nextloop(nextl)) { + edok = bmesh_cycle_validate(nextl->f->len,nextl->f->lbase); + if(!edok) bmesh_error(); + } + /*deallocate edge*/ + BLI_remlink(&(bm->edges), &(ke->head)); + bmesh_free_edge(bm, ke); + /*deallocate vertex*/ + BLI_remlink(&(bm->verts), &(kv->head)); + bmesh_free_vert(bm, kv); + return 1; + } + } + return 0; +} + + +/** + * bmesh_loop_reverse + * + * FLIP FACE EULER + * + * Changes the winding order of a face from CW to CCW or vice versa. + * This euler is a bit peculiar in compairson to others as it is its + * own inverse. + * + * BMESH_TODO: reinsert validation code. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_loop_reverse(BMesh *bm, BMFace *f) +{ + BMLoop *l = f->loopbase, *curloop, *oldprev, *oldnext; + int i, j, edok, len = 0; + + len = bmesh_cycle_length(l); + if(bm->edarlen < len) { + MEM_freeN(bm->edar); + bm->edar = MEM_callocN(sizeof(BMEdge *)* len, "BM Edge pointer array"); + bm->edarlen = len; + } + + for(i=0, curloop = l; i< len; i++, curloop= ((curloop->next)) ) { + curloop->e->head.eflag1 = 0; + curloop->e->head.eflag2 = bmesh_cycle_length(&curloop->radial); + bmesh_radial_remove_loop(curloop, curloop->e); + /*in case of border edges we HAVE to zero out curloop->radial Next/Prev*/ + curloop->radial.next = curloop->radial.prev = NULL; + bm->edar[i] = curloop->e; + } + + /*actually reverse the loop. This belongs in bmesh_cycle_reverse!*/ + for(i=0, curloop = l; i < len; i++) { + oldnext = ((curloop->next)); + oldprev = ((curloop->prev)); + curloop->next = (BMHeader*)oldprev; + curloop->prev = (BMHeader*)oldnext; + curloop = oldnext; + } + + if(len == 2) { //two edged face + //do some verification here! + l->e = bm->edar[1]; + ((l->next))->e = bm->edar[0]; + } + else { + for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) { + edok = 0; + for(j=0; j < len; j++) { + edok = bmesh_verts_in_edge(curloop->v, ((curloop->next))->v, bm->edar[j]); + if(edok) { + curloop->e = bm->edar[j]; + break; + } + } + } + } + /*rebuild radial*/ + for(i=0, curloop = l; i < len; i++, curloop = curloop->next ) bmesh_radial_append(curloop->e, curloop); + + /*validate radial*/ + for(i=0, curloop = l; i < len; i++, curloop = ((curloop->next)) ) { + edok = bmesh_cycle_validate(curloop->e->head.eflag2, &(curloop->radial)); + if(!edok) { + bmesh_error(); + } + } + return 1; +} + +/** + * bmesh_JFKE + * + * JOIN FACE KILL EDGE: + * + * Takes two faces joined by a single 2-manifold edge and fuses them togather. + * The edge shared by the faces must not be connected to any other edges which have + * Both faces in its radial cycle + * + * Examples: + * + * A B + * ---------- ---------- + * | | | | + * | f1 | | f1 | + * v1========v2 = Ok! v1==V2==v3 == Wrong! + * | f2 | | f2 | + * | | | | + * ---------- ---------- + * + * In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used. + * In example B however, f1 and 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 f1 and f2. + * + * Also note that the order of arguments decides whether or not certain per-face attributes are present + * in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited + * from f1, not f2. + * + * Returns - + * A BMFace pointer +*/ + +//disregarding f1loop and f2loop, if a vertex appears in a joined face more than once, we cancel + +BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + + BMLoop *curloop, *f1loop=NULL, *f2loop=NULL; + int loopok = 0, newlen = 0,i, f1len=0, f2len=0, radlen=0, edok, shared; + + if(f1 == f2) return NULL; //can't join a face to itself + /*verify that e is in both f1 and f2*/ + f1len = bmesh_cycle_length(f1->lbase); + f2len = bmesh_cycle_length(f2->lbase); + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) { + if(curloop->e == e) { + f1loop = curloop; + break; + } + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) { + if(curloop->e==e) { + f2loop = curloop; + break; + } + } + if(!(f1loop && f2loop)) return NULL; + + /*validate that edge is 2-manifold edge*/ + radlen = bmesh_cycle_length(&(f1loop->radial)); + if(radlen != 2) return NULL; + + /*validate direction of f2's loop cycle is compatible.*/ + if(f1loop->v == f2loop->v) return NULL; + + /* + validate that for each face, each vertex has another edge in its disk cycle that is + not e, and not shared. + */ + if(bmesh_radial_find_face( ((f1loop->next))->e,f2)) return NULL; + if(bmesh_radial_find_face( ((f1loop->prev))->e,f2)) return NULL; + if(bmesh_radial_find_face( ((f2loop->next))->e,f1)) return NULL; + if(bmesh_radial_find_face( ((f2loop->prev))->e,f1)) return NULL; + + /*validate only one shared edge*/ + shared = BM_face_share_edges(f1,f2); + if(shared > 1) return NULL; + + /*validate no internal joins*/ + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0; + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) curloop->v->head.eflag1 = 0; + + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) { + if(curloop != f1loop) + curloop->v->head.eflag1++; + } + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) { + if(curloop != f2loop) + curloop->v->head.eflag1++; + } + + for(i=0, curloop = f1->loopbase; i < f1len; i++, curloop = ((curloop->next)) ) { + if(curloop->v->head.eflag1 > 1) + return NULL; + } + + for(i=0, curloop = f2->loopbase; i < f2len; i++, curloop = ((curloop->next)) ) { + if(curloop->v->head.eflag1 > 1) + return NULL; + } + + /*join the two loops*/ + f1loop->prev->next = f2loop->next; + f2loop->next->prev = f1loop->prev; + + f1loop->next->prev = f2loop->prev; + f2loop->prev->next = f1loop->next; + + /*if f1loop was baseloop, give f1loop->next the base.*/ + if(f1->loopbase == f1loop) f1->lbase = ((f1loop->next)); + + /*validate the new loop*/ + loopok = bmesh_cycle_validate((f1len+f2len)-2, f1->lbase); + if(!loopok) bmesh_error(); + + /*make sure each loop points to the proper face*/ + newlen = bmesh_cycle_length(f1->lbase); + for(i = 0, curloop = f1->loopbase; i < newlen; i++, curloop = ((curloop->next)) ) curloop->f = f1; + + f1->len = newlen; + + edok = bmesh_cycle_validate(f1->len, f1->lbase); + if(!edok) bmesh_error(); + + /*remove edge from the disk cycle of its two vertices.*/ + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1); + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /*deallocate edge and its two loops as well as f2*/ + BLI_remlink(&(bm->edges), &(f1loop->e->head)); + BLI_remlink(&(bm->polys), &(f2->head)); + bmesh_free_edge(bm, f1loop->e); + bmesh_free_loop(bm, f1loop); + bmesh_free_loop(bm, f2loop); + bmesh_free_poly(bm, f2); + return f1; +} + +/** +* bmesh_URMV +* +* UNGLUE REGION MAKE VERT: +* +* Takes a locally manifold disk of face corners and 'unglues' it +* creating a new vertex +* +**/ + +#define URMV_VISIT 1 +#define URMV_VISIT2 2 + +BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv) +{ + BMVert *nv = NULL; + BMLoop *l = NULL, *sl = NULL; + BMEdge *curedge = NULL; + int numloops = 0, numedges = 0, i, maxedges, maxloops; + + + /*BMESH_TODO: Validation*/ + /*validate radial cycle of all collected loops*/ + /*validate the disk cycle of sv, and nv*/ + /*validate the face length of all faces? overkill?*/ + /*validate the l->e pointers of all affected faces, ie: l->v and l->next->v should be equivalent to l->e*/ + + /*verify that sv has edges*/ + if(sv->e == NULL) + return NULL; + + /*first verify no wire edges on sv*/ + curedge = sv->e; + do { + if(curedge->l == NULL) + return NULL; + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*next verify that sv is in sf*/ + l = sf->loopbase; + do { + if(l->v == sv) { + sl = l; + break; + } + l = (l->next); + } while(l != sf->lbase); + + if(sl == NULL) + return NULL; + + /*clear euler flags*/ + sv->head.eflag1 = 0; + + curedge = sv->e; + do { + curedge->head.eflag1 = 0; + l = curedge->l; + do { + l->head.eflag1 = 0; + l->f->head.eflag1 = 0; + l = bmesh_radial_nextloop(l); + } while(l != curedge->l); + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*search through face disk and flag elements as we go.*/ + /*Note, test this to make sure that it works correct on + non-manifold faces! + */ + l = sl; + l->e->head.eflag1 |= URMV_VISIT; + l->f->head.eflag1 |= URMV_VISIT; + do { + if(l->v == sv) + l = bmesh_radial_nextloop((l->prev)); + else + l = bmesh_radial_nextloop((l->next)); + l->e->head.eflag1 |= URMV_VISIT; + l->f->head.eflag1 |= URMV_VISIT; + } while(l != sl && (bmesh_cycle_length(&(l->radial)) > 1) ); + + /*Verify that all visited edges are at least 1 or 2 manifold*/ + curedge = sv->e; + do { + if(curedge->head.eflag1 && (bmesh_cycle_length(&(curedge->l->radial)) > 2) ) + return NULL; + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*allocate temp storage - we overallocate here instead of trying to be clever*/ + maxedges = 0; + maxloops = 0; + curedge = sv->e; + do { + if(curedge->l) { + l = curedge->l; + do { + maxloops += l->f->len; + l = bmesh_radial_nextloop(l); + } while(l != curedge->l); + } + maxedges+= 1; + curedge = bmesh_disk_nextedge(curedge,sv); + } while(curedge != sv->e); + + if(bm->edarlen < maxedges) { + MEM_freeN(bm->edar); + bm->edar = MEM_callocN(sizeof(BMEdge *) * maxedges, "BM Edge pointer array"); + bm->edarlen = maxedges; + } + if(bm->lparlen < maxloops) { + MEM_freeN(bm->lpar); + bm->lpar = MEM_callocN(sizeof(BMLoop *) * maxloops, "BM Loop pointer array"); + bm->lparlen = maxloops; + } + + /*first get loops by looping around edges and loops around that edges faces*/ + curedge = sv->e; + do { + if(curedge->l) { + l = curedge->l; + do { + if( (l->head.eflag1 & URMV_VISIT) && (!(l->head.eflag1 & URMV_VISIT2)) ) { + bm->lpar[numloops] = l; + l->head.eflag1 |= URMV_VISIT2; + numloops++; + } + l = bmesh_radial_nextloop(l); + } while(l != curedge->l); + } + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*now collect edges by looping around edges and looking at visited flags*/ + curedge = sv->e; + do { + if(curedge->head.eflag1 & URMV_VISIT) { + bm->edar[numedges] = curedge; + numedges++; + } + curedge = bmesh_disk_nextedge(curedge, sv); + } while(curedge != sv->e); + + /*make new vertex*/ + nv = bmesh_addvertlist(bm, sv); + + /*go through and relink edges*/ + for(i = 0; i < numedges; i++) { + curedge = bm->edar[i]; + /*remove curedge from sv*/ + bmesh_disk_remove_edge(curedge, sv); + /*swap out sv for nv in curedge*/ + bmesh_edge_swapverts(curedge, sv, nv); + /*add curedge to nv's disk cycle*/ + bmesh_disk_append_edge(curedge, nv); + } + + /*go through and relink loops*/ + for(i = 0; i < numloops; i ++) { + l = bm->lpar[i]; + if(l->v == sv) + l->v = nv; + } + return nv; +} +#endif diff --git a/source/blender/bmesh/intern/bmesh_inline.c b/source/blender/bmesh/intern/bmesh_inline.c new file mode 100644 index 00000000000..4433aaa0fc6 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_inline.c @@ -0,0 +1,71 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_inline.c + * \ingroup bmesh + * + * BM Inline functions. + */ + +#ifndef __BMESH_INLINE_C__ +#define __BMESH_INLINE_C__ + +#include "bmesh.h" + +BM_INLINE char BM_elem_flag_test(const void *element, const char hflag) +{ + return ((const BMHeader *)element)->hflag & hflag; +} + +BM_INLINE void BM_elem_flag_enable(void *element, const char hflag) +{ + ((BMHeader *)element)->hflag |= hflag; +} + +BM_INLINE void BM_elem_flag_disable(void *element, const char hflag) +{ + ((BMHeader *)element)->hflag &= ~hflag; +} + +BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag) +{ + ((BMHeader *)element)->hflag ^= hflag; +} + +BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b) +{ + ((BMHeader *)element_a)->hflag = + ((BMHeader *)element_b)->hflag = (((BMHeader *)element_a)->hflag | + ((BMHeader *)element_b)->hflag); +} + +BM_INLINE void BM_elem_index_set(void *element, const int index) +{ + ((BMHeader *)element)->index = index; +} + +BM_INLINE int BM_elem_index_get(const void *element) +{ + return ((BMHeader *)element)->index; +} + +#endif /* __BMESH_INLINE_C__ */ diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c new file mode 100644 index 00000000000..d5f22690d63 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -0,0 +1,984 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_interp.c + * \ingroup bmesh + * + * Functions for interpolating data across the surface of a mesh. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_multires.h" + +#include "BLI_array.h" +#include "BLI_math.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/** + * bmesh_data_interp_from_verts + * + * Interpolates per-vertex data from two sources to a target. + */ +void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac) +{ + if (v1->head.data && v2->head.data) { + /* first see if we can avoid interpolation */ + if (fac <= 0.0f) { + if (v1 == v) { + /* do nothing */ + } + else { + CustomData_bmesh_free_block(&bm->vdata, &v->head.data); + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v1->head.data, &v->head.data); + } + } + else if (fac >= 1.0f) { + if (v2 == v) { + /* do nothing */ + } + else { + CustomData_bmesh_free_block(&bm->vdata, &v->head.data); + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v2->head.data, &v->head.data); + } + } + else { + void *src[2]; + float w[2]; + + src[0] = v1->head.data; + src[1] = v2->head.data; + w[0] = 1.0f-fac; + w[1] = fac; + CustomData_bmesh_interp(&bm->vdata, src, w, NULL, 2, v->head.data); + } + } +} + +/* + * BM Data Vert Average + * + * Sets all the customdata (e.g. vert, loop) associated with a vert + * to the average of the face regions surrounding it. + */ + + +static void UNUSED_FUNCTION(BM_Data_Vert_Average)(BMesh *UNUSED(bm), BMFace *UNUSED(f)) +{ + // BMIter iter; +} + +/** + * bmesh_data_facevert_edgeinterp + * + * Walks around the faces of an edge and interpolates the per-face-edge + * data between two sources to a target. + * + * Returns - + * Nothing + */ + +void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BMVert *v, BMEdge *e1, const float fac) +{ + void *src[2]; + float w[2]; + BMLoop *v1loop = NULL, *vloop = NULL, *v2loop = NULL; + BMLoop *l_iter = NULL; + + if (!e1->l) { + return; + } + + w[1] = 1.0f - fac; + w[0] = fac; + + l_iter = e1->l; + do { + if (l_iter->v == v1) { + v1loop = l_iter; + vloop = v1loop->next; + v2loop = vloop->next; + } + else if (l_iter->v == v) { + v1loop = l_iter->next; + vloop = l_iter; + v2loop = l_iter->prev; + } + + if (!v1loop || !v2loop) + return; + + src[0] = v1loop->head.data; + src[1] = v2loop->head.data; + + CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, vloop->head.data); + } while ((l_iter = l_iter->radial_next) != e1->l); +} + +void BM_loops_to_corners(BMesh *bm, Mesh *me, int findex, + BMFace *f, int numTex, int numCol) +{ + BMLoop *l; + BMIter iter; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + int i, j; + + for (i = 0; i < numTex; i++) { + texface = CustomData_get_n(&me->fdata, CD_MTFACE, findex, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->head.data, CD_MTEXPOLY, i); + + ME_MTEXFACE_CPY(texface, texpoly); + + j = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i); + copy_v2_v2(texface->uv[j], mloopuv->uv); + + j++; + } + + } + + for (i = 0; i < numCol; i++) { + mcol = CustomData_get_n(&me->fdata, CD_MCOL, findex, i); + + j = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPCOL, i); + mcol[j].r = mloopcol->r; + mcol[j].g = mloopcol->g; + mcol[j].b = mloopcol->b; + mcol[j].a = mloopcol->a; + + j++; + } + } +} + +/** + * BM_data_interp_from_face + * + * projects target onto source, and pulls interpolated customdata from + * source. + * + * Returns - + * Nothing + */ +void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source) +{ + BMLoop *l_iter; + BMLoop *l_first; + + void **blocks = NULL; + float (*cos)[3] = NULL, *w = NULL; + BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__); + int i; + + BM_elem_attrs_copy(bm, bm, source, target); + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + copy_v3_v3(cos[i], l_iter->v->co); + blocks[i] = l_iter->head.data; + i++; + } while ((l_iter = l_iter->next) != l_first); + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(target); + do { + interp_weights_poly_v3(w, cos, source->len, l_iter->v->co); + CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, l_iter->head.data); + i++; + } while ((l_iter = l_iter->next) != l_first); + + BLI_array_fixedstack_free(cos); + BLI_array_fixedstack_free(w); + BLI_array_fixedstack_free(blocks); +} + +/* some math stuff for dealing with doubles, put here to + * avoid merge errors - joeedh */ + +#define VECMUL(a, b) (((a)[0] = (a)[0] * (b)), ((a)[1] = (a)[1] * (b)), ((a)[2] = (a)[2] * (b))) +#define VECADD2(a, b) (((a)[0] = (a)[0] + (b)[0]), ((a)[1] = (a)[1] + (b)[1]), ((a)[2] = (a)[2] + (b)[2])) +#define VECSUB2(a, b) (((a)[0] = (a)[0] - (b)[0]), ((a)[1] = (a)[1] - (b)[1]), ((a)[2] = (a)[2] - (b)[2])) + +/* find closest point to p on line through l1, l2 and return lambda, + * where (0 <= lambda <= 1) when cp is in the line segement l1, l2 + */ +static double closest_to_line_v3_d(double cp[3], const double p[3], const double l1[3], const double l2[3]) +{ + double h[3], u[3], lambda; + VECSUB(u, l2, l1); + VECSUB(h, p, l1); + lambda = INPR(u, h) / INPR(u, u); + cp[0] = l1[0] + u[0] * lambda; + cp[1] = l1[1] + u[1] * lambda; + cp[2] = l1[2] + u[2] * lambda; + return lambda; +} + +/* point closest to v1 on line v2-v3 in 3D */ +static void UNUSED_FUNCTION(closest_to_line_segment_v3_d)(double *closest, double v1[3], double v2[3], double v3[3]) +{ + double lambda, cp[3]; + + lambda = closest_to_line_v3_d(cp, v1, v2, v3); + + if (lambda <= 0.0) { + VECCOPY(closest, v2); + } + else if (lambda >= 1.0) { + VECCOPY(closest, v3); + } + else { + VECCOPY(closest, cp); + } +} + +static double UNUSED_FUNCTION(len_v3_d)(const double a[3]) +{ + return sqrt(INPR(a, a)); +} + +static double UNUSED_FUNCTION(len_v3v3_d)(const double a[3], const double b[3]) +{ + double d[3]; + + VECSUB(d, b, a); + return sqrt(INPR(d, d)); +} + +static void cent_quad_v3_d(double *cent, double *v1, double *v2, double *v3, double *v4) +{ + cent[0] = 0.25 * (v1[0] + v2[0] + v3[0] + v4[0]); + cent[1] = 0.25 * (v1[1] + v2[1] + v3[1] + v4[1]); + cent[2] = 0.25 * (v1[2] + v2[2] + v3[2] + v4[2]); +} + +static void UNUSED_FUNCTION(cent_tri_v3_d)(double *cent, double *v1, double *v2, double *v3) +{ + cent[0] = (1.0 / 3.0) * (v1[0] + v2[0] + v3[0]); + cent[1] = (1.0 / 3.0) * (v1[1] + v2[1] + v3[1]); + cent[2] = (1.0 / 3.0) * (v1[2] + v2[2] + v3[2]); +} + +static void UNUSED_FUNCTION(cross_v3_v3v3_d)(double r[3], const double a[3], const double b[3]) +{ + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; +} + +/* distance v1 to line-piece v2-v3 */ +static double UNUSED_FUNCTION(dist_to_line_segment_v2_d)(double v1[3], double v2[3], double v3[3]) +{ + double labda, rc[2], pt[2], len; + + rc[0] = v3[0] - v2[0]; + rc[1] = v3[1] - v2[1]; + len = rc[0] * rc[0] + rc[1] * rc[1]; + if (len == 0.0) { + rc[0] = v1[0] - v2[0]; + rc[1] = v1[1] - v2[1]; + return sqrt(rc[0] * rc[0] + rc[1] * rc[1]); + } + + labda = (rc[0] * (v1[0] - v2[0]) + rc[1] * (v1[1] - v2[1])) / len; + if (labda <= 0.0) { + pt[0] = v2[0]; + pt[1] = v2[1]; + } + else if (labda >= 1.0) { + pt[0] = v3[0]; + pt[1] = v3[1]; + } + else { + pt[0] = labda * rc[0] + v2[0]; + pt[1] = labda * rc[1] + v2[1]; + } + + rc[0] = pt[0] - v1[0]; + rc[1] = pt[1] - v1[1]; + return sqrt(rc[0] * rc[0] + rc[1] * rc[1]); +} + + +MINLINE double line_point_side_v2_d(const double *l1, const double *l2, const double *pt) +{ + return ((l1[0] - pt[0]) * (l2[1] - pt[1])) - + ((l2[0] - pt[0]) * (l1[1] - pt[1])); +} + +/* point in quad - only convex quads */ +static int isect_point_quad_v2_d(double pt[2], double v1[2], double v2[2], double v3[2], double v4[2]) +{ + if (line_point_side_v2_d(v1, v2, pt) >= 0.0) { + if (line_point_side_v2_d(v2, v3, pt) >= 0.0) { + if (line_point_side_v2_d(v3, v4, pt) >= 0.0) { + if (line_point_side_v2_d(v4, v1, pt) >= 0.0) { + return 1; + } + } + } + } + else { + if (! (line_point_side_v2_d(v2, v3, pt) >= 0.0)) { + if (! (line_point_side_v2_d(v3, v4, pt) >= 0.0)) { + if (! (line_point_side_v2_d(v4, v1, pt) >= 0.0)) { + return -1; + } + } + } + } + + return 0; +} + +/***** multires interpolation***** + * + * mdisps is a grid of displacements, ordered thus: + * + * v1/center----v4/next -> x + * | | + * | | + * v2/prev------v3/cur + * | + * V + * y + */ + +static int compute_mdisp_quad(BMLoop *l, double v1[3], double v2[3], double v3[3], double v4[3], + double e1[3], double e2[3]) +{ + double cent[3] = {0.0, 0.0, 0.0}, n[3], p[3]; + BMLoop *l_first; + BMLoop *l_iter; + + /* computer center */ + l_iter = l_first = BM_FACE_FIRST_LOOP(l->f); + do { + cent[0] += (double)l_iter->v->co[0]; + cent[1] += (double)l_iter->v->co[1]; + cent[2] += (double)l_iter->v->co[2]; + } while ((l_iter = l_iter->next) != l_first); + + VECMUL(cent, (1.0 / (double)l->f->len)); + + VECADD(p, l->prev->v->co, l->v->co); + VECMUL(p, 0.5); + VECADD(n, l->next->v->co, l->v->co); + VECMUL(n, 0.5); + + VECCOPY(v1, cent); + VECCOPY(v2, p); + VECCOPY(v3, l->v->co); + VECCOPY(v4, n); + + VECSUB(e1, v2, v1); + VECSUB(e2, v3, v4); + + return 1; +} + +/* funnily enough, I think this is identical to face_to_crn_interp, heh */ +static double quad_coord(double aa[3], double bb[3], double cc[3], double dd[3], int a1, int a2) +{ + double x, y, z, f1; + + x = aa[a1] * cc[a2] - cc[a1] * aa[a2]; + y = aa[a1] * dd[a2] + bb[a1] * cc[a2] - cc[a1] * bb[a2] - dd[a1] * aa[a2]; + z = bb[a1] * dd[a2] - dd[a1] * bb[a2]; + + if (fabs(2 * (x - y + z)) > DBL_EPSILON * 10.0) { + double f2; + + f1 = (sqrt(y * y - 4.0 * x * z) - y + 2.0 * z) / (2.0 * (x - y + z)); + f2 = (-sqrt(y * y - 4.0 * x * z) - y + 2.0 * z) / (2.0 * (x - y + z)); + + f1 = fabs(f1); + f2 = fabs(f2); + f1 = MIN2(f1, f2); + CLAMP(f1, 0.0, 1.0 + DBL_EPSILON); + } + else { + f1 = -z / (y - 2 * z); + CLAMP(f1, 0.0, 1.0 + DBL_EPSILON); + + if (isnan(f1) || f1 > 1.0 || f1 < 0.0) { + int i; + + for (i = 0; i < 2; i++) { + if (fabsf(aa[i]) < FLT_EPSILON * 100) + return aa[(i + 1) % 2] / fabs(bb[(i + 1) % 2] - aa[(i + 1) % 2]); + if (fabsf(cc[i]) < FLT_EPSILON * 100) + return cc[(i + 1) % 2] / fabs(dd[(i + 1) % 2] - cc[(i + 1) % 2]); + } + } + } + + return f1; +} + +static int quad_co(double *x, double *y, double v1[3], double v2[3], double v3[3], double v4[3], + double p[3], float n[3]) +{ + float projverts[5][3], n2[3]; + double dprojverts[4][3], origin[3] = {0.0f, 0.0f, 0.0f}; + int i; + + /* project points into 2d along normal */ + VECCOPY(projverts[0], v1); + VECCOPY(projverts[1], v2); + VECCOPY(projverts[2], v3); + VECCOPY(projverts[3], v4); + VECCOPY(projverts[4], p); + + normal_quad_v3(n2, projverts[0], projverts[1], projverts[2], projverts[3]); + + if (INPR(n, n2) < -FLT_EPSILON) { + return 0; + } + + /* rotate */ + poly_rotate_plane(n, projverts, 5); + + /* flatten */ + for (i = 0; i < 5; i++) { + projverts[i][2] = 0.0f; + } + + /* subtract origin */ + for (i = 0; i < 4; i++) { + VECSUB2(projverts[i], projverts[4]); + } + + VECCOPY(dprojverts[0], projverts[0]); + VECCOPY(dprojverts[1], projverts[1]); + VECCOPY(dprojverts[2], projverts[2]); + VECCOPY(dprojverts[3], projverts[3]); + + if (!isect_point_quad_v2_d(origin, dprojverts[0], dprojverts[1], dprojverts[2], dprojverts[3])) { + return 0; + } + + *y = quad_coord(dprojverts[1], dprojverts[0], dprojverts[2], dprojverts[3], 0, 1); + *x = quad_coord(dprojverts[2], dprojverts[1], dprojverts[3], dprojverts[0], 0, 1); + + return 1; +} + + +/* tl is loop to project onto, l is loop whose internal displacement, co, is being + * projected. x and y are location in loop's mdisps grid of point co. */ +static int mdisp_in_mdispquad(BMesh *bm, BMLoop *l, BMLoop *tl, double p[3], double *x, double *y, int res) +{ + double v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3]; + double eps = FLT_EPSILON * 4000; + + if (len_v3(l->v->no) == 0.0f) + BM_vert_normal_update_all(bm, l->v); + if (len_v3(tl->v->no) == 0.0f) + BM_vert_normal_update_all(bm, tl->v); + + compute_mdisp_quad(tl, v1, v2, v3, v4, e1, e2); + + /* expand quad a bit */ + cent_quad_v3_d(c, v1, v2, v3, v4); + + VECSUB2(v1, c); VECSUB2(v2, c); + VECSUB2(v3, c); VECSUB2(v4, c); + VECMUL(v1, 1.0 + eps); VECMUL(v2, 1.0 + eps); + VECMUL(v3, 1.0 + eps); VECMUL(v4, 1.0 + eps); + VECADD2(v1, c); VECADD2(v2, c); + VECADD2(v3, c); VECADD2(v4, c); + + if (!quad_co(x, y, v1, v2, v3, v4, p, l->v->no)) + return 0; + + *x *= res - 1; + *y *= res - 1; + + return 1; +} + +static void bmesh_loop_interp_mdisps(BMesh *bm, BMLoop *target, BMFace *source) +{ + MDisps *mdisps; + BMLoop *l_iter; + BMLoop *l_first; + double x, y, d, v1[3], v2[3], v3[3], v4[3] = {0.0f, 0.0f, 0.0f}, e1[3], e2[3]; + int ix, iy, res; + + /* ignore 2-edged faces */ + if (target->f->len < 3) + return; + + if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) + return; + + mdisps = CustomData_bmesh_get(&bm->ldata, target->head.data, CD_MDISPS); + compute_mdisp_quad(target, v1, v2, v3, v4, e1, e2); + + /* if no disps data allocate a new grid, the size of the first grid in source. */ + if (!mdisps->totdisp) { + MDisps *md2 = CustomData_bmesh_get(&bm->ldata, BM_FACE_FIRST_LOOP(source)->head.data, CD_MDISPS); + + mdisps->totdisp = md2->totdisp; + if (mdisps->totdisp) { + mdisps->disps = MEM_callocN(sizeof(float) * 3 * mdisps->totdisp, + "mdisp->disps in bmesh_loop_intern_mdisps"); + } + else { + return; + } + } + + res = (int)sqrt(mdisps->totdisp); + d = 1.0 / (double)(res - 1); + for (x = 0.0f, ix = 0; ix < res; x += d, ix++) { + for (y = 0.0f, iy = 0; iy < res; y += d, iy++) { + double co1[3], co2[3], co[3]; + /* double xx, yy; */ /* UNUSED */ + + VECCOPY(co1, e1); + + /* if (!iy) yy = y + (double)FLT_EPSILON * 20; */ + /* else yy = y - (double)FLT_EPSILON * 20; */ + + VECMUL(co1, y); + VECADD2(co1, v1); + + VECCOPY(co2, e2); + VECMUL(co2, y); + VECADD2(co2, v4); + + /* if (!ix) xx = x + (double)FLT_EPSILON * 20; */ /* UNUSED */ + /* else xx = x - (double)FLT_EPSILON * 20; */ /* UNUSED */ + + VECSUB(co, co2, co1); + VECMUL(co, x); + VECADD2(co, co1); + + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + double x2, y2; + MDisps *md1, *md2; + + md1 = CustomData_bmesh_get(&bm->ldata, target->head.data, CD_MDISPS); + md2 = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS); + + if (mdisp_in_mdispquad(bm, target, l_iter, co, &x2, &y2, res)) { + /* int ix2 = (int)x2; */ /* UNUSED */ + /* int iy2 = (int)y2; */ /* UNUSED */ + + old_mdisps_bilinear(md1->disps[iy * res + ix], md2->disps, res, (float)x2, (float)y2); + } + } while ((l_iter = l_iter->next) != l_first); + } + } +} + +void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) +{ + BMLoop *l; + BMIter liter; + + if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) + return; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + MDisps *mdp = CustomData_bmesh_get(&bm->ldata, l->prev->head.data, CD_MDISPS); + MDisps *mdl = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS); + MDisps *mdn = CustomData_bmesh_get(&bm->ldata, l->next->head.data, CD_MDISPS); + float co1[3]; + int sides; + int y; + + /* + * mdisps is a grid of displacements, ordered thus: + * + * v4/next + * | + * | v1/cent-----mid2 ---> x + * | | | + * | | | + * v2/prev---mid1-----v3/cur + * | + * V + * y + */ + + sides = (int)sqrt(mdp->totdisp); + for (y = 0; y < sides; y++) { + add_v3_v3v3(co1, mdn->disps[y * sides], mdl->disps[y]); + mul_v3_fl(co1, 0.5); + + copy_v3_v3(mdn->disps[y * sides], co1); + copy_v3_v3(mdl->disps[y], co1); + } + } + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + MDisps *mdl1 = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS); + MDisps *mdl2; + float co1[3], co2[3], co[3]; + int sides; + int y; + + /* + * mdisps is a grid of displacements, ordered thus: + * + * v4/next + * | + * | v1/cent-----mid2 ---> x + * | | | + * | | | + * v2/prev---mid1-----v3/cur + * | + * V + * y + */ + + if (l->radial_next == l) + continue; + + if (l->radial_next->v == l->v) + mdl2 = CustomData_bmesh_get(&bm->ldata, l->radial_next->head.data, CD_MDISPS); + else + mdl2 = CustomData_bmesh_get(&bm->ldata, l->radial_next->next->head.data, CD_MDISPS); + + sides = (int)sqrt(mdl1->totdisp); + for (y = 0; y < sides; y++) { + int a1, a2, o1, o2; + + if (l->v != l->radial_next->v) { + a1 = sides * y + sides - 2; + a2 = (sides - 2) * sides + y; + + o1 = sides * y + sides - 1; + o2 = (sides - 1) * sides + y; + } + else { + a1 = sides * y + sides - 2; + a2 = sides * y + sides - 2; + o1 = sides * y + sides - 1; + o2 = sides * y + sides - 1; + } + + /* magic blending numbers, hardcoded! */ + add_v3_v3v3(co1, mdl1->disps[a1], mdl2->disps[a2]); + mul_v3_fl(co1, 0.18); + + add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]); + mul_v3_fl(co2, 0.32); + + add_v3_v3v3(co, co1, co2); + + copy_v3_v3(mdl1->disps[o1], co); + copy_v3_v3(mdl2->disps[o2], co); + } + } +} + +void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source) +{ + bmesh_loop_interp_mdisps(bm, target, source); +} + +void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source, + int do_vertex, int do_multires) +{ + BMLoop *l_iter; + BMLoop *l_first; + void **blocks = NULL; + void **vblocks = NULL; + float (*cos)[3] = NULL, co[3], *w = NULL; + float cent[3] = {0.0f, 0.0f, 0.0f}; + BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(vblocks, BM_NGON_STACK_SIZE, do_vertex ? source->len : 0, __func__); + int i, ax, ay; + + BM_elem_attrs_copy(bm, bm, source, target->f); + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + copy_v3_v3(cos[i], l_iter->v->co); + add_v3_v3(cent, cos[i]); + + w[i] = 0.0f; + blocks[i] = l_iter->head.data; + + if (do_vertex) { + vblocks[i] = l_iter->v->head.data; + } + i++; + + } while ((l_iter = l_iter->next) != l_first); + + /* find best projection of face XY, XZ or YZ: barycentric weights of + * the 2d projected coords are the same and faster to compute */ + + axis_dominant_v3(&ax, &ay, source->no); + + /* scale source face coordinates a bit, so points sitting directonly on an + * edge will work. */ + mul_v3_fl(cent, 1.0f / (float)source->len); + for (i = 0; i < source->len; i++) { + float vec[3], tmp[3]; + sub_v3_v3v3(vec, cent, cos[i]); + mul_v3_fl(vec, 0.001f); + add_v3_v3(cos[i], vec); + + copy_v3_v3(tmp, cos[i]); + cos[i][0] = tmp[ax]; + cos[i][1] = tmp[ay]; + cos[i][2] = 0.0; + } + + + /* interpolate */ + co[0] = target->v->co[ax]; + co[1] = target->v->co[ay]; + co[2] = 0.0f; + + interp_weights_poly_v3(w, cos, source->len, co); + CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, target->head.data); + if (do_vertex) { + CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, source->len, target->v->head.data); + BLI_array_fixedstack_free(vblocks); + } + + BLI_array_fixedstack_free(cos); + BLI_array_fixedstack_free(w); + BLI_array_fixedstack_free(blocks); + + if (do_multires) { + if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + bmesh_loop_interp_mdisps(bm, target, source); + } + } +} + + +void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source) +{ + BMLoop *l_iter; + BMLoop *l_first; + void **blocks = NULL; + float (*cos)[3] = NULL, *w = NULL; + float cent[3] = {0.0f, 0.0f, 0.0f}; + BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(w, BM_NGON_STACK_SIZE, source->len, __func__); + BLI_array_fixedstack_declare(blocks, BM_NGON_STACK_SIZE, source->len, __func__); + int i; + + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(source); + do { + copy_v3_v3(cos[i], l_iter->v->co); + add_v3_v3(cent, cos[i]); + + w[i] = 0.0f; + blocks[i] = l_iter->v->head.data; + i++; + } while ((l_iter = l_iter->next) != l_first); + + /* scale source face coordinates a bit, so points sitting directonly on an + * edge will work. */ + mul_v3_fl(cent, 1.0f / (float)source->len); + for (i = 0; i < source->len; i++) { + float vec[3]; + sub_v3_v3v3(vec, cent, cos[i]); + mul_v3_fl(vec, 0.01); + add_v3_v3(cos[i], vec); + } + + /* interpolate */ + interp_weights_poly_v3(w, cos, source->len, v->co); + CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, source->len, v->head.data); + + BLI_array_fixedstack_free(cos); + BLI_array_fixedstack_free(w); + BLI_array_fixedstack_free(blocks); +} + +static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) +{ + BMIter iter; + BLI_mempool *oldpool = olddata->pool; + void *block; + + CustomData_bmesh_init_pool(data, data == &bm->ldata ? 2048 : 512); + + if (data == &bm->vdata) { + BMVert *eve; + + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, eve->head.data, &block); + CustomData_bmesh_free_block(olddata, &eve->head.data); + eve->head.data = block; + } + } + else if (data == &bm->edata) { + BMEdge *eed; + + BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, eed->head.data, &block); + CustomData_bmesh_free_block(olddata, &eed->head.data); + eed->head.data = block; + } + } + else if (data == &bm->pdata || data == &bm->ldata) { + BMIter liter; + BMFace *efa; + BMLoop *l; + + BM_ITER(efa, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (data == &bm->pdata) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, efa->head.data, &block); + CustomData_bmesh_free_block(olddata, &efa->head.data); + efa->head.data = block; + } + + if (data == &bm->ldata) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, efa) { + block = NULL; + CustomData_bmesh_set_default(data, &block); + CustomData_bmesh_copy_data(olddata, data, l->head.data, &block); + CustomData_bmesh_free_block(olddata, &l->head.data); + l->head.data = block; + } + } + } + } + + if (oldpool) { + /* this should never happen but can when dissolve fails - [#28960] */ + BLI_assert(data->pool != oldpool); + + BLI_mempool_destroy(oldpool); + } +} + +void BM_data_layer_add(BMesh *bm, CustomData *data, int type) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_add_layer(data, type, CD_DEFAULT, NULL, 0); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_add_layer_named(data, type, CD_DEFAULT, NULL, 0, name); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void BM_data_layer_free(BMesh *bm, CustomData *data, int type) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_free_layer_active(data, type, 0); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n) +{ + CustomData olddata; + + olddata = *data; + olddata.layers = (olddata.layers) ? MEM_dupallocN(olddata.layers): NULL; + + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + CustomData_free_layer(data, type, 0, CustomData_get_layer_index_n(data, type, n)); + + update_data_blocks(bm, &olddata, data); + if (olddata.layers) MEM_freeN(olddata.layers); +} + +float BM_elem_float_data_get(CustomData *cd, void *element, int type) +{ + float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type); + return f ? *f : 0.0f; +} + +void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val) +{ + float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type); + if (f) *f = val; +} diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c new file mode 100644 index 00000000000..0c6ac24f456 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -0,0 +1,417 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_iterators.c + * \ingroup bmesh + * + * Functions to abstract looping over bmesh data structures. + * + * See: bmesh_iterators_inlin.c too, some functions are here for speed reasons. + */ + + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * note, we have BM_vert_at_index/BM_edge_at_index/BM_face_at_index for arrays + */ +void *BM_iter_at_index(struct BMesh *bm, const char itype, void *data, int index) +{ + BMIter iter; + void *val; + int i; + + /* sanity check */ + if (index < 0) { + return NULL; + } + + val = BM_iter_new(&iter, bm, itype, data); + + i = 0; + while (i < index) { + val = BM_iter_step(&iter); + i++; + } + + return val; +} + + +/* + * ITERATOR AS ARRAY + * + * Sometimes its convenient to get the iterator as an array + * to avoid multiple calls to BM_iter_at_index. + */ + +int BM_iter_as_array(struct BMesh *bm, const char type, void *data, void **array, const int len) +{ + int i = 0; + + /* sanity check */ + if (len > 0) { + + BMIter iter; + void *val; + + BM_ITER(val, &iter, bm, type, data) { + array[i] = val; + i++; + if (i == len) { + return len; + } + } + } + + return i; +} + + +/* + * INIT ITERATOR + * + * Clears the internal state of an iterator + * For begin() callbacks. + * + */ + +static void init_iterator(BMIter *iter) +{ + iter->firstvert = iter->nextvert = NULL; + iter->firstedge = iter->nextedge = NULL; + iter->firstloop = iter->nextloop = NULL; + iter->firstpoly = iter->nextpoly = NULL; + iter->ldata = NULL; +} + +/* + * Notes on iterator implementation: + * + * Iterators keep track of the next element + * in a sequence. When a step() callback is + * invoked the current value of 'next' is stored + * to be returned later and the next variable is + * incremented. + * + * When the end of a sequence is + * reached, next should always equal NULL + * + * The 'bmiter__' prefix is used because these are used in + * bmesh_iterators_inine.c but should otherwise be seen as + * private. + */ + +/* + * VERT OF MESH CALLBACKS + * + */ + +void bmiter__vert_of_mesh_begin(BMIter *iter) +{ + BLI_mempool_iternew(iter->bm->vpool, &iter->pooliter); +} + +void *bmiter__vert_of_mesh_step(BMIter *iter) +{ + return BLI_mempool_iterstep(&iter->pooliter); + +} + +void bmiter__edge_of_mesh_begin(BMIter *iter) +{ + BLI_mempool_iternew(iter->bm->epool, &iter->pooliter); +} + +void *bmiter__edge_of_mesh_step(BMIter *iter) +{ + return BLI_mempool_iterstep(&iter->pooliter); + +} + +void bmiter__face_of_mesh_begin(BMIter *iter) +{ + BLI_mempool_iternew(iter->bm->fpool, &iter->pooliter); +} + +void *bmiter__face_of_mesh_step(BMIter *iter) +{ + return BLI_mempool_iterstep(&iter->pooliter); + +} + +/* + * EDGE OF VERT CALLBACKS + * + */ + +void bmiter__edge_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + if (iter->vdata->e) { + iter->firstedge = iter->vdata->e; + iter->nextedge = iter->vdata->e; + } +} + +void *bmiter__edge_of_vert_step(BMIter *iter) +{ + BMEdge *current = iter->nextedge; + + if (iter->nextedge) + iter->nextedge = bmesh_disk_nextedge(iter->nextedge, iter->vdata); + + if (iter->nextedge == iter->firstedge) iter->nextedge = NULL; + + return current; +} + +/* + * FACE OF VERT CALLBACKS + * + */ + +void bmiter__face_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + iter->count = 0; + if (iter->vdata->e) + iter->count = bmesh_disk_count_facevert(iter->vdata); + if (iter->count) { + iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->e, iter->vdata); + iter->nextedge = iter->firstedge; + iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } +} +void *bmiter__face_of_vert_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->count && iter->nextloop) { + iter->count--; + iter->nextloop = bmesh_radial_find_next_facevert(iter->nextloop, iter->vdata); + if (iter->nextloop == iter->firstloop) { + iter->nextedge = bmesh_disk_find_next_faceedge(iter->nextedge, iter->vdata); + iter->firstloop = bmesh_radial_find_first_facevert(iter->nextedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } + } + + if (!iter->count) iter->nextloop = NULL; + + return current ? current->f : NULL; +} + + +/* + * LOOP OF VERT CALLBACKS + * + */ + +void bmiter__loop_of_vert_begin(BMIter *iter) +{ + init_iterator(iter); + iter->count = 0; + if (iter->vdata->e) + iter->count = bmesh_disk_count_facevert(iter->vdata); + if (iter->count) { + iter->firstedge = bmesh_disk_find_first_faceedge(iter->vdata->e, iter->vdata); + iter->nextedge = iter->firstedge; + iter->firstloop = bmesh_radial_find_first_facevert(iter->firstedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } +} +void *bmiter__loop_of_vert_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->count) { + iter->count--; + iter->nextloop = bmesh_radial_find_next_facevert(iter->nextloop, iter->vdata); + if (iter->nextloop == iter->firstloop) { + iter->nextedge = bmesh_disk_find_next_faceedge(iter->nextedge, iter->vdata); + iter->firstloop = bmesh_radial_find_first_facevert(iter->nextedge->l, iter->vdata); + iter->nextloop = iter->firstloop; + } + } + + if (!iter->count) iter->nextloop = NULL; + + + if (current) { + return current; + } + + return NULL; +} + + +void bmiter__loops_of_edge_begin(BMIter *iter) +{ + BMLoop *l; + + l = iter->edata->l; + + /* note sure why this sets ldata ... */ + init_iterator(iter); + + iter->firstloop = iter->nextloop = l; +} + +void *bmiter__loops_of_edge_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) + iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if (iter->nextloop == iter->firstloop) + iter->nextloop = NULL; + + if (current) { + return current; + } + + return NULL; +} + +void bmiter__loops_of_loop_begin(BMIter *iter) +{ + BMLoop *l; + + l = iter->ldata; + + /* note sure why this sets ldata ... */ + init_iterator(iter); + + iter->firstloop = l; + iter->nextloop = bmesh_radial_nextloop(iter->firstloop); + + if (iter->nextloop == iter->firstloop) + iter->nextloop = NULL; +} + +void *bmiter__loops_of_loop_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + if (current) { + return current; + } + + return NULL; +} + +/* + * FACE OF EDGE CALLBACKS + * + */ + +void bmiter__face_of_edge_begin(BMIter *iter) +{ + init_iterator(iter); + + if (iter->edata->l) { + iter->firstloop = iter->edata->l; + iter->nextloop = iter->edata->l; + } +} + +void *bmiter__face_of_edge_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = bmesh_radial_nextloop(iter->nextloop); + + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current ? current->f : NULL; +} + +/* + * VERT OF FACE CALLBACKS + * + */ + +void bmiter__vert_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); +} + +void *bmiter__vert_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = iter->nextloop->next; + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current ? current->v : NULL; +} + +/* + * EDGE OF FACE CALLBACKS + * + */ + +void bmiter__edge_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); +} + +void *bmiter__edge_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = iter->nextloop->next; + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current ? current->e : NULL; +} + +/* + * LOOP OF FACE CALLBACKS + * + */ + +void bmiter__loop_of_face_begin(BMIter *iter) +{ + init_iterator(iter); + iter->firstloop = iter->nextloop = BM_FACE_FIRST_LOOP(iter->pdata); +} + +void *bmiter__loop_of_face_step(BMIter *iter) +{ + BMLoop *current = iter->nextloop; + + if (iter->nextloop) iter->nextloop = iter->nextloop->next; + if (iter->nextloop == iter->firstloop) iter->nextloop = NULL; + + return current; +} diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.c b/source/blender/bmesh/intern/bmesh_iterators_inline.c new file mode 100644 index 00000000000..ef644f96ce0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.c @@ -0,0 +1,160 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_iterators_inline.c + * \ingroup bmesh + * + * BMesh inline iterator functions. + */ + +#ifndef __BMESH_ITERATORS_INLINE_C__ +#define __BMESH_ITERATORS_INLINE_C__ + +#include "bmesh.h" + +/* inline here optimizes out the switch statement when called with + * constant values (which is very common), nicer for loop-in-loop situations */ + +/* + * BMESH ITERATOR STEP + * + * Calls an iterators step fucntion to return + * the next element. + */ + +BM_INLINE void *BM_iter_step(BMIter *iter) +{ + return iter->step(iter); +} + + +/* + * BMESH ITERATOR INIT + * + * Takes a bmesh iterator structure and fills + * it with the appropriate function pointers based + * upon its type and then calls BMeshIter_step() + * to return the first element of the iterator. + * + */ +BM_INLINE void *BM_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data) +{ + /* int argtype; */ + iter->itype = itype; + iter->bm = bm; + + /* inlining optimizes out this switch when called with the defined type */ + switch (itype) { + case BM_VERTS_OF_MESH: + iter->begin = bmiter__vert_of_mesh_begin; + iter->step = bmiter__vert_of_mesh_step; + break; + case BM_EDGES_OF_MESH: + iter->begin = bmiter__edge_of_mesh_begin; + iter->step = bmiter__edge_of_mesh_step; + break; + case BM_FACES_OF_MESH: + iter->begin = bmiter__face_of_mesh_begin; + iter->step = bmiter__face_of_mesh_step; + break; + case BM_EDGES_OF_VERT: + if (!data) + return NULL; + + iter->begin = bmiter__edge_of_vert_begin; + iter->step = bmiter__edge_of_vert_step; + iter->vdata = data; + break; + case BM_FACES_OF_VERT: + if (!data) + return NULL; + + iter->begin = bmiter__face_of_vert_begin; + iter->step = bmiter__face_of_vert_step; + iter->vdata = data; + break; + case BM_LOOPS_OF_VERT: + if (!data) + return NULL; + + iter->begin = bmiter__loop_of_vert_begin; + iter->step = bmiter__loop_of_vert_step; + iter->vdata = data; + break; + case BM_FACES_OF_EDGE: + if (!data) + return NULL; + + iter->begin = bmiter__face_of_edge_begin; + iter->step = bmiter__face_of_edge_step; + iter->edata = data; + break; + case BM_VERTS_OF_FACE: + if (!data) + return NULL; + + iter->begin = bmiter__vert_of_face_begin; + iter->step = bmiter__vert_of_face_step; + iter->pdata = data; + break; + case BM_EDGES_OF_FACE: + if (!data) + return NULL; + + iter->begin = bmiter__edge_of_face_begin; + iter->step = bmiter__edge_of_face_step; + iter->pdata = data; + break; + case BM_LOOPS_OF_FACE: + if (!data) + return NULL; + + iter->begin = bmiter__loop_of_face_begin; + iter->step = bmiter__loop_of_face_step; + iter->pdata = data; + break; + case BM_LOOPS_OF_LOOP: + if (!data) + return NULL; + + iter->begin = bmiter__loops_of_loop_begin; + iter->step = bmiter__loops_of_loop_step; + iter->ldata = data; + break; + case BM_LOOPS_OF_EDGE: + if (!data) + return NULL; + + iter->begin = bmiter__loops_of_edge_begin; + iter->step = bmiter__loops_of_edge_step; + iter->edata = data; + break; + default: + break; + } + + iter->begin(iter); + return BM_iter_step(iter); +} + + +#endif /* __BMESH_ITERATORS_INLINE_C__ */ diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c new file mode 100644 index 00000000000..8a8ccb5efb1 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -0,0 +1,910 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_marking.c + * \ingroup bmesh + * + * Selection routines for bmesh structures. + * This is actually all old code ripped from + * editmesh_lib.c and slightly modified to work + * for bmesh's. This also means that it has some + * of the same problems.... something that + * that should be addressed eventually. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" + +#include "bmesh.h" + + +/* + * BMESH SELECTMODE FLUSH + * + * Makes sure to flush selections + * 'upwards' (ie: all verts of an edge + * selects the edge and so on). This + * should only be called by system and not + * tool authors. + * + */ + +static void recount_totsels(BMesh *bm) +{ + BMIter iter; + BMHeader *ele; + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + int *tots[3]; + int i; + + /* recount (tot * sel) variables */ + bm->totvertsel = bm->totedgesel = bm->totfacesel = 0; + tots[0] = &bm->totvertsel; + tots[1] = &bm->totedgesel; + tots[2] = &bm->totfacesel; + + for (i = 0; i < 3; i++) { + ele = BM_iter_new(&iter, bm, iter_types[i], NULL); + for ( ; ele; ele = BM_iter_step(&iter)) { + if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) *tots[i] += 1; + } + } +} + +void BM_mesh_select_mode_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l_iter; + BMLoop *l_first; + BMFace *f; + + BMIter edges; + BMIter faces; + + int ok; + + if (bm->selectmode & SCE_SELECT_VERTEX) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) + { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + } + else { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + } + } + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + } + else { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + } + } + } + else if (bm->selectmode & SCE_SELECT_EDGE) { + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(&(l_iter->e->head), BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + } + else { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + } + } + } + + /* Remove any deselected elements from the BMEditSelection */ + BM_select_history_validate(bm); + + recount_totsels(bm); +} + +/* BMESH NOTE: matches EM_deselect_flush() behavior from trunk */ +void BM_mesh_deselect_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l_iter; + BMLoop *l_first; + BMFace *f; + + BMIter edges; + BMIter faces; + + int ok; + + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (!(BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN))) + { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + } + } + + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok == FALSE) { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + } + } + + /* Remove any deselected elements from the BMEditSelection */ + BM_select_history_validate(bm); + + recount_totsels(bm); +} + + +/* BMESH NOTE: matches EM_select_flush() behavior from trunk */ +void BM_mesh_select_flush(BMesh *bm) +{ + BMEdge *e; + BMLoop *l_iter; + BMLoop *l_first; + BMFace *f; + + BMIter edges; + BMIter faces; + + int ok; + + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) + { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + } + } + + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + ok = TRUE; + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + ok = FALSE; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + else { + ok = FALSE; + } + + if (ok) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + } + } + + recount_totsels(bm); +} + +/* + * BMESH SELECT VERT + * + * Changes selection state of a single vertex + * in a mesh + * + */ + +void BM_vert_select_set(BMesh *bm, BMVert *v, int select) +{ + /* BMIter iter; */ + /* BMEdge *e; */ + + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) { + bm->totvertsel += 1; + BM_elem_flag_enable(v, BM_ELEM_SELECT); + } + } + else { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + bm->totvertsel -= 1; + BM_elem_flag_disable(v, BM_ELEM_SELECT); + } + } +} + +/* + * BMESH SELECT EDGE + * + * Changes selection state of a single edge + * in a mesh. + * + */ + +void BM_edge_select_set(BMesh *bm, BMEdge *e, int select) +{ + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel += 1; + + BM_elem_flag_enable(&(e->head), BM_ELEM_SELECT); + BM_elem_select_set(bm, e->v1, TRUE); + BM_elem_select_set(bm, e->v2, TRUE); + } + else { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel -= 1; + BM_elem_flag_disable(&(e->head), BM_ELEM_SELECT); + + if ( bm->selectmode == SCE_SELECT_EDGE || + bm->selectmode == SCE_SELECT_FACE || + bm->selectmode == (SCE_SELECT_EDGE | SCE_SELECT_FACE)) + { + + BMIter iter; + BMVert *verts[2] = {e->v1, e->v2}; + BMEdge *e2; + int i; + + for (i = 0; i < 2; i++) { + int deselect = 1; + + for (e2 = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, verts[i]); e2; e2 = BM_iter_step(&iter)) { + if (e2 == e) { + continue; + } + + if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) { + deselect = 0; + break; + } + } + + if (deselect) BM_vert_select_set(bm, verts[i], FALSE); + } + } + else { + BM_elem_select_set(bm, e->v1, FALSE); + BM_elem_select_set(bm, e->v2, FALSE); + } + + } +} + +/* + * + * BMESH SELECT FACE + * + * Changes selection state of a single + * face in a mesh. + * + */ + +void BM_face_select_set(BMesh *bm, BMFace *f, int select) +{ + BMLoop *l_iter; + BMLoop *l_first; + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + bm->totfacesel++; + } + + BM_elem_flag_enable(&(f->head), BM_ELEM_SELECT); + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_vert_select_set(bm, l_iter->v, TRUE); + BM_edge_select_set(bm, l_iter->e, TRUE); + } while ((l_iter = l_iter->next) != l_first); + } + else { + BMIter liter; + BMLoop *l; + + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) bm->totfacesel -= 1; + BM_elem_flag_disable(&(f->head), BM_ELEM_SELECT); + + /* flush down to edges */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BMIter fiter; + BMFace *f2; + BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) { + if (BM_elem_flag_test(f2, BM_ELEM_SELECT)) + break; + } + + if (!f2) + { + BM_elem_select_set(bm, l->e, FALSE); + } + } + + /* flush down to verts */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BMIter eiter; + BMEdge *e; + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, l->v) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) + break; + } + + if (!e) { + BM_elem_select_set(bm, l->v, FALSE); + } + } + } +} + +/* + * BMESH SELECTMODE SET + * + * Sets the selection mode for the bmesh + * + */ + +void BM_select_mode_set(BMesh *bm, int selectmode) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + bm->selectmode = selectmode; + + if (bm->selectmode & SCE_SELECT_VERTEX) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) + BM_elem_flag_disable(e, 0); + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) + BM_elem_flag_disable(f, 0); + BM_mesh_select_mode_flush(bm); + } + else if (bm->selectmode & SCE_SELECT_EDGE) { + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) + BM_elem_flag_disable(v, 0); + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + if (BM_elem_flag_test(&(e->head), BM_ELEM_SELECT)) { + BM_edge_select_set(bm, e, TRUE); + } + } + BM_mesh_select_mode_flush(bm); + } + else if (bm->selectmode & SCE_SELECT_FACE) { + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) + BM_elem_flag_disable(e, 0); + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + if (BM_elem_flag_test(&(f->head), BM_ELEM_SELECT)) { + BM_face_select_set(bm, f, TRUE); + } + } + BM_mesh_select_mode_flush(bm); + } +} + + +int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide) +{ + BMHeader *head; + BMIter iter; + int tot = 0; + + if (htype & BM_VERT) { + for (head = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); head; head = BM_iter_step(&iter)) { + if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue; + if (BM_elem_flag_test(head, hflag)) tot++; + } + } + if (htype & BM_EDGE) { + for (head = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) { + if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue; + if (BM_elem_flag_test(head, hflag)) tot++; + } + } + if (htype & BM_FACE) { + for (head = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); head; head = BM_iter_step(&iter)) { + if (respecthide && BM_elem_flag_test(head, BM_ELEM_HIDDEN)) continue; + if (BM_elem_flag_test(head, hflag)) tot++; + } + } + + return tot; +} + +/* note: by design, this will not touch the editselection history stuff */ +void BM_elem_select_set(struct BMesh *bm, void *element, int select) +{ + BMHeader *head = element; + + if (head->htype == BM_VERT) BM_vert_select_set(bm, (BMVert *)element, select); + else if (head->htype == BM_EDGE) BM_edge_select_set(bm, (BMEdge *)element, select); + else if (head->htype == BM_FACE) BM_face_select_set(bm, (BMFace *)element, select); +} + +/* this replaces the active flag used in uv/face mode */ +void BM_active_face_set(BMesh *bm, BMFace *efa) +{ + bm->act_face = efa; +} + +BMFace *BM_active_face_get(BMesh *bm, int sloppy) +{ + if (bm->act_face) { + return bm->act_face; + } + else if (sloppy) { + BMIter iter; + BMFace *f = NULL; + BMEditSelection *ese; + + /* Find the latest non-hidden face from the BMEditSelection */ + ese = bm->selected.last; + for ( ; ese; ese = ese->prev) { + if (ese->htype == BM_FACE) { + f = (BMFace *)ese->data; + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + f = NULL; + } + else { + break; + } + } + } + /* Last attempt: try to find any selected face */ + if (f == NULL) { + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + break; + } + } + } + return f; /* can still be null */ + } + return NULL; +} + +/* Generic way to get data from an EditSelection type + * These functions were written to be used by the Modifier widget + * when in Rotate about active mode, but can be used anywhere. + * + * - EM_editselection_center + * - EM_editselection_normal + * - EM_editselection_plane + */ +void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese) +{ + if (ese->htype == BM_VERT) { + BMVert *eve = ese->data; + copy_v3_v3(r_center, eve->co); + } + else if (ese->htype == BM_EDGE) { + BMEdge *eed = ese->data; + add_v3_v3v3(r_center, eed->v1->co, eed->v2->co); + mul_v3_fl(r_center, 0.5); + } + else if (ese->htype == BM_FACE) { + BMFace *efa = ese->data; + BM_face_center_bounds_calc(bm, efa, r_center); + } +} + +void BM_editselection_normal(float r_normal[3], BMEditSelection *ese) +{ + if (ese->htype == BM_VERT) { + BMVert *eve = ese->data; + copy_v3_v3(r_normal, eve->no); + } + else if (ese->htype == BM_EDGE) { + BMEdge *eed = ese->data; + float plane[3]; /* need a plane to correct the normal */ + float vec[3]; /* temp vec storage */ + + add_v3_v3v3(r_normal, eed->v1->no, eed->v2->no); + sub_v3_v3v3(plane, eed->v2->co, eed->v1->co); + + /* the 2 vertex normals will be close but not at rightangles to the edge + * for rotate about edge we want them to be at right angles, so we need to + * do some extra colculation to correct the vert normals, + * we need the plane for this */ + cross_v3_v3v3(vec, r_normal, plane); + cross_v3_v3v3(r_normal, plane, vec); + normalize_v3(r_normal); + + } + else if (ese->htype == BM_FACE) { + BMFace *efa = ese->data; + copy_v3_v3(r_normal, efa->no); + } +} + +/* ref - editmesh_lib.cL:EM_editselection_plane() */ + +/* Calculate a plane that is rightangles to the edge/vert/faces normal + * also make the plane run along an axis that is related to the geometry, + * because this is used for the manipulators Y axis. */ +void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese) +{ + if (ese->htype == BM_VERT) { + BMVert *eve = ese->data; + float vec[3] = {0.0f, 0.0f, 0.0f}; + + if (ese->prev) { /* use previously selected data to make a useful vertex plane */ + BM_editselection_center(bm, vec, ese->prev); + sub_v3_v3v3(r_plane, vec, eve->co); + } + else { + /* make a fake plane thats at rightangles to the normal + * we cant make a crossvec from a vec thats the same as the vec + * unlikely but possible, so make sure if the normal is (0, 0, 1) + * that vec isnt the same or in the same direction even. */ + if (eve->no[0] < 0.5f) vec[0] = 1.0f; + else if (eve->no[1] < 0.5f) vec[1] = 1.0f; + else vec[2] = 1.0f; + cross_v3_v3v3(r_plane, eve->no, vec); + } + } + else if (ese->htype == BM_EDGE) { + BMEdge *eed = ese->data; + + /* the plane is simple, it runs along the edge + * however selecting different edges can swap the direction of the y axis. + * this makes it less likely for the y axis of the manipulator + * (running along the edge).. to flip less often. + * at least its more pradictable */ + if (eed->v2->co[1] > eed->v1->co[1]) { /* check which to do first */ + sub_v3_v3v3(r_plane, eed->v2->co, eed->v1->co); + } + else { + sub_v3_v3v3(r_plane, eed->v1->co, eed->v2->co); + } + + } + else if (ese->htype == BM_FACE) { + BMFace *efa = ese->data; + float vec[3] = {0.0f, 0.0f, 0.0f}; + + /* for now, use face normal */ + + /* make a fake plane thats at rightangles to the normal + * we cant make a crossvec from a vec thats the same as the vec + * unlikely but possible, so make sure if the normal is (0, 0, 1) + * that vec isnt the same or in the same direction even. */ + if (efa->len < 3) { + /* crappy fallback method */ + if (efa->no[0] < 0.5f) vec[0] = 1.0f; + else if (efa->no[1] < 0.5f) vec[1] = 1.0f; + else vec[2] = 1.0f; + cross_v3_v3v3(r_plane, efa->no, vec); + } + else { + BMVert *verts[4] = {NULL}; + + BM_iter_as_array(bm, BM_VERTS_OF_FACE, efa, (void **)verts, 4); + + if (efa->len == 4) { + float vecA[3], vecB[3]; + sub_v3_v3v3(vecA, verts[3]->co, verts[2]->co); + sub_v3_v3v3(vecB, verts[0]->co, verts[1]->co); + add_v3_v3v3(r_plane, vecA, vecB); + + sub_v3_v3v3(vecA, verts[0]->co, verts[3]->co); + sub_v3_v3v3(vecB, verts[1]->co, verts[2]->co); + add_v3_v3v3(vec, vecA, vecB); + /* use the biggest edge length */ + if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) { + copy_v3_v3(r_plane, vec); + } + } + else { + /* BMESH_TODO (not urgent, use longest ngon edge for alignment) */ + + /* start with v1-2 */ + sub_v3_v3v3(r_plane, verts[0]->co, verts[1]->co); + + /* test the edge between v2-3, use if longer */ + sub_v3_v3v3(vec, verts[1]->co, verts[2]->co); + if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) + copy_v3_v3(r_plane, vec); + + /* test the edge between v1-3, use if longer */ + sub_v3_v3v3(vec, verts[2]->co, verts[0]->co); + if (dot_v3v3(r_plane, r_plane) < dot_v3v3(vec, vec)) { + copy_v3_v3(r_plane, vec); + } + } + + } + } + normalize_v3(r_plane); +} + +int BM_select_history_check(BMesh *bm, void *data) +{ + BMEditSelection *ese; + + for (ese = bm->selected.first; ese; ese = ese->next) { + if (ese->data == data) { + return TRUE; + } + } + + return FALSE; +} + +void BM_select_history_remove(BMesh *bm, void *data) +{ + BMEditSelection *ese; + for (ese = bm->selected.first; ese; ese = ese->next) { + if (ese->data == data) { + BLI_freelinkN(&(bm->selected), ese); + break; + } + } +} + +void BM_select_history_clear(BMesh *bm) +{ + BLI_freelistN(&bm->selected); + bm->selected.first = bm->selected.last = NULL; +} + +void BM_select_history_store(BMesh *bm, void *data) +{ + BMEditSelection *ese; + if (!BM_select_history_check(bm, data)) { + ese = (BMEditSelection *) MEM_callocN(sizeof(BMEditSelection), "BMEdit Selection"); + ese->htype = ((BMHeader *)data)->htype; + ese->data = data; + BLI_addtail(&(bm->selected), ese); + } +} + +void BM_select_history_validate(BMesh *bm) +{ + BMEditSelection *ese, *nextese; + + ese = bm->selected.first; + + while (ese) { + nextese = ese->next; + if (!BM_elem_flag_test(ese->data, BM_ELEM_SELECT)) { + BLI_freelinkN(&(bm->selected), ese); + } + ese = nextese; + } +} + +void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + BMIter iter; + BMHeader *ele; + int i; + + if (hflag & BM_ELEM_SELECT) { + BM_select_history_clear(bm); + } + + for (i = 0; i < 3; i++) { + if (htype & iter_types[i]) { + ele = BM_iter_new(&iter, bm, iter_types[i], NULL); + for ( ; ele; ele = BM_iter_step(&iter)) { + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, ele, FALSE); + } + BM_elem_flag_disable(ele, hflag); + } + } + } +} + +void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + BMIter iter; + BMHeader *ele; + int i; + + if (hflag & BM_ELEM_SELECT) { + BM_select_history_clear(bm); + } + + for (i = 0; i < 3; i++) { + if (htype & iter_types[i]) { + ele = BM_iter_new(&iter, bm, iter_types[i], NULL); + for ( ; ele; ele = BM_iter_step(&iter)) { + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, ele, TRUE); + } + BM_elem_flag_enable(ele, hflag); + } + } + } +} + +/***************** Mesh Hiding stuff *********** */ + +#define BM_ELEM_HIDE_SET(ele, hide) \ + (hide) ? BM_elem_flag_enable(ele, BM_ELEM_HIDDEN) : BM_elem_flag_disable(ele, BM_ELEM_HIDDEN); + +static void vert_flush_hide_set(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMEdge *e; + int hide = TRUE; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + hide = hide && BM_elem_flag_test(e, BM_ELEM_HIDDEN); + } + + BM_ELEM_HIDE_SET(v, hide); +} + +static void edge_flush_hide(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMFace *f; + int hide = TRUE; + + BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) { + hide = hide && BM_elem_flag_test(f, BM_ELEM_HIDDEN); + } + + BM_ELEM_HIDE_SET(e, hide); +} + +void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide) +{ + /* vert hiding: vert + surrounding edges and faces */ + BMIter iter, fiter; + BMEdge *e; + BMFace *f; + + BM_ELEM_HIDE_SET(v, hide); + + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + BM_ELEM_HIDE_SET(e, hide); + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + BM_ELEM_HIDE_SET(f, hide); + } + } +} + +void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide) +{ + BMIter iter; + BMFace *f; + /* BMVert *v; */ + + /* edge hiding: faces around the edge */ + BM_ITER(f, &iter, bm, BM_FACES_OF_EDGE, e) { + BM_ELEM_HIDE_SET(f, hide); + } + + BM_ELEM_HIDE_SET(e, hide); + + /* hide vertices if necassary */ + vert_flush_hide_set(bm, e->v1); + vert_flush_hide_set(bm, e->v2); +} + +void BM_face_hide_set(BMesh *bm, BMFace *f, int hide) +{ + BMIter iter; + BMLoop *l; + + BM_ELEM_HIDE_SET(f, hide); + + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + edge_flush_hide(bm, l->e); + } + + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + vert_flush_hide_set(bm, l->v); + } +} + +#undef BM_ELEM_HIDE_SET + + +void BM_elem_hide_set(BMesh *bm, void *element, int hide) +{ + BMHeader *h = element; + + /* Follow convention of always deselecting before + * hiding an element */ + if (hide) { + BM_elem_select_set(bm, element, FALSE); + } + + switch (h->htype) { + case BM_VERT: + BM_vert_hide_set(bm, element, hide); + break; + case BM_EDGE: + BM_edge_hide_set(bm, element, hide); + break; + case BM_FACE: + BM_face_hide_set(bm, element, hide); + break; + } +} diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c new file mode 100644 index 00000000000..a432049e238 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -0,0 +1,625 @@ +/* + * ***** 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): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_mesh.c + * \ingroup bmesh + * + * BM mesh level functions. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_utildefines.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_tessmesh.h" +#include "BKE_customdata.h" +#include "BKE_multires.h" + +#include "ED_mesh.h" + +#include "bmesh_private.h" + +/* used as an extern, defined in bmesh.h */ +int bm_mesh_allocsize_default[4] = {512, 512, 2048, 512}; + +/* bmesh_error stub */ +void bmesh_error(void) +{ + printf("BM modelling error!\n"); + + /* This placeholder assert makes modelling errors easier to catch + * in the debugger, until bmesh_error is replaced with something + * better. */ + BLI_assert(0); +} + +static void bmesh_mempool_init(BMesh *bm, const int allocsize[4]) +{ + bm->vpool = BLI_mempool_create(sizeof(BMVert), allocsize[0], allocsize[0], FALSE, TRUE); + bm->epool = BLI_mempool_create(sizeof(BMEdge), allocsize[1], allocsize[1], FALSE, TRUE); + bm->lpool = BLI_mempool_create(sizeof(BMLoop), allocsize[2], allocsize[2], FALSE, FALSE); + bm->fpool = BLI_mempool_create(sizeof(BMFace), allocsize[3], allocsize[3], FALSE, TRUE); + +#ifdef USE_BMESH_HOLES + bm->looplistpool = BLI_mempool_create(sizeof(BMLoopList), allocsize[3], allocsize[3], FALSE, FALSE); +#endif + + /* allocate one flag pool that we dont get rid of. */ + bm->toolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), 512, 512, FALSE, FALSE); +} + +/* + * BMESH MAKE MESH + * + * Allocates a new BMesh structure. + * Returns - + * Pointer to a BM + * + */ + +BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4]) +{ + /* allocate the structure */ + BMesh *bm = MEM_callocN(sizeof(BMesh), __func__); + + bm->ob = ob; + + /* allocate the memory pools for the mesh elements */ + bmesh_mempool_init(bm, allocsize); + + /* allocate one flag pool that we dont get rid of. */ + bm->stackdepth = 1; + bm->totflags = 1; + + return bm; +} + +/* + * BMESH FREE MESH + * + * Frees a BMesh structure. + */ + +void BM_mesh_data_free(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMLoop *l; + BMFace *f; + + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + for (v = BM_iter_new(&verts, bm, BM_VERTS_OF_MESH, bm); v; v = BM_iter_step(&verts)) { + CustomData_bmesh_free_block(&(bm->vdata), &(v->head.data)); + } + for (e = BM_iter_new(&edges, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&edges)) { + CustomData_bmesh_free_block(&(bm->edata), &(e->head.data)); + } + for (f = BM_iter_new(&faces, bm, BM_FACES_OF_MESH, bm); f; f = BM_iter_step(&faces)) { + CustomData_bmesh_free_block(&(bm->pdata), &(f->head.data)); + for (l = BM_iter_new(&loops, bm, BM_LOOPS_OF_FACE, f); l; l = BM_iter_step(&loops)) { + CustomData_bmesh_free_block(&(bm->ldata), &(l->head.data)); + } + } + + /* Free custom data pools, This should probably go in CustomData_free? */ + if (bm->vdata.totlayer) BLI_mempool_destroy(bm->vdata.pool); + if (bm->edata.totlayer) BLI_mempool_destroy(bm->edata.pool); + if (bm->ldata.totlayer) BLI_mempool_destroy(bm->ldata.pool); + if (bm->pdata.totlayer) BLI_mempool_destroy(bm->pdata.pool); + + /* free custom data */ + CustomData_free(&bm->vdata, 0); + CustomData_free(&bm->edata, 0); + CustomData_free(&bm->ldata, 0); + CustomData_free(&bm->pdata, 0); + + /* destroy element pools */ + BLI_mempool_destroy(bm->vpool); + BLI_mempool_destroy(bm->epool); + BLI_mempool_destroy(bm->lpool); + BLI_mempool_destroy(bm->fpool); + + /* destroy flag pool */ + BLI_mempool_destroy(bm->toolflagpool); + +#ifdef USE_BMESH_HOLES + BLI_mempool_destroy(bm->looplistpool); +#endif + + /* These tables aren't used yet, so it's not stricly necessary + * to 'end' them (with 'e' param) but if someone tries to start + * using them, having these in place will save a lot of pain */ + mesh_octree_table(NULL, NULL, NULL, 'e'); + mesh_mirrtopo_table(NULL, 'e'); + + BLI_freelistN(&bm->selected); + + BMO_error_clear(bm); +} + +void BM_mesh_clear(BMesh *bm) +{ + Object *ob = bm->ob; + + /* free old mesh */ + BM_mesh_data_free(bm); + memset(bm, 0, sizeof(BMesh)); + + /* re-initialize mesh */ + bm->ob = ob; + + /* allocate the memory pools for the mesh elements */ + bmesh_mempool_init(bm, bm_mesh_allocsize_default); + + bm->stackdepth = 1; + bm->totflags = 1; +} + +/* + * BMESH FREE MESH + * + * Frees a BMesh structure. + */ + +void BM_mesh_free(BMesh *bm) +{ + BM_mesh_data_free(bm); + MEM_freeN(bm); +} + +/* + * BMESH COMPUTE NORMALS + * + * Updates the normals of a mesh. + * Note that this can only be called + * + */ + +void BM_mesh_normals_update(BMesh *bm) +{ + BMVert *v; + BMFace *f; + BMLoop *l; + BMEdge *e; + BMIter verts; + BMIter faces; + BMIter loops; + BMIter edges; + unsigned int maxlength = 0; + int index; + float (*projectverts)[3]; + float (*edgevec)[3]; + + /* first, find out the largest face in mesh */ + BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) + continue; + + if (f->len > maxlength) maxlength = f->len; + } + + /* make sure we actually have something to do */ + if (maxlength < 3) return; + + /* allocate projectverts array */ + projectverts = MEM_callocN(sizeof(float) * maxlength * 3, "BM normal computation array"); + + /* calculate all face normals */ + BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) + continue; +#if 0 /* UNUSED */ + if (f->head.flag & BM_NONORMCALC) + continue; +#endif + + bmesh_update_face_normal(bm, f, f->no, projectverts); + } + + /* Zero out vertex normals */ + BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + continue; + + zero_v3(v->no); + } + + /* compute normalized direction vectors for each edge. directions will be + * used below for calculating the weights of the face normals on the vertex + * normals */ + index = 0; + edgevec = MEM_callocN(sizeof(float) * 3 * bm->totedge, "BM normal computation array"); + BM_ITER(e, &edges, bm, BM_EDGES_OF_MESH, NULL) { + BM_elem_index_set(e, index); /* set_inline */ + + if (e->l) { + sub_v3_v3v3(edgevec[index], e->v2->co, e->v1->co); + normalize_v3(edgevec[index]); + } + else { + /* the edge vector will not be needed when the edge has no radial */ + } + + index++; + } + bm->elem_index_dirty &= ~BM_EDGE; + + /* add weighted face normals to vertices */ + BM_ITER(f, &faces, bm, BM_FACES_OF_MESH, NULL) { + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) + continue; + + BM_ITER(l, &loops, bm, BM_LOOPS_OF_FACE, f) { + float *e1diff, *e2diff; + float dotprod; + float fac; + + /* calculate the dot product of the two edges that + * meet at the loop's vertex */ + e1diff = edgevec[BM_elem_index_get(l->prev->e)]; + e2diff = edgevec[BM_elem_index_get(l->e)]; + dotprod = dot_v3v3(e1diff, e2diff); + + /* edge vectors are calculated from e->v1 to e->v2, so + * adjust the dot product if one but not both loops + * actually runs from from e->v2 to e->v1 */ + if ((l->prev->e->v1 == l->prev->v) ^ (l->e->v1 == l->v)) { + dotprod = -dotprod; + } + + fac = saacos(-dotprod); + + /* accumulate weighted face normal into the vertex's normal */ + madd_v3_v3fl(l->v->no, f->no, fac); + } + } + + /* normalize the accumulated vertex normals */ + BM_ITER(v, &verts, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + continue; + + if (normalize_v3(v->no) == 0.0f) { + normalize_v3_v3(v->no, v->co); + } + } + + MEM_freeN(edgevec); + MEM_freeN(projectverts); +} + +/* + This function ensures correct normals for the mesh, but + sets the flag BM_ELEM_TAG in flipped faces, to allow restoration + of original normals. + + if undo is 0: calculate right normals + if undo is 1: restore original normals + */ +//keep in sycn with utils.c! +#define FACE_FLIP 8 +static void bmesh_rationalize_normals(BMesh *bm, int undo) +{ + BMOperator bmop; + BMFace *f; + BMIter iter; + + if (undo) { + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BM_elem_flag_test(f, BM_ELEM_TAG)) { + BM_face_normal_flip(bm, f); + } + BM_elem_flag_disable(f, BM_ELEM_TAG); + } + + return; + } + + BMO_op_initf(bm, &bmop, "righthandfaces faces=%af doflip=%d", FALSE); + + BMO_push(bm, &bmop); + bmesh_righthandfaces_exec(bm, &bmop); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, f, FACE_FLIP)) + BM_elem_flag_enable(f, BM_ELEM_TAG); + else BM_elem_flag_disable(f, BM_ELEM_TAG); + } + + BMO_pop(bm); + BMO_op_finish(bm, &bmop); +} + +static void bmesh_set_mdisps_space(BMesh *bm, int from, int to) +{ + /* switch multires data out of tangent space */ + if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + Object *ob = bm->ob; + BMEditMesh *em = BMEdit_Create(bm, FALSE); + DerivedMesh *dm = CDDM_from_BMEditMesh(em, NULL, TRUE, FALSE); + MDisps *mdisps; + BMFace *f; + BMIter iter; + // int i = 0; // UNUSED + + multires_set_space(dm, ob, from, to); + + mdisps = CustomData_get_layer(&dm->loopData, CD_MDISPS); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BMLoop *l; + BMIter liter; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + MDisps *lmd = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MDISPS); + + if (!lmd->disps) { + printf("%s: warning - 'lmd->disps' == NULL\n", __func__); + } + + if (lmd->disps && lmd->totdisp == mdisps->totdisp) { + memcpy(lmd->disps, mdisps->disps, sizeof(float) * 3 * lmd->totdisp); + } + else if (mdisps->disps) { + if (lmd->disps) + MEM_freeN(lmd->disps); + + lmd->disps = MEM_dupallocN(mdisps->disps); + lmd->totdisp = mdisps->totdisp; + } + + mdisps++; + // i += 1; + } + } + + dm->needsFree = 1; + dm->release(dm); + + /* setting this to NULL prevents BMEdit_Free from freeing it */ + em->bm = NULL; + BMEdit_Free(em); + MEM_freeN(em); + } +} + +/* + * BMESH BEGIN/END EDIT + * + * Functions for setting up a mesh for editing and cleaning up after + * the editing operations are done. These are called by the tools/operator + * API for each time a tool is executed. + */ +void bmesh_begin_edit(BMesh *bm, int flag) +{ + bm->opflag = flag; + + /* Most operators seem to be using BMO_OP_FLAG_UNTAN_MULTIRES to change the MDisps to + * absolute space during mesh edits. With this enabled, changes to the topology + * (loop cuts, edge subdivides, etc) are not reflected in the higher levels of + * the mesh at all, which doesn't seem right. Turning off completely for now, + * until this is shown to be better for certain types of mesh edits. */ +#if BMOP_UNTAN_MULTIRES_ENABLED + /* switch multires data out of tangent space */ + if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + bmesh_set_mdisps_space(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE); + + /* ensure correct normals, if possible */ + bmesh_rationalize_normals(bm, 0); + BM_mesh_normals_update(bm); + } + else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 0); + } +#else + if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 0); + } +#endif +} + +void bmesh_end_edit(BMesh *bm, int flag) +{ + /* BMO_OP_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_begin_edit. */ +#if BMOP_UNTAN_MULTIRES_ENABLED + /* switch multires data into tangent space */ + if ((flag & BMO_OP_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* set normals to their previous winding */ + bmesh_rationalize_normals(bm, 1); + bmesh_set_mdisps_space(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT); + } + else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 1); + } +#else + if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 1); + } +#endif + + bm->opflag = 0; + + /* compute normals, clear temp flags and flush selections */ + BM_mesh_normals_update(bm); + BM_mesh_select_mode_flush(bm); +} + +void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag) +{ + BMIter iter; + BMHeader *ele; + +#ifdef DEBUG + BM_ELEM_INDEX_VALIDATE(bm, "Should Never Fail!", __func__); +#endif + + if (hflag & BM_VERT) { + if (bm->elem_index_dirty & BM_VERT) { + int index = 0; + BM_ITER(ele, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + bm->elem_index_dirty &= ~BM_VERT; + BLI_assert(index == bm->totvert); + } + else { + // printf("%s: skipping vert index calc!\n", __func__); + } + } + + if (hflag & BM_EDGE) { + if (bm->elem_index_dirty & BM_EDGE) { + int index = 0; + BM_ITER(ele, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + bm->elem_index_dirty &= ~BM_EDGE; + BLI_assert(index == bm->totedge); + } + else { + // printf("%s: skipping edge index calc!\n", __func__); + } + } + + if (hflag & BM_FACE) { + if (bm->elem_index_dirty & BM_FACE) { + int index = 0; + BM_ITER(ele, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + bm->elem_index_dirty &= ~BM_FACE; + BLI_assert(index == bm->totface); + } + else { + // printf("%s: skipping face index calc!\n", __func__); + } + } +} + + +/* array checking/setting macros */ +/* currently vert/edge/loop/face index data is being abused, but we should + * eventually be able to rely on it being valid. To this end, there are macros + * that validate them (so blender doesnt crash), but also print errors so we can + * fix the offending parts of the code, this way after some months we can + * confine this code for debug mode. + * + * + */ + +void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func, + const char *msg_a, const char *msg_b) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + + const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE}; + const char *type_names[3] = {"vert", "edge", "face"}; + + BMIter iter; + BMHeader *ele; + int i; + int is_any_error = 0; + + for (i = 0; i < 3; i++) { + const int is_dirty = (flag_types[i] & bm->elem_index_dirty); + int index = 0; + int is_error = FALSE; + int err_val = 0; + int err_idx = 0; + + BM_ITER(ele, &iter, bm, iter_types[i], NULL) { + if (!is_dirty) { + if (BM_elem_index_get(ele) != index) { + err_val = BM_elem_index_get(ele); + err_idx = index; + is_error = TRUE; + } + } + + BM_elem_index_set(ele, index); /* set_ok */ + index++; + } + + if ((is_error == TRUE) && (is_dirty == FALSE)) { + is_any_error = TRUE; + fprintf(stderr, + "Invalid Index: at %s, %s, %s[%d] invalid index %d, '%s', '%s'\n", + location, func, type_names[i], err_idx, err_val, msg_a, msg_b); + } + else if ((is_error == FALSE) && (is_dirty == TRUE)) { + +#if 0 /* mostly annoying */ + + /* dirty may have been incorrectly set */ + fprintf(stderr, + "Invalid Dirty: at %s, %s (%s), dirty flag was set but all index values are correct, '%s', '%s'\n", + location, func, type_names[i], msg_a, msg_b); +#endif + } + } + +#if 0 /* mostly annoying, even in debug mode */ +#ifdef DEBUG + if (is_any_error == 0) { + fprintf(stderr, + "Valid Index Success: at %s, %s, '%s', '%s'\n", + location, func, msg_a, msg_b); + } +#endif +#endif + (void) is_any_error; /* shut up the compiler */ + +} + +BMVert *BM_vert_at_index(BMesh *bm, const int index) +{ + return BLI_mempool_findelem(bm->vpool, index); +} + +BMEdge *BM_edge_at_index(BMesh *bm, const int index) +{ + return BLI_mempool_findelem(bm->epool, index); +} + +BMFace *BM_face_at_index(BMesh *bm, const int index) +{ + return BLI_mempool_findelem(bm->fpool, index); +} diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c new file mode 100644 index 00000000000..246c8a4655b --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -0,0 +1,769 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_mods.c + * \ingroup bmesh + * + * This file contains functions for locally modifying + * the topology of existing mesh data. (split, join, flip etc). + */ + +#include "MEM_guardedalloc.h" + + +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_smallhash.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/** + * bmesh_dissolve_disk + * + * Turns the face region surrounding a manifold vertex into + * A single polygon. + * + * + * Example: + * + * |=========| |=========| + * | \ / | | | + * Before: | V | After: | | + * | / \ | | | + * |=========| |=========| + * + * + */ +#if 1 +int BM_vert_dissolve(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMEdge *e; + int len = 0; + + if (!v) { + return FALSE; + } + + e = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, v); + for ( ; e; e = BM_iter_step(&iter)) { + len++; + } + + if (len == 1) { + if (v->e) + BM_edge_kill(bm, v->e); + BM_vert_kill(bm, v); + return TRUE; + } + + if (!BM_vert_is_manifold(bm, v)) { + if (!v->e) BM_vert_kill(bm, v); + else if (!v->e->l) { + BM_edge_kill(bm, v->e); + BM_vert_kill(bm, v); + } + else { + return FALSE; + } + + return TRUE; + } + + return BM_disk_dissolve(bm, v); +} + +int BM_disk_dissolve(BMesh *bm, BMVert *v) +{ + BMFace *f, *f2; + BMEdge *e, *keepedge = NULL, *baseedge = NULL; + int len = 0; + + if (!BM_vert_is_manifold(bm, v)) { + return FALSE; + } + + if (v->e) { + /* v->e we keep, what else */ + e = v->e; + do { + e = bmesh_disk_nextedge(e, v); + if (!(BM_edge_share_faces(e, v->e))) { + keepedge = e; + baseedge = v->e; + break; + } + len++; + } while (e != v->e); + } + + /* this code for handling 2 and 3-valence verts + * may be totally bad */ + if (keepedge == NULL && len == 3) { + /* handle specific case for three-valence. solve it by + * increasing valence to four. this may be hackish. . */ + BMLoop *loop = e->l; + if (loop->v == v) loop = loop->next; + if (!BM_face_split(bm, loop->f, v, loop->v, NULL, NULL)) + return FALSE; + + if (!BM_disk_dissolve(bm, v)) { + return FALSE; + } + return TRUE; + } + else if (keepedge == NULL && len == 2) { + /* collapse the verte */ + e = BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE); + + if (!e) { + return FALSE; + } + + /* handle two-valenc */ + f = e->l->f; + f2 = e->l->radial_next->f; + + if (f != f2 && !BM_faces_join_pair(bm, f, f2, e)) { + return FALSE; + } + + return TRUE; + } + + if (keepedge) { + int done = 0; + + while (!done) { + done = 1; + e = v->e; + do { + f = NULL; + len = bmesh_radial_length(e->l); + if (len == 2 && (e != baseedge) && (e != keepedge)) { + f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e); + /* return if couldn't join faces in manifold + * conditions */ + //!disabled for testing why bad things happen + if (!f) { + return FALSE; + } + } + + if (f) { + done = 0; + break; + } + e = bmesh_disk_nextedge(e, v); + } while (e != v->e); + } + + /* collapse the verte */ + e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, TRUE); + + if (!e) { + return FALSE; + } + + /* get remaining two face */ + f = e->l->f; + f2 = e->l->radial_next->f; + + if (f != f2) { + /* join two remaining face */ + if (!BM_faces_join_pair(bm, f, f2, e)) { + return FALSE; + } + } + } + + return TRUE; +} +#else +void BM_disk_dissolve(BMesh *bm, BMVert *v) +{ + BMFace *f; + BMEdge *e; + BMIter iter; + int done, len; + + if (v->e) { + done = 0; + while (!done) { + done = 1; + + /* loop the edges looking for an edge to dissolv */ + for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, v); e; + e = BM_iter_step(&iter)) { + f = NULL; + len = bmesh_cycle_length(&(e->l->radial)); + if (len == 2) { + f = BM_faces_join_pair(bm, e->l->f, ((BMLoop *)(e->l->radial_next))->f, e); + } + if (f) { + done = 0; + break; + } + }; + } + BM_vert_collapse_faces(bm, v->e, v, 1.0, TRUE); + } +} +#endif + +/** + * BM_faces_join_pair + * + * Joins two adjacenct faces togather. + * + * Because this method calls to BM_faces_join to do its work, ff a pair + * of faces share multiple edges, the pair of faces will be joined at + * every edge (not just edge e). This part of the functionality might need + * to be reconsidered. + * + * If the windings do not match the winding of the new face will follow + * f1's winding (i.e. f2 will be reversed before the join). + * + * Returns: + * pointer to the combined face + */ + +BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + BMLoop *l1, *l2; + BMEdge *jed = NULL; + BMFace *faces[2] = {f1, f2}; + + jed = e; + if (!jed) { + BMLoop *l_first; + /* search for an edge that has both these faces in its radial cycl */ + l1 = l_first = BM_FACE_FIRST_LOOP(f1); + do { + if (l1->radial_next->f == f2) { + jed = l1->e; + break; + } + } while ((l1 = l1->next) != l_first); + } + + if (!jed) { + bmesh_error(); + return NULL; + } + + l1 = jed->l; + + if (!l1) { + bmesh_error(); + return NULL; + } + + l2 = l1->radial_next; + if (l1->v == l2->v) { + bmesh_loop_reverse(bm, f2); + } + + f1 = BM_faces_join(bm, faces, 2); + + return f1; +} + +/* connects two verts together, automatically (if very naively) finding the + * face they both share (if there is one) and splittling it. use this at your + * own risk, as it doesn't handle the many complex cases it should (like zero-area faces, + * multiple faces, etc). + * + * this is really only meant for cases where you don't know before hand the face + * the two verts belong to for splitting (e.g. the subdivision operator). + */ + +BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf) +{ + BMIter iter, iter2; + BMVert *v; + BMLoop *nl; + BMFace *face; + + /* be warned: this can do weird things in some ngon situation, see BM_LegalSplit */ + for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) { + for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) { + if (v == v2) { + face = BM_face_split(bm, face, v1, v2, &nl, NULL); + + if (nf) *nf = face; + return nl->e; + } + } + } + + return NULL; +} + +/** + * BM_face_split + * + * Splits a single face into two. + * + * f - the original face + * v1 & v2 - vertices which define the split edge, must be different + * nl - pointer which will receive the BMLoop for the split edge in the new face + * + * Notes: the + + * Returns - + * Pointer to the newly created face representing one side of the split + * if the split is successful (and the original original face will be the + * other side). NULL if the split fails. + * + */ + +BMFace *BM_face_split(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, BMLoop **nl, BMEdge *UNUSED(example)) +{ + const int has_mdisp = CustomData_has_layer(&bm->ldata, CD_MDISPS); + BMFace *nf, *of; + + /* do we have a multires layer */ + if (has_mdisp) { + of = BM_face_copy(bm, f, 0, 0); + } + +#ifdef USE_BMESH_HOLES + nf = bmesh_sfme(bm, f, v1, v2, nl, NULL); +#else + nf = bmesh_sfme(bm, f, v1, v2, nl); +#endif + + if (nf) { + BM_elem_attrs_copy(bm, bm, f, nf); + copy_v3_v3(nf->no, f->no); + + /* handle multires update */ + if (has_mdisp && (nf != f)) { + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE); + } while ((l_iter = l_iter->next) != l_first); + + l_iter = l_first = BM_FACE_FIRST_LOOP(nf); + do { + BM_loop_interp_from_face(bm, l_iter, of, FALSE, TRUE); + } while ((l_iter = l_iter->next) != l_first); + + BM_face_kill(bm, of); + + BM_face_multires_bounds_smooth(bm, f); + BM_face_multires_bounds_smooth(bm, nf); + } + } + + return nf; +} + +/** + * BM_vert_collapse_faces + * + * Collapses a vertex that has only two manifold edges + * onto a vertex it shares an edge with. Fac defines + * the amount of interpolation for Custom Data. + * + * Note that this is not a general edge collapse function. + * + * Note this function is very close to 'BM_vert_collapse_edges', both collapse + * a vertex and return a new edge. Except this takes a factor and merges + * custom data. + * + * BMESH_TODO: + * Insert error checking for KV valance. + * + * @param fac The factor along the edge + * @param join_faces When true the faces around the vertex will be joined + * otherwise collapse the vertex by merging the 2 edges this vert touches into one. + * @returns The New Edge + */ + +BMEdge *BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces) +{ + BMEdge *ne = NULL; + BMVert *tv = bmesh_edge_getothervert(ke, kv); + + BMEdge *e2; + BMVert *tv2; + + BMIter iter; + BMLoop *l_iter = NULL, *kvloop = NULL, *tvloop = NULL; + + void *src[2]; + float w[2]; + + /* Only intended to be called for 2-valence vertices */ + BLI_assert(bmesh_disk_count(kv) <= 2); + + + /* first modify the face loop data */ + w[0] = 1.0f - fac; + w[1] = fac; + + if (ke->l) { + l_iter = ke->l; + do { + if (l_iter->v == tv && l_iter->next->v == kv) { + tvloop = l_iter; + kvloop = l_iter->next; + + src[0] = kvloop->head.data; + src[1] = tvloop->head.data; + CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, kvloop->head.data); + } + } while ((l_iter = l_iter->radial_next) != ke->l); + } + + /* now interpolate the vertex data */ + BM_data_interp_from_verts(bm, kv, tv, kv, fac); + + e2 = bmesh_disk_nextedge(ke, kv); + tv2 = BM_edge_other_vert(e2, kv); + + if (join_faces) { + BMFace **faces = NULL, *f; + BLI_array_staticdeclare(faces, 8); + + BM_ITER(f, &iter, bm, BM_FACES_OF_VERT, kv) { + BLI_array_append(faces, f); + } + + if (BLI_array_count(faces) >= 2) { + BMFace *f2 = BM_faces_join(bm, faces, BLI_array_count(faces)); + if (f2) { + BMLoop *nl = NULL; + if (BM_face_split(bm, f2, tv, tv2, &nl, NULL)) { + ne = nl->e; + } + } + } + + BLI_array_free(faces); + + return ne; + } + + /* single face or no faces */ + /* same as BM_vert_collapse_edges() however we already + * have vars to perform this operation so dont call. */ + bmesh_jekv(bm, ke, kv); + ne = BM_edge_exists(tv, tv2); + + return ne; +} + + +/** + * BM_vert_collapse_edges + * + * Collapses a vertex onto another vertex it shares an edge with. + * + * Returns - + * The New Edge + */ + +BMEdge *BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + /* nice example implimentation but we want loops to have their customdata + * accounted for */ +#if 0 + BMEdge *ne = NULL; + + /* Collapse between 2 edges */ + + /* in this case we want to keep all faces and not join them, + * rather just get rid of the veretex - see bug [#28645] */ + BMVert *tv = bmesh_edge_getothervert(ke, kv); + if (tv) { + BMEdge *e2 = bmesh_disk_nextedge(ke, kv); + if (e2) { + BMVert *tv2 = BM_edge_other_vert(e2, kv); + if (tv2) { + /* only action, other calls here only get the edge to return */ + bmesh_jekv(bm, ke, kv); + + ne = BM_edge_exists(tv, tv2); + } + } + } + + return ne; +#else + /* with these args faces are never joined, same as above + * but account for loop customdata */ + return BM_vert_collapse_faces(bm, ke, kv, 1.0f, FALSE); +#endif +} + +#undef DO_V_INTERP + +/** + * BM_split_edge + * + * Splits an edge. v should be one of the vertices in e and + * defines the direction of the splitting operation for interpolation + * purposes. + * + * Returns - + * the new vert + */ + +BMVert *BM_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent) +{ + BMVert *nv, *v2; + BMFace **oldfaces = NULL; + BMEdge *dummy; + BLI_array_staticdeclare(oldfaces, 32); + SmallHash hash; + + /* we need this for handling multire */ + if (!ne) + ne = &dummy; + + /* do we have a multires layer */ + if (CustomData_has_layer(&bm->ldata, CD_MDISPS) && e->l) { + BMLoop *l; + int i; + + l = e->l; + do { + BLI_array_append(oldfaces, l->f); + l = l->radial_next; + } while (l != e->l); + + /* create a hash so we can differentiate oldfaces from new face */ + BLI_smallhash_init(&hash); + + for (i = 0; i < BLI_array_count(oldfaces); i++) { + oldfaces[i] = BM_face_copy(bm, oldfaces[i], 1, 1); + BLI_smallhash_insert(&hash, (intptr_t)oldfaces[i], NULL); + } + } + + v2 = bmesh_edge_getothervert(e, v); + nv = bmesh_semv(bm, v, e, ne); + if (nv == NULL) { + return NULL; + } + + sub_v3_v3v3(nv->co, v2->co, v->co); + madd_v3_v3v3fl(nv->co, v->co, nv->co, percent); + + if (ne) { + (*ne)->head.hflag = e->head.hflag; + BM_elem_attrs_copy(bm, bm, e, *ne); + } + + /* v->nv->v2 */ + BM_data_interp_face_vert_edge(bm, v2, v, nv, e, percent); + BM_data_interp_from_verts(bm, v, v2, nv, percent); + + if (CustomData_has_layer(&bm->ldata, CD_MDISPS) && e->l && nv) { + int i, j; + + /* interpolate new/changed loop data from copied old face */ + for (j = 0; j < 2; j++) { + for (i = 0; i < BLI_array_count(oldfaces); i++) { + BMEdge *e1 = j ? *ne : e; + BMLoop *l, *l2; + + l = e1->l; + if (!l) { + bmesh_error(); + break; + } + + do { + if (!BLI_smallhash_haskey(&hash, (intptr_t)l->f)) { + BMLoop *l2_first; + + l2 = l2_first = BM_FACE_FIRST_LOOP(l->f); + do { + BM_loop_interp_multires(bm, l2, oldfaces[i]); + } while ((l2 = l2->next) != l2_first); + } + l = l->radial_next; + } while (l != e1->l); + } + } + + /* destroy the old face */ + for (i = 0; i < BLI_array_count(oldfaces); i++) { + BM_face_verts_kill(bm, oldfaces[i]); + } + + /* fix boundaries a bit, doesn't work too well quite ye */ +#if 0 + for (j = 0; j < 2; j++) { + BMEdge *e1 = j ? *ne : e; + BMLoop *l, *l2; + + l = e1->l; + if (!l) { + bmesh_error(); + break; + } + + do { + BM_face_multires_bounds_smooth(bm, l->f); + l = l->radial_next; + } while (l != e1->l); + } +#endif + + BLI_array_free(oldfaces); + BLI_smallhash_release(&hash); + } + + return nv; +} + +BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts) +{ + int i; + float percent; + BMVert *nv = NULL; + + for (i = 0; i < numcuts; i++) { + percent = 1.0f / (float)(numcuts + 1 - i); + nv = BM_edge_split(bm, e->v2, e, NULL, percent); + } + return nv; +} + +int BM_face_validate(BMesh *bm, BMFace *face, FILE *err) +{ + BMIter iter; + BLI_array_declare(verts); + BMVert **verts = NULL; + BMLoop *l; + int ret = 1, i, j; + + if (face->len == 2) { + fprintf(err, "warning: found two-edged face. face ptr: %p\n", face); + fflush(err); + } + + for (l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, face); l; l = BM_iter_step(&iter)) { + BLI_array_growone(verts); + verts[BLI_array_count(verts) - 1] = l->v; + + if (l->e->v1 == l->e->v2) { + fprintf(err, "Found bmesh edge with identical verts!\n"); + fprintf(err, " edge ptr: %p, vert: %p\n", l->e, l->e->v1); + fflush(err); + ret = 0; + } + } + + for (i = 0; i < BLI_array_count(verts); i++) { + for (j = 0; j < BLI_array_count(verts); j++) { + if (j == i) { + continue; + } + + if (verts[i] == verts[j]) { + fprintf(err, "Found duplicate verts in bmesh face!\n"); + fprintf(err, " face ptr: %p, vert: %p\n", face, verts[i]); + fflush(err); + ret = 0; + } + } + } + + BLI_array_free(verts); + return ret; +} + +/* + * BM Rotate Edge + * + * Spins an edge topologically, either counter-clockwise or clockwise. + * If ccw is true, the edge is spun counter-clockwise, otherwise it is + * spun clockwise. + * + * Returns the spun edge. Note that this works by dissolving the edge + * then re-creating it, so the returned edge won't have the same pointer + * address as the original one. + * + * Returns NULL on error (e.g., if the edge isn't surrounded by exactly + * two faces). + */ +BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw) +{ + BMVert *v1, *v2; + BMLoop *l, *l1, *l2, *nl; + BMFace *f; + BMIter liter; + + v1 = e->v1; + v2 = e->v2; + + if (BM_edge_face_count(e) != 2) + return NULL; + + /* If either of e's vertices has valence 2, then + * dissolving the edge would leave a spur, so not allowed */ + if (BM_vert_edge_count(e->v1) == 2 || BM_vert_edge_count(e->v2) == 2) + return NULL; + + f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e); + + if (f == NULL) + return NULL; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (l->v == v1) + l1 = l; + else if (l->v == v2) + l2 = l; + } + + if (ccw) { + l1 = l1->prev; + l2 = l2->prev; + } + else { + l1 = l1->next; + l2 = l2->next; + } + + if (!BM_face_split(bm, f, l1->v, l2->v, &nl, NULL)) + return NULL; + + return nl->e; +} + +BMVert *BM_vert_rip ( BMesh *bm, BMFace *sf, BMVert *sv) +{ + return bmesh_urmv(bm, sf, sv); +} diff --git a/source/blender/bmesh/intern/bmesh_newcore.c b/source/blender/bmesh/intern/bmesh_newcore.c new file mode 100644 index 00000000000..47b8536b3df --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_newcore.c @@ -0,0 +1,2024 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_newcore.c + * \ingroup bmesh + * + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math_vector.h" + +#include "BKE_DerivedMesh.h" + +#include "BLI_listbase.h" +#include "BLI_array.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* use so valgrinds memcheck alerts us when undefined index is used. + * TESTING ONLY! */ +// #define USE_DEBUG_INDEX_MEMCHECK + +#ifdef USE_DEBUG_INDEX_MEMCHECK +#define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \ + { \ + int undef_idx; \ + BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \ + } \ + +#endif + +BMVert *BM_vert_create(BMesh *bm, const float co[3], const struct BMVert *example) +{ + BMVert *v = BLI_mempool_calloc(bm->vpool); + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(v) +#else + BM_elem_index_set(v, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_VERT; /* may add to middle of the pool */ + + bm->totvert++; + + v->head.htype = BM_VERT; + + /* 'v->no' is handled by BM_elem_attrs_copy */ + if (co) copy_v3_v3(v->co, co); + + /* allocate flag */ + v->oflags = BLI_mempool_calloc(bm->toolflagpool); + + CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + + if (example) { + BM_elem_attrs_copy(bm, bm, (BMVert *)example, (BMVert *)v); + } + + BM_CHECK_ELEMENT(bm, v); + + return (BMVert *) v; +} + +/** + * BMESH EDGE EXIST + * + * Finds out if two vertices already have an edge + * connecting them. Note that multiple edges may + * exist between any two vertices, and therefore + * This function only returns the first one found. + * + * Returns - + * BMEdge pointer + */ +BMEdge *BM_edge_exists(BMVert *v1, BMVert *v2) +{ + BMIter iter; + BMEdge *e; + + BM_ITER(e, &iter, NULL, BM_EDGES_OF_VERT, v1) { + if (e->v1 == v2 || e->v2 == v2) + return e; + } + + return NULL; +} + +BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble) +{ + BMEdge *e; + + if (nodouble && (e = BM_edge_exists(v1, v2))) + return (BMEdge *)e; + + e = BLI_mempool_calloc(bm->epool); + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(e) +#else + BM_elem_index_set(e, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_EDGE; /* may add to middle of the pool */ + + bm->totedge++; + + e->head.htype = BM_EDGE; + + /* allocate flag */ + e->oflags = BLI_mempool_calloc(bm->toolflagpool); + + e->v1 = (BMVert *) v1; + e->v2 = (BMVert *) v2; + + + CustomData_bmesh_set_default(&bm->edata, &e->head.data); + + bmesh_disk_append_edge(e, e->v1); + bmesh_disk_append_edge(e, e->v2); + + if (example) + BM_elem_attrs_copy(bm, bm, (BMEdge *)example, (BMEdge *)e); + + BM_CHECK_ELEMENT(bm, e); + + return (BMEdge *) e; +} + +static BMLoop *bmesh_create_loop(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, const BMLoop *example) +{ + BMLoop *l = NULL; + + l = BLI_mempool_calloc(bm->lpool); + l->next = l->prev = NULL; + l->v = v; + l->e = e; + l->f = f; + l->radial_next = l->radial_prev = NULL; + l->head.data = NULL; + l->head.htype = BM_LOOP; + + bm->totloop++; + + if (example) + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, example->head.data, &l->head.data); + else + CustomData_bmesh_set_default(&bm->ldata, &l->head.data); + + return l; +} + +static BMLoop *bm_face_boundry_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool); +#endif + BMLoop *l = bmesh_create_loop(bm, startv, starte, f, NULL); + + 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; +} + +BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts) +{ + BMEdge **edges = NULL; + BMVert **verts = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + BMLoop *l_iter; + BMLoop *l_first; + BMLoop *l2; + BMFace *f2; + int i; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (copyverts) { + BMVert *v = BM_vert_create(bm, l_iter->v->co, l_iter->v); + BLI_array_append(verts, v); + } + else { + BLI_array_append(verts, l_iter->v); + } + } while ((l_iter = l_iter->next) != l_first); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + i = 0; + do { + if (copyedges) { + BMEdge *e; + 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]; + } + + e = BM_edge_create(bm, v1, v2, l_iter->e, FALSE); + BLI_array_append(edges, e); + } + else { + BLI_array_append(edges, l_iter->e); + } + + i++; + } while ((l_iter = l_iter->next) != l_first); + + f2 = BM_face_create(bm, verts, edges, f->len, FALSE); + + BM_elem_attrs_copy(bm, bm, f, f2); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + l2 = BM_FACE_FIRST_LOOP(f2); + do { + BM_elem_attrs_copy(bm, bm, l_iter, l2); + l2 = l2->next; + } while ((l_iter = l_iter->next) != l_first); + + return f2; +} + +BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble) +{ + BMFace *f = NULL; + BMLoop *l, *startl, *lastl; + int i, overlap; + + if (len == 0) { + /* just return NULL for no */ + return NULL; + } + + if (nodouble) { + /* Check if face already exists */ + overlap = BM_face_exists(bm, verts, len, &f); + if (overlap) { + return f; + } + else { + BLI_assert(f == NULL); + } + } + + f = BLI_mempool_calloc(bm->fpool); + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(f) +#else + BM_elem_index_set(f, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */ + + bm->totface++; + + f->head.htype = BM_FACE; + + startl = lastl = bm_face_boundry_add(bm, (BMFace *)f, verts[0], edges[0]); + + startl->v = (BMVert *)verts[0]; + startl->e = (BMEdge *)edges[0]; + for (i = 1; i < len; i++) { + l = bmesh_create_loop(bm, verts[i], edges[i], (BMFace *)f, edges[i]->l); + + l->f = (BMFace *) f; + bmesh_radial_append(edges[i], l); + + l->prev = lastl; + lastl->next = l; + lastl = l; + } + + /* allocate flag */ + f->oflags = BLI_mempool_calloc(bm->toolflagpool); + + CustomData_bmesh_set_default(&bm->pdata, &f->head.data); + + startl->prev = lastl; + lastl->next = startl; + + f->len = len; + +#ifdef USE_BMESH_HOLES + f->totbounds = 0; +#endif + + BM_CHECK_ELEMENT(bm, f); + + return (BMFace *) f; +} + +int bmesh_check_element(BMesh *UNUSED(bm), void *element, const char htype) +{ + BMHeader *head = element; + int err = 0; + + if (!element) + return 1; + + if (head->htype != htype) + return 2; + + switch (htype) { + case BM_VERT: { + BMVert *v = element; + if (v->e && v->e->head.htype != BM_EDGE) { + err |= 4; + } + break; + } + case BM_EDGE: { + BMEdge *e = element; + if (e->l && e->l->head.htype != BM_LOOP) + err |= 8; + if (e->l && e->l->f->head.htype != BM_FACE) + err |= 16; + 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) + { + err |= 32; + } + if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL)) + err |= 64; + if (e->l && e->l->f->len <= 0) + err |= 128; + break; + } + case BM_LOOP: { + BMLoop *l = element, *l2; + int i; + + if (l->f->head.htype != BM_FACE) + err |= 256; + if (l->e->head.htype != BM_EDGE) + err |= 512; + if (l->v->head.htype != BM_VERT) + err |= 1024; + if (!BM_vert_in_edge(l->e, l->v)) { + fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__); + err |= 2048; + } + + if (l->radial_next == NULL || l->radial_prev == NULL) + err |= (1 << 12); + if (l->f->len <= 0) + err |= (1 << 13); + + /* validate boundary loop--invalid for hole loops, of course, + * but we won't be allowing those for a while ye */ + 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; + } + case BM_FACE: { + 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); + } + } + + if (err) { + bmesh_error(); + } + + return err; +} + +/* low level function, only free's, + * does not change adjust surrounding geometry */ +static void bmesh_kill_only_vert(BMesh *bm, BMVert *v) +{ + bm->totvert--; + bm->elem_index_dirty |= BM_VERT; + + BM_select_history_remove(bm, v); + if (v->head.data) + CustomData_bmesh_free_block(&bm->vdata, &v->head.data); + + BLI_mempool_free(bm->toolflagpool, v->oflags); + BLI_mempool_free(bm->vpool, v); +} + +static void bmesh_kill_only_edge(BMesh *bm, BMEdge *e) +{ + bm->totedge--; + bm->elem_index_dirty |= BM_EDGE; + + BM_select_history_remove(bm, e); + + if (e->head.data) + CustomData_bmesh_free_block(&bm->edata, &e->head.data); + + BLI_mempool_free(bm->toolflagpool, e->oflags); + BLI_mempool_free(bm->epool, e); +} + +static void bmesh_kill_only_face(BMesh *bm, BMFace *f) +{ + if (bm->act_face == f) + bm->act_face = NULL; + + bm->totface--; + bm->elem_index_dirty |= BM_FACE; + + BM_select_history_remove(bm, f); + + if (f->head.data) + CustomData_bmesh_free_block(&bm->pdata, &f->head.data); + + BLI_mempool_free(bm->toolflagpool, f->oflags); + BLI_mempool_free(bm->fpool, f); +} + +static void bmesh_kill_only_loop(BMesh *bm, BMLoop *l) +{ + bm->totloop--; + if (l->head.data) + CustomData_bmesh_free_block(&bm->ldata, &l->head.data); + + BLI_mempool_free(bm->lpool, l); +} + +void BM_face_edges_kill(BMesh *bm, BMFace *f) +{ + BMEdge **edges = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BMLoop *l_iter; + BMLoop *l_first; + int i; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BLI_array_append(edges, l_iter->e); + } while ((l_iter = l_iter->next) != l_first); + + for (i = 0; i < BLI_array_count(edges); i++) { + BM_edge_kill(bm, edges[i]); + } + + BLI_array_free(edges); +} + +void BM_face_verts_kill(BMesh *bm, BMFace *f) +{ + BMVert **verts = NULL; + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + BMLoop *l_iter; + BMLoop *l_first; + int i; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BLI_array_append(verts, l_iter->v); + } while ((l_iter = l_iter->next) != l_first); + + for (i = 0; i < BLI_array_count(verts); i++) { + BM_vert_kill(bm, verts[i]); + } + + BLI_array_free(verts); +} + +void BM_face_kill(BMesh *bm, BMFace *f) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *ls, *ls_next; +#endif + + BM_CHECK_ELEMENT(bm, f); + +#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; + + bmesh_radial_remove_loop(l_iter, l_iter->e); + bmesh_kill_only_loop(bm, l_iter); + + } while ((l_iter = l_next) != l_first); + +#ifdef USE_BMESH_HOLES + BLI_mempool_free(bm->looplistpool, ls); +#endif + } + + bmesh_kill_only_face(bm, f); +} + +void BM_edge_kill(BMesh *bm, BMEdge *e) +{ + + bmesh_disk_remove_edge(e, e->v1); + bmesh_disk_remove_edge(e, e->v2); + + 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); + } + + bmesh_kill_only_edge(bm, e); +} + +void BM_vert_kill(BMesh *bm, BMVert *v) +{ + if (v->e) { + BMEdge *e, *nexte; + + e = v->e; + while (v->e) { + nexte = bmesh_disk_nextedge(e, v); + BM_edge_kill(bm, e); + e = nexte; + } + } + + bmesh_kill_only_vert(bm, v); +} + +/********** private disk and radial cycle functions ********** */ + +/** + * bmesh_loop_reverse + * + * FLIP FACE EULER + * + * Changes the winding order of a face from CW to CCW or vice versa. + * This euler is a bit peculiar in compairson to others as it is its + * own inverse. + * + * BMESH_TODO: reinsert validation code. + * + * Returns - + * 1 for success, 0 for failure. + */ + +static int bmesh_loop_length(BMLoop *l) +{ + BMLoop *l_first = l; + int i = 0; + + do { + i++; + } while ((l = l->next) != l_first); + + return i; +} + +static int bmesh_loop_reverse_loop(BMesh *bm, BMFace *f +#ifdef USE_BMESH_HOLES + , BMLoopList *lst +#endif + ) +{ + +#ifdef USE_BMESH_HOLES + BMLoop *l_first = lst->first; +#else + BMLoop *l_first = f->l_first; +#endif + + BMLoop *l_iter, *oldprev, *oldnext; + BMEdge **edar = NULL; + MDisps *md; + BLI_array_staticdeclare(edar, BM_NGON_STACK_SIZE); + int i, j, edok, len = 0, do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS); + + len = bmesh_loop_length(l_first); + + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { + BMEdge *curedge = l_iter->e; + bmesh_radial_remove_loop(l_iter, curedge); + BLI_array_append(edar, curedge); + } + + /* 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; + + 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]); + } + } + } + } + + 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++) { + edok = bmesh_verts_in_edge(l_iter->v, l_iter->next->v, edar[j]); + if (edok) { + l_iter->e = edar[j]; + break; + } + } + } + } + /* rebuild radia */ + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) + bmesh_radial_append(l_iter->e, l_iter); + + /* validate radia */ + for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { + BM_CHECK_ELEMENT(bm, l_iter); + BM_CHECK_ELEMENT(bm, l_iter->e); + BM_CHECK_ELEMENT(bm, l_iter->v); + BM_CHECK_ELEMENT(bm, l_iter->f); + } + + BLI_array_free(edar); + + BM_CHECK_ELEMENT(bm, f); + + return 1; +} + +int bmesh_loop_reverse(BMesh *bm, BMFace *f) +{ +#ifdef USE_BMESH_HOLES + return bmesh_loop_reverse_loop(bm, f, f->loops.first); +#else + return bmesh_loop_reverse_loop(bm, f); +#endif +} + +static void bmesh_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag) +{ + BMHeader **eles = veles; + int i; + + for (i = 0; i < tot; i++) { + BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], flag); + } +} + +static void bmesh_clear_systag_elements(BMesh *UNUSED(bm), void *veles, int tot, int flag) +{ + BMHeader **eles = veles; + int i; + + for (i = 0; i < tot; i++) { + BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], flag); + } +} + +#define FACE_MARK (1 << 10) + +static int count_flagged_radial(BMesh *bm, BMLoop *l, int flag) +{ + BMLoop *l2 = l; + int i = 0, c = 0; + + do { + if (!l2) { + bmesh_error(); + goto error; + } + + i += BM_ELEM_API_FLAG_TEST(l2->f, flag) ? 1 : 0; + l2 = bmesh_radial_nextloop(l2); + if (c >= BM_LOOP_RADIAL_MAX) { + bmesh_error(); + goto error; + } + c++; + } while (l2 != l); + + return i; + +error: + BMO_error_raise(bm, bm->currentop, BMERR_MESH_ERROR, NULL); + return 0; +} + +static int UNUSED_FUNCTION(count_flagged_disk)(BMVert *v, int flag) +{ + BMEdge *e = v->e; + int i = 0; + + if (!e) + return 0; + + do { + i += BM_ELEM_API_FLAG_TEST(e, flag) ? 1 : 0; + e = bmesh_disk_nextedge(e, v); + } while (e != v->e); + + return i; +} + +static int disk_is_flagged(BMVert *v, int flag) +{ + BMEdge *e = v->e; + + if (!e) + return FALSE; + + do { + BMLoop *l = e->l; + + if (!l) { + return FALSE; + } + + if (bmesh_radial_length(l) == 1) + return FALSE; + + do { + if (!BM_ELEM_API_FLAG_TEST(l->f, flag)) + return FALSE; + + l = l->radial_next; + } while (l != e->l); + + e = bmesh_disk_nextedge(e, v); + } while (e != v->e); + + return TRUE; +} + +/* Midlevel Topology Manipulation Functions */ + +/* + * BM_faces_join + * + * Joins a collected group of faces into one. Only restriction on + * the input data is that the faces must be connected to each other. + * + * If a pair of faces share multiple edges, the pair of + * faces will be joined at every edge. + * + * Returns a pointer to the combined face. + */ +BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface) +{ + BMFace *f, *newf; +#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; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BLI_array_staticdeclare(deledges, BM_NGON_STACK_SIZE); + BLI_array_staticdeclare(delverts, BM_NGON_STACK_SIZE); + BMVert *v1 = NULL, *v2 = NULL; + const char *err = NULL; + int i, tote = 0; + + if (!totface) { + bmesh_error(); + return NULL; + } + + if (totface == 1) + return faces[0]; + + bmesh_systag_elements(bm, faces, totface, _FLAG_JF); + + for (i = 0; i < totface; i++) { + f = faces[i]; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + int rlen = count_flagged_radial(bm, l_iter, _FLAG_JF); + + if (rlen > 2) { + err = "Input faces do not form a contiguous manifold region"; + 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 */ + if (BM_edge_face_count(l_iter->e) <= 2) { + BLI_array_append(deledges, l_iter->e); + BM_ELEM_API_FLAG_ENABLE(l_iter->e, _FLAG_JF); + } + } + else { + if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) { + BLI_array_append(delverts, l_iter->e->v1); + BM_ELEM_API_FLAG_ENABLE(l_iter->e->v1, _FLAG_JF); + } + + if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) { + BLI_array_append(delverts, l_iter->e->v2); + 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) { + if (lst == f->loops.first) continue; + + BLI_remlink(&f->loops, lst); + BLI_addtail(&holes, lst); + } +#endif + + } + + /* create region fac */ + newf = BM_face_create_ngon(bm, v1, v2, edges, tote, FALSE); + if (!newf || BMO_error_occurred(bm)) { + if (!BMO_error_occurred(bm)) + err = "Invalid boundary region to join faces"; + goto error; + } + + /* copy over loop data */ + l_iter = l_first = BM_FACE_FIRST_LOOP(newf); + 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) { + /* I think this is correct */ + 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); + + BM_elem_attrs_copy(bm, bm, faces[0], newf); + +#ifdef USE_BMESH_HOLES + /* add hole */ + BLI_movelisttolist(&newf->loops, &holes); +#endif + + /* update loop face pointer */ +#ifdef USE_BMESH_HOLES + for (lst = newf->loops.first; lst; lst = lst->next) +#endif + { +#ifdef USE_BMESH_HOLES + l_iter = l_first = lst->first; +#else + l_iter = l_first = BM_FACE_FIRST_LOOP(newf); +#endif + do { + l_iter->f = newf; + } while ((l_iter = l_iter->next) != l_first); + } + + bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF); + BM_ELEM_API_FLAG_DISABLE(newf, _FLAG_JF); + + /* handle multires data */ + if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(newf); + do { + for (i = 0; i < totface; i++) { + BM_loop_interp_multires(bm, l_iter, faces[i]); + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* delete old geometr */ + for (i = 0; i < BLI_array_count(deledges); i++) { + BM_edge_kill(bm, deledges[i]); + } + + for (i = 0; i < BLI_array_count(delverts); i++) { + BM_vert_kill(bm, delverts[i]); + } + + BLI_array_free(edges); + BLI_array_free(deledges); + BLI_array_free(delverts); + + BM_CHECK_ELEMENT(bm, newf); + return newf; +error: + bmesh_clear_systag_elements(bm, faces, totface, _FLAG_JF); + 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; +} + +static BMFace *bmesh_addpolylist(BMesh *bm, BMFace *UNUSED(example)) +{ + BMFace *f; +#ifdef USE_BMESH_HOLES + BMLoopList *lst; +#endif + + f = BLI_mempool_calloc(bm->fpool); +#ifdef USE_BMESH_HOLES + lst = BLI_mempool_calloc(bm->looplistpool); +#endif + + f->head.htype = BM_FACE; +#ifdef USE_BMESH_HOLES + BLI_addtail(&f->loops, lst); +#endif + +#ifdef USE_DEBUG_INDEX_MEMCHECK + DEBUG_MEMCHECK_INDEX_INVALIDATE(f) +#else + BM_elem_index_set(f, -1); /* set_ok_invalid */ +#endif + + bm->elem_index_dirty |= BM_FACE; /* may add to middle of the pool */ + + bm->totface++; + + /* allocate flag */ + f->oflags = BLI_mempool_calloc(bm->toolflagpool); + + CustomData_bmesh_set_default(&bm->pdata, &f->head.data); + + f->len = 0; + +#ifdef USE_BMESH_HOLES + f->totbounds = 1; +#endif + + return (BMFace *) f; +} + +/** + * bmesh_SFME + * + * SPLIT FACE MAKE EDGE: + * + * 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. + * + * Examples: + * + * Before: After: + * ---------- ---------- + * | | | | + * | | | f1 | + * v1 f1 v2 v1======v2 + * | | | f2 | + * | | | | + * ---------- ---------- + * + * Note that 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. + * + * If 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). + * + * Note that holes represents which holes goes to the new face, and of + * course this requires removing them from the exitsing face first, since + * you cannot have linked list links inside multiple lists. + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMVert *v1, BMVert *v2, + BMLoop **rl +#ifdef USE_BMESH_HOLES + , ListBase *holes +#endif + ) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *lst, *lst2; +#endif + + BMFace *f2; + BMLoop *l_iter, *l_first; + BMLoop *v1loop = NULL, *v2loop = NULL, *f1loop = NULL, *f2loop = NULL; + BMEdge *e; + int i, len, f1len, f2len; + + /* verify that v1 and v2 are in face */ + len = f->len; + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < len; i++, l_iter = l_iter->next) { + if (l_iter->v == v1) v1loop = l_iter; + else if (l_iter->v == v2) v2loop = l_iter; + } + + if (!v1loop || !v2loop) { + return NULL; + } + + /* allocate new edge between v1 and v2 */ + e = BM_edge_create(bm, v1, v2, NULL, FALSE); + + f2 = bmesh_addpolylist(bm, f); + f1loop = bmesh_create_loop(bm, v2, e, f, v2loop); + f2loop = bmesh_create_loop(bm, v1, e, f2, v1loop); + + f1loop->prev = v2loop->prev; + f2loop->prev = v1loop->prev; + v2loop->prev->next = f1loop; + v1loop->prev->next = f2loop; + + f1loop->next = v1loop; + f2loop->next = v2loop; + v1loop->prev = f1loop; + v2loop->prev = f2loop; + +#ifdef USE_BMESH_HOLES + lst = f->loops.first; + lst2 = f2->loops.first; + + lst2->first = lst2->last = f2loop; + lst->first = lst->last = f1loop; +#else + f2->l_first = f2loop; + f->l_first = f1loop; +#endif + + /* validate both loop */ + /* I dont know how many loops are supposed to be in each face at this point! FIXME */ + + /* 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); + + /* link up the new loops into the new edges radia */ + bmesh_radial_append(e, f1loop); + bmesh_radial_append(e, f2loop); + + 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; + + if (rl) *rl = f2loop; + +#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 + + BM_CHECK_ELEMENT(bm, e); + BM_CHECK_ELEMENT(bm, f); + BM_CHECK_ELEMENT(bm, f2); + + return f2; +} + +/** + * bmesh_SEMV + * + * SPLIT EDGE MAKE VERT: + * Takes a given edge and splits it into two, creating a new vert. + * + * + * Before: OV---------TV + * After: OV----NV---TV + * + * Returns - + * BMVert pointer. + * + */ + +BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **re) +{ + BMLoop *nextl; + BMEdge *ne; + BMVert *nv, *ov; + int i, edok, valence1 = 0, valence2 = 0; + + if (bmesh_vert_in_edge(e, tv) == 0) { + return NULL; + } + ov = bmesh_edge_getothervert(e, tv); + + /* count valence of v1 */ + valence1 = bmesh_disk_count(ov); + + /* count valence of v2 */ + valence2 = bmesh_disk_count(tv); + + nv = BM_vert_create(bm, tv->co, tv); + ne = BM_edge_create(bm, nv, tv, e, FALSE); + + bmesh_disk_remove_edge(ne, tv); + bmesh_disk_remove_edge(ne, nv); + + /* remove e from v2's disk cycle */ + bmesh_disk_remove_edge(e, tv); + + /* swap out tv for nv in e */ + bmesh_edge_swapverts(e, tv, nv); + + /* add e to nv's disk cycl */ + bmesh_disk_append_edge(e, nv); + + /* add ne to nv's disk cycl */ + bmesh_disk_append_edge(ne, nv); + + /* add ne to tv's disk cycl */ + bmesh_disk_append_edge(ne, tv); + + /* verify disk cycle */ + edok = bmesh_disk_validate(valence1, ov->e, ov); + if (!edok) bmesh_error(); + edok = bmesh_disk_validate(valence2, tv->e, tv); + if (!edok) bmesh_error(); + edok = bmesh_disk_validate(2, nv->e, nv); + if (!edok) bmesh_error(); + + /* Split the radial cycle if presen */ + nextl = e->l; + e->l = NULL; + if (nextl) { + BMLoop *nl, *l; + int radlen = bmesh_radial_length(nextl); + int first1 = 0, first2 = 0; + + /* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */ + while (nextl) { + l = nextl; + l->f->len++; + nextl = nextl != nextl->radial_next ? nextl->radial_next : NULL; + bmesh_radial_remove_loop(l, NULL); + + nl = bmesh_create_loop(bm, NULL, NULL, l->f, l); + nl->prev = l; + nl->next = (l->next); + nl->prev->next = nl; + nl->next->prev = nl; + nl->v = nv; + + /* assign the correct edge to the correct loo */ + if (bmesh_verts_in_edge(nl->v, nl->next->v, e)) { + nl->e = e; + l->e = ne; + + /* append l into ne's rad cycl */ + if (!first1) { + first1 = 1; + l->radial_next = l->radial_prev = NULL; + } + + if (!first2) { + first2 = 1; + l->radial_next = l->radial_prev = NULL; + } + + bmesh_radial_append(nl->e, nl); + bmesh_radial_append(l->e, l); + } + else if (bmesh_verts_in_edge(nl->v, nl->next->v, ne)) { + nl->e = ne; + l->e = e; + + /* append l into ne's rad cycl */ + if (!first1) { + first1 = 1; + l->radial_next = l->radial_prev = NULL; + } + + if (!first2) { + first2 = 1; + l->radial_next = l->radial_prev = NULL; + } + + bmesh_radial_append(nl->e, nl); + bmesh_radial_append(l->e, l); + } + + } + + /* verify length of radial cycl */ + edok = bmesh_radial_validate(radlen, e->l); + if (!edok) bmesh_error(); + edok = bmesh_radial_validate(radlen, ne->l); + if (!edok) bmesh_error(); + + /* verify loop->v and loop->next->v pointers for */ + for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) { + if (!(l->e == e)) bmesh_error(); + //if (!(l->radial_next == l)) bmesh_error(); + if (l->prev->e != ne && l->next->e != ne) { + bmesh_error(); + } + edok = bmesh_verts_in_edge(l->v, l->next->v, e); + if (!edok) bmesh_error(); + if (l->v == l->next->v) bmesh_error(); + if (l->e == l->next->e) bmesh_error(); + + /* verify loop cycle for kloop-> */ + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->v); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->f); + } + /* verify loop->v and loop->next->v pointers for n */ + for (i = 0, l = ne->l; i < radlen; i++, l = l->radial_next) { + if (!(l->e == ne)) bmesh_error(); + //if (!(l->radial_next == l)) bmesh_error(); + if (l->prev->e != e && l->next->e != e) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, l->next->v, ne); + if (!edok) bmesh_error(); + if (l->v == l->next->v) bmesh_error(); + if (l->e == l->next->e) bmesh_error(); + + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->v); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->f); + } + } + + BM_CHECK_ELEMENT(bm, ne); + BM_CHECK_ELEMENT(bm, nv); + BM_CHECK_ELEMENT(bm, ov); + BM_CHECK_ELEMENT(bm, e); + BM_CHECK_ELEMENT(bm, tv); + + if (re) *re = ne; + return nv; +} + +/** + * bmesh_JEKV + * + * JOIN EDGE KILL VERT: + * Takes a an edge and pointer to one of its vertices and collapses + * the edge on that vertex. + * + * Before: OE KE + * ------- ------- + * | || | + * OV KV TV + * + * + * After: OE + * --------------- + * | | + * OV TV + * + * + * 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). + * + * It should also be noted that 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. + * + * Returns - + * 1 for success, 0 for failure. + */ +int bmesh_jekv(BMesh *bm, BMEdge *ke, BMVert *kv) +{ + BMEdge *oe; + BMVert *ov, *tv; + BMLoop *killoop, *l; + int len, radlen = 0, halt = 0, i, valence1, valence2, edok; + + if (bmesh_vert_in_edge(ke, kv) == 0) { + return FALSE; + } + + len = bmesh_disk_count(kv); + + if (len == 2) { + oe = bmesh_disk_nextedge(ke, kv); + tv = bmesh_edge_getothervert(ke, kv); + ov = bmesh_edge_getothervert(oe, kv); + halt = bmesh_verts_in_edge(kv, tv, oe); /* check for double edge */ + + if (halt) { + return FALSE; + } + else { + /* For verification later, count valence of ov and t */ + valence1 = bmesh_disk_count(ov); + valence2 = bmesh_disk_count(tv); + + /* remove oe from kv's disk cycl */ + bmesh_disk_remove_edge(oe, kv); + /* relink oe->kv to be oe->t */ + bmesh_edge_swapverts(oe, kv, tv); + /* append oe to tv's disk cycl */ + bmesh_disk_append_edge(oe, tv); + /* remove ke from tv's disk cycl */ + bmesh_disk_remove_edge(ke, tv); + + /* deal with radial cycle of k */ + radlen = bmesh_radial_length(ke->l); + if (ke->l) { + /* first step, fix the neighboring loops of all loops in ke's radial cycl */ + for (i = 0, killoop = ke->l; i < radlen; i++, killoop = bmesh_radial_nextloop(killoop)) { + /* relink loops and fix vertex pointer */ + if (killoop->next->v == kv) { + killoop->next->v = tv; + } + + killoop->next->prev = killoop->prev; + killoop->prev->next = killoop->next; + if (BM_FACE_FIRST_LOOP(killoop->f) == killoop) { + BM_FACE_FIRST_LOOP(killoop->f) = killoop->next; + } + killoop->next = NULL; + killoop->prev = NULL; + + /* fix len attribute of fac */ + killoop->f->len--; + } + /* second step, remove all the hanging loops attached to k */ + radlen = bmesh_radial_length(ke->l); + + if (LIKELY(radlen)) { + BMLoop **loops = NULL; + BLI_array_fixedstack_declare(loops, BM_NGON_STACK_SIZE, radlen, __func__); + + killoop = ke->l; + + /* this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well.. */ + for (i = 0; i < radlen; i++) { + loops[i] = killoop; + killoop = bmesh_radial_nextloop(killoop); + } + for (i = 0; i < radlen; i++) { + bm->totloop--; + BLI_mempool_free(bm->lpool, loops[i]); + } + BLI_array_fixedstack_free(loops); + } + + /* Validate radial cycle of o */ + edok = bmesh_radial_validate(radlen, oe->l); + if (!edok) { + bmesh_error(); + } + } + + /* deallocate edg */ + bmesh_kill_only_edge(bm, ke); + + /* deallocate verte */ + bmesh_kill_only_vert(bm, kv); + + /* Validate disk cycle lengths of ov, tv are unchange */ + edok = bmesh_disk_validate(valence1, ov->e, ov); + if (!edok) bmesh_error(); + edok = bmesh_disk_validate(valence2, tv->e, tv); + if (!edok) bmesh_error(); + + /* Validate loop cycle of all faces attached to o */ + for (i = 0, l = oe->l; i < radlen; i++, l = bmesh_radial_nextloop(l)) { + if (l->e != oe) bmesh_error(); + edok = bmesh_verts_in_edge(l->v, l->next->v, oe); + if (!edok) bmesh_error(); + edok = bmesh_loop_validate(l->f); + if (!edok) bmesh_error(); + + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->v); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->f); + } + + BM_CHECK_ELEMENT(bm, ov); + BM_CHECK_ELEMENT(bm, tv); + BM_CHECK_ELEMENT(bm, oe); + + return TRUE; + } + } + return FALSE; +} + +/** + * bmesh_JFKE + * + * JOIN FACE KILL EDGE: + * + * Takes two faces joined by a single 2-manifold edge and fuses them togather. + * The edge shared by the faces must not be connected to any other edges which have + * Both faces in its radial cycle + * + * Examples: + * + * A B + * ---------- ---------- + * | | | | + * | f1 | | f1 | + * v1========v2 = Ok! v1==V2==v3 == Wrong! + * | f2 | | f2 | + * | | | | + * ---------- ---------- + * + * In the example A, faces f1 and f2 are joined by a single edge, and the euler can safely be used. + * In example B however, f1 and 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 f1 and f2. + * + * Also note that the order of arguments decides whether or not certain per-face attributes are present + * in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited + * from f1, not f2. + * + * Returns - + * A BMFace pointer + */ +BMFace *bmesh_jfke(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) +{ + BMLoop *l_iter, *f1loop = NULL, *f2loop = NULL; + int newlen = 0, i, f1len = 0, f2len = 0, radlen = 0, edok, shared; + BMIter iter; + + /* can't join a face to itsel */ + if (f1 == f2) { + return NULL; + } + + /* verify that e is in both f1 and f2 */ + f1len = f1->len; + f2len = f2->len; + BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f1) { + if (l_iter->e == e) { + f1loop = l_iter; + break; + } + } + BM_ITER(l_iter, &iter, bm, BM_LOOPS_OF_FACE, f2) { + if (l_iter->e == e) { + f2loop = l_iter; + break; + } + } + if (!(f1loop && f2loop)) { + return NULL; + } + + /* validate that edge is 2-manifold edg */ + radlen = bmesh_radial_length(f1loop); + if (radlen != 2) { + return NULL; + } + + /* validate direction of f2's loop cycle is compatible */ + if (f1loop->v == f2loop->v) { + return NULL; + } + + /* validate that for each face, each vertex has another edge in its disk cycle that is + * not e, and not shared. */ + if ( bmesh_radial_find_face(f1loop->next->e, f2) || + bmesh_radial_find_face(f1loop->prev->e, f2) || + bmesh_radial_find_face(f2loop->next->e, f1) || + bmesh_radial_find_face(f2loop->prev->e, f1) ) + { + return NULL; + } + + /* validate only one shared edg */ + shared = BM_face_share_edges(f1, f2); + if (shared > 1) { + return NULL; + } + + /* validate no internal join */ + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } + + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) { + if (l_iter != f1loop) { + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + } + } + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) { + if (l_iter != f2loop) { + /* as soon as a duplicate is found, bail out */ + if (BM_elem_flag_test(l_iter->v, BM_ELEM_TAG)) { + return NULL; + } + } + } + + /* join the two loop */ + f1loop->prev->next = f2loop->next; + f2loop->next->prev = f1loop->prev; + + f1loop->next->prev = f2loop->prev; + f2loop->prev->next = f1loop->next; + + /* if f1loop was baseloop, make f1loop->next the base. */ + if (BM_FACE_FIRST_LOOP(f1) == f1loop) + BM_FACE_FIRST_LOOP(f1) = f1loop->next; + + /* increase length of f1 */ + f1->len += (f2->len - 2); + + /* make sure each loop points to the proper fac */ + 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 */ + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v1); + bmesh_disk_remove_edge(f1loop->e, f1loop->e->v2); + + /* deallocate edge and its two loops as well as f2 */ + BLI_mempool_free(bm->toolflagpool, f1loop->e->oflags); + BLI_mempool_free(bm->epool, f1loop->e); + bm->totedge--; + BLI_mempool_free(bm->lpool, f1loop); + bm->totloop--; + BLI_mempool_free(bm->lpool, f2loop); + bm->totloop--; + BLI_mempool_free(bm->toolflagpool, f2->oflags); + BLI_mempool_free(bm->fpool, f2); + bm->totface--; + /* account for both above */ + bm->elem_index_dirty |= BM_EDGE | BM_FACE; + + BM_CHECK_ELEMENT(bm, f1); + + /* validate the new loop cycle */ + edok = bmesh_loop_validate(f1); + if (!edok) bmesh_error(); + + return f1; +} + +/* + * BMESH SPLICE VERT + * + * merges two verts into one (v into vtarget). + */ +static int bmesh_splicevert(BMesh *bm, BMVert *v, BMVert *vtarget) +{ + BMEdge *e; + BMLoop *l; + BMIter liter; + + /* verts already spliced */ + if (v == vtarget) { + return FALSE; + } + + /* retarget all the loops of v to vtarget */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + l->v = vtarget; + } + + /* move all the edges from v's disk to vtarget's disk */ + e = v->e; + while (e != NULL) { + bmesh_disk_remove_edge(e, v); + bmesh_edge_swapverts(e, v, vtarget); + bmesh_disk_append_edge(e, vtarget); + e = v->e; + } + + BM_CHECK_ELEMENT(bm, v); + BM_CHECK_ELEMENT(bm, vtarget); + + /* v is unused now, and can be killed */ + BM_vert_kill(bm, v); + + return TRUE; +} + +/* BMESH CUT VERT + * + * cut all disjoint fans that meet at a vertex, making a unique + * vertex for each region. returns an array of all resulting + * vertices. + */ +static int bmesh_cutvert(BMesh *bm, BMVert *v, BMVert ***vout, int *len) +{ + BMEdge **stack = NULL; + BLI_array_declare(stack); + BMVert **verts = NULL; + GHash *visithash; + BMIter eiter, liter; + BMLoop *l; + BMEdge *e; + int i, maxindex; + BMLoop *nl; + + visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh_cutvert visithash"); + + maxindex = 0; + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (BLI_ghash_haskey(visithash, e)) { + continue; + } + + /* Prime the stack with this unvisited edge */ + BLI_array_append(stack, e); + + /* Considering only edges and faces incident on vertex v, walk + * the edges & faces and assign an index to each connected set */ + while ((e = BLI_array_pop(stack))) { + BLI_ghash_insert(visithash, e, SET_INT_IN_POINTER(maxindex)); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + nl = (l->v == v) ? l->prev : l->next; + if (!BLI_ghash_haskey(visithash, nl->e)) { + BLI_array_append(stack, nl->e); + } + } + } + + maxindex++; + } + + /* Make enough verts to split v for each group */ + verts = MEM_callocN(sizeof(BMVert *) * maxindex, "bmesh_cutvert"); + verts[0] = v; + for (i = 1; i < maxindex; i++) { + verts[i] = BM_vert_create(bm, v->co, v); + } + + /* Replace v with the new verts in each group */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e)); + if (i == 0) { + continue; + } + + /* Loops here should alway refer to an edge that has v as an + * 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. */ + if (l->v == v) { + l->v = verts[i]; + } + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, e)); + if (i == 0) { + continue; + } + + BLI_assert(e->v1 == v || e->v2 == v); + bmesh_disk_remove_edge(e, v); + bmesh_edge_swapverts(e, v, verts[i]); + bmesh_disk_append_edge(e, verts[i]); + } + + BLI_ghash_free(visithash, NULL, NULL); + BLI_array_free(stack); + + for (i = 0; i < maxindex; i++) { + BM_CHECK_ELEMENT(bm, verts[i]); + } + + if (len != NULL) { + *len = maxindex; + } + + if (vout != NULL) { + *vout = verts; + } + else { + MEM_freeN(verts); + } + + return TRUE; +} + +/* BMESH SPLICE EDGE + * + * splice two unique edges which share the same two vertices into one edge. + * + * edges must already have the same vertices + */ +static int UNUSED_FUNCTION(bmesh_spliceedge)(BMesh *bm, BMEdge *e, BMEdge *etarget) +{ + BMLoop *l; + + if (!BM_vert_in_edge(e, etarget->v1) || !BM_vert_in_edge(e, etarget->v2)) { + /* not the same vertices can't splice */ + return FALSE; + } + + while (e->l) { + l = e->l; + BLI_assert(BM_vert_in_edge(etarget, l->v)); + BLI_assert(BM_vert_in_edge(etarget, l->next->v)); + bmesh_radial_remove_loop(l, e); + bmesh_radial_append(etarget, l); + } + + BLI_assert(bmesh_radial_length(e->l) == 0); + + BM_CHECK_ELEMENT(bm, e); + BM_CHECK_ELEMENT(bm, etarget); + + BM_edge_kill(bm, e); + + return TRUE; +} + +/* + * BMESH CUT EDGE + * + * Cuts a single edge into two edge: the original edge and + * a new edge that has only "cutl" in its radial. + * + * Does nothing if cutl is already the only loop in the + * edge radial. + */ +static int bmesh_cutedge(BMesh *bm, BMEdge *e, BMLoop *cutl) +{ + BMEdge *ne; + int radlen; + + BLI_assert(cutl->e == e); + BLI_assert(e->l); + + radlen = bmesh_radial_length(e->l); + if (radlen < 2) { + /* no cut required */ + return TRUE; + } + + if (cutl == e->l) { + e->l = cutl->radial_next; + } + + ne = BM_edge_create(bm, e->v1, e->v2, e, FALSE); + bmesh_radial_remove_loop(cutl, e); + bmesh_radial_append(ne, cutl); + cutl->e = ne; + + BLI_assert(bmesh_radial_length(e->l) == radlen - 1); + BLI_assert(bmesh_radial_length(ne->l) == 1); + + BM_CHECK_ELEMENT(bm, ne); + BM_CHECK_ELEMENT(bm, e); + + return TRUE; +} + +/* + * BMESH UNGLUE REGION MAKE VERT + * + * Disconnects a face from its vertex fan at loop sl. + */ +static BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *sl) +{ + BMVert **vtar; + int len, i; + BMVert *nv = NULL; + BMVert *sv = sl->v; + + /* peel the face from the edge radials on both sides of the + * loop vert, disconnecting the face from its fan */ + bmesh_cutedge(bm, sl->e, sl); + bmesh_cutedge(bm, sl->prev->e, sl->prev); + + if (bmesh_disk_count(sv) == 2) { + /* If there are still only two edges out of sv, then + * this whole URMV was just a no-op, so exit now. */ + return sv; + } + + /* Update the disk start, so that v->e points to an edge + * not touching the split loop. This is so that bmesh_cutvert + * will leave the original sv on some *other* fan (not the + * one-face fan that holds the unglue face). */ + while (sv->e == sl->e || sv->e == sl->prev->e) { + sv->e = bmesh_disk_nextedge(sv->e, sv); + } + + /* Split all fans connected to the vert, duplicating it for + * each fans. */ + bmesh_cutvert(bm, sv, &vtar, &len); + + /* There should have been at least two fans cut apart here, + * otherwise the early exit would have kicked in. */ + BLI_assert(len >= 2); + + nv = sl->v; + + /* 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. */ + BLI_assert(nv != sv); + BLI_assert(sv == vtar[0]); + + /* 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++) { + if (vtar[i] == nv) { + 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++) { + bmesh_splicevert(bm, vtar[i], vtar[0]); + } + } + } + + MEM_freeN(vtar); + + return nv; +} + +/* + * BMESH UNGLUE REGION MAKE VERT + * + * Disconnects sf from the vertex fan at sv + */ +BMVert *bmesh_urmv(BMesh *bm, BMFace *sf, BMVert *sv) +{ + BMLoop *l_first; + BMLoop *l_iter; + + l_iter = l_first = BM_FACE_FIRST_LOOP(sf); + do { + if (l_iter->v == sv) { + break; + } + } while ((l_iter = l_iter->next) != l_first); + + if (l_iter->v != sv) { + /* sv is not part of sf */ + return NULL; + } + + return bmesh_urmv_loop(bm, l_iter); +} diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c new file mode 100644 index 00000000000..19e9971619a --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -0,0 +1,1145 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_opdefines.c + * \ingroup bmesh + * + * BMesh operator definitions. + * + * This file defines (and documents) all bmesh operators (bmops). + * + * Do not rename any operator or slot names! otherwise you must go + * through the code and find all references to them! + * + * A word on slot names: + * + * For geometry input slots, the following are valid names: + * - verts + * - edges + * - faces + * - edgefacein + * - vertfacein + * - vertedgein + * - vertfacein + * - geom + * + * The basic rules are, for single-type geometry slots, use the plural of the + * type name (e.g. edges). for double-type slots, use the two type names plus + * "in" (e.g. edgefacein). for three-type slots, use geom. + * + * for output slots, for single-type geometry slots, use the type name plus "out", + * (e.g. vertout), for double-type slots, use the two type names plus "out", + * (e.g. vertfaceout), for three-type slots, use geom. note that you can also + * use more esohteric names (e.g. skirtout) so long as the comment next to the + * slot definition tells you what types of elements are in it. + * + */ + +#include "bmesh.h" +#include "bmesh_private.h" + +/* ok, I'm going to write a little docgen script. so all + * bmop comments must conform to the following template/rules: + * + * template (py quotes used because nested comments don't work + * on all C compilers): + * + * """ + * Region Extend. + * + * paragraph1, Extends bleh bleh bleh. + * Bleh Bleh bleh. + * + * Another paragraph. + * + * Another paragraph. + * """ + * + * so the first line is the "title" of the bmop. + * subsequent line blocks seperated by blank lines + * are paragraphs. individual descriptions of slots + * would be extracted from comments + * next to them, e.g. + * + * {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, boundary region + * + * the doc generator would automatically detect the presence of "output slot" + * and flag the slot as an output. the same happens for "input slot". also + * note that "edges", "faces", "verts", "loops", and "geometry" are valid + * substitutions for "slot". + * + * note that slots default to being input slots. + */ + +/* + * Vertex Smooth + * + * Smoothes vertices by using a basic vertex averaging scheme. + */ +static BMOpDefine def_vertexsmooth = { + "vertexsmooth", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_INT, "mirror_clip_x"}, //set vertices close to the x axis before the operation to 0 + {BMO_OP_SLOT_INT, "mirror_clip_y"}, //set vertices close to the y axis before the operation to 0 + {BMO_OP_SLOT_INT, "mirror_clip_z"}, //set vertices close to the z axis before the operation to 0 + {BMO_OP_SLOT_FLT, "clipdist"}, //clipping threshod for the above three slots + {0} /* null-terminating sentine */, + }, + bmesh_vertexsmooth_exec, + 0 +}; + +/* + * Right-Hand Faces + * + * Computes an "outside" normal for the specified input faces. + */ + +static BMOpDefine def_righthandfaces = { + "righthandfaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, + {BMO_OP_SLOT_INT, "doflip"}, //internal flag, used by bmesh_rationalize_normals + {0} /* null-terminating sentine */, + }, + bmesh_righthandfaces_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Region Extend + * + * used to implement the select more/less tools. + * this puts some geometry surrounding regions of + * geometry in geom into geomout. + * + * if usefaces is 0 then geomout spits out verts and edges, + * otherwise it spits out faces. + */ +static BMOpDefine def_regionextend = { + "regionextend", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output slot, computed boundary geometry. + {BMO_OP_SLOT_INT, "constrict"}, //find boundary inside the regions, not outside. + {BMO_OP_SLOT_INT, "usefaces"}, //extend from faces instead of edges + {0} /* null-terminating sentine */, + }, + bmesh_regionextend_exec, + 0 +}; + +/* + * Edge Rotate + * + * Rotates edges topologically. Also known as "spin edge" to some people. + * Simple example: [/] becomes [|] then [\]. + */ +static BMOpDefine def_edgerotate = { + "edgerotate", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //newly spun edges + {BMO_OP_SLOT_INT, "ccw"}, //rotate edge counter-clockwise if true, othewise clockwise + {0} /* null-terminating sentine */, + }, + bmesh_edgerotate_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Reverse Faces + * + * Reverses the winding (vertex order) of faces. This has the effect of + * flipping the normal. + */ +static BMOpDefine def_reversefaces = { + "reversefaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces + {0} /* null-terminating sentine */, + }, + bmesh_reversefaces_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Edge Bisect + * + * Splits input edges (but doesn't do anything else). + * This creates a 2-valence vert. + */ +static BMOpDefine def_edgebisect = { + "edgebisect", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges + {BMO_OP_SLOT_INT, "numcuts"}, //number of cuts + {BMO_OP_SLOT_ELEMENT_BUF, "outsplit"}, //newly created vertices and edges + {0} /* null-terminating sentine */, + }, + esplit_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Mirror + * + * Mirrors geometry along an axis. The resulting geometry is welded on using + * mergedist. Pairs of original/mirrored vertices are welded using the mergedist + * parameter (which defines the minimum distance for welding to happen). + */ + +static BMOpDefine def_mirror = { + "mirror", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry + {BMO_OP_SLOT_MAT, "mat"}, //matrix defining the mirror transformation + {BMO_OP_SLOT_FLT, "mergedist"}, //maximum distance for merging. does no merging if 0. + {BMO_OP_SLOT_ELEMENT_BUF, "newout"}, //output geometry, mirrored + {BMO_OP_SLOT_INT, "axis"}, //the axis to use, 0, 1, or 2 for x, y, z + {BMO_OP_SLOT_INT, "mirror_u"}, //mirror UVs across the u axis + {BMO_OP_SLOT_INT, "mirror_v"}, //mirror UVs across the v axis + {0, /* null-terminating sentine */}}, + bmesh_mirror_exec, + 0, +}; + +/* + * Find Doubles + * + * Takes input verts and find vertices they should weld to. Outputs a + * mapping slot suitable for use with the weld verts bmop. + */ +static BMOpDefine def_finddoubles = { + "finddoubles", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_ELEMENT_BUF, "keepverts"}, //list of verts to keep + {BMO_OP_SLOT_FLT, "dist"}, //minimum distance + {BMO_OP_SLOT_MAPPING, "targetmapout"}, + {0, /* null-terminating sentine */}}, + bmesh_finddoubles_exec, + 0, +}; + +/* + * Remove Doubles + * + * Finds groups of vertices closer then dist and merges them together, + * using the weld verts bmop. + */ +static BMOpDefine def_removedoubles = { + "removedoubles", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts + {BMO_OP_SLOT_FLT, "dist"}, //minimum distance + {0, /* null-terminating sentine */}}, + bmesh_removedoubles_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Auto Merge + * + * Finds groups of vertices closer then dist and merges them together, + * using the weld verts bmop. The merges must go from a vert not in + * verts to one in verts. + */ +static BMOpDefine def_automerge = { + "automerge", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input verts + {BMO_OP_SLOT_FLT, "dist"}, //minimum distance + {0, /* null-terminating sentine */}}, + bmesh_automerge_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Collapse Connected + * + * Collapses connected vertices + */ +static BMOpDefine def_collapse = { + "collapse", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {0, /* null-terminating sentine */}}, + bmesh_collapse_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + + +/* + * Facedata point Merge + * + * Merge uv/vcols at a specific vertex. + */ +static BMOpDefine def_pointmerge_facedata = { + "pointmerge_facedata", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */ + {BMO_OP_SLOT_ELEMENT_BUF, "snapv"}, /* snap verte */ + {0, /* null-terminating sentine */}}, + bmesh_pointmerge_facedata_exec, + 0, +}; + +/* + * Average Vertices Facevert Data + * + * Merge uv/vcols associated with the input vertices at + * the bounding box center. (I know, it's not averaging but + * the vert_snap_to_bb_center is just too long). + */ +static BMOpDefine def_vert_average_facedata = { + "vert_average_facedata", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */ + {0, /* null-terminating sentine */}}, + bmesh_vert_average_facedata_exec, + 0, +}; + +/* + * Point Merge + * + * Merge verts together at a point. + */ +static BMOpDefine def_pointmerge = { + "pointmerge", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertice */ + {BMO_OP_SLOT_VEC, "mergeco"}, + {0, /* null-terminating sentine */}}, + bmesh_pointmerge_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Collapse Connected UVs + * + * Collapses connected UV vertices. + */ +static BMOpDefine def_collapse_uvs = { + "collapse_uvs", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {0, /* null-terminating sentine */}}, + bmesh_collapsecon_exec, + 0, +}; + +/* + * Weld Verts + * + * Welds verts together (kindof like remove doubles, merge, etc, all of which + * use or will use this bmop). You pass in mappings from vertices to the vertices + * they weld with. + */ +static BMOpDefine def_weldverts = { + "weldverts", + {{BMO_OP_SLOT_MAPPING, "targetmap"}, /* maps welded vertices to verts they should weld to */ + {0, /* null-terminating sentine */}}, + bmesh_weldverts_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Make Vertex + * + * Creates a single vertex; this bmop was necassary + * for click-create-vertex. + */ +static BMOpDefine def_makevert = { + "makevert", + {{BMO_OP_SLOT_VEC, "co"}, //the coordinate of the new vert + {BMO_OP_SLOT_ELEMENT_BUF, "newvertout"}, //the new vert + {0, /* null-terminating sentine */}}, + bmesh_makevert_exec, + 0, +}; + +/* + * Join Triangles + * + * Tries to intelligently join triangles according + * to various settings and stuff. + */ +static BMOpDefine def_join_triangles = { + "join_triangles", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input geometry. + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //joined faces + {BMO_OP_SLOT_INT, "compare_sharp"}, + {BMO_OP_SLOT_INT, "compare_uvs"}, + {BMO_OP_SLOT_INT, "compare_vcols"}, + {BMO_OP_SLOT_INT, "compare_materials"}, + {BMO_OP_SLOT_FLT, "limit"}, + {0, /* null-terminating sentine */}}, + bmesh_jointriangles_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Contextual Create + * + * This is basically fkey, it creates + * new faces from vertices, makes stuff from edge nets, + * makes wire edges, etc. It also dissolves + * faces. + * + * Three verts become a triangle, four become a quad. Two + * become a wire edge. + */ +static BMOpDefine def_contextual_create = { + "contextual_create", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, //input geometry. + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //newly-made face(s) + {0, /* null-terminating sentine */}}, + bmesh_contextual_create_exec, + BMO_OP_FLAG_UNTAN_MULTIRES, +}; + +/* + * Bridge edge loops with faces + */ +static BMOpDefine def_bridge_loops = { + "bridge_loops", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */ + {0, /* null-terminating sentine */}}, + bmesh_bridge_loops_exec, + 0, +}; + +static BMOpDefine def_edgenet_fill = { + "edgenet_fill", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edge */ + {BMO_OP_SLOT_MAPPING, "restrict"}, /* restricts edges to groups. maps edges to integer */ + {BMO_OP_SLOT_INT, "use_restrict"}, + {BMO_OP_SLOT_ELEMENT_BUF, "excludefaces"}, /* list of faces to ignore for manifold check */ + {BMO_OP_SLOT_MAPPING, "faceout_groupmap"}, /* maps new faces to the group numbers they came fro */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* new face */ + {0, /* null-terminating sentine */}}, + bmesh_edgenet_fill_exec, + 0, +}; + +/* + * Edgenet Prepare + * + * Identifies several useful edge loop cases and modifies them so + * they'll become a face when edgenet_fill is called. The cases covered are: + * + * - One single loop; an edge is added to connect the ends + * - Two loops; two edges are added to connect the endpoints (based on the + * shortest distance between each endpont). + */ +static BMOpDefine def_edgenet_prepare = { + "edgenet_prepare", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input edges + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //new edges + {0, /* null-terminating sentine */}}, + bmesh_edgenet_prepare, + 0, +}; + +/* + * Rotate + * + * Rotate vertices around a center, using a 3x3 rotation + * matrix. Equivilent of the old rotateflag function. + */ +static BMOpDefine def_rotate = { + "rotate", + {{BMO_OP_SLOT_VEC, "cent"}, //center of rotation + {BMO_OP_SLOT_MAT, "mat"}, //matrix defining rotation + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_rotate_exec, + 0, +}; + +/* + * Translate + * + * Translate vertices by an offset. Equivelent of the + * old translateflag function. + */ +static BMOpDefine def_translate = { + "translate", + {{BMO_OP_SLOT_VEC, "vec"}, //translation offset + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_translate_exec, + 0, +}; + +/* + * Scale + * + * Scales vertices by an offset. + */ +static BMOpDefine def_scale = { + "scale", + {{BMO_OP_SLOT_VEC, "vec"}, //scale factor + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_scale_exec, + 0, +}; + + +/* + * Transform + * + * Transforms a set of vertices by a matrix. Multiplies + * the vertex coordinates with the matrix. + */ +static BMOpDefine def_transform = { + "transform", + {{BMO_OP_SLOT_MAT, "mat"}, //transform matrix + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {0, /* null-terminating sentine */}}, + bmesh_transform_exec, + 0, +}; + +/* + * Object Load BMesh + * + * Loads a bmesh into an object/mesh. This is a "private" + * bmop. + */ +static BMOpDefine def_object_load_bmesh = { + "object_load_bmesh", + {{BMO_OP_SLOT_PNT, "scene"}, + {BMO_OP_SLOT_PNT, "object"}, + {0, /* null-terminating sentine */}}, + object_load_bmesh_exec, + 0, +}; + + +/* + * BMesh to Mesh + * + * Converts a bmesh to a Mesh. This is reserved for exiting editmode. + */ +static BMOpDefine def_bmesh_to_mesh = { + "bmesh_to_mesh", + {{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a mesh structure to fill in + {BMO_OP_SLOT_PNT, "object"}, //pointer to an object structure + {BMO_OP_SLOT_INT, "notesselation"}, //don't calculate mfaces + {0, /* null-terminating sentine */}}, + bmesh_to_mesh_exec, + 0, +}; + +/* + * Mesh to BMesh + * + * Load the contents of a mesh into the bmesh. this bmop is private, it's + * reserved exclusively for entering editmode. + */ +static BMOpDefine def_mesh_to_bmesh = { + "mesh_to_bmesh", + {{BMO_OP_SLOT_PNT, "mesh"}, //pointer to a Mesh structure + {BMO_OP_SLOT_PNT, "object"}, //pointer to an Object structure + {BMO_OP_SLOT_INT, "set_shapekey"}, //load active shapekey coordinates into verts + {0, /* null-terminating sentine */}}, + mesh_to_bmesh_exec, + 0 +}; + +/* + * Individual Face Extrude + * + * Extrudes faces individually. + */ +static BMOpDefine def_extrude_indivface = { + "extrude_face_indiv", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, //input faces + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, //output faces + {BMO_OP_SLOT_ELEMENT_BUF, "skirtout"}, //output skirt geometry, faces and edges + {0} /* null-terminating sentine */}, + bmesh_extrude_face_indiv_exec, + 0 +}; + +/* + * Extrude Only Edges + * + * Extrudes Edges into faces, note that this is very simple, there's no fancy + * winged extrusion. + */ +static BMOpDefine def_extrude_onlyedge = { + "extrude_edge_only", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, //input vertices + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, //output geometry + {0} /* null-terminating sentine */}, + bmesh_extrude_onlyedge_exec, + 0 +}; + +/* + * Individual Vertex Extrude + * + * Extrudes wire edges from vertices. + */ +static BMOpDefine def_extrudeverts_indiv = { + "extrude_vert_indiv", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, //output wire edges + {BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output vertices + {0} /* null-terminating sentine */}, + extrude_vert_indiv_exec, + 0 +}; + +static BMOpDefine def_connectverts = { + "connectverts", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, + {0} /* null-terminating sentine */}, + connectverts_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_extrudefaceregion = { + "extrudefaceregion", + {{BMO_OP_SLOT_ELEMENT_BUF, "edgefacein"}, + {BMO_OP_SLOT_MAPPING, "exclude"}, + {BMO_OP_SLOT_INT, "alwayskeeporig"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, + {0} /* null-terminating sentine */}, + extrude_edge_context_exec, + 0 +}; + +static BMOpDefine def_dissolvevertsop = { + "dissolveverts", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {0} /* null-terminating sentine */}, + dissolveverts_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolveedgessop = { + "dissolveedges", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {BMO_OP_SLOT_ELEMENT_BUF, "regionout"}, + {BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges. + {0} /* null-terminating sentine */}, + dissolveedges_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolveedgeloopsop = { + "dissolveedgeloop", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {BMO_OP_SLOT_ELEMENT_BUF, "regionout"}, + {0} /* null-terminating sentine */}, + dissolve_edgeloop_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolvefacesop = { + "dissolvefaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, + {BMO_OP_SLOT_ELEMENT_BUF, "regionout"}, + {BMO_OP_SLOT_INT, "use_verts"}, // dissolve verts left between only 2 edges. + {0} /* null-terminating sentine */}, + dissolvefaces_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_dissolvelimitop = { + "dissolvelimit", + {{BMO_OP_SLOT_FLT, "angle_limit"}, /* total rotation angle (degrees) */ + {BMO_OP_SLOT_ELEMENT_BUF, "verts"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {0} /* null-terminating sentine */}, + dissolvelimit_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_triangop = { + "triangulate", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, + {BMO_OP_SLOT_MAPPING, "facemap"}, + {0} /* null-terminating sentine */}, + triangulate_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_subdop = { + "esubd", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, + {BMO_OP_SLOT_INT, "numcuts"}, + {BMO_OP_SLOT_FLT, "smooth"}, + {BMO_OP_SLOT_FLT, "fractal"}, + {BMO_OP_SLOT_INT, "beauty"}, + {BMO_OP_SLOT_INT, "seed"}, + {BMO_OP_SLOT_MAPPING, "custompatterns"}, + {BMO_OP_SLOT_MAPPING, "edgepercents"}, + + /* these next three can have multiple types of elements in them */ + {BMO_OP_SLOT_ELEMENT_BUF, "outinner"}, + {BMO_OP_SLOT_ELEMENT_BUF, "outsplit"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* contains all output geometr */ + + {BMO_OP_SLOT_INT, "quadcornertype"}, //quad corner type, see bmesh_operators.h + {BMO_OP_SLOT_INT, "gridfill"}, //fill in fully-selected faces with a grid + {BMO_OP_SLOT_INT, "singleedge"}, //tesselate the case of one edge selected in a quad or triangle + + {0} /* null-terminating sentine */, + }, + esubdivide_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +static BMOpDefine def_delop = { + "del", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, {BMO_OP_SLOT_INT, "context"}, + {0} /* null-terminating sentine */}, + delop_exec, + 0 +}; + +static BMOpDefine def_dupeop = { + "dupe", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_ELEMENT_BUF, "origout"}, + {BMO_OP_SLOT_ELEMENT_BUF, "newout"}, + /* facemap maps from source faces to dupe + * faces, and from dupe faces to source faces */ + {BMO_OP_SLOT_MAPPING, "facemap"}, + {BMO_OP_SLOT_MAPPING, "boundarymap"}, + {BMO_OP_SLOT_MAPPING, "isovertmap"}, + {BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */ + {0} /* null-terminating sentine */}, + dupeop_exec, + 0 +}; + +static BMOpDefine def_splitop = { + "split", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, + {BMO_OP_SLOT_MAPPING, "boundarymap"}, + {BMO_OP_SLOT_MAPPING, "isovertmap"}, + {BMO_OP_SLOT_PNT, "dest"}, /* destination bmesh, if NULL will use current on */ + {0} /* null-terminating sentine */}, + splitop_exec, + 0 +}; + +/* + * Spin + * + * Extrude or duplicate geometry a number of times, + * rotating and possibly translating after each step + */ +static BMOpDefine def_spinop = { + "spin", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_ELEMENT_BUF, "lastout"}, /* result of last step */ + {BMO_OP_SLOT_VEC, "cent"}, /* rotation center */ + {BMO_OP_SLOT_VEC, "axis"}, /* rotation axis */ + {BMO_OP_SLOT_VEC, "dvec"}, /* translation delta per step */ + {BMO_OP_SLOT_FLT, "ang"}, /* total rotation angle (degrees) */ + {BMO_OP_SLOT_INT, "steps"}, /* number of steps */ + {BMO_OP_SLOT_INT, "dupli"}, /* duplicate or extrude? */ + {0} /* null-terminating sentine */}, + spinop_exec, + 0 +}; + + +/* + * Similar faces search + * + * Find similar faces (area/material/perimeter, ...). + */ +static BMOpDefine def_similarfaces = { + "similarfaces", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_ELEMENT_BUF, "faceout"}, /* output faces */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /* null-terminating sentine */}, + bmesh_similarfaces_exec, + 0 +}; + +/* + * Similar edges search + * + * Find similar edges (length, direction, edge, seam, ...). + */ +static BMOpDefine def_similaredges = { + "similaredges", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout"}, /* output edges */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /* null-terminating sentine */}, + bmesh_similaredges_exec, + 0 +}; + +/* + * Similar vertices search + * + * Find similar vertices (normal, face, vertex group, ...). + */ +static BMOpDefine def_similarverts = { + "similarverts", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, /* input vertices */ + {BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {BMO_OP_SLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /* null-terminating sentine */}, + bmesh_similarverts_exec, + 0 +}; + +/* + * uv rotation + * cycle the uvs + */ +static BMOpDefine def_meshrotateuvs = { + "meshrotateuvs", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_INT, "dir"}, /* direction */ + {0} /* null-terminating sentine */}, + bmesh_rotateuvs_exec, + 0 +}; + +/* + * uv reverse + * reverse the uvs + */ +static BMOpDefine def_meshreverseuvs = { + "meshreverseuvs", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {0} /* null-terminating sentine */}, + bmesh_reverseuvs_exec, + 0 +}; + +/* + * color rotation + * cycle the colors + */ +static BMOpDefine def_meshrotatecolors = { + "meshrotatecolors", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_INT, "dir"}, /* direction */ + {0} /* null-terminating sentine */}, + bmesh_rotatecolors_exec, + 0 +}; + +/* + * color reverse + * reverse the colors + */ +static BMOpDefine def_meshreversecolors = { + "meshreversecolors", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {0} /* null-terminating sentine */}, + bmesh_reversecolors_exec, + 0 +}; + +/* + * Similar vertices search + * + * Find similar vertices (normal, face, vertex group, ...). + */ +static BMOpDefine def_vertexshortestpath = { + "vertexshortestpath", + {{BMO_OP_SLOT_ELEMENT_BUF, "startv"}, /* start vertex */ + {BMO_OP_SLOT_ELEMENT_BUF, "endv"}, /* end vertex */ + {BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, /* output vertices */ + {BMO_OP_SLOT_INT, "type"}, /* type of selection */ + {0} /* null-terminating sentine */}, + bmesh_vertexshortestpath_exec, + 0 +}; + +/* + * Edge Split + * + * Disconnects faces along input edges. + */ +static BMOpDefine def_edgesplit = { + "edgesplit", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout1"}, /* old output disconnected edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "edgeout2"}, /* new output disconnected edges */ + {0} /* null-terminating sentine */}, + bmesh_edgesplitop_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Create Grid + * + * Creates a grid with a variable number of subdivisions + */ +static BMOpDefine def_create_grid = { + "create_grid", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "xsegments"}, //number of x segments + {BMO_OP_SLOT_INT, "ysegments"}, //number of y segments + {BMO_OP_SLOT_FLT, "size"}, //size of the grid + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with + {0, /* null-terminating sentine */}}, + bmesh_create_grid_exec, + 0, +}; + +/* + * Create UV Sphere + * + * Creates a grid with a variable number of subdivisions + */ +static BMOpDefine def_create_uvsphere = { + "create_uvsphere", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "segments"}, //number of u segments + {BMO_OP_SLOT_INT, "revolutions"}, //number of v segment + {BMO_OP_SLOT_FLT, "diameter"}, //diameter + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_uvsphere_exec, + 0, +}; + +/* + * Create Ico Sphere + * + * Creates a grid with a variable number of subdivisions + */ +static BMOpDefine def_create_icosphere = { + "create_icosphere", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "subdivisions"}, //how many times to recursively subdivide the sphere + {BMO_OP_SLOT_FLT, "diameter"}, //diameter + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with + {0, /* null-terminating sentine */}}, + bmesh_create_icosphere_exec, + 0, +}; + +/* + * Create Suzanne + * + * Creates a monkey. Be wary. + */ +static BMOpDefine def_create_monkey = { + "create_monkey", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_monkey_exec, + 0, +}; + +/* + * Create Cone + * + * Creates a cone with variable depth at both ends + */ +static BMOpDefine def_create_cone = { + "create_cone", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces + {BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons + {BMO_OP_SLOT_INT, "segments"}, + {BMO_OP_SLOT_FLT, "diameter1"}, //diameter of one end + {BMO_OP_SLOT_FLT, "diameter2"}, //diameter of the opposite + {BMO_OP_SLOT_FLT, "depth"}, //distance between ends + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_cone_exec, + 0, +}; + +/* + * Creates a circle + */ +static BMOpDefine def_create_circle = { + "create_circle", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_INT, "cap_ends"}, //wheter or not to fill in the ends with faces + {BMO_OP_SLOT_INT, "cap_tris"}, //fill ends with triangles instead of ngons + {BMO_OP_SLOT_INT, "segments"}, + {BMO_OP_SLOT_FLT, "diameter"}, //diameter of one end + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_circle_exec, + 0, +}; + +/* + * Create Cone + * + * Creates a cone with variable depth at both ends + */ +static BMOpDefine def_create_cube = { + "create_cube", + {{BMO_OP_SLOT_ELEMENT_BUF, "vertout"}, //output verts + {BMO_OP_SLOT_FLT, "size"}, //size of the cube + {BMO_OP_SLOT_MAT, "mat"}, //matrix to multiply the new geometry with-- + {0, /* null-terminating sentine */}}, + bmesh_create_cube_exec, + 0, +}; + +/* + * Bevel + * + * Bevels edges and vertices + */ +static BMOpDefine def_bevel = { + "bevel", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, /* input edges and vertices */ + {BMO_OP_SLOT_ELEMENT_BUF, "face_spans"}, /* new geometry */ + {BMO_OP_SLOT_ELEMENT_BUF, "face_holes"}, /* new geometry */ + {BMO_OP_SLOT_INT, "use_lengths"}, /* grab edge lengths from a PROP_FLT customdata laye */ + {BMO_OP_SLOT_INT, "use_even"}, /* corner vert placement: use shell/angle calculations */ + {BMO_OP_SLOT_INT, "use_dist"}, /* corner vert placement: evaluate percent as a distance, + * modifier uses this. We could do this as another float setting */ + {BMO_OP_SLOT_INT, "lengthlayer"}, /* which PROP_FLT layer to us */ + {BMO_OP_SLOT_FLT, "percent"}, /* percentage to expand bevelled edge */ + {0} /* null-terminating sentine */}, + bmesh_bevel_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Beautify Fill + * + * Makes triangle a bit nicer + */ +static BMOpDefine def_beautify_fill = { +"beautify_fill", + {{BMO_OP_SLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMO_OP_SLOT_ELEMENT_BUF, "constrain_edges"}, /* edges that can't be flipped */ + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new flipped faces and edges */ + {0} /* null-terminating sentine */}, + bmesh_beautify_fill_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Triangle Fill + * + * Fill edges with triangles + */ +static BMOpDefine def_triangle_fill = { + "triangle_fill", + {{BMO_OP_SLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, /* new faces and edges */ + {0} /* null-terminating sentine */}, + bmesh_triangle_fill_exec, + BMO_OP_FLAG_UNTAN_MULTIRES +}; + +/* + * Solidify + * + * Turns a mesh into a shell with thickness + */ +static BMOpDefine def_solidify = { + "solidify", + {{BMO_OP_SLOT_ELEMENT_BUF, "geom"}, + {BMO_OP_SLOT_FLT, "thickness"}, + {BMO_OP_SLOT_ELEMENT_BUF, "geomout"}, + {0}}, + bmesh_solidify_face_region_exec, + 0 +}; + +BMOpDefine *opdefines[] = { + &def_splitop, + &def_spinop, + &def_dupeop, + &def_delop, + &def_subdop, + &def_triangop, + &def_dissolvefacesop, + &def_dissolveedgessop, + &def_dissolveedgeloopsop, + &def_dissolvevertsop, + &def_dissolvelimitop, + &def_extrudefaceregion, + &def_connectverts, + //&def_makeprim, + &def_extrudeverts_indiv, + &def_mesh_to_bmesh, + &def_object_load_bmesh, + &def_transform, + &def_translate, + &def_rotate, + &def_edgenet_fill, + &def_contextual_create, + &def_makevert, + &def_weldverts, + &def_removedoubles, + &def_finddoubles, + &def_mirror, + &def_edgebisect, + &def_reversefaces, + &def_edgerotate, + &def_regionextend, + &def_righthandfaces, + &def_vertexsmooth, + &def_extrude_onlyedge, + &def_extrude_indivface, + &def_collapse_uvs, + &def_pointmerge, + &def_collapse, + &def_similarfaces, + &def_similaredges, + &def_similarverts, + &def_pointmerge_facedata, + &def_vert_average_facedata, + &def_meshrotateuvs, + &def_bmesh_to_mesh, + &def_meshreverseuvs, + &def_edgenet_prepare, + &def_meshrotatecolors, + &def_meshreversecolors, + &def_vertexshortestpath, + &def_scale, + &def_edgesplit, + &def_automerge, + &def_create_uvsphere, + &def_create_grid, + &def_create_icosphere, + &def_create_monkey, + &def_create_cube, + &def_create_circle, + &def_create_cone, + &def_join_triangles, + &def_bevel, + &def_beautify_fill, + &def_triangle_fill, + &def_bridge_loops, + &def_solidify, +}; + +int bmesh_total_ops = (sizeof(opdefines) / sizeof(void *)); diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c new file mode 100644 index 00000000000..66222c39c68 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -0,0 +1,1376 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_operators.c + * \ingroup bmesh + * + * BMesh operator access. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_listbase.h" +#include "BLI_array.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* forward declarations */ +static void bmo_flag_layer_alloc(BMesh *bm); +static void bmo_flag_layer_free(BMesh *bm); +static void bmo_flag_layer_clear(BMesh *bm); +static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name); +static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name); +static int bmesh_opname_to_opcode(const char *opname); + +static const char *bmo_error_messages[] = { + NULL, + "Self intersection error", + "Could not dissolve vert", + "Could not connect vertices", + "Could not traverse mesh", + "Could not dissolve faces", + "Could not dissolve vertices", + "Tesselation error", + "Can not deal with non-manifold geometry", + "Invalid selection", + "Internal mesh error", +}; + + +/* operator slot type information - size of one element of the type given. */ +const int BMO_OPSLOT_TYPEINFO[] = { + 0, + sizeof(int), + sizeof(float), + sizeof(void *), + 0, /* unused */ + 0, /* unused */ + 0, /* unused */ + sizeof(void *), /* pointer buffer */ + sizeof(BMOElemMapping) +}; + +/* Dummy slot so there is something to return when slot name lookup fails */ +static BMOpSlot BMOpEmptySlot = {0}; + +void BMO_op_flag_enable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag) +{ + op->flag |= op_flag; +} + +void BMO_op_flag_disable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag) +{ + op->flag &= ~op_flag; +} + +/* + * BMESH OPSTACK PUSH + * + * Pushes the opstack down one level + * and allocates a new flag layer if + * appropriate. + */ +void BMO_push(BMesh *bm, BMOperator *UNUSED(op)) +{ + bm->stackdepth++; + + /* add flag layer, if appropriate */ + if (bm->stackdepth > 1) + bmo_flag_layer_alloc(bm); + else + bmo_flag_layer_clear(bm); +} + +/* + * BMESH OPSTACK POP + * + * Pops the opstack one level + * and frees a flag layer if appropriate + * BMESH_TODO: investigate NOT freeing flag + * layers. + */ +void BMO_pop(BMesh *bm) +{ + if (bm->stackdepth > 1) + bmo_flag_layer_free(bm); + + bm->stackdepth--; +} + +/* + * BMESH OPSTACK INIT OP + * + * Initializes an operator structure + * to a certain type + */ +void BMO_op_init(BMesh *bm, BMOperator *op, const char *opname) +{ + int i, opcode = bmesh_opname_to_opcode(opname); + +#ifdef DEBUG + BM_ELEM_INDEX_VALIDATE(bm, "pre bmo", opname); +#else + (void)bm; +#endif + + if (opcode == -1) { + opcode = 0; /* error!, already printed, have a better way to handle this? */ + } + + memset(op, 0, sizeof(BMOperator)); + op->type = opcode; + op->flag = opdefines[opcode]->flag; + + /* initialize the operator slot types */ + for (i = 0; opdefines[opcode]->slottypes[i].type; i++) { + op->slots[i].slottype = opdefines[opcode]->slottypes[i].type; + op->slots[i].index = i; + } + + /* callback */ + op->exec = opdefines[opcode]->exec; + + /* memarena, used for operator's slot buffers */ + op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmesh operator"); + BLI_memarena_use_calloc (op->arena); +} + +/* + * BMESH OPSTACK EXEC OP + * + * Executes a passed in operator. This handles + * the allocation and freeing of temporary flag + * layers and starting/stopping the modelling + * loop. Can be called from other operators + * exec callbacks as well. + */ +void BMO_op_exec(BMesh *bm, BMOperator *op) +{ + + BMO_push(bm, op); + + if (bm->stackdepth == 2) + bmesh_begin_edit(bm, op->flag); + op->exec(bm, op); + + if (bm->stackdepth == 2) + bmesh_end_edit(bm, op->flag); + + BMO_pop(bm); +} + +/* + * BMESH OPSTACK FINISH OP + * + * Does housekeeping chores related to finishing + * up an operator. + */ +void BMO_op_finish(BMesh *bm, BMOperator *op) +{ + BMOpSlot *slot; + int i; + + for (i = 0; opdefines[op->type]->slottypes[i].type; i++) { + slot = &op->slots[i]; + if (slot->slottype == BMO_OP_SLOT_MAPPING) { + if (slot->data.ghash) + BLI_ghash_free(slot->data.ghash, NULL, NULL); + } + } + + BLI_memarena_free(op->arena); + +#ifdef DEBUG + BM_ELEM_INDEX_VALIDATE(bm, "post bmo", opdefines[op->type]->name); +#else + (void)bm; +#endif +} + +/* + * BMESH OPSTACK HAS SLOT + * + * Returns 1 if the named slot exists on the given operator, + * otherwise returns 0. + */ +int BMO_slot_exists(BMOperator *op, const char *slotname) +{ + int slotcode = bmesh_name_to_slotcode(opdefines[op->type], slotname); + return (slotcode >= 0); +} + +/* + * BMESH OPSTACK GET SLOT + * + * Returns a pointer to the slot of + * type 'slotcode' + */ +BMOpSlot *BMO_slot_get(BMOperator *op, const char *slotname) +{ + int slotcode = bmesh_name_to_slotcode_check(opdefines[op->type], slotname); + + if (slotcode < 0) { + return &BMOpEmptySlot; + } + + return &(op->slots[slotcode]); +} + +/* + * BMESH OPSTACK COPY SLOT + * + * Copies data from one slot to another + */ +void BMO_slot_copy(BMOperator *source_op, BMOperator *dest_op, const char *src, const char *dst) +{ + BMOpSlot *source_slot = BMO_slot_get(source_op, src); + BMOpSlot *dest_slot = BMO_slot_get(dest_op, dst); + + if (source_slot == dest_slot) + return; + + if (source_slot->slottype != dest_slot->slottype) + return; + + if (dest_slot->slottype > BMO_OP_SLOT_VEC) { + if (dest_slot->slottype != BMO_OP_SLOT_MAPPING) { + /* do buffer copy */ + dest_slot->data.buf = NULL; + dest_slot->len = source_slot->len; + if (dest_slot->len) { + const int slot_alloc_size = BMO_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len; + dest_slot->data.buf = BLI_memarena_alloc(dest_op->arena, slot_alloc_size); + memcpy(dest_slot->data.buf, source_slot->data.buf, slot_alloc_size); + } + } + else { + GHashIterator it; + BMOElemMapping *srcmap, *dstmap; + + /* sanity check */ + if (!source_slot->data.ghash) return; + + if (!dest_slot->data.ghash) { + dest_slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash, + BLI_ghashutil_ptrcmp, "bmesh operator 2"); + } + + BLI_ghashIterator_init(&it, source_slot->data.ghash); + for ( ; (srcmap = BLI_ghashIterator_getValue(&it)); + BLI_ghashIterator_step(&it)) + { + dstmap = BLI_memarena_alloc(dest_op->arena, sizeof(*dstmap) + srcmap->len); + + dstmap->element = srcmap->element; + dstmap->len = srcmap->len; + memcpy(dstmap + 1, srcmap + 1, srcmap->len); + + BLI_ghash_insert(dest_slot->data.ghash, dstmap->element, dstmap); + } + } + } + else { + dest_slot->data = source_slot->data; + } +} + +/* + * BMESH OPSTACK SET XXX + * + * Sets the value of a slot depending on it's type + * + */ + +void BMO_slot_float_set(BMOperator *op, const char *slotname, const float f) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_FLT)) + return; + + slot->data.f = f; +} + +void BMO_slot_int_set(BMOperator *op, const char *slotname, const int i) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_INT)) + return; + + slot->data.i = i; +} + +/* only supports square mats */ +void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_MAT)) + return; + + slot->len = 4; + slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float) * 4 * 4); + + if (size == 4) { + memcpy(slot->data.p, mat, sizeof(float) * 4 * 4); + } + else if (size == 3) { + copy_m4_m3(slot->data.p, (float (*)[3])mat); + } + else { + fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size); + + memset(slot->data.p, 0, sizeof(float) * 4 * 4); + } +} + +void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_MAT)) + return; + + copy_m4_m4(r_mat, (float (*)[4])slot->data.p); +} + +void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_MAT)) + return; + + copy_m3_m4(r_mat, slot->data.p); +} + +void BMO_slot_ptr_set(BMOperator *op, const char *slotname, void *p) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_PNT)) + return; + + slot->data.p = p; +} + +void BMO_slot_vec_set(BMOperator *op, const char *slotname, const float vec[3]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_VEC)) + return; + + copy_v3_v3(slot->data.vec, vec); +} + + +float BMO_slot_float_get(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_FLT)) + return 0.0f; + + return slot->data.f; +} + +int BMO_slot_int_get(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_INT)) + return 0; + + return slot->data.i; +} + + +void *BMO_slot_ptr_get(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_PNT)) + return NULL; + + return slot->data.p; +} + +void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3]) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + if (!(slot->slottype == BMO_OP_SLOT_VEC)) + return; + + copy_v3_v3(r_vec, slot->data.vec); +} + +/* + * BMO_COUNTFLAG + * + * Counts the number of elements of a certain type that + * have a specific flag set. + * + */ + +int BMO_mesh_flag_count(BMesh *bm, const short oflag, const char htype) +{ + BMIter elements; + int count = 0; + BMElemF *ele_f; + + if (htype & BM_VERT) { + for (ele_f = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, ele_f, oflag)) + count++; + } + } + if (htype & BM_EDGE) { + for (ele_f = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, ele_f, oflag)) + count++; + } + } + if (htype & BM_FACE) { + for (ele_f = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, ele_f, oflag)) + count++; + } + } + + return count; +} + +void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *UNUSED(op), const char htype, const short oflag) +{ + const char iter_types[3] = {BM_VERTS_OF_MESH, + BM_EDGES_OF_MESH, + BM_FACES_OF_MESH}; + + const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE}; + + BMIter iter; + BMElemF *ele; + int i; + + for (i = 0; i < 3; i++) { + if (htype & flag_types[i]) { + BM_ITER(ele, &iter, bm, iter_types[i], NULL) { + BMO_elem_flag_disable(bm, ele, oflag); + } + } + } +} + +int BMO_slot_buf_count(struct BMesh *UNUSED(bm), struct BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /* check if its actually a buffer */ + if (!(slot->slottype > BMO_OP_SLOT_VEC)) + return 0; + + return slot->len; +} + +int BMO_slot_map_count(BMesh *UNUSED(bm), BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /* check if its actually a buffer */ + if (!(slot->slottype == BMO_OP_SLOT_MAPPING)) + return 0; + + return slot->data.ghash ? BLI_ghash_size(slot->data.ghash) : 0; +} + +#if 0 +void *BMO_Grow_Array(BMesh *bm, BMOperator *op, int slotcode, int totadd) +{ + BMOpSlot *slot = &op->slots[slotcode]; + void *tmp; + ssize_t allocsize; + + /* check if its actually a buffer */ + if (!(slot->slottype > BMO_OP_SLOT_VEC)) + return NULL; + + if (slot->flag & BMOS_DYNAMIC_ARRAY) { + if (slot->len >= slot->size) { + slot->size = (slot->size + 1 + totadd) * 2; + + allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->size; + + tmp = slot->data.buf; + slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array"); + memcpy(slot->data.buf, tmp, allocsize); + MEM_freeN(tmp); + } + + slot->len += totadd; + } + else { + slot->flag |= BMOS_DYNAMIC_ARRAY; + slot->len += totadd; + slot->size = slot->len + 2; + + allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->len; + + tmp = slot->data.buf; + slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array"); + memcpy(slot->data.buf, tmp, allocsize); + } + + return slot->data.buf; +} +#endif + +void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op, + const char *slotname, const short oflag) +{ + GHashIterator it; + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMElemF *ele_f; + + /* sanity check */ + if (slot->slottype != BMO_OP_SLOT_MAPPING) return; + if (!slot->data.ghash) return; + + BLI_ghashIterator_init(&it, slot->data.ghash); + for ( ; (ele_f = BLI_ghashIterator_getKey(&it)); BLI_ghashIterator_step(&it)) { + BMO_elem_flag_enable(bm, ele_f, oflag); + } +} + +static void *bmo_slot_buffer_alloc(BMOperator *op, const char *slotname, int len) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + /* check if its actually a buffer */ + if (!(slot->slottype > BMO_OP_SLOT_VEC)) + return NULL; + + slot->len = len; + if (len) + slot->data.buf = BLI_memarena_alloc(op->arena, BMO_OPSLOT_TYPEINFO[slot->slottype] * len); + return slot->data.buf; +} + +/* + * BMO_ALL_TO_SLOT + * + * Copies all elements of a certain type into an operator slot. + * + */ + +static void BMO_slot_from_all(BMesh *bm, BMOperator *op, const char *slotname, const char htype) +{ + BMIter elements; + BMHeader *e; + BMOpSlot *output = BMO_slot_get(op, slotname); + int totelement = 0, i = 0; + + if (htype & BM_VERT) totelement += bm->totvert; + if (htype & BM_EDGE) totelement += bm->totedge; + if (htype & BM_FACE) totelement += bm->totface; + + if (totelement) { + bmo_slot_buffer_alloc(op, slotname, totelement); + + if (htype & BM_VERT) { + for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + + if (htype & BM_EDGE) { + for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + + if (htype & BM_FACE) { + for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } +} + +/* + * BMO_HEADERFLAG_TO_SLOT + * + * Copies elements of a certain type, which have a certain header flag set + * into a slot for an operator. + */ + +void BMO_slot_from_hflag(BMesh *bm, BMOperator *op, const char *slotname, + const char hflag, const char htype) +{ + BMIter elements; + BMHeader *e; + BMOpSlot *output = BMO_slot_get(op, slotname); + int totelement = 0, i = 0; + + totelement = BM_mesh_count_flag(bm, htype, hflag, 1); + + if (totelement) { + bmo_slot_buffer_alloc(op, slotname, totelement); + + if (htype & BM_VERT) { + for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } + + if (htype & BM_EDGE) { + for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } + + if (htype & BM_FACE) { + for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) { + ((BMHeader **)output->data.p)[i] = e; + i++; + } + } + } + } + else { + output->len = 0; + } +} + +/* + * BMO_FLAG_TO_SLOT + * + * Copies elements of a certain type, which have a certain flag set + * into an output slot for an operator. + */ +void BMO_slot_from_flag(BMesh *bm, BMOperator *op, const char *slotname, + const short oflag, const char htype) +{ + BMIter elements; + BMHeader *ele; + BMOpSlot *output = BMO_slot_get(op, slotname); + int totelement = BMO_mesh_flag_count(bm, oflag, htype), i = 0; + + if (totelement) { + bmo_slot_buffer_alloc(op, slotname, totelement); + + if (htype & BM_VERT) { + for (ele = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) { + ((BMHeader **)output->data.p)[i] = ele; + i++; + } + } + } + + if (htype & BM_EDGE) { + for (ele = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) { + ((BMHeader **)output->data.p)[i] = ele; + i++; + } + } + } + + if (htype & BM_FACE) { + for (ele = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) { + if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) { + ((BMHeader **)output->data.p)[i] = ele; + i++; + } + } + } + } + else { + output->len = 0; + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Header Flags elements in a slots buffer, automatically + * using the selection API where appropriate. + */ +void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOperator *op, const char *slotname, + const char hflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, data[i], TRUE); + } + BM_elem_flag_enable(data[i], hflag); + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Removes flags from elements in a slots buffer, automatically + * using the selection API where appropriate. + */ +void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOperator *op, const char *slotname, + const char hflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + if (hflag & BM_ELEM_SELECT) { + BM_elem_select_set(bm, data[i], FALSE); + } + + BM_elem_flag_disable(data[i], hflag); + } +} +int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag) +{ + int count = 0; + + if (v->e) { + BMEdge *curedge; + const int len = bmesh_disk_count(v); + int i; + + for (i = 0, curedge = v->e; i < len; i++) { + if (BMO_elem_flag_test(bm, curedge, oflag)) + count++; + curedge = bmesh_disk_nextedge(curedge, v); + } + } + + return count; +} + +/* + * + * BMO_FLAG_BUFFER + * + * Flags elements in a slots buffer + */ +void BMO_slot_buffer_flag_enable(BMesh *bm, BMOperator *op, const char *slotname, + const short oflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + BMO_elem_flag_enable(bm, (BMElemF *)data[i], oflag); + } +} + +/* + * + * BMO_FLAG_BUFFER + * + * Removes flags from elements in a slots buffer + */ +void BMO_slot_buffer_flag_disable(BMesh *bm, BMOperator *op, const char *slotname, + const short oflag, const char htype) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + BMHeader **data = slot->data.p; + int i; + + for (i = 0; i < slot->len; i++) { + if (!(htype & data[i]->htype)) + continue; + + BMO_elem_flag_disable(bm, (BMElemF *)data[i], oflag); + } +} + + +/* + * + * ALLOC/FREE FLAG LAYER + * + * Used by operator stack to free/allocate + * private flag data. This is allocated + * using a mempool so the allocation/frees + * should be quite fast. + * + * BMESH_TODO: + * Investigate not freeing flag layers until + * all operators have been executed. This would + * save a lot of realloc potentially. + */ +static void bmo_flag_layer_alloc(BMesh *bm) +{ + BMElemF *ele; + /* set the index values since we are looping over all data anyway, + * may save time later on */ + int i; + + BMIter iter; + BLI_mempool *oldpool = bm->toolflagpool; /* old flag pool */ + BLI_mempool *newpool; + void *oldflags; + + /* store memcpy size for reuse */ + const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); + + bm->totflags++; + + /* allocate new flag poo */ + bm->toolflagpool = newpool = BLI_mempool_create(sizeof(BMFlagLayer)*bm->totflags, 512, 512, FALSE, FALSE); + + /* now go through and memcpy all the flags. Loops don't get a flag layer at this time.. */ + for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, old_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, old_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, old_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + + bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE); + + BLI_mempool_destroy(oldpool); +} + +static void bmo_flag_layer_free(BMesh *bm) +{ + BMElemF *ele; + /* set the index values since we are looping over all data anyway, + * may save time later on */ + int i; + + BMIter iter; + BLI_mempool *oldpool = bm->toolflagpool; + BLI_mempool *newpool; + void *oldflags; + + /* store memcpy size for reuse */ + const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer)); + + /* de-increment the totflags first.. */ + bm->totflags--; + /* allocate new flag poo */ + bm->toolflagpool = newpool = BLI_mempool_create(new_totflags_size, 512, 512, TRUE, FALSE); + + /* now go through and memcpy all the flag */ + for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, new_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, new_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + oldflags = ele->oflags; + ele->oflags = BLI_mempool_calloc(newpool); + memcpy(ele->oflags, oldflags, new_totflags_size); + BM_elem_index_set(ele, i); /* set_inline */ + } + + bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE); + + BLI_mempool_destroy(oldpool); +} + +static void bmo_flag_layer_clear(BMesh *bm) +{ + BMElemF *ele; + /* set the index values since we are looping over all data anyway, + * may save time later on */ + int i; + + BMIter iter; + const int totflags_offset = bm->totflags - 1; + + /* now go through and memcpy all the flag */ + for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer)); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer)); + BM_elem_index_set(ele, i); /* set_inline */ + } + for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) { + memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer)); + BM_elem_index_set(ele, i); /* set_inline */ + } + + bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE); +} + +void *BMO_slot_elem_first(BMOperator *op, const char *slotname) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + if (slot->slottype != BMO_OP_SLOT_ELEMENT_BUF) + return NULL; + + return slot->data.buf ? *(void **)slot->data.buf : NULL; +} + +void *BMO_iter_new(BMOIter *iter, BMesh *UNUSED(bm), BMOperator *op, + const char *slotname, const char restrictmask) +{ + BMOpSlot *slot = BMO_slot_get(op, slotname); + + memset(iter, 0, sizeof(BMOIter)); + + iter->slot = slot; + iter->cur = 0; + iter->restrictmask = restrictmask; + + if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) { + if (iter->slot->data.ghash) { + BLI_ghashIterator_init(&iter->giter, slot->data.ghash); + } + else { + return NULL; + } + } + + return BMO_iter_step(iter); +} + +void *BMO_iter_step(BMOIter *iter) +{ + if (iter->slot->slottype == BMO_OP_SLOT_ELEMENT_BUF) { + BMHeader *h; + + if (iter->cur >= iter->slot->len) { + return NULL; + } + + h = ((void **)iter->slot->data.buf)[iter->cur++]; + while (!(iter->restrictmask & h->htype)) { + if (iter->cur >= iter->slot->len) { + return NULL; + } + + h = ((void **)iter->slot->data.buf)[iter->cur++]; + } + + return h; + } + else if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) { + struct BMOElemMapping *map; + void *ret = BLI_ghashIterator_getKey(&iter->giter); + map = BLI_ghashIterator_getValue(&iter->giter); + + iter->val = map + 1; + + BLI_ghashIterator_step(&iter->giter); + + return ret; + } + + return NULL; +} + +/* used for iterating over mapping */ +void *BMO_iter_map_value(BMOIter *iter) +{ + return iter->val; +} + +void *BMO_iter_map_value_p(BMOIter *iter) +{ + return *((void **)iter->val); +} + +float BMO_iter_map_value_f(BMOIter *iter) +{ + return *((float *)iter->val); +} + +/* error syste */ +typedef struct BMOpError { + struct BMOpError *next, *prev; + int errorcode; + BMOperator *op; + const char *msg; +} BMOpError; + +void BMO_error_clear(BMesh *bm) +{ + while (BMO_error_pop(bm, NULL, NULL)); +} + +void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg) +{ + BMOpError *err = MEM_callocN(sizeof(BMOpError), "bmop_error"); + + err->errorcode = errcode; + if (!msg) msg = bmo_error_messages[errcode]; + err->msg = msg; + err->op = owner; + + BLI_addhead(&bm->errorstack, err); +} + +int BMO_error_occurred(BMesh *bm) +{ + return bm->errorstack.first != NULL; +} + +/* returns error code or 0 if no erro */ +int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op) +{ + BMOpError *err = bm->errorstack.first; + if (!err) { + return 0; + } + + if (msg) *msg = err->msg; + if (op) *op = err->op; + + return err->errorcode; +} + +int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op) +{ + int errorcode = BMO_error_get(bm, msg, op); + + if (errorcode) { + BMOpError *err = bm->errorstack.first; + + BLI_remlink(&bm->errorstack, bm->errorstack.first); + MEM_freeN(err); + } + + return errorcode; +} + +/* example: + * BMO_CallOp(bm, "del %d %hv", DEL_ONLYFACES, BM_ELEM_SELECT); + * + * d - int + * i - int + * f - float + * hv - header flagged verts + * he - header flagged edges + * hf - header flagged faces + * fv - flagged verts + * fe - flagged edges + * ff - flagged faces + */ + +#define NEXT_CHAR(fmt) ((fmt)[0] != 0 ? (fmt)[1] : 0) + +static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name) +{ + int i; + + for (i = 0; def->slottypes[i].type; i++) { + if (!strncmp(name, def->slottypes[i].name, MAX_SLOTNAME)) { + return i; + } + } + + return -1; +} + +static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name) +{ + int i = bmesh_name_to_slotcode(def, name); + if (i < 0) { + fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, name); + } + + return i; +} + +static int bmesh_opname_to_opcode(const char *opname) +{ + int i; + + for (i = 0; i < bmesh_total_ops; i++) { + if (!strcmp(opname, opdefines[i]->name)) { + return i; + } + } + + fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, opname); + return -1; +} + +int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *_fmt, va_list vlist) +{ + BMOpDefine *def; + char *opname, *ofmt, *fmt; + char slotname[64] = {0}; + int i /*, n = strlen(fmt) */, stop /*, slotcode = -1 */, ret, type, state; + int noslot = 0; + + + /* basic useful info to help find where bmop formatting strings fail */ + int lineno = -1; +# define GOTO_ERROR { lineno = __LINE__; goto error; } + + + /* we muck around in here, so dup i */ + fmt = ofmt = BLI_strdup(_fmt); + + /* find operator nam */ + i = strcspn(fmt, " \t"); + + opname = fmt; + if (!opname[i]) noslot = 1; + opname[i] = '\0'; + + fmt += i + (noslot ? 0 : 1); + + i = bmesh_opname_to_opcode(opname); + + if (i == -1) { + MEM_freeN(ofmt); + return FALSE; + } + + BMO_op_init(bm, op, opname); + def = opdefines[i]; + + i = 0; + state = 1; /* 0: not inside slotcode name, 1: inside slotcode name */ + + while (*fmt) { + if (state) { + /* jump past leading whitespac */ + i = strspn(fmt, " \t"); + fmt += i; + + /* ignore trailing whitespac */ + if (!fmt[i]) + break; + + /* find end of slot name. currently this is + * a little flexible, allowing "slot=%f", + * "slot %f", "slot%f", and "slot\t%f". */ + i = strcspn(fmt, "= \t%"); + if (!fmt[i]) GOTO_ERROR; + + fmt[i] = 0; + + if (bmesh_name_to_slotcode_check(def, fmt) < 0) GOTO_ERROR; + + BLI_strncpy(slotname, fmt, sizeof(slotname)); + + state = 0; + fmt += i; + } + else { + switch (*fmt) { + case ' ': + case '\t': + case '=': + case '%': + break; + case 'm': { + int size, c; + + c = NEXT_CHAR(fmt); + fmt++; + + if (c == '3') size = 3; + else if (c == '4') size = 4; + else GOTO_ERROR; + + BMO_slot_mat_set(op, slotname, va_arg(vlist, void *), size); + state = 1; + break; + } + case 'v': { + BMO_slot_vec_set(op, slotname, va_arg(vlist, float *)); + state = 1; + break; + } + case 'e': { + BMHeader *ele = va_arg(vlist, void *); + BMOpSlot *slot = BMO_slot_get(op, slotname); + + slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(void *) * 4); + slot->len = 1; + *((void **)slot->data.buf) = ele; + + state = 1; + break; + } + case 's': { + BMOperator *op2 = va_arg(vlist, void *); + const char *slotname2 = va_arg(vlist, char *); + + BMO_slot_copy(op2, op, slotname2, slotname); + state = 1; + break; + } + case 'i': + case 'd': + BMO_slot_int_set(op, slotname, va_arg(vlist, int)); + state = 1; + break; + case 'p': + BMO_slot_ptr_set(op, slotname, va_arg(vlist, void *)); + state = 1; + break; + case 'f': + case 'h': + case 'a': + type = *fmt; + + if (NEXT_CHAR(fmt) == ' ' || NEXT_CHAR(fmt) == '\t' || NEXT_CHAR(fmt) == '\0') { + BMO_slot_float_set(op, slotname, va_arg(vlist, double)); + } + else { + ret = 0; + stop = 0; + while (1) { + switch (NEXT_CHAR(fmt)) { + case 'f': ret |= BM_FACE; break; + case 'e': ret |= BM_EDGE; break; + case 'v': ret |= BM_VERT; break; + default: + stop = 1; + break; + } + if (stop) { + break; + } + + fmt++; + } + + if (type == 'h') { + BMO_slot_from_hflag(bm, op, slotname, va_arg(vlist, int), ret); + } + else if (type == 'a') { + BMO_slot_from_all(bm, op, slotname, ret); + } + else { + BMO_slot_from_flag(bm, op, slotname, va_arg(vlist, int), ret); + } + } + + state = 1; + break; + default: + fprintf(stderr, + "%s: unrecognized bmop format char: %c, %d in '%s'\n", + __func__, *fmt, (int)(fmt - ofmt), ofmt); + break; + } + } + fmt++; + } + + MEM_freeN(ofmt); + return TRUE; +error: + + /* non urgent todo - explain exactly what is failing */ + fprintf(stderr, + "%s: error parsing formatting string, %d in '%s'\n see - %s:%d\n", + __func__, (int)(fmt - ofmt), _fmt, __FILE__, lineno); + MEM_freeN(ofmt); + + BMO_op_finish(bm, op); + return FALSE; + +#undef GOTO_ERROR + +} + + +int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...) +{ + va_list list; + + va_start(list, fmt); + if (!BMO_op_vinitf(bm, op, fmt, list)) { + printf("%s: failed\n", __func__); + va_end(list); + return FALSE; + } + va_end(list); + + return TRUE; +} + +int BMO_op_callf(BMesh *bm, const char *fmt, ...) +{ + va_list list; + BMOperator op; + + va_start(list, fmt); + if (!BMO_op_vinitf(bm, &op, fmt, list)) { + printf("%s: failed, format is:\n \"%s\"\n", __func__, fmt); + va_end(list); + return FALSE; + } + + BMO_op_exec(bm, &op); + BMO_op_finish(bm, &op); + + va_end(list); + return TRUE; +} diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h new file mode 100644 index 00000000000..4cc338bfdd0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -0,0 +1,106 @@ +/* + * ***** 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 ***** + */ + +#ifndef __BMESH_OPERATORS_PRIVATE_H__ +#define __BMESH_OPERATORS_PRIVATE_H__ + +/** \file blender/bmesh/intern/bmesh_operators_private.h + * \ingroup bmesh + */ + +struct BMesh; +struct BMOperator; + +void BMO_push(BMesh *bm, BMOperator *op); +void BMO_pop(BMesh *bm); + +void splitop_exec(BMesh *bm, BMOperator *op); +void spinop_exec(BMesh *bm, BMOperator *op); +void dupeop_exec(BMesh *bm, BMOperator *op); +void delop_exec(BMesh *bm, BMOperator *op); +void esubdivide_exec(BMesh *bmesh, BMOperator *op); +void edit2bmesh_exec(BMesh *bmesh, BMOperator *op); +void bmesh2edit_exec(BMesh *bmesh, BMOperator *op); +void triangulate_exec(BMesh *bmesh, BMOperator *op); +void dissolvefaces_exec(BMesh *bmesh, BMOperator *op); +void dissolveverts_exec(BMesh *bmesh, BMOperator *op); +void dissolvelimit_exec(BMesh *bmesh, BMOperator *op); +void bmesh_make_fgons_exec(BMesh *bmesh, BMOperator *op); +void extrude_edge_context_exec(BMesh *bm, BMOperator *op); +void connectverts_exec(BMesh *bm, BMOperator *op); +void makeprim_exec(BMesh *bm, BMOperator *op); +void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op); +void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op); +void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op); +void bmesh_translate_exec(BMesh *bm, BMOperator *op); +void bmesh_transform_exec(BMesh *bm, BMOperator *op); +void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op); +void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op); +void bmesh_rotate_exec(BMesh *bm, BMOperator *op); +void bmesh_makevert_exec(BMesh *bm, BMOperator *op); +void dissolveedges_exec(BMesh *bm, BMOperator *op); +void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op); +void bmesh_weldverts_exec(BMesh *bm, BMOperator *op); +void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op); +void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op); +void bmesh_mirror_exec(BMesh *bm, BMOperator *op); +void esplit_exec(BMesh *bm, BMOperator *op); +void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op); +void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op); +void bmesh_regionextend_exec(BMesh *bm, BMOperator *op); +void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op); +void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op); +void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op); +void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op); +void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op); +void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op); +void bmesh_collapse_exec(BMesh *bm, BMOperator *op); +void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op); +void bmesh_similaredges_exec(BMesh *bm, BMOperator *op); +void bmesh_similarverts_exec(BMesh *bm, BMOperator *op); +void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op); +void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op); +void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op); +void object_load_bmesh_exec(BMesh *bm, BMOperator *op); +void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op); +void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op); +void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op); +void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op); +void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op); +void bmesh_scale_exec(BMesh *bm, BMOperator *op); +void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op); +void bmesh_automerge_exec(BMesh *bm, BMOperator *op); +void bmesh_create_cone_exec(BMesh *bm, BMOperator *op); +void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op); +void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op); +void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op); +void bmesh_create_grid_exec(BMesh *bm, BMOperator *op); +void bmesh_create_cube_exec(BMesh *bm, BMOperator *op); +void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op); +void bmesh_bevel_exec(BMesh *bm, BMOperator *op); +void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op); +void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op); +void bmesh_create_circle_exec(BMesh *bm, BMOperator *op); +void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op); +void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op); + +#endif /* __BMESH_OPERATORS_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c new file mode 100644 index 00000000000..07945466a0c --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -0,0 +1,1069 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_polygon.c + * \ingroup bmesh + * + * This file contains code for dealing + * with polygons (normal/area calculation, + * tesselation, etc) + * + * BMESH_TODO: + * - Add in Tesselator frontend that creates + * BMTriangles from copied faces + * + * - Add in Function that checks for and flags + * degenerate faces. + */ + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "MEM_guardedalloc.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* + * TEST EDGE SIDE and POINT IN TRIANGLE + * + * Point in triangle tests stolen from scanfill.c. + * Used for tesselator + * + */ + +static short testedgeside(const double v1[2], const double v2[2], const double v3[2]) +{ + /* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */ + double inp; + + //inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]); + inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]); + + if (inp < 0.0) { + return FALSE; + } + else if (inp == 0) { + if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE; + if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE; + } + return TRUE; +} + +static short testedgesidef(const float v1[2], const float v2[2], const float v3[2]) +{ + /* is v3 to the right of v1 - v2 ? With exception: v3 == v1 || v3 == v2 */ + double inp; + + //inp = (v2[cox] - v1[cox]) * (v1[coy] - v3[coy]) + (v1[coy] - v2[coy]) * (v1[cox] - v3[cox]); + inp = (v2[0] - v1[0]) * (v1[1] - v3[1]) + (v1[1] - v2[1]) * (v1[0] - v3[0]); + + if (inp < 0.0) { + return FALSE; + } + else if (inp == 0) { + if (v1[0] == v3[0] && v1[1] == v3[1]) return FALSE; + if (v2[0] == v3[0] && v2[1] == v3[1]) return FALSE; + } + return TRUE; +} + +static int point_in_triangle(const double v1[2], const double v2[2], const double v3[2], const double pt[2]) +{ + if (testedgeside(v1, v2, pt) && testedgeside(v2, v3, pt) && testedgeside(v3, v1, pt)) { + return TRUE; + } + return FALSE; +} + +/* + * COMPUTE POLY NORMAL + * + * Computes the normal of a planar + * polygon See Graphics Gems for + * computing newell normal. + * + */ + +static void compute_poly_normal(float normal[3], float (*verts)[3], int nverts) +{ + + float u[3], v[3], w[3]; /*, *w, v1[3], v2[3]; */ + float n[3] = {0.0f, 0.0f, 0.0f} /*, l, v1[3], v2[3] */; + /* double l2; */ + int i /*, s = 0 */; + + /* this fixes some weird numerical erro */ + add_v3_fl(verts[0], 0.0001f); + + for (i = 0; i < nverts; i++) { + copy_v3_v3(u, verts[i]); + copy_v3_v3(v, verts[(i + 1) % nverts]); + copy_v3_v3(w, verts[(i + 2) % nverts]); + +#if 0 + sub_v3_v3v3(v1, w, v); + sub_v3_v3v3(v2, v, u); + normalize_v3(v1); + normalize_v3(v2); + + l = dot_v3v3(v1, v2); + if (fabsf(l - 1.0) < 0.1f) { + continue; + } +#endif + /* newell's method + + so thats?: + (a[1] - b[1]) * (a[2] + b[2]); + a[1]*b[2] - b[1]*a[2] - b[1]*b[2] + a[1]*a[2] + + odd. half of that is the cross product. . .what's the + other half? + + also could be like a[1]*(b[2] + a[2]) - b[1]*(a[2] - b[2]) + */ + + n[0] += (u[1] - v[1]) * (u[2] + v[2]); + n[1] += (u[2] - v[2]) * (u[0] + v[0]); + n[2] += (u[0] - v[0]) * (u[1] + v[1]); + } + + if (normalize_v3_v3(normal, n) == 0.0f) { + normal[2] = 1.0f; /* other axis set to 0.0 */ + } + +#if 0 + l = len_v3(n); + /* fast square root, newton/babylonian method: + l2 = l * 0.1; + + l2 = (l / l2 + l2) * 0.5; + l2 = (l / l2 + l2) * 0.5; + l2 = (l / l2 + l2) * 0.5; + */ + + if (l == 0.0) { + normal[0] = 0.0f; + normal[1] = 0.0f; + normal[2] = 1.0f; + + return; + } + else { + l = 1.0f / l; + } + + mul_v3_fl(n, l); + + copy_v3_v3(normal, n); +#endif +} + +/* + * COMPUTE POLY CENTER + * + * Computes the centroid and + * area of a polygon in the X/Y + * plane. + * + */ + +static int compute_poly_center(float center[3], float *r_area, float (*verts)[3], int nverts) +{ + int i, j; + float atmp = 0.0f, xtmp = 0.0f, ytmp = 0.0f, ai; + + zero_v3(center); + + if (nverts < 3) + return FALSE; + + i = nverts - 1; + j = 0; + + while (j < nverts) { + ai = verts[i][0] * verts[j][1] - verts[j][0] * verts[i][1]; + atmp += ai; + xtmp += (verts[j][0] + verts[i][0]) * ai; + ytmp += (verts[j][1] + verts[i][1]) * ai; + i = j; + j += 1; + } + + if (r_area) + *r_area = atmp / 2.0f; + + if (atmp != 0) { + center[0] = xtmp / (3.0f * atmp); + center[1] = xtmp / (3.0f * atmp); + return TRUE; + } + return FALSE; +} + +float BM_face_area_calc(BMesh *bm, BMFace *f) +{ + BMLoop *l; + BMIter iter; + float (*verts)[3]; + float center[3]; + float area = 0.0f; + int i; + + BLI_array_fixedstack_declare(verts, BM_NGON_STACK_SIZE, f->len, __func__); + + i = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + copy_v3_v3(verts[i], l->v->co); + i++; + } + + compute_poly_center(center, &area, verts, f->len); + + BLI_array_fixedstack_free(verts); + + return area; +} + +/* + * computes center of face in 3d. uses center of bounding box. + */ +void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float r_cent[3]) +{ + BMIter iter; + BMLoop *l; + float min[3], max[3]; + int i; + + INIT_MINMAX(min, max); + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for (i = 0; l; l = BM_iter_step(&iter), i++) { + DO_MINMAX(l->v->co, min, max); + } + + mid_v3_v3v3(r_cent, min, max); +} + +void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float r_cent[3]) +{ + BMIter iter; + BMLoop *l; + int i; + + zero_v3(r_cent); + + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for (i = 0; l; l = BM_iter_step(&iter), i++) { + add_v3_v3(r_cent, l->v->co); + } + + if (f->len) mul_v3_fl(r_cent, 1.0f / (float)f->len); +} + +/* + * COMPUTE POLY PLANE + * + * Projects a set polygon's vertices to + * a plane defined by the average + * of its edges cross products + * + */ + +void compute_poly_plane(float (*verts)[3], int nverts) +{ + + float avgc[3], norm[3], mag, avgn[3]; + float *v1, *v2, *v3; + int i; + + if (nverts < 3) + return; + + zero_v3(avgn); + zero_v3(avgc); + + for (i = 0; i < nverts; i++) { + v1 = verts[i]; + v2 = verts[(i + 1) % nverts]; + v3 = verts[(i + 2) % nverts]; + normal_tri_v3(norm, v1, v2, v3); + + add_v3_v3(avgn, norm); + } + + /* what was this bit for */ + if (is_zero_v3(avgn)) { + avgn[0] = 0.0f; + avgn[1] = 0.0f; + avgn[2] = 1.0f; + } + else { + /* XXX, why is this being divided and _then_ normalized + * division could be removed? - campbell */ + avgn[0] /= nverts; + avgn[1] /= nverts; + avgn[2] /= nverts; + normalize_v3(avgn); + } + + for (i = 0; i < nverts; i++) { + v1 = verts[i]; + mag = dot_v3v3(v1, avgn); + madd_v3_v3fl(v1, avgn, -mag); + } +} + +/* + * BM LEGAL EDGES + * + * takes in a face and a list of edges, and sets to NULL any edge in + * the list that bridges a concave region of the face or intersects + * any of the faces's edges. + */ +#if 0 /* needs BLI math double versions of these functions */ +static void shrink_edged(double *v1, double *v2, double fac) +{ + double mid[3]; + + mid_v3_v3v3(mid, v1, v2); + + sub_v3_v3v3(v1, v1, mid); + sub_v3_v3v3(v2, v2, mid); + + mul_v3_fl(v1, fac); + mul_v3_fl(v2, fac); + + add_v3_v3v3(v1, v1, mid); + add_v3_v3v3(v2, v2, mid); +} +#endif + +static void shrink_edgef(float v1[3], float v2[3], const float fac) +{ + float mid[3]; + + mid_v3_v3v3(mid, v1, v2); + + sub_v3_v3v3(v1, v1, mid); + sub_v3_v3v3(v2, v2, mid); + + mul_v3_fl(v1, fac); + mul_v3_fl(v2, fac); + + add_v3_v3v3(v1, v1, mid); + add_v3_v3v3(v2, v2, mid); +} + + +/* + * POLY ROTATE PLANE + * + * Rotates a polygon so that it's + * normal is pointing towards the mesh Z axis + * + */ + +void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts) +{ + + float up[3] = {0.0f, 0.0f, 1.0f}, axis[3], q[4]; + float mat[3][3]; + double angle; + int i; + + cross_v3_v3v3(axis, normal, up); + + angle = saacos(dot_v3v3(normal, up)); + + if (angle == 0.0) return; + + axis_angle_to_quat(q, axis, (float)angle); + quat_to_mat3(mat, q); + + for (i = 0; i < nverts; i++) + mul_m3_v3(mat, verts[i]); +} + +/* + * BMESH UPDATE FACE NORMAL + * + * Updates the stored normal for the + * given face. Requires that a buffer + * of sufficient length to store projected + * coordinates for all of the face's vertices + * is passed in as well. + * + */ + +void BM_face_normal_update(BMesh *bm, BMFace *f) +{ + if (f->len >= 3) { + float (*proj)[3]; + + BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__); + + bmesh_update_face_normal(bm, f, f->no, proj); + + BLI_array_fixedstack_free(proj); + } +} +/* same as BM_face_normal_update but takes vertex coords */ +void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3]) +{ + if (f->len >= 3) { + float (*proj)[3]; + + BLI_array_fixedstack_declare(proj, BM_NGON_STACK_SIZE, f->len, __func__); + + bmesh_update_face_normal_vertex_cos(bm, f, no, proj, vertexCos); + + BLI_array_fixedstack_free(proj); + } +} + +void BM_edge_normals_update(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMFace *f; + + f = BM_iter_new(&iter, bm, BM_FACES_OF_EDGE, e); + for ( ; f; f = BM_iter_step(&iter)) { + BM_face_normal_update(bm, f); + } + + BM_vert_normal_update(bm, e->v1); + BM_vert_normal_update(bm, e->v2); +} + +void BM_vert_normal_update(BMesh *bm, BMVert *v) +{ + /* TODO, we can normalize each edge only once, then compare with previous edge */ + + BMIter eiter, liter; + BMEdge *e; + BMLoop *l; + float vec1[3], vec2[3], fac; + int len = 0; + + zero_v3(v->no); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + if (l->v == v) { + /* Same calculation used in BM_mesh_normals_update */ + sub_v3_v3v3(vec1, l->v->co, l->prev->v->co); + sub_v3_v3v3(vec2, l->next->v->co, l->v->co); + normalize_v3(vec1); + normalize_v3(vec2); + + fac = saacos(-dot_v3v3(vec1, vec2)); + + madd_v3_v3fl(v->no, l->f->no, fac); + + len++; + } + } + } + + if (len) { + normalize_v3(v->no); + } +} + +void BM_vert_normal_update_all(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMFace *f; + int len = 0; + + f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&iter), len++) { + BM_face_normal_update(bm, f); + } + + BM_vert_normal_update(bm, v); +} + +void bmesh_update_face_normal(BMesh *bm, BMFace *f, float no[3], + float (*projectverts)[3]) +{ + BMLoop *l; + + /* common cases first */ + switch (f->len) { + case 4: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l = l->next)->v; + BMVert *v4 = (l->next)->v; + normal_quad_v3(no, v1->co, v2->co, v3->co, v4->co); + break; + } + case 3: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l->next)->v; + normal_tri_v3(no, v1->co, v2->co, v3->co); + break; + } + case 0: + { + zero_v3(no); + break; + } + default: + { + BMIter iter; + int i = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + copy_v3_v3(projectverts[i], l->v->co); + i += 1; + } + compute_poly_normal(no, projectverts, f->len); + break; + } + } +} +/* exact same as 'bmesh_update_face_normal' but accepts vertex coords */ +void bmesh_update_face_normal_vertex_cos(BMesh *bm, BMFace *f, float no[3], + float (*projectverts)[3], float (*vertexCos)[3]) +{ + BMLoop *l; + + /* must have valid index data */ + BLI_assert((bm->elem_index_dirty & BM_VERT) == 0); + + /* common cases first */ + switch (f->len) { + case 4: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l = l->next)->v; + BMVert *v4 = (l->next)->v; + normal_quad_v3(no, + vertexCos[BM_elem_index_get(v1)], + vertexCos[BM_elem_index_get(v2)], + vertexCos[BM_elem_index_get(v3)], + vertexCos[BM_elem_index_get(v4)]); + break; + } + case 3: + { + BMVert *v1 = (l = BM_FACE_FIRST_LOOP(f))->v; + BMVert *v2 = (l = l->next)->v; + BMVert *v3 = (l->next)->v; + normal_tri_v3(no, + vertexCos[BM_elem_index_get(v1)], + vertexCos[BM_elem_index_get(v2)], + vertexCos[BM_elem_index_get(v3)]); + break; + } + case 0: + { + zero_v3(no); + break; + } + default: + { + BMIter iter; + int i = 0; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + copy_v3_v3(projectverts[i], vertexCos[BM_elem_index_get(l->v)]); + i += 1; + } + compute_poly_normal(no, projectverts, f->len); + break; + } + } +} + +/* + * BMESH FLIP NORMAL + * + * Reverses the winding of a face. + * Note that this updates the calculated + * normal. + */ +void BM_face_normal_flip(BMesh *bm, BMFace *f) +{ + bmesh_loop_reverse(bm, f); + negate_v3(f->no); +} + +/* detects if two line segments cross each other (intersects). + * note, there could be more winding cases then there needs to be. */ +static int UNUSED_FUNCTION(linecrosses)(const double v1[2], const double v2[2], const double v3[2], const double v4[2]) +{ + int w1, w2, w3, w4, w5; + + /* w1 = winding(v1, v3, v4); + w2 = winding(v2, v3, v4); + w3 = winding(v3, v1, v2); + w4 = winding(v4, v1, v2); + + return (w1 == w2) && (w3 == w4); */ + + w1 = testedgeside(v1, v3, v2); + w2 = testedgeside(v2, v4, v1); + w3 = !testedgeside(v1, v2, v3); + w4 = testedgeside(v3, v2, v4); + w5 = !testedgeside(v3, v1, v4); + return w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5; +} + +/* detects if two line segments cross each other (intersects). + * note, there could be more winding cases then there needs to be. */ +static int linecrossesf(const float v1[2], const float v2[2], const float v3[2], const float v4[2]) +{ + int w1, w2, w3, w4, w5 /*, re */; + float mv1[2], mv2[2], mv3[2], mv4[2]; + + /* now test windin */ + w1 = testedgesidef(v1, v3, v2); + w2 = testedgesidef(v2, v4, v1); + w3 = !testedgesidef(v1, v2, v3); + w4 = testedgesidef(v3, v2, v4); + w5 = !testedgesidef(v3, v1, v4); + + if (w1 == w2 && w2 == w3 && w3 == w4 && w4 == w5) { + return TRUE; + } + +#define GETMIN2_AXIS(a, b, ma, mb, axis) ma[axis] = MIN2(a[axis], b[axis]), mb[axis] = MAX2(a[axis], b[axis]) +#define GETMIN2(a, b, ma, mb) GETMIN2_AXIS(a, b, ma, mb, 0); GETMIN2_AXIS(a, b, ma, mb, 1); + + GETMIN2(v1, v2, mv1, mv2); + GETMIN2(v3, v4, mv3, mv4); + + /* do an interval test on the x and y axe */ + /* first do x axi */ +#define T FLT_EPSILON * 15 + if ( ABS(v1[1] - v2[1]) < T && + ABS(v3[1] - v4[1]) < T && + ABS(v1[1] - v3[1]) < T) + { + return (mv4[0] >= mv1[0] && mv3[0] <= mv2[0]); + } + + /* now do y axi */ + if ( ABS(v1[0] - v2[0]) < T && + ABS(v3[0] - v4[0]) < T && + ABS(v1[0] - v3[0]) < T) + { + return (mv4[1] >= mv1[1] && mv3[1] <= mv2[1]); + } + + return FALSE; +} + +/* + * BM POINT IN FACE + * + * Projects co onto face f, and returns true if it is inside + * the face bounds. Note that this uses a best-axis projection + * test, instead of projecting co directly into f's orientation + * space, so there might be accuracy issues. + */ +int BM_face_point_inside_test(BMesh *bm, BMFace *f, const float co[3]) +{ + int ax, ay; + float co2[3], cent[3] = {0.0f, 0.0f, 0.0f}, out[3] = {FLT_MAX * 0.5f, FLT_MAX * 0.5f, 0}; + BMLoop *l_iter; + BMLoop *l_first; + int crosses = 0; + float eps = 1.0f + (float)FLT_EPSILON * 150.0f; + + if (dot_v3v3(f->no, f->no) <= FLT_EPSILON * 10) + BM_face_normal_update(bm, f); + + /* find best projection of face XY, XZ or YZ: barycentric weights of + * the 2d projected coords are the same and faster to compute + * + * this probably isn't all that accurate, but it has the advantage of + * being fast (especially compared to projecting into the face orientation) + */ + axis_dominant_v3(&ax, &ay, f->no); + + co2[0] = co[ax]; + co2[1] = co[ay]; + co2[2] = 0; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + cent[0] += l_iter->v->co[ax]; + cent[1] += l_iter->v->co[ay]; + } while ((l_iter = l_iter->next) != l_first); + + mul_v2_fl(cent, 1.0f / (float)f->len); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + float v1[3], v2[3]; + + v1[0] = (l_iter->prev->v->co[ax] - cent[ax]) * eps + cent[ax]; + v1[1] = (l_iter->prev->v->co[ay] - cent[ay]) * eps + cent[ay]; + v1[2] = 0.0f; + + v2[0] = (l_iter->v->co[ax] - cent[ax]) * eps + cent[ax]; + v2[1] = (l_iter->v->co[ay] - cent[ay]) * eps + cent[ay]; + v2[2] = 0.0f; + + crosses += linecrossesf(v1, v2, co2, out) != 0; + } while ((l_iter = l_iter->next) != l_first); + + return crosses % 2 != 0; +} + +static int goodline(float (*projectverts)[3], BMFace *f, int v1i, + int v2i, int v3i, int UNUSED(nvert)) +{ + BMLoop *l_iter; + BMLoop *l_first; + double v1[3], v2[3], v3[3], pv1[3], pv2[3]; + int i; + + VECCOPY(v1, projectverts[v1i]); + VECCOPY(v2, projectverts[v2i]); + VECCOPY(v3, projectverts[v3i]); + + if (testedgeside(v1, v2, v3)) { + return FALSE; + } + + //for (i = 0; i < nvert; i++) { + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + i = BM_elem_index_get(l_iter->v); + if (i == v1i || i == v2i || i == v3i) { + continue; + } + + VECCOPY(pv1, projectverts[BM_elem_index_get(l_iter->v)]); + VECCOPY(pv2, projectverts[BM_elem_index_get(l_iter->next->v)]); + + //if (linecrosses(pv1, pv2, v1, v3)) return FALSE; + + if ( point_in_triangle(v1, v2, v3, pv1) || + point_in_triangle(v3, v2, v1, pv1)) + { + return FALSE; + } + } while ((l_iter = l_iter->next) != l_first); + return TRUE; +} +/* + * FIND EAR + * + * Used by tesselator to find + * the next triangle to 'clip off' + * of a polygon while tesselating. + * + */ + +static BMLoop *find_ear(BMesh *UNUSED(bm), BMFace *f, float (*verts)[3], const int nvert) +{ + BMVert *v1, *v2, *v3; + BMLoop *bestear = NULL; + + BMLoop *l_iter; + BMLoop *l_first; + /* float angle, bestangle = 180.0f; */ + int isear /*, i = 0 */; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + isear = 1; + + v1 = l_iter->prev->v; + v2 = l_iter->v; + v3 = l_iter->next->v; + + if (BM_edge_exists(v1, v3)) { + isear = 0; + } + else if (!goodline(verts, f, BM_elem_index_get(v1), BM_elem_index_get(v2), BM_elem_index_get(v3), nvert)) { + isear = 0; + } + + if (isear) { +#if 0 + angle = angle_v3v3v3(verts[v1->head.eflag2], verts[v2->head.eflag2], verts[v3->head.eflag2]); + if (!bestear || ABS(angle-45.0f) < bestangle) { + bestear = l; + bestangle = ABS(45.0f - angle); + } + + if (angle > 20 && angle < 90) break; + if (angle < 100 && i > 5) break; + i += 1; +#endif + + bestear = l_iter; + break; + } + } while ((l_iter = l_iter->next) != l_first); + + return bestear; +} + +/* + * BMESH TRIANGULATE FACE + * + * Triangulates a face using a + * simple 'ear clipping' algorithm + * that tries to favor non-skinny + * triangles (angles less than + * 90 degrees). If the triangulator + * has bits left over (or cannot + * triangulate at all) it uses a + * simple fan triangulation + * + * newfaces, if non-null, must be an array of BMFace pointers, + * with a length equal to f->len. it will be filled with the new + * triangles, and will be NULL-terminated. + */ +void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3], + const short newedge_oflag, const short newface_oflag, BMFace **newfaces) +{ + int i, done, nvert, nf_i = 0; + BMLoop *newl, *nextloop; + BMLoop *l_iter; + BMLoop *l_first; + /* BMVert *v; */ /* UNUSED */ + + /* copy vertex coordinates to vertspace arra */ + i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + copy_v3_v3(projectverts[i], l_iter->v->co); + BM_elem_index_set(l_iter->v, i); /* set dirty! */ + i++; + } while ((l_iter = l_iter->next) != l_first); + + bm->elem_index_dirty |= BM_VERT; /* see above */ + + ///bmesh_update_face_normal(bm, f, f->no, projectverts); + + compute_poly_normal(f->no, projectverts, f->len); + poly_rotate_plane(f->no, projectverts, i); + + nvert = f->len; + + //compute_poly_plane(projectverts, i); + for (i = 0; i < nvert; i++) { + projectverts[i][2] = 0.0f; + } + + done = 0; + while (!done && f->len > 3) { + done = 1; + l_iter = find_ear(bm, f, projectverts, nvert); + if (l_iter) { + done = 0; + /* v = l->v; */ /* UNUSED */ + f = BM_face_split(bm, l_iter->f, l_iter->prev->v, + l_iter->next->v, + &newl, NULL); + copy_v3_v3(f->no, l_iter->f->no); + + if (!f) { + fprintf(stderr, "%s: triangulator failed to split face! (bmesh internal error)\n", __func__); + break; + } + + BMO_elem_flag_enable(bm, newl->e, newedge_oflag); + BMO_elem_flag_enable(bm, f, newface_oflag); + + if (newfaces) newfaces[nf_i++] = f; + + /* l = f->loopbase; + do { + if (l->v == v) { + f->loopbase = l; + break; + } + l = l->next; + } while (l != f->loopbase); */ + } + } + + if (f->len > 3) { + l_iter = BM_FACE_FIRST_LOOP(f); + while (l_iter->f->len > 3) { + nextloop = l_iter->next->next; + f = BM_face_split(bm, l_iter->f, l_iter->v, nextloop->v, + &newl, NULL); + if (!f) { + printf("triangle fan step of triangulator failed.\n"); + + /* NULL-terminat */ + if (newfaces) newfaces[nf_i] = NULL; + return; + } + + if (newfaces) newfaces[nf_i++] = f; + + BMO_elem_flag_enable(bm, newl->e, newedge_oflag); + BMO_elem_flag_enable(bm, f, newface_oflag); + l_iter = nextloop; + } + } + + /* NULL-terminat */ + if (newfaces) newfaces[nf_i] = NULL; +} + +/* each pair of loops defines a new edge, a split. this function goes + * through and sets pairs that are geometrically invalid to null. a + * split is invalid, if it forms a concave angle or it intersects other + * edges in the face, or it intersects another split. in the case of + * intersecting splits, only the first of the set of intersecting + * splits survives */ +void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len) +{ + BMIter iter; + BMLoop *l; + float v1[3], v2[3], v3[3]/*, v4[3 */, no[3], mid[3], *p1, *p2, *p3, *p4; + float out[3] = {-234324.0f, -234324.0f, 0.0f}; + float (*projverts)[3]; + float (*edgeverts)[3]; + float fac1 = 1.0000001f, fac2 = 0.9f; //9999f; //0.999f; + int i, j, a = 0, clen; + + BLI_array_fixedstack_declare(projverts, BM_NGON_STACK_SIZE, f->len, "projvertsb"); + BLI_array_fixedstack_declare(edgeverts, BM_NGON_STACK_SIZE * 2, len * 2, "edgevertsb"); + + i = 0; + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&iter)) { + BM_elem_index_set(l, i); /* set_loop */ + copy_v3_v3(projverts[i], l->v->co); + i++; + } + + for (i = 0; i < len; i++) { + copy_v3_v3(v1, loops[i][0]->v->co); + copy_v3_v3(v2, loops[i][1]->v->co); + + shrink_edgef(v1, v2, fac2); + + copy_v3_v3(edgeverts[a], v1); + a++; + copy_v3_v3(edgeverts[a], v2); + a++; + } + + compute_poly_normal(no, projverts, f->len); + poly_rotate_plane(no, projverts, f->len); + poly_rotate_plane(no, edgeverts, len * 2); + + for (i = 0, l = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l = l->next) { + p1 = projverts[i]; + out[0] = MAX2(out[0], p1[0]) + 0.01f; + out[1] = MAX2(out[1], p1[1]) + 0.01f; + out[2] = 0.0f; + p1[2] = 0.0f; + + //copy_v3_v3(l->v->co, p1); + } + + for (i = 0; i < len; i++) { + edgeverts[i * 2][2] = 0.0f; + edgeverts[i * 2 + 1][2] = 0.0f; + } + + /* do convexity test */ + for (i = 0; i < len; i++) { + copy_v3_v3(v2, edgeverts[i * 2]); + copy_v3_v3(v3, edgeverts[i * 2 + 1]); + + mid_v3_v3v3(mid, v2, v3); + + clen = 0; + for (j = 0; j < f->len; j++) { + p1 = projverts[j]; + p2 = projverts[(j + 1) % f->len]; + + copy_v3_v3(v1, p1); + copy_v3_v3(v2, p2); + + shrink_edgef(v1, v2, fac1); + + if (linecrossesf(p1, p2, mid, out)) clen++; + } + + if (clen % 2 == 0) { + loops[i][0] = NULL; + } + } + + /* do line crossing test */ + for (i = 0; i < f->len; i++) { + p1 = projverts[i]; + p2 = projverts[(i + 1) % f->len]; + + copy_v3_v3(v1, p1); + copy_v3_v3(v2, p2); + + shrink_edgef(v1, v2, fac1); + + for (j = 0; j < len; j++) { + if (!loops[j][0]) continue; + + p3 = edgeverts[j * 2]; + p4 = edgeverts[j * 2 + 1]; + + if (linecrossesf(v1, v2, p3, p4)) { + loops[j][0] = NULL; + } + } + } + + for (i = 0; i < len; i++) { + for (j = 0; j < len; j++) { + if (j == i) continue; + if (!loops[i][0]) continue; + if (!loops[j][0]) continue; + + p1 = edgeverts[i * 2]; + p2 = edgeverts[i * 2 + 1]; + p3 = edgeverts[j * 2]; + p4 = edgeverts[j * 2 + 1]; + + copy_v3_v3(v1, p1); + copy_v3_v3(v2, p2); + + shrink_edgef(v1, v2, fac1); + + if (linecrossesf(v1, v2, p3, p4)) { + loops[i][0] = NULL; + } + } + } + + BLI_array_fixedstack_free(projverts); + BLI_array_fixedstack_free(edgeverts); +} diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h new file mode 100644 index 00000000000..694d68549cd --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -0,0 +1,98 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_PRIVATE_H__ +#define __BMESH_PRIVATE_H__ + +/** \file blender/bmesh/intern/bmesh_private.h + * \ingroup bmesh + * + * Private function prototypes for bmesh public API. + * This file is a grab-bag of functions from various + * parts of the bmesh internals. + */ + +struct Link; +struct BMLoop; + +/* returns positive nonzero on error */ +int bmesh_check_element(BMesh *bm, void *element, const char htype); + +#define BM_CHECK_ELEMENT(bm, el) \ + if (bmesh_check_element(bm, el, ((BMHeader*)el)->htype)) { \ + printf("check_element failure, with code %i on line %i in file\n" \ + " \"%s\"\n\n", \ + bmesh_check_element(bm, el, ((BMHeader*)el)->htype), \ + __LINE__, __FILE__); \ + } + +#define BM_EDGE_DISK_LINK_GET(e, v) ( \ + ((v) == ((BMEdge*)(e))->v1) ? \ + &((e)->v1_disk_link) : \ + &((e)->v2_disk_link) \ + ) + +int bmesh_radial_length(struct BMLoop *l); +int bmesh_disk_count(BMVert *v); + +/* internal selection flushing */ +void bmesh_selectmode_flush(struct BMesh *bm); + +/*internal filter API*/ +void *bmesh_get_filter_callback(int type); +int bmesh_get_filter_argtype(int type); + +/* NOTE: ensure different parts of the API do not conflict + * on using these internal flags!*/ +#define _FLAG_JF 1 /* join faces */ +#define _FLAG_MF 2 /* make face */ + +#define BM_ELEM_API_FLAG_ENABLE(element, f) ((element)->oflags[0].pflag |= (f)) +#define BM_ELEM_API_FLAG_DISABLE(element, f) ((element)->oflags[0].pflag &= ~(f)) +#define BM_ELEM_API_FLAG_TEST(element, f) ((element)->oflags[0].pflag & (f)) + +/* Polygon Utilities ? FIXME... where do these each go? */ +/* newedgeflag sets a flag layer flag, obviously not the header flag. */ +void BM_face_triangulate(BMesh *bm, BMFace *f, float (*projectverts)[3], + const short newedge_oflag, const short newface_oflag, BMFace **newfaces); +void bmesh_update_face_normal(struct BMesh *bm, struct BMFace *f, float no[3], + float (*projectverts)[3]); +void bmesh_update_face_normal_vertex_cos(struct BMesh *bm, struct BMFace *f, float no[3], + float (*projectverts)[3], float (*vertexCos)[3]); + +void compute_poly_plane(float (*verts)[3], int nverts); +void poly_rotate_plane(const float normal[3], float (*verts)[3], const int nverts); +void bmesh_flip_normal(struct BMesh *bm, struct BMFace *f); + +BMEdge *bmesh_disk_next(BMEdge *e, BMVert *v); +BMEdge *bmesh_disk_prev(BMEdge *e, BMVert *v); + +/* include the rest of our private declarations */ +#include "bmesh_structure.h" +#include "bmesh_operators_private.h" + +#endif /* __BMESH_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c new file mode 100644 index 00000000000..089bc79e25d --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -0,0 +1,658 @@ +/* + * ***** 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 ***** + */ + +/** \file blender/bmesh/intern/bmesh_queries.c + * \ingroup bmesh + * + * This file contains functions for answering common + * Topological and geometric queries about a mesh, such + * as, "What is the angle between these two faces?" or, + * "How many faces are incident upon this vertex?" Tool + * authors should use the functions in this file instead + * of inspecting the mesh structure directly. + */ + +#include "BLI_math.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#define BM_OVERLAP (1 << 13) + +/* + * BMESH COUNT ELEMENT + * + * Return the amount of element of + * type 'type' in a given bmesh. + */ + +int BM_mesh_elem_count(BMesh *bm, const char htype) +{ + if (htype == BM_VERT) return bm->totvert; + else if (htype == BM_EDGE) return bm->totedge; + else if (htype == BM_FACE) return bm->totface; + + return 0; +} + + +/* + * BMESH VERT IN EDGE + * + * Returns whether or not a given vertex is + * is part of a given edge. + * + */ + +int BM_vert_in_edge(BMEdge *e, BMVert *v) +{ + return bmesh_vert_in_edge(e, v); +} + +/* + * BMESH OTHER EDGE IN FACE SHARING A VERTEX + * + * Returns an opposing loop that shares the same face. + * + */ + +BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v) +{ + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + + do { + if (l_iter->e == e) { + break; + } + } while ((l_iter = l_iter->next) != l_first); + + return l_iter->v == v ? l_iter->prev : l_iter->next; +} + +/* + * BMESH VERT IN FACE + * + * Returns whether or not a given vertex is + * is part of a given face. + * + */ + +int BM_vert_in_face(BMFace *f, BMVert *v) +{ + BMLoop *l_iter, *l_first; + +#ifdef USE_BMESH_HOLES + BMLoopList *lst; + for (lst = f->loops.first; lst; lst = lst->next) +#endif + { +#ifdef USE_BMESH_HOLES + l_iter = l_first = lst->first; +#else + l_iter = l_first = f->l_first; +#endif + do { + if (l_iter->v == v) { + return TRUE; + } + } while ((l_iter = l_iter->next) != l_first); + } + + return FALSE; +} + +/* + * BMESH VERTS IN FACE + * + * Compares the number of vertices in an array + * that appear in a given face + * + */ +int BM_verts_in_face(BMesh *bm, BMFace *f, BMVert **varr, int len) +{ + BMLoop *l_iter, *l_first; + +#ifdef USE_BMESH_HOLES + BMLoopList *lst; +#endif + + int i, count = 0; + + for (i = 0; i < len; i++) BMO_elem_flag_enable(bm, varr[i], BM_OVERLAP); + +#ifdef USE_BMESH_HOLES + for (lst = f->loops.first; lst; lst = lst->next) +#endif + { + +#ifdef USE_BMESH_HOLES + l_iter = l_first = lst->first; +#else + l_iter = l_first = f->l_first; +#endif + + do { + if (BMO_elem_flag_test(bm, l_iter->v, BM_OVERLAP)) { + count++; + } + + } while ((l_iter = l_iter->next) != l_first); + } + + for (i = 0; i < len; i++) BMO_elem_flag_disable(bm, varr[i], BM_OVERLAP); + + return count; +} + +/* + * BMESH EDGE IN FACE + * + * Returns whether or not a given edge is + * is part of a given face. + * + */ + +int BM_edge_in_face(BMFace *f, BMEdge *e) +{ + BMLoop *l_iter; + BMLoop *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + + do { + if (l_iter->e == e) { + return TRUE; + } + } while ((l_iter = l_iter->next) != l_first); + + return FALSE; +} + +/* + * BMESH VERTS IN EDGE + * + * Returns whether or not two vertices are in + * a given edge + * + */ + +int BM_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e) +{ + return bmesh_verts_in_edge(v1, v2, e); +} + +/* + * BMESH GET OTHER EDGEVERT + * + * Given a edge and one of its vertices, returns + * the other vertex. + * + */ + +BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v) +{ + return bmesh_edge_getothervert(e, v); +} + +/* + * BMESH VERT EDGECOUNT + * + * Returns the number of edges around this vertex. + */ + +int BM_vert_edge_count(BMVert *v) +{ + return bmesh_disk_count(v); +} + +/* + * BMESH EDGE FACECOUNT + * + * Returns the number of faces around this edge + */ + +int BM_edge_face_count(BMEdge *e) +{ + int count = 0; + BMLoop *curloop = NULL; + + if (e->l) { + curloop = e->l; + do { + count++; + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != e->l); + } + + return count; +} + +/* + * BMESH VERT FACECOUNT + * + * Returns the number of faces around this vert + */ + +int BM_vert_face_count(BMVert *v) +{ + int count = 0; + BMLoop *l; + BMIter iter; + + BM_ITER(l, &iter, NULL, BM_LOOPS_OF_VERT, v) { + count++; + } + + return count; +#if 0 //this code isn't working + BMEdge *curedge = NULL; + + if (v->e) { + curedge = v->e; + do { + if (curedge->l) count += BM_edge_face_count(curedge); + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + } + return count; +#endif +} + +/* + * BMESH WIRE VERT + * + * Tests whether or not the vertex is part of a wire edge. + * (ie: has no faces attached to it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_vert_is_wire(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *curedge; + + if (v->e == NULL) { + return FALSE; + } + + curedge = v->e; + do { + if (curedge->l) { + return FALSE; + } + + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + + return TRUE; +} + +/* + * BMESH WIRE EDGE + * + * Tests whether or not the edge is part of a wire. + * (ie: has no faces attached to it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_edge_is_wire(BMesh *UNUSED(bm), BMEdge *e) +{ + return (e->l) ? FALSE : TRUE; +} + +/* + * BMESH NONMANIFOLD VERT + * + * A vertex is non-manifold if it meets the following conditions: + * 1: Loose - (has no edges/faces incident upon it) + * 2: Joins two distinct regions - (two pyramids joined at the tip) + * 3: Is part of a non-manifold edge (edge with more than 2 faces) + * 4: Is part of a wire edge + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_vert_is_manifold(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *e, *oe; + BMLoop *l; + int len, count, flag; + + if (v->e == NULL) { + /* loose vert */ + return FALSE; + } + + /* count edges while looking for non-manifold edges */ + oe = v->e; + for (len = 0, e = v->e; e != oe || (e == oe && len == 0); len++, e = bmesh_disk_nextedge(e, v)) { + if (e->l == NULL) { + /* loose edge */ + return FALSE; + } + + if (bmesh_radial_length(e->l) > 2) { + /* edge shared by more than two faces */ + return FALSE; + } + } + + count = 1; + flag = 1; + e = NULL; + oe = v->e; + l = oe->l; + while (e != oe) { + l = (l->v == v) ? l->prev : l->next; + e = l->e; + count++; /* count the edges */ + + if (flag && l->radial_next == l) { + /* we've hit the edge of an open mesh, reset once */ + flag = 0; + count = 1; + oe = e; + e = NULL; + l = oe->l; + } + else if (l->radial_next == l) { + /* break the loop */ + e = oe; + } + else { + l = l->radial_next; + } + } + + if (count < len) { + /* vert shared by multiple regions */ + return FALSE; + } + + return TRUE; +} + +/* + * BMESH NONMANIFOLD EDGE + * + * Tests whether or not this edge is manifold. + * A manifold edge either has 1 or 2 faces attached + * to it. + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_edge_is_manifold(BMesh *UNUSED(bm), BMEdge *e) +{ + int count = BM_edge_face_count(e); + if (count != 2 && count != 1) { + return FALSE; + } + return TRUE; +} + +/* + * BMESH BOUNDARY EDGE + * + * Tests whether or not an edge is on the boundary + * of a shell (has one face associated with it) + * + * Returns - + * 1 for true, 0 for false. + */ + +int BM_edge_is_boundry(BMEdge *e) +{ + int count = BM_edge_face_count(e); + if (count == 1) { + return TRUE; + } + return FALSE; +} + +/* + * BMESH FACE SHAREDEDGES + * + * Counts the number of edges two faces share (if any) + * + * BMESH_TODO: + * Move this to structure, and wrap. + * + * Returns - + * Integer + */ + +int BM_face_share_edges(BMFace *f1, BMFace *f2) +{ + BMLoop *l_iter; + BMLoop *l_first; + int count = 0; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f1); + do { + if (bmesh_radial_find_face(l_iter->e, f2)) { + count++; + } + } while ((l_iter = l_iter->next) != l_first); + + return count; +} + +/* + * + * BMESH EDGE SHARE FACES + * + * Tests to see if e1 shares any faces with e2 + * + */ + +int BM_edge_share_faces(BMEdge *e1, BMEdge *e2) +{ + BMLoop *l; + BMFace *f; + + if (e1->l && e2->l) { + l = e1->l; + do { + f = l->f; + if (bmesh_radial_find_face(e2, f)) { + return TRUE; + } + l = l->radial_next; + } while (l != e1->l); + } + return FALSE; +} + +/** + * + * BMESH EDGE SHARE A VERTEX + * + * Tests to see if e1 shares a vertex with e2 + * + */ + +int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2) +{ + return (e1->v1 == e2->v1 || + e1->v1 == e2->v2 || + e1->v2 == e2->v1 || + e1->v2 == e2->v2); +} + +/** + * + * BMESH EDGE ORDERED VERTS + * + * Returns the verts of an edge as used in a face + * if used in a face at all, otherwise just assign as used in the edge. + * + * Useful to get a determanistic winding order when calling + * BM_face_create_ngon() on an arbitrary array of verts, + * though be sure to pick an edge which has a face. + * + */ + +void BM_edge_ordered_verts(BMEdge *edge, BMVert **r_v1, BMVert **r_v2) +{ + if ( (edge->l == NULL) || + ( ((edge->l->prev->v == edge->v1) && (edge->l->v == edge->v2)) || + ((edge->l->v == edge->v1) && (edge->l->next->v == edge->v2)) ) + ) + { + *r_v1 = edge->v1; + *r_v2 = edge->v2; + } + else { + *r_v1 = edge->v2; + *r_v2 = edge->v1; + } +} + +/* + * BMESH FACE ANGLE + * + * Calculates the angle between two faces. + * Assumes the face normals are correct. + * + * Returns - + * Float. + */ + +float BM_edge_face_angle(BMesh *UNUSED(bm), BMEdge *e) +{ + if (BM_edge_face_count(e) == 2) { + BMLoop *l1 = e->l; + BMLoop *l2 = e->l->radial_next; + return angle_normalized_v3v3(l1->f->no, l2->f->no); + } + else { + return (float)M_PI / 2.0f; /* acos(0.0) */ + } +} + +/* + * BMESH FACE ANGLE + * + * Calculates the angle a verts 2 edges. + * + * Returns - + * Float. + */ +float BM_vert_edge_angle(BMesh *UNUSED(bm), BMVert *v) +{ + BMEdge *e1, *e2; + + /* saves BM_vert_edge_count(v) and and edge iterator, + * get the edges and count them both at once */ + + if ((e1 = v->e) && + (e2 = bmesh_disk_nextedge(e1, v)) && + /* make sure we come full circle and only have 2 connected edges */ + (e1 == bmesh_disk_nextedge(e2, v))) + { + BMVert *v1 = BM_edge_other_vert(e1, v); + BMVert *v2 = BM_edge_other_vert(e2, v); + + return M_PI - angle_v3v3v3(v1->co, v->co, v2->co); + } + else { + return (float)M_PI / 2.0f; /* acos(0.0) */ + } +} + +/* + * BMESH EXIST FACE OVERLAPS + * + * Given a set of vertices (varr), find out if + * all those vertices overlap an existing face. + * + * Returns: + * 0 for no overlap + * 1 for overlap + * + * + */ + +int BM_face_exists_overlap(BMesh *bm, BMVert **varr, int len, BMFace **overlapface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + if (overlapface) *overlapface = NULL; + + for (i = 0; i < len; i++) { + f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]); + while (f) { + amount = BM_verts_in_face(bm, f, varr, len); + if (amount >= len) { + if (overlapface) *overlapface = f; + return TRUE; + } + f = BM_iter_step(&vertfaces); + } + } + return FALSE; +} + +/* + * BMESH FACE EXISTS + * + * Given a set of vertices (varr), find out if + * there is a face with exactly those vertices + * (and only those vertices). + * + * Returns: + * 0 for no face found + * 1 for face found + */ + +int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + if (existface) *existface = NULL; + + for (i = 0; i < len; i++) { + f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]); + while (f) { + amount = BM_verts_in_face(bm, f, varr, len); + if (amount == len && amount == f->len) { + if (existface) *existface = f; + return TRUE; + } + f = BM_iter_step(&vertfaces); + } + } + return FALSE; +} diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c new file mode 100644 index 00000000000..268d8bfa346 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -0,0 +1,1103 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_structure.c + * \ingroup bmesh + * + * Low level routines for manipulating the BM structure. + */ + +#include "bmesh.h" +#include "bmesh_private.h" + +/** + * MISC utility functions. + * + */ + +int bmesh_vert_in_edge(BMEdge *e, BMVert *v) +{ + if (e->v1 == v || e->v2 == v) return TRUE; + return FALSE; +} +int bmesh_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e) +{ + if (e->v1 == v1 && e->v2 == v2) return TRUE; + else if (e->v1 == v2 && e->v2 == v1) return TRUE; + return FALSE; +} + +BMVert *bmesh_edge_getothervert(BMEdge *e, BMVert *v) { + if (e->v1 == v) { + return e->v2; + } + else if (e->v2 == v) { + return e->v1; + } + return NULL; +} + +int bmesh_edge_swapverts(BMEdge *e, BMVert *orig, BMVert *newv) +{ + if (e->v1 == orig) { + e->v1 = newv; + e->v1_disk_link.next = e->v1_disk_link.prev = NULL; + return TRUE; + } + else if (e->v2 == orig) { + e->v2 = newv; + e->v2_disk_link.next = e->v2_disk_link.prev = NULL; + return TRUE; + } + return FALSE; +} + +/** + * BMESH CYCLES + * (this is somewhat outdate, though bits of its API are still used) - joeedh + * + * Cycles are circular doubly linked lists that form the basis of adjacency + * information in the BME modeller. Full adjacency relations can be derived + * from examining these cycles very quickly. Although each cycle is a double + * circular linked list, each one is considered to have a 'base' or 'head', + * and care must be taken by Euler code when modifying the contents of a cycle. + * + * The contents of this file are split into two parts. First there are the + * bmesh_cycle family of functions which are generic circular double linked list + * procedures. The second part contains higher level procedures for supporting + * modification of specific cycle types. + * + * The three cycles explicitly stored in the BM data structure are as follows: + * + * 1: The Disk Cycle - A circle of edges around a vertex + * Base: vertex->edge pointer. + * + * This cycle is the most complicated in terms of its structure. Each bmesh_Edge contains + * two bmesh_CycleNode structures to keep track of that edge's membership in the disk cycle + * of each of its vertices. However for any given vertex it may be the first in some edges + * in its disk cycle and the second for others. The bmesh_disk_XXX family of functions contain + * some nice utilities for navigating disk cycles in a way that hides this detail from the + * tool writer. + * + * Note that the disk cycle is completley independant from face data. One advantage of this + * is that wire edges are fully integrated into the topology database. Another is that the + * the disk cycle has no problems dealing with non-manifold conditions involving faces. + * + * Functions relating to this cycle: + * + * bmesh_disk_append_edge + * bmesh_disk_remove_edge + * bmesh_disk_nextedge + * bmesh_disk_getpointer + * + * 2: The Radial Cycle - A circle of face edges (bmesh_Loop) around an edge + * Base: edge->l->radial structure. + * + * The radial cycle is similar to the radial cycle in the radial edge data structure.* + * Unlike the radial edge however, the radial cycle does not require a large amount of memory + * to store non-manifold conditions since BM does not keep track of region/shell + * information. + * + * Functions relating to this cycle: + * + * bmesh_radial_append + * bmesh_radial_remove_loop + * bmesh_radial_nextloop + * bmesh_radial_find_face + * + * + * 3: The Loop Cycle - A circle of face edges around a polygon. + * Base: polygon->lbase. + * + * The loop cycle keeps track of a faces vertices and edges. It should be noted that the + * direction of a loop cycle is either CW or CCW depending on the face normal, and is + * not oriented to the faces editedges. + * + * Functions relating to this cycle: + * + * bmesh_cycle_XXX family of functions. + * + * + * Note that the order of elements in all cycles except the loop cycle is undefined. This + * leads to slightly increased seek time for deriving some adjacency relations, however the + * advantage is that no intrinsic properties of the data structures are dependant upon the + * cycle order and all non-manifold conditions are represented trivially. + * + */ +int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v) +{ + if (!v->e) { + BMDiskLink *dl1 = BM_EDGE_DISK_LINK_GET(e, v); + + v->e = e; + dl1->next = dl1->prev = e; + } + else { + BMDiskLink *dl1, *dl2, *dl3; + + dl1 = BM_EDGE_DISK_LINK_GET(e, v); + dl2 = BM_EDGE_DISK_LINK_GET(v->e, v); + dl3 = dl2->prev ? BM_EDGE_DISK_LINK_GET(dl2->prev, v) : NULL; + + dl1->next = v->e; + dl1->prev = dl2->prev; + + dl2->prev = e; + if (dl3) + dl3->next = e; + } + + return TRUE; +} + +void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v) +{ + BMDiskLink *dl1, *dl2; + + dl1 = BM_EDGE_DISK_LINK_GET(e, v); + if (dl1->prev) { + dl2 = BM_EDGE_DISK_LINK_GET(dl1->prev, v); + dl2->next = dl1->next; + } + + if (dl1->next) { + dl2 = BM_EDGE_DISK_LINK_GET(dl1->next, v); + dl2->prev = dl1->prev; + } + + if (v->e == e) + v->e = (e != (BMEdge *)dl1->next) ? (BMEdge *)dl1->next : NULL; + + dl1->next = dl1->prev = NULL; +} + +struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v) +{ + if (v == e->v1) + return e->v1_disk_link.next; + if (v == e->v2) + return e->v2_disk_link.next; + return NULL; +} + +static BMEdge *bmesh_disk_prevedge(BMEdge *e, BMVert *v) +{ + if (v == e->v1) + return e->v1_disk_link.prev; + if (v == e->v2) + return e->v2_disk_link.prev; + return NULL; +} + +BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2) +{ + BMEdge *curedge, *startedge; + + if (v1->e) { + startedge = v1->e; + curedge = startedge; + do { + if (bmesh_verts_in_edge(v1, v2, curedge)) { + return curedge; + } + + curedge = bmesh_disk_nextedge(curedge, v1); + } while (curedge != startedge); + } + + return NULL; +} + +int bmesh_disk_count(struct BMVert *v) +{ + BMEdge *e = v->e; + int i = 0; + + if (!e) { + return 0; + } + + do { + if (!e) { + return 0; + } + + e = bmesh_disk_nextedge(e, v); + + if (i >= (1 << 20)) { + printf("bmesh error: infinite loop in disk cycle!\n"); + return 0; + } + + i++; + } while (e != v->e); + + return i; +} + +int bmesh_disk_validate(int len, BMEdge *e, BMVert *v) +{ + BMEdge *e2; + + if (!BM_vert_in_edge(e, v)) + return FALSE; + if (bmesh_disk_count(v) != len || len == 0) + return FALSE; + + e2 = e; + do { + if (len != 1 && bmesh_disk_prevedge(e2, v) == e2) { + return FALSE; + } + + e2 = bmesh_disk_nextedge(e2, v); + } while (e2 != e); + + return TRUE; +} + +/* + * BME DISK COUNT FACE VERT + * + * Counts the number of loop users + * for this vertex. Note that this is + * equivalent to counting the number of + * faces incident upon this vertex + */ + +int bmesh_disk_count_facevert(BMVert *v) +{ + BMEdge *curedge; + int count = 0; + + /* is there an edge on this vert at all */ + if (!v->e) + return count; + + /* first, loop around edge */ + curedge = v->e; + do { + if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v); + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + + return count; +} + +struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = e; + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + + return NULL; +} + +struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = bmesh_disk_nextedge(e, v); + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + return e; +} + +/*****radial cycle functions, e.g. loops surrounding edges**** */ +int bmesh_radial_validate(int radlen, BMLoop *l) +{ + BMLoop *l2 = l; + int i = 0; + + if (bmesh_radial_length(l) != radlen) + return FALSE; + + do { + if (!l2) { + bmesh_error(); + return FALSE; + } + + if (l2->e != l->e) + return FALSE; + if (l2->v != l->e->v1 && l2->v != l->e->v2) + return FALSE; + + if (i > BM_LOOP_RADIAL_MAX) { + bmesh_error(); + return FALSE; + } + + i++; + l2 = l2->radial_next; + } while (l2 != l); + + return TRUE; +} + +/* + * BMESH RADIAL REMOVE LOOP + * + * Removes a loop from an radial cycle. If edge e is non-NULL + * it should contain the radial cycle, and it will also get + * updated (in the case that the edge's link into the radial + * cycle was the loop which is being removed from the cycle). + */ +void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e) +{ + /* if e is non-NULL, l must be in the radial cycle of e */ + if (e && e != l->e) { + bmesh_error(); + } + + if (l->radial_next != l) { + if (e && l == e->l) + e->l = l->radial_next; + + l->radial_next->radial_prev = l->radial_prev; + l->radial_prev->radial_next = l->radial_next; + } + else { + if (e) { + if (l == e->l) { + e->l = NULL; + } + else { + bmesh_error(); + } + } + } + + /* l is no longer in a radial cycle; empty the links + * to the cycle and the link back to an edge */ + l->radial_next = l->radial_prev = NULL; + l->e = NULL; +} + + +/* + * BME RADIAL FIND FIRST FACE VERT + * + * Finds the first loop of v around radial + * cycle + */ +BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = l; + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return NULL; +} + +BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = bmesh_radial_nextloop(l); + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return l; +} + +BMLoop *bmesh_radial_nextloop(BMLoop *l) +{ + return l->radial_next; +} + +int bmesh_radial_length(BMLoop *l) +{ + BMLoop *l2 = l; + int i = 0; + + if (!l) + return 0; + + do { + if (!l2) { + /* radial cycle is broken (not a circulat loop) */ + bmesh_error(); + return 0; + } + + i++; + l2 = l2->radial_next; + if (i >= BM_LOOP_RADIAL_MAX) { + bmesh_error(); + return -1; + } + } while (l2 != l); + + return i; +} + +void bmesh_radial_append(BMEdge *e, BMLoop *l) +{ + if (e->l == NULL) { + e->l = l; + l->radial_next = l->radial_prev = l; + } + else { + l->radial_prev = e->l; + l->radial_next = e->l->radial_next; + + e->l->radial_next->radial_prev = l; + e->l->radial_next = l; + + e->l = l; + } + + if (l->e && l->e != e) { + /* l is already in a radial cycle for a different edge */ + bmesh_error(); + } + + l->e = e; +} + +int bmesh_radial_find_face(BMEdge *e, BMFace *f) +{ + BMLoop *curloop; + int i, len; + + len = bmesh_radial_length(e->l); + for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) { + if (curloop->f == f) + return TRUE; + } + return FALSE; +} + +/* + * BME RADIAL COUNT FACE VERT + * + * Returns the number of times a vertex appears + * in a radial cycle + * + */ + +int bmesh_radial_count_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + int count = 0; + curloop = l; + do { + if (curloop->v == v) count++; + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return count; +} + +/*****loop cycle functions, e.g. loops surrounding a face**** */ +int bmesh_loop_validate(BMFace *f) +{ + int i; + int len = f->len; + BMLoop *l_iter, *l_first; + + l_first = BM_FACE_FIRST_LOOP(f); + + if (l_first == NULL) { + return FALSE; + } + + /* Validate that the face loop cycle is the length specified by f->len */ + for (i = 1, l_iter = l_first->next; i < len; i++, l_iter = l_iter->next) { + if ( (l_iter->f != f) || + (l_iter == l_first)) + { + return FALSE; + } + } + if (l_iter != l_first) { + return FALSE; + } + + /* Validate the loop->prev links also form a cycle of length f->len */ + for (i = 1, l_iter = l_first->prev; i < len; i++, l_iter = l_iter->prev) { + if (l_iter == l_first) { + return FALSE; + } + } + if (l_iter != l_first) { + return FALSE; + } + + return TRUE; +} + + +#if 0 +void bmesh_cycle_append(void *h, void *nt) +{ + BMNode *oldtail, *head, *newtail; + + head = (BMNode *)h; + newtail = (BMNode *)nt; + + if (head->next == NULL) { + head->next = newtail; + head->prev = newtail; + newtail->next = head; + newtail->prev = head; + } + else { + oldtail = head->prev; + oldtail->next = newtail; + newtail->next = head; + newtail->prev = oldtail; + head->prev = newtail; + + } +} + +/** + * bmesh_cycle_length + * + * Count the nodes in a cycle. + * + * Returns - + * Integer + */ + +int bmesh_cycle_length(BMEdge *e, BMVert *v) +{ + BMEdge *next, *prev, *cur; + int len, vi = v == e->v1 ? 0 : 1; + + /* should skip 2 forward if v is 1, happily reduces to (v * 2) */ + prev = *(&e->v1_prev + vi * 2); + + cur = e; + len = 1; + while (cur != prev) { + vi = cur->v1 == v ? 0 : 1; + + len++; + cur = *(&cur->v1_next + vi * 2); + } + + return len; +} + +/** + * bmesh_cycle_remove + * + * Removes a node from a cycle. + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_cycle_remove(void *h, void *remn) +{ + int i, len; + BMNode *head, *remnode, *curnode; + + head = (BMNode *)h; + remnode = (BMNode *)remn; + len = bmesh_cycle_length(h); + + if (len == 1 && head == remnode) { + head->next = NULL; + head->prev = NULL; + return TRUE; + } + else { + for (i = 0, curnode = head; i < len; curnode = curnode->next) { + if (curnode == remnode) { + remnode->prev->next = remnode->next; + remnode->next->prev = remnode->prev; + /* zero out remnode pointers, important */ + //remnode->next = NULL; + //remnode->prev = NULL; + return TRUE; + + } + } + } + return FALSE; +} + +/** + * bmesh_cycle_validate + * + * Validates a cycle. Takes as an argument the expected length of the cycle and + * a pointer to the cycle head or base. + * + * + * Returns - + * 1 for success, 0 for failure. + */ + +int bmesh_cycle_validate(int len, void *h) +{ + int i; + BMNode *curnode, *head; + head = (BMNode *)h; + + /* forward validatio */ + for (i = 0, curnode = head; i < len; i++, curnode = curnode->next); + if (curnode != head) { + return FALSE; + } + + /* reverse validatio */ + for (i = 0, curnode = head; i < len; i++, curnode = curnode->prev); + if (curnode != head) { + return FALSE; + } + + return TRUE; +} + +/* Begin Disk Cycle routine */ + +/** + * bmesh_disk_nextedge + * + * Find the next edge in a disk cycle + * + * Returns - + * Pointer to the next edge in the disk cycle for the vertex v. + */ + +BMEdge *bmesh_disk_nextedge(BMEdge *e, BMVert *v) +{ + if (bmesh_vert_in_edge(e, v)) { + if (e->v1 == v) { + return e->d1.next->data; + } + else if (e->v2 == v) { + return e->d2.next->data; + } + } + return NULL; +} + +/** + * bmesh_disk_getpointer + * + * Given an edge and one of its vertices, find the apporpriate CycleNode + * + * Returns - + * Pointer to bmesh_CycleNode. + */ +BMNode *bmesh_disk_getpointer(BMEdge *e, BMVert *v) +{ + /* returns pointer to the cycle node for the appropriate vertex in this dis */ + if (e->v1 == v) { + return &(e->d1); + } + else if (e->v2 == v) { + return &(e->d2); + } + return NULL; +} + +/** + * bmesh_disk_append_edge + * + * Appends edge to the end of a vertex disk cycle. + * + * Returns - + * 1 for success, 0 for failure + */ + +int bmesh_disk_append_edge(BMEdge *e, BMVert *v) +{ + + BMNode *base, *tail; + + /* check to make sure v is in */ + if (bmesh_vert_in_edge(e, v) == 0) { + return FALSE; + } + + /* check for loose vert firs */ + if (v->e == NULL) { + v->e = e; + base = tail = bmesh_disk_getpointer(e, v); + bmesh_cycle_append(base, tail); /* circular reference is ok */ + return TRUE; + } + + /* insert e at the end of disk cycle and make it the new v-> */ + base = bmesh_disk_getpointer(v->e, v); + tail = bmesh_disk_getpointer(e, v); + bmesh_cycle_append(base, tail); + return TRUE; +} + +/** + * bmesh_disk_remove_edge + * + * Removes an edge from a disk cycle. If the edge to be removed is + * at the base of the cycle, the next edge becomes the new base. + * + * + * Returns - + * Nothing + */ + +void bmesh_disk_remove_edge(BMEdge *e, BMVert *v) +{ + BMNode *base, *remnode; + BMEdge *newbase; + int len; + + base = bmesh_disk_getpointer(v->e, v); + remnode = bmesh_disk_getpointer(e, v); + + /* first deal with v->e pointer.. */ + len = bmesh_cycle_length(base); + if (len == 1) newbase = NULL; + else if (v->e == e) newbase = base->next-> data; + else newbase = v->e; + + /* remove and rebas */ + bmesh_cycle_remove(base, remnode); + v->e = newbase; +} + +/** + * bmesh_disk_next_edgeflag + * + * Searches the disk cycle of v, starting with e, for the + * next edge that has either eflag or tflag. + * + * bmesh_Edge pointer. + */ + +BMEdge *bmesh_disk_next_edgeflag(BMEdge *e, BMVert *v, int eflag, int tflag) +{ + + BMNode *diskbase; + BMEdge *curedge; + int len, ok; + + if (eflag && tflag) { + return NULL; + } + + ok = bmesh_vert_in_edge(e, v); + if (ok) { + diskbase = bmesh_disk_getpointer(e, v); + len = bmesh_cycle_length(diskbase); + curedge = bmesh_disk_nextedge(e, v); + while (curedge != e) { + if (eflag) { + if (curedge->head.eflag1 == eflag) { + return curedge; + } + } + + curedge = bmesh_disk_nextedge(curedge, v); + } + } + return NULL; +} + +/** + * bmesh_disk_count_edgeflag + * + * Counts number of edges in this verts disk cycle which have + * either eflag or tflag (but not both!) + * + * Returns - + * Integer. + */ + +int bmesh_disk_count_edgeflag(BMVert *v, int eflag, int tflag) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len = 0, count = 0; + + if (v->e) { + + /* tflag and eflag are reserved for different functions */ + if (eflag && tflag) { + return 0; + } + + diskbase = bmesh_disk_getpointer(v->e, v); + len = bmesh_cycle_length(diskbase); + + for (i = 0, curedge = v->e; i < len; i++) { + if (eflag) { + if (curedge->head.eflag1 == eflag) count++; + } + curedge = bmesh_disk_nextedge(curedge, v); + } + } + return count; +} + + +int bmesh_disk_hasedge(BMVert *v, BMEdge *e) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len = 0; + + if (v->e) { + diskbase = bmesh_disk_getpointer(v->e, v); + len = bmesh_cycle_length(diskbase); + + for (i = 0, curedge = v->e; i < len; i++) { + if (curedge == e) { + return TRUE; + } + else curedge = bmesh_disk_nextedge(curedge, v); + } + } + return FALSE; +} + +BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2) +{ + BMNode *diskbase; + BMEdge *curedge; + int i, len = 0; + + if (v1->e) { + diskbase = bmesh_disk_getpointer(v1->e, v1); + len = bmesh_cycle_length(diskbase); + + for (i = 0, curedge = v1->e; i < len; i++, curedge = bmesh_disk_nextedge(curedge, v1)) { + if (bmesh_verts_in_edge(v1, v2, curedge)) { + return curedge; + } + } + } + + return NULL; +} + +/* end disk cycle routine */ +BMLoop *bmesh_radial_nextloop(BMLoop *l) +{ + return l->radial_next; +} + +void bmesh_radial_append(BMEdge *e, BMLoop *l) +{ + if (e->l == NULL) e->l = l; + bmesh_cycle_append(&(e->l->radial), &(l->radial)); +} + +void bmesh_radial_remove_loop(BMLoop *l, BMEdge *e) +{ + BMLoop *newbase; + int len; + + /* deal with edge->l pointe */ + len = bmesh_cycle_length(&(e->l->radial)); + if (len == 1) newbase = NULL; + else if (e->l == l) newbase = e->l->radial_next; + else newbase = e->l; + + /* remove and rebas */ + bmesh_cycle_remove(&(e->l->radial), &(l->radial)); + e->l = newbase; +} + +int bmesh_radial_find_face(BMEdge *e, BMFace *f) +{ + + BMLoop *curloop; + int i, len; + + len = bmesh_cycle_length(&(e->l->radial)); + for (i = 0, curloop = e->l; i < len; i++, curloop = curloop->radial_next) { + if (curloop->f == f) { + return TRUE; + } + } + return FALSE; +} + + +/* + * BME RADIAL COUNT FACE VERT + * + * Returns the number of times a vertex appears + * in a radial cycle + * + */ + +int bmesh_radial_count_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + int count = 0; + curloop = l; + do { + if (curloop->v == v) count++; + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return count; +} + +/* + * BME DISK COUNT FACE VERT + * + * Counts the number of loop users + * for this vertex. Note that this is + * equivalent to counting the number of + * faces incident upon this vertex + * + */ + +int bmesh_disk_count_facevert(BMVert *v) +{ + BMEdge *curedge; + int count = 0; + + /* is there an edge on this vert at all */ + if (!v->e) + return count; + + /* first, loop around edge */ + curedge = v->e; + do { + if (curedge->l) count += bmesh_radial_count_facevert(curedge->l, v); + curedge = bmesh_disk_nextedge(curedge, v); + } while (curedge != v->e); + + return count; +} + +/* + * BME RADIAL FIND FIRST FACE VERT + * + * Finds the first loop of v around radial + * cycle + * + */ +BMLoop *bmesh_radial_find_first_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = l; + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return NULL; +} + +BMLoop *bmesh_radial_find_next_facevert(BMLoop *l, BMVert *v) +{ + BMLoop *curloop; + curloop = bmesh_radial_nextloop(l); + do { + if (curloop->v == v) { + return curloop; + } + + curloop = bmesh_radial_nextloop(curloop); + } while (curloop != l); + return l; +} + + +/* + * BME FIND FIRST FACE EDGE + * + * Finds the first edge in a vertices + * Disk cycle that has one of this + * vert's loops attached + * to it. + */ + +BMEdge *bmesh_disk_find_first_faceedge(BMEdge *e, BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = e; + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + + return NULL; +} + +BMEdge *bmesh_disk_find_next_faceedge(BMEdge *e, BMVert *v) +{ + BMEdge *searchedge = NULL; + searchedge = bmesh_disk_nextedge(e, v); + do { + if (searchedge->l && bmesh_radial_count_facevert(searchedge->l, v)) { + return searchedge; + } + searchedge = bmesh_disk_nextedge(searchedge, v); + } while (searchedge != e); + return e; +} + + + + + +struct BMLoop *bmesh_loop_find_loop(struct BMFace *f, struct BMVert *v) +{ + BMLoop *l; + int i, len; + + len = bmesh_cycle_length(f->lbase); + for (i = 0, l = f->loopbase; i < len; i++, l = l->next) { + if (l->v == v) { + return l; + } + } + return NULL; +} + +#endif diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h new file mode 100644 index 00000000000..aff90dcced7 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -0,0 +1,106 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_STRUCTURE_H__ +#define __BMESH_STRUCTURE_H__ + +/** \file blender/bmesh/intern/bmesh_structure.h + * \ingroup bmesh + * + * The lowest level of functionality for manipulating bmesh structures. + * None of these functions should ever be exported to the rest of Blender. + * + * in the vast majority of cases thes should not be used directly. + * if absolutely necassary, see function defitions in code for + * descriptive comments. but seriously, don't use this stuff. + */ + +struct ListBase; + +void remove_loop_radial_link(BMLoop *l); + +/*DOUBLE CIRCULAR LINKED LIST FUNCTIONS*/ +void bmesh_cycle_append(void *h, void *nt); +int bmesh_cycle_remove(void *h, void *remn); +int bmesh_cycle_validate(int len, void *h); +int bmesh_cycle_length(void *h); + +/* LOOP CYCLE MANAGEMENT */ +int bmesh_loop_validate(BMFace *f); + +/*DISK CYCLE MANAGMENT*/ +int bmesh_disk_append_edge(struct BMEdge *e, struct BMVert *v); +void bmesh_disk_remove_edge(struct BMEdge *e, struct BMVert *v); +struct BMEdge *bmesh_disk_nextedge(struct BMEdge *e, struct BMVert *v); +struct BMNode *bmesh_disk_getpointer(struct BMEdge *e, struct BMVert *v); +int bmesh_disk_count_facevert(struct BMVert *v); +struct BMEdge *bmesh_disk_find_first_faceedge(struct BMEdge *e, struct BMVert *v); +struct BMEdge *bmesh_disk_find_next_faceedge(struct BMEdge *e, struct BMVert *v); + +/*RADIAL CYCLE MANAGMENT*/ +void bmesh_radial_append(struct BMEdge *e, struct BMLoop *l); +void bmesh_radial_remove_loop(struct BMLoop *l, struct BMEdge *e); +int bmesh_radial_find_face(struct BMEdge *e, struct BMFace *f); +struct BMLoop *bmesh_radial_nextloop(struct BMLoop *l); +int bmesh_radial_count_facevert(struct BMLoop *l, struct BMVert *v); +struct BMLoop *bmesh_radial_find_first_facevert(struct BMLoop *l, struct BMVert *v); +struct BMLoop *bmesh_radial_find_next_facevert(struct BMLoop *l, struct BMVert *v); +int bmesh_radial_validate(int radlen, struct BMLoop *l); + +/*EDGE UTILITIES*/ +int bmesh_vert_in_edge(struct BMEdge *e, struct BMVert *v); +int bmesh_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e); +int bmesh_edge_swapverts(struct BMEdge *e, struct BMVert *orig, struct BMVert *newv); /*relink edge*/ +struct BMVert *bmesh_edge_getothervert(struct BMEdge *e, struct BMVert *v); +int bmesh_disk_hasedge(struct BMVert *v, struct BMEdge *e); +struct BMEdge *bmesh_disk_existedge(BMVert *v1, BMVert *v2); +struct BMEdge *bmesh_disk_next_edgeflag(struct BMEdge *e, struct BMVert *v, int eflag, int tflag); +int bmesh_disk_count_edgeflag(struct BMVert *v, int eflag, int tflag); +int bmesh_disk_validate(int len, struct BMEdge *e, struct BMVert *v); + +/*EULER API - For modifying structure*/ +struct BMVert *bmesh_mv(struct BMesh *bm, float *vec); +struct BMEdge *bmesh_me(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2); +struct BMFace *bmesh_mf(struct BMesh *bm, struct BMVert *v1, struct BMVert *v2, struct BMEdge **elist, int len); +int bmesh_kv(struct BMesh *bm, struct BMVert *v); +int bmesh_ke(struct BMesh *bm, struct BMEdge *e); +int bmesh_kf(struct BMesh *bm, struct BMFace *bply); +struct BMVert *bmesh_semv(struct BMesh *bm, struct BMVert *tv, struct BMEdge *e, struct BMEdge **re); +struct BMFace *bmesh_sfme(struct BMesh *bm, struct BMFace *f, struct BMVert *v1, + struct BMVert *v2, struct BMLoop **rl +#ifdef USE_BMESH_HOLES + , ListBase *holes +#endif + ); +int bmesh_jekv(struct BMesh *bm, struct BMEdge *ke, struct BMVert *kv); +int bmesh_loop_reverse(struct BMesh *bm, struct BMFace *f); +struct BMFace *bmesh_jfke(struct BMesh *bm, struct BMFace *f1, struct BMFace *f2, struct BMEdge *e); + +struct BMVert *bmesh_urmv(struct BMesh *bm, struct BMFace *sf, struct BMVert *sv); +//int *bmesh_grkv(struct BMesh *bm, struct BMFace *sf, struct BMVert *kv); + +#endif /* __BMESH_STRUCTURE_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c new file mode 100644 index 00000000000..9cba90c1bf5 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers.c @@ -0,0 +1,266 @@ +/* + * ***** 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, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_walkers.c + * \ingroup bmesh + * + * BMesh Walker API. + */ + +#include <stdlib.h> + + + +#include "BLI_listbase.h" + +#include "bmesh.h" + +#include "bmesh_walkers_private.h" + +/* - joeedh - + * design notes: + * + * original desing: walkers directly emulation recursive functions. + * functions save their state onto a worklist, and also add new states + * to implement recursive or looping behaviour. generally only one + * state push per call with a specific state is desired. + * + * basic design pattern: the walker step function goes through it's + * list of possible choices for recursion, and recurses (by pushing a new state) + * using the first non-visited one. this choise is the flagged as visited using + * the ghash. each step may push multiple new states onto the worklist at once. + * + * - walkers use tool flags, not header flags + * - walkers now use ghash for storing visited elements, + * rather then stealing flags. ghash can be rewritten + * to be faster if necassary, in the far future :) . + * - tools should ALWAYS have necassary error handling + * for if walkers fail. + */ + +void *BMW_begin(BMWalker *walker, void *start) +{ + walker->begin(walker, start); + + return BMW_current_state(walker) ? walker->step(walker) : NULL; +} + +/* + * BMW_CREATE + * + * Allocates and returns a new mesh walker of + * a given type. The elements visited are filtered + * by the bitmask 'searchmask'. + */ + +void BMW_init(BMWalker *walker, BMesh *bm, int type, + short mask_vert, short mask_edge, short mask_loop, short mask_face, + int layer) +{ + memset(walker, 0, sizeof(BMWalker)); + + walker->layer = layer; + walker->bm = bm; + + walker->mask_vert = mask_vert; + walker->mask_edge = mask_edge; + walker->mask_loop = mask_loop; + walker->mask_face = mask_face; + + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 1"); + + if (type >= BMW_MAXWALKERS || type < 0) { + bmesh_error(); + fprintf(stderr, + "Invalid walker type in BMW_init; type: %d, " + "searchmask: (v:%d, e:%d, l:%d, f:%d), flag: %d\n", + type, mask_vert, mask_edge, mask_loop, mask_face, layer); + } + + if (type != BMW_CUSTOM) { + walker->begin = bm_walker_types[type]->begin; + walker->yield = bm_walker_types[type]->yield; + walker->step = bm_walker_types[type]->step; + walker->structsize = bm_walker_types[type]->structsize; + walker->order = bm_walker_types[type]->order; + walker->valid_mask = bm_walker_types[type]->valid_mask; + + /* safety checks */ + /* if this raises an error either the caller is wrong or + * 'bm_walker_types' needs updating */ + BLI_assert(mask_vert == 0 || (walker->valid_mask & BM_VERT)); + BLI_assert(mask_edge == 0 || (walker->valid_mask & BM_EDGE)); + BLI_assert(mask_loop == 0 || (walker->valid_mask & BM_LOOP)); + BLI_assert(mask_face == 0 || (walker->valid_mask & BM_FACE)); + } + + walker->worklist = BLI_mempool_create(walker->structsize, 100, 100, TRUE, FALSE); + walker->states.first = walker->states.last = NULL; +} + +/* + * BMW_end + * + * Frees a walker's worklist. + */ + +void BMW_end(BMWalker *walker) +{ + BLI_mempool_destroy(walker->worklist); + BLI_ghash_free(walker->visithash, NULL, NULL); +} + + +/* + * BMW_step + */ + +void *BMW_step(BMWalker *walker) +{ + BMHeader *head; + + head = BMW_walk(walker); + + return head; +} + +/* + * BMW_current_depth + * + * Returns the current depth of the walker. + */ + +int BMW_current_depth(BMWalker *walker) +{ + return walker->depth; +} + +/* + * BMW_WALK + * + * Steps a mesh walker forward by one element + * + * BMESH_TODO: + * -add searchmask filtering + */ + +void *BMW_walk(BMWalker *walker) +{ + void *current = NULL; + + while (BMW_current_state(walker)) { + current = walker->step(walker); + if (current) { + return current; + } + } + return NULL; +} + +/* + * BMW_current_state + * + * Returns the first state from the walker state + * worklist. This state is the the next in the + * worklist for processing. + */ + +void *BMW_current_state(BMWalker *walker) +{ + bmesh_walkerGeneric *currentstate = walker->states.first; + if (currentstate) { + /* Automatic update of depth. For most walkers that + * follow the standard "Step" pattern of: + * - read current state + * - remove current state + * - push new states + * - return walk result from just-removed current state + * this simple automatic update should keep track of depth + * just fine. Walkers that deviate from that pattern may + * need to manually update the depth if they care about + * keeping it correct. */ + walker->depth = currentstate->depth + 1; + } + return currentstate; +} + +/* + * BMW_state_remove + * + * Remove and free an item from the end of the walker state + * worklist. + */ + +void BMW_state_remove(BMWalker *walker) +{ + void *oldstate; + oldstate = BMW_current_state(walker); + BLI_remlink(&walker->states, oldstate); + BLI_mempool_free(walker->worklist, oldstate); +} + +/* + * BMW_newstate + * + * Allocate a new empty state and put it on the worklist. + * A pointer to the new state is returned so that the caller + * can fill in the state data. The new state will be inserted + * at the front for depth-first walks, and at the end for + * breadth-first walks. + */ + +void *BMW_state_add(BMWalker *walker) +{ + bmesh_walkerGeneric *newstate; + newstate = BLI_mempool_alloc(walker->worklist); + newstate->depth = walker->depth; + switch (walker->order) + { + case BMW_DEPTH_FIRST: + BLI_addhead(&walker->states, newstate); + break; + case BMW_BREADTH_FIRST: + BLI_addtail(&walker->states, newstate); + break; + default: + BLI_assert(0); + break; + } + return newstate; +} + +/* + * BMW_reset + * + * Frees all states from the worklist, resetting the walker + * for reuse in a new walk. + */ + +void BMW_reset(BMWalker *walker) +{ + while (BMW_current_state(walker)) { + BMW_state_remove(walker); + } + walker->depth = 0; + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 1"); +} diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c new file mode 100644 index 00000000000..e6bcbf405fe --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -0,0 +1,911 @@ +/* + * ***** 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, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_walkers_impl.c + * \ingroup bmesh + * + * BMesh Walker Code. + */ + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" +#include "bmesh_walkers_private.h" + +/* Shell Walker: + * + * Starts at a vertex on the mesh and walks over the 'shell' it belongs + * to via visiting connected edges. + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * + */ + +static void shellWalker_visitEdge(BMWalker *walker, BMEdge *e) +{ + shellWalker *shellWalk = NULL; + + if (BLI_ghash_haskey(walker->visithash, e)) { + return; + } + + if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, e, walker->mask_edge)) { + return; + } + + shellWalk = BMW_state_add(walker); + shellWalk->curedge = e; + BLI_ghash_insert(walker->visithash, e, NULL); +} + +static void shellWalker_begin(BMWalker *walker, void *data) +{ + BMIter eiter; + BMHeader *h = data; + BMEdge *e; + BMVert *v; + + if (h == NULL) + { + return; + } + + switch (h->htype) { + case BM_VERT: + { + /* starting the walk at a vert, add all the edges + * to the worklist */ + v = (BMVert *)h; + BM_ITER(e, &eiter, walker->bm, BM_EDGES_OF_VERT, v) { + shellWalker_visitEdge(walker, e); + } + break; + } + + case BM_EDGE: + { + /* starting the walk at an edge, add the single edge + * to the worklist */ + e = (BMEdge *)h; + shellWalker_visitEdge(walker, e); + break; + } + } +} + +static void *shellWalker_yield(BMWalker *walker) +{ + shellWalker *shellWalk = BMW_current_state(walker); + return shellWalk->curedge; +} + +static void *shellWalker_step(BMWalker *walker) +{ + shellWalker *swalk = BMW_current_state(walker); + BMEdge *e, *e2; + BMVert *v; + BMIter iter; + int i; + + e = swalk->curedge; + BMW_state_remove(walker); + + for (i = 0; i < 2; i++) { + v = i ? e->v2 : e->v1; + BM_ITER(e2, &iter, walker->bm, BM_EDGES_OF_VERT, v) { + shellWalker_visitEdge(walker, e2); + } + } + + return e; +} + +#if 0 +static void *shellWalker_step(BMWalker *walker) +{ + BMEdge *curedge, *next = NULL; + BMVert *ov = NULL; + int restrictpass = 1; + shellWalker shellWalk = *((shellWalker *)BMW_current_state(walker)); + + if (!BLI_ghash_haskey(walker->visithash, shellWalk.base)) { + BLI_ghash_insert(walker->visithash, shellWalk.base, NULL); + } + + BMW_state_remove(walker); + + + /* find the next edge whose other vertex has not been visite */ + curedge = shellWalk.curedge; + do { + if (!BLI_ghash_haskey(walker->visithash, curedge)) { + if (!walker->restrictflag || + (walker->restrictflag && BMO_elem_flag_test(walker->bm, curedge, walker->restrictflag))) + { + shellWalker *newstate; + + ov = BM_edge_other_vert(curedge, shellWalk.base); + + /* push a new state onto the stac */ + newState = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, curedge, NULL); + + /* populate the new stat */ + + newState->base = ov; + newState->curedge = curedge; + } + } + curedge = bmesh_disk_nextedge(curedge, shellWalk.base); + } while (curedge != shellWalk.curedge); + + return shellWalk.curedge; +} +#endif + +/* Connected Vertex Walker: + * + * Similar to shell walker, but visits vertices instead of edges. + * + */ + +static void connectedVertexWalker_visitVertex(BMWalker *walker, BMVert *v) +{ + connectedVertexWalker *vwalk; + + if (BLI_ghash_haskey(walker->visithash, v)) { + /* already visited */ + return; + } + if (walker->mask_vert && !BMO_elem_flag_test(walker->bm, v, walker->mask_vert)) { + /* not flagged for walk */ + return; + } + + vwalk = BMW_state_add(walker); + vwalk->curvert = v; + BLI_ghash_insert(walker->visithash, v, NULL); +} + +static void connectedVertexWalker_begin(BMWalker *walker, void *data) +{ + BMVert *v = data; + connectedVertexWalker_visitVertex(walker, v); +} + +static void *connectedVertexWalker_yield(BMWalker *walker) +{ + connectedVertexWalker *vwalk = BMW_current_state(walker); + return vwalk->curvert; +} + +static void *connectedVertexWalker_step(BMWalker *walker) +{ + connectedVertexWalker *vwalk = BMW_current_state(walker); + BMVert *v, *v2; + BMEdge *e; + BMIter iter; + + v = vwalk->curvert; + + BMW_state_remove(walker); + + BM_ITER(e, &iter, walker->bm, BM_EDGES_OF_VERT, v) { + v2 = BM_edge_other_vert(e, v); + if (!BLI_ghash_haskey(walker->visithash, v2)) { + connectedVertexWalker_visitVertex(walker, v2); + } + } + + return v; +} + +/* Island Boundary Walker: + * + * Starts at a edge on the mesh and walks over the boundary of an + * island it belongs to. + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * + */ + +static void islandboundWalker_begin(BMWalker *walker, void *data) +{ + BMLoop *l = data; + islandboundWalker *iwalk = NULL; + + iwalk = BMW_state_add(walker); + + iwalk->base = iwalk->curloop = l; + iwalk->lastv = l->v; + + BLI_ghash_insert(walker->visithash, data, NULL); + +} + +static void *islandboundWalker_yield(BMWalker *walker) +{ + islandboundWalker *iwalk = BMW_current_state(walker); + + return iwalk->curloop; +} + +static void *islandboundWalker_step(BMWalker *walker) +{ + islandboundWalker *iwalk = BMW_current_state(walker), owalk; + BMVert *v; + BMEdge *e = iwalk->curloop->e; + BMFace *f; + BMLoop *l = iwalk->curloop; + /* int found = 0; */ + + owalk = *iwalk; + + if (iwalk->lastv == e->v1) v = e->v2; + else v = e->v1; + + if (!BM_vert_is_manifold(walker->bm, v)) { + BMW_reset(walker); + BMO_error_raise(walker->bm, NULL, BMERR_WALKER_FAILED, + "Non-manifold vert " + "while searching region boundary"); + return NULL; + } + + /* pop off current stat */ + BMW_state_remove(walker); + + f = l->f; + + while (1) { + l = BM_face_other_loop(e, f, v); + if (bmesh_radial_nextloop(l) != l) { + l = bmesh_radial_nextloop(l); + f = l->f; + e = l->e; + if (walker->mask_face && !BMO_elem_flag_test(walker->bm, f, walker->mask_face)) { + l = l->radial_next; + break; + } + } + else { + f = l->f; + e = l->e; + break; + } + } + + if (l == owalk.curloop) { + return NULL; + } + else if (BLI_ghash_haskey(walker->visithash, l)) { + return owalk.curloop; + } + + BLI_ghash_insert(walker->visithash, l, NULL); + iwalk = BMW_state_add(walker); + iwalk->base = owalk.base; + + //if (!BMO_elem_flag_test(walker->bm, l->f, walker->restrictflag)) + // iwalk->curloop = l->radial_next; + iwalk->curloop = l; //else iwalk->curloop = l; + iwalk->lastv = v; + + return owalk.curloop; +} + + +/* Island Walker: + * + * Starts at a tool flagged-face and walks over the face region + * + * TODO: + * + * Add restriction flag/callback for wire edges. + * + */ + +static void islandWalker_begin(BMWalker *walker, void *data) +{ + islandWalker *iwalk = NULL; + + if (walker->mask_face && !BMO_elem_flag_test(walker->bm, (BMElemF *)data, walker->mask_face)) { + return; + } + + iwalk = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, data, NULL); + + iwalk->cur = data; +} + +static void *islandWalker_yield(BMWalker *walker) +{ + islandWalker *iwalk = BMW_current_state(walker); + + return iwalk->cur; +} + +static void *islandWalker_step(BMWalker *walker) +{ + islandWalker *iwalk = BMW_current_state(walker); + /* islandWalker *owalk = iwalk; */ /* UNUSED */ + BMIter iter, liter; + BMFace *f, *curf = iwalk->cur; + BMLoop *l; + + BMW_state_remove(walker); + + l = BM_iter_new(&liter, walker->bm, BM_LOOPS_OF_FACE, iwalk->cur); + for ( ; l; l = BM_iter_step(&liter)) { + /* could skip loop here too, but dont add unless we need it */ + if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, l->e, walker->mask_edge)) { + continue; + } + + f = BM_iter_new(&iter, walker->bm, BM_FACES_OF_EDGE, l->e); + for ( ; f; f = BM_iter_step(&iter)) { + if (walker->mask_face && !BMO_elem_flag_test(walker->bm, f, walker->mask_face)) { + continue; + } + if (BLI_ghash_haskey(walker->visithash, f)) continue; + + iwalk = BMW_state_add(walker); + iwalk->cur = f; + BLI_ghash_insert(walker->visithash, f, NULL); + break; + } + } + + return curf; +} + + +/* Edge Loop Walker: + * + * Starts at a tool-flagged edge and walks over the edge loop + * + */ + +static void loopWalker_begin(BMWalker *walker, void *data) +{ + loopWalker *lwalk = NULL, owalk; + BMEdge *e = data; + BMVert *v; + /* int found = 1, val; */ /* UNUSED */ + + v = e->v1; + + /* val = BM_vert_edge_count(v); */ /* UNUSED */ + + lwalk = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, e, NULL); + + lwalk->cur = lwalk->start = e; + lwalk->lastv = lwalk->startv = v; + lwalk->stage2 = 0; + lwalk->startrad = BM_edge_face_count(e); + + /* rewin */ + while (BMW_current_state(walker)) { + owalk = *((loopWalker *)BMW_current_state(walker)); + BMW_walk(walker); + } + + lwalk = BMW_state_add(walker); + *lwalk = owalk; + + if (lwalk->lastv == owalk.cur->v1) lwalk->lastv = owalk.cur->v2; + else lwalk->lastv = owalk.cur->v1; + + lwalk->startv = lwalk->lastv; + + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 2"); + BLI_ghash_insert(walker->visithash, owalk.cur, NULL); +} + +static void *loopWalker_yield(BMWalker *walker) +{ + loopWalker *lwalk = BMW_current_state(walker); + + return lwalk->cur; +} + +static void *loopWalker_step(BMWalker *walker) +{ + loopWalker *lwalk = BMW_current_state(walker), owalk; + BMIter eiter; + BMEdge *e = lwalk->cur, *nexte = NULL; + BMLoop *l, *l2; + BMVert *v; + int val, rlen /* , found = 0 */, i = 0, stopi; + + owalk = *lwalk; + BMW_state_remove(walker); + + l = e->l; + + /* handle wire edge case */ + if (!l) { + + /* match trunk: mark all connected wire edges */ + for (i = 0; i < 2; i++) { + v = i ? e->v2 : e->v1; + + BM_ITER(nexte, &eiter, walker->bm, BM_EDGES_OF_VERT, v) { + if ((nexte->l == NULL) && !BLI_ghash_haskey(walker->visithash, nexte)) { + lwalk = BMW_state_add(walker); + lwalk->cur = nexte; + lwalk->lastv = v; + lwalk->startrad = owalk.startrad; + + BLI_ghash_insert(walker->visithash, nexte, NULL); + } + } + } + + return owalk.cur; + } + + v = (e->v1 == lwalk->lastv) ? e->v2 : e->v1; + + val = BM_vert_edge_count(v); + + rlen = owalk.startrad; + + if (val == 4 || val == 2 || rlen == 1) { + i = 0; + stopi = val / 2; + while (1) { + if (rlen != 1 && i == stopi) break; + + l = BM_face_other_loop(l->e, l->f, v); + + if (!l) + break; + + l2 = bmesh_radial_nextloop(l); + + if (l2 == l) { + break; + } + + l = l2; + i += 1; + } + } + + if (!l) { + return owalk.cur; + } + + if (l != e->l && !BLI_ghash_haskey(walker->visithash, l->e)) { + if (!(rlen != 1 && i != stopi)) { + lwalk = BMW_state_add(walker); + lwalk->cur = l->e; + lwalk->lastv = v; + lwalk->startrad = owalk.startrad; + BLI_ghash_insert(walker->visithash, l->e, NULL); + } + } + + return owalk.cur; +} + +/* Face Loop Walker: + * + * Starts at a tool-flagged face and walks over the face loop + * Conditions for starting and stepping the face loop have been + * tuned in an attempt to match the face loops built by EditMesh + * + */ + +/* Check whether the face loop should includes the face specified + * by the given BMLoop */ +static int faceloopWalker_include_face(BMWalker *walker, BMLoop *l) +{ + /* face must have degree 4 */ + if (l->f->len != 4) { + return FALSE; + } + + /* the face must not have been already visite */ + if (BLI_ghash_haskey(walker->visithash, l->f)) { + return FALSE; + } + + return TRUE; +} + +/* Check whether the face loop can start from the given edge */ +static int faceloopWalker_edge_begins_loop(BMWalker *walker, BMEdge *e) +{ + BMesh *bm = walker->bm; + + /* There is no face loop starting from a wire edge */ + if (BM_edge_is_wire(bm, e)) { + return FALSE; + } + + /* Don't start a loop from a boundary edge if it cannot + * be extended to cover any faces */ + if (BM_edge_face_count(e) == 1) { + if (!faceloopWalker_include_face(walker, e->l)) { + return FALSE; + } + } + + /* Don't start a face loop from non-manifold edges */ + if (!BM_edge_is_manifold(bm, e)) { + return FALSE; + } + + return TRUE; +} + +static void faceloopWalker_begin(BMWalker *walker, void *data) +{ + faceloopWalker *lwalk, owalk; + BMEdge *e = data; + /* BMesh *bm = walker->bm; */ /* UNUSED */ + /* int fcount = BM_edge_face_count(e); */ /* UNUSED */ + + if (!faceloopWalker_edge_begins_loop(walker, e)) + return; + + lwalk = BMW_state_add(walker); + lwalk->l = e->l; + lwalk->nocalc = 0; + BLI_ghash_insert(walker->visithash, lwalk->l->f, NULL); + + /* rewin */ + while (BMW_current_state(walker)) { + owalk = *((faceloopWalker *)BMW_current_state(walker)); + BMW_walk(walker); + } + + lwalk = BMW_state_add(walker); + *lwalk = owalk; + lwalk->nocalc = 0; + + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 3"); + BLI_ghash_insert(walker->visithash, lwalk->l->f, NULL); +} + +static void *faceloopWalker_yield(BMWalker *walker) +{ + faceloopWalker *lwalk = BMW_current_state(walker); + + if (!lwalk) { + return NULL; + } + + return lwalk->l->f; +} + +static void *faceloopWalker_step(BMWalker *walker) +{ + faceloopWalker *lwalk = BMW_current_state(walker); + BMFace *f = lwalk->l->f; + BMLoop *l = lwalk->l, *origl = lwalk->l; + + BMW_state_remove(walker); + + l = l->radial_next; + + if (lwalk->nocalc) + return f; + + if (!faceloopWalker_include_face(walker, l)) { + l = lwalk->l; + l = l->next->next; + if (BM_edge_face_count(l->e) != 2) { + l = l->prev->prev; + } + l = l->radial_next; + } + + if (faceloopWalker_include_face(walker, l)) { + lwalk = BMW_state_add(walker); + lwalk->l = l; + + if (l->f->len != 4) { + lwalk->nocalc = 1; + lwalk->l = origl; + } + else { + lwalk->nocalc = 0; + } + + BLI_ghash_insert(walker->visithash, l->f, NULL); + } + + return f; +} + +/* Edge Ring Walker: + * + * Starts at a tool-flagged edge and walks over the edge ring + * Conditions for starting and stepping the edge ring have been + * tuned in an attempt to match the edge rings built by EditMesh + * + */ + +static void edgeringWalker_begin(BMWalker *walker, void *data) +{ + edgeringWalker *lwalk, owalk; + BMEdge *e = data; + + lwalk = BMW_state_add(walker); + lwalk->l = e->l; + + if (!lwalk->l) { + lwalk->wireedge = e; + return; + } + else { + lwalk->wireedge = NULL; + } + + BLI_ghash_insert(walker->visithash, lwalk->l->e, NULL); + + /* rewin */ + while (BMW_current_state(walker)) { + owalk = *((edgeringWalker *)BMW_current_state(walker)); + BMW_walk(walker); + } + + lwalk = BMW_state_add(walker); + *lwalk = owalk; + + if (lwalk->l->f->len != 4) + lwalk->l = lwalk->l->radial_next; + + BLI_ghash_free(walker->visithash, NULL, NULL); + walker->visithash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh walkers 4"); + BLI_ghash_insert(walker->visithash, lwalk->l->e, NULL); +} + +static void *edgeringWalker_yield(BMWalker *walker) +{ + edgeringWalker *lwalk = BMW_current_state(walker); + + if (!lwalk) { + return NULL; + } + + if (lwalk->l) + return lwalk->l->e; + else + return lwalk->wireedge; +} + +static void *edgeringWalker_step(BMWalker *walker) +{ + edgeringWalker *lwalk = BMW_current_state(walker); + BMEdge *e; + BMLoop *l = lwalk->l /* , *origl = lwalk->l */; + BMesh *bm = walker->bm; + + BMW_state_remove(walker); + + if (!l) + return lwalk->wireedge; + + e = l->e; + if (!BM_edge_is_manifold(bm, e)) { + /* walker won't traverse to a non-manifold edge, but may + * be started on one, and should not traverse *away* from + * a non-manfold edge (non-manifold edges are never in an + * edge ring with manifold edges */ + return e; + } + + l = l->radial_next; + l = l->next->next; + + if ((l->f->len != 4) || !BM_edge_is_manifold(bm, l->e)) { + l = lwalk->l->next->next; + } + + /* only walk to manifold edge */ + if ((l->f->len == 4) && BM_edge_is_manifold(bm, l->e) && + !BLI_ghash_haskey(walker->visithash, l->e)) { + lwalk = BMW_state_add(walker); + lwalk->l = l; + lwalk->wireedge = NULL; + + BLI_ghash_insert(walker->visithash, l->e, NULL); + } + + return e; +} + +static void uvedgeWalker_begin(BMWalker *walker, void *data) +{ + uvedgeWalker *lwalk; + BMLoop *l = data; + + if (BLI_ghash_haskey(walker->visithash, l)) + return; + + lwalk = BMW_state_add(walker); + lwalk->l = l; + BLI_ghash_insert(walker->visithash, l, NULL); +} + +static void *uvedgeWalker_yield(BMWalker *walker) +{ + uvedgeWalker *lwalk = BMW_current_state(walker); + + if (!lwalk) { + return NULL; + } + + return lwalk->l; +} + +static void *uvedgeWalker_step(BMWalker *walker) +{ + uvedgeWalker *lwalk = BMW_current_state(walker); + BMLoop *l, *l2, *l3, *nl, *cl; + BMIter liter; + void *d1, *d2; + int i, j, rlen, type; + + l = lwalk->l; + nl = l->next; + type = walker->bm->ldata.layers[walker->layer].type; + + BMW_state_remove(walker); + + if (walker->mask_edge && !BMO_elem_flag_test(walker->bm, l->e, walker->mask_edge)) + return l; + + /* go over loops around l->v and nl->v and see which ones share l and nl's + * mloopuv's coordinates. in addition, push on l->next if necassary */ + for (i = 0; i < 2; i++) { + cl = i ? nl : l; + BM_ITER(l2, &liter, walker->bm, BM_LOOPS_OF_VERT, cl->v) { + d1 = CustomData_bmesh_get_layer_n(&walker->bm->ldata, + cl->head.data, walker->layer); + + rlen = BM_edge_face_count(l2->e); + for (j = 0; j < rlen; j++) { + if (BLI_ghash_haskey(walker->visithash, l2)) + continue; + if (walker->mask_edge && !(BMO_elem_flag_test(walker->bm, l2->e, walker->mask_edge))) { + if (l2->v != cl->v) + continue; + } + + l3 = l2->v != cl->v ? l2->next : l2; + d2 = CustomData_bmesh_get_layer_n(&walker->bm->ldata, + l3->head.data, walker->layer); + + if (!CustomData_data_equals(type, d1, d2)) + continue; + + lwalk = BMW_state_add(walker); + BLI_ghash_insert(walker->visithash, l2, NULL); + + lwalk->l = l2; + + l2 = l2->radial_next; + } + } + } + + return l; +} + +static BMWalker shell_walker_type = { + shellWalker_begin, + shellWalker_step, + shellWalker_yield, + sizeof(shellWalker), + BMW_BREADTH_FIRST, + BM_EDGE, /* valid restrict masks */ +}; + +static BMWalker islandbound_walker_type = { + islandboundWalker_begin, + islandboundWalker_step, + islandboundWalker_yield, + sizeof(islandboundWalker), + BMW_DEPTH_FIRST, + BM_FACE, /* valid restrict masks */ +}; + +static BMWalker island_walker_type = { + islandWalker_begin, + islandWalker_step, + islandWalker_yield, + sizeof(islandWalker), + BMW_BREADTH_FIRST, + BM_EDGE | BM_FACE, /* valid restrict masks */ +}; + +static BMWalker loop_walker_type = { + loopWalker_begin, + loopWalker_step, + loopWalker_yield, + sizeof(loopWalker), + BMW_DEPTH_FIRST, + 0, /* valid restrict masks */ /* could add flags here but so far none are used */ +}; + +static BMWalker faceloop_walker_type = { + faceloopWalker_begin, + faceloopWalker_step, + faceloopWalker_yield, + sizeof(faceloopWalker), + BMW_DEPTH_FIRST, + 0, /* valid restrict masks */ /* could add flags here but so far none are used */ +}; + +static BMWalker edgering_walker_type = { + edgeringWalker_begin, + edgeringWalker_step, + edgeringWalker_yield, + sizeof(edgeringWalker), + BMW_DEPTH_FIRST, + 0, /* valid restrict masks */ /* could add flags here but so far none are used */ +}; + +static BMWalker loopdata_region_walker_type = { + uvedgeWalker_begin, + uvedgeWalker_step, + uvedgeWalker_yield, + sizeof(uvedgeWalker), + BMW_DEPTH_FIRST, + BM_EDGE, /* valid restrict masks */ +}; + +static BMWalker connected_vertex_walker_type = { + connectedVertexWalker_begin, + connectedVertexWalker_step, + connectedVertexWalker_yield, + sizeof(connectedVertexWalker), + BMW_BREADTH_FIRST, + BM_VERT, /* valid restrict masks */ +}; + +BMWalker *bm_walker_types[] = { + &shell_walker_type, /* BMW_SHELL */ + &loop_walker_type, /* BMW_LOOP */ + &faceloop_walker_type, /* BMW_FACELOOP */ + &edgering_walker_type, /* BMW_EDGERING */ + &loopdata_region_walker_type, /* BMW_LOOPDATA_ISLAND */ + &islandbound_walker_type, /* BMW_ISLANDBOUND */ + &island_walker_type, /* BMW_ISLAND */ + &connected_vertex_walker_type, /* BMW_CONNECTED_VERTEX */ +}; + +int bm_totwalkers = sizeof(bm_walker_types) / sizeof(*bm_walker_types); diff --git a/source/blender/bmesh/intern/bmesh_walkers_private.h b/source/blender/bmesh/intern/bmesh_walkers_private.h new file mode 100644 index 00000000000..2d59a940448 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_walkers_private.h @@ -0,0 +1,89 @@ +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_WALKERS_PRIVATE_H__ +#define __BMESH_WALKERS_PRIVATE_H__ + +/** \file blender/bmesh/intern/bmesh_walkers_private.h + * \ingroup bmesh + * + * BMesh walker API. + */ + +extern BMWalker *bm_walker_types[]; +extern int bm_totwalkers; + + +/* Pointer hiding*/ +typedef struct bmesh_walkerGeneric{ + Link link; + int depth; +} bmesh_walkerGeneric; + + +typedef struct shellWalker{ + bmesh_walkerGeneric header; + BMEdge *curedge; +} shellWalker; + +typedef struct islandboundWalker { + bmesh_walkerGeneric header; + BMLoop *base; + BMVert *lastv; + BMLoop *curloop; +} islandboundWalker; + +typedef struct islandWalker { + bmesh_walkerGeneric header; + BMFace *cur; +} islandWalker; + +typedef struct loopWalker { + bmesh_walkerGeneric header; + BMEdge *cur, *start; + BMVert *lastv, *startv; + int startrad, stage2; +} loopWalker; + +typedef struct faceloopWalker { + bmesh_walkerGeneric header; + BMLoop *l; + int nocalc; +} faceloopWalker; + +typedef struct edgeringWalker { + bmesh_walkerGeneric header; + BMLoop *l; + BMEdge *wireedge; +} edgeringWalker; + +typedef struct uvedgeWalker { + bmesh_walkerGeneric header; + BMLoop *l; +} uvedgeWalker; + +typedef struct connectedVertexWalker { + bmesh_walkerGeneric header; + BMVert *curvert; +} connectedVertexWalker; + +#endif /* __BMESH_WALKERS_PRIVATE_H__ */ diff --git a/source/blender/bmesh/intern/in-progress/BME_conversions.c b/source/blender/bmesh/intern/in-progress/BME_conversions.c new file mode 100644 index 00000000000..279d8f7fd0e --- /dev/null +++ b/source/blender/bmesh/intern/in-progress/BME_conversions.c @@ -0,0 +1,479 @@ +#if 0 + +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle, Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" +#include "BKE_customdata.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_utildefines.h" +#include "BKE_mesh.h" +#include "bmesh.h" +#include "BKE_global.h" +#include "BKE_DerivedMesh.h" +#include "BKE_cdderivedmesh.h" + +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_edgehash.h" +#include "bmesh_private.h" + + + +/* + * BMESH DERIVED MESH CONVERSION FUNCTIONS + * + * The functions in this file provides + * methods for converting to and from + * a bmesh. + * +*/ + + +/* + * DMCORNERS TO LOOPS + * + * Function to convert derived mesh per-face + * corner data (uvs, vertex colors), to n-gon + * per-loop data. + * +*/ + +static void DMcorners_to_loops(BMMesh *bm, CustomData *facedata, int index, BMFace *f, int numCol, int numTex) +{ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i< numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texpoly->tpage = texface[index].tpage; + texpoly->flag = texface[index].flag; + texpoly->transp = texface[index].transp; + texpoly->mode = texface[index].mode; + texpoly->tile = texface[index].tile; + texpoly->unwrap = texface[index].unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + mloopuv->uv[0] = texface[index].uv[j][0]; + mloopuv->uv[1] = texface[index].uv[j][1]; + j++; + l = l->next; + }while(l!=f->loopbase); + } + + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata, CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mloopcol->r = mcol[(index*4)+j].r; + mloopcol->g = mcol[(index*4)+j].g; + mloopcol->b = mcol[(index*4)+j].b; + mloopcol->a = mcol[(index*4)+j].a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +/* + * LOOPS TO DMCORNERS + * + * Function to convert n-gon per-loop data + * (uvs, vertex colors, ect)to derived mesh + * face corner data. + * +*/ + +static void loops_to_DMcorners(BMMesh *bm, CustomData *facedata, int index, BMFace *f,int numCol, int numTex) +{ + int i, j; + BMLoop *l; + MTFace *texface; + MTexPoly *texpoly; + MCol *mcol; + MLoopCol *mloopcol; + MLoopUV *mloopuv; + + for(i=0; i < numTex; i++){ + texface = CustomData_get_layer_n(facedata, CD_MTFACE, i); + texpoly = CustomData_bmesh_get_n(&bm->pdata, f->data, CD_MTEXPOLY, i); + + texface[index].tpage = texpoly->tpage; + texface[index].flag = texpoly->flag; + texface[index].transp = texpoly->transp; + texface[index].mode = texpoly->mode; + texface[index].tile = texpoly->tile; + texface[index].unwrap = texpoly->unwrap; + + j = 0; + l = f->loopbase; + do{ + mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPUV, i); + texface[index].uv[j][0] = mloopuv->uv[0]; + texface[index].uv[j][1] = mloopuv->uv[1]; + j++; + l = l->next; + }while(l!=f->loopbase); + + } + for(i=0; i < numCol; i++){ + mcol = CustomData_get_layer_n(facedata,CD_MCOL, i); + j = 0; + l = f->loopbase; + do{ + mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->data, CD_MLOOPCOL, i); + mcol[(index*4) + j].r = mloopcol->r; + mcol[(index*4) + j].g = mloopcol->g; + mcol[(index*4) + j].b = mloopcol->b; + mcol[(index*4) + j].a = mloopcol->a; + j++; + l = l->next; + }while(l!=f->loopbase); + } +} + +/* + * MVERT TO BMESHVERT + * + * Converts a MVert to a BMVert + * +*/ +static BMVert *mvert_to_bmeshvert(BMMesh *bm, BMVert **vert_array, int index, MVert *mv, CustomData *data) +{ + BMVert *v = NULL; + + v = bmesh_make_vert(bm, mv->co, NULL); + vert_array[index] = v; + if(mv->flag & SELECT) bmesh_set_flag(v, BMESH_SELECT); + v->bweight = mv->bweight/255.0f; + CustomData_to_bmesh_block(data, &bm->vdata, index, &v->data); + + return v; +} + +/* + * MEDGE TO BMESHEDGE + * + * Converts a MEdge to a BMEdge + * +*/ + +static BMEdge *medge_to_bmeshedge(BMMesh *bm, BMVert **vert_array, int index, MEdge *me, CustomData *data, Edge_Hash *edge_hash) +{ + BMVert *v1, *v2; + BMEdge *e = NULL; + + v1 = vert_array[me->v1]; + v2 = vert_array[me->v2]; + e = bmesh_make_edge(bm, v1, v2, NULL, 0); + e->crease = me->crease/255.0f; + e->bweight = me->bweight/255.0f; + if(me->flag & 1) bmesh_set_flag(e, BMESH_SELECT); + if(me->flag & ME_SEAM) bmesh_set_flag(e, BMESH_SEAM); + BLI_edgehash_insert(edge_hash,me->v1,me->v2,e); + CustomData_to_bmesh_block(data, &bm->edata, index, &e->data); + + return e; +} + +/* + * MFACE TO BMESHFACE + * + * Converts a MFace to a BMFace. + * Note that this will fail on eekadoodle + * faces. + * +*/ + +static BMFace *mface_to_bmeshface(BMMesh *bm, BMVert **vert_array, int index, MFace *mf, CustomData *data, Edge_Hash *edge_hash) +{ + BMVert *v1, *v2; + BMEdge *edar[4]; + BMFace *f = NULL; + int len; + + if(mf->v4) len = 4; + else len = 3; + + edar[0] = BLI_edgehash_lookup(edge_hash,mf->v1,mf->v2); + edar[1] = BLI_edgehash_lookup(edge_hash,mf->v2,mf->v3); + if(len == 4){ + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v4); + edar[3] = BLI_edgehash_lookup(edge_hash,mf->v4,mf->v1); + } + else + edar[2] = BLI_edgehash_lookup(edge_hash,mf->v3,mf->v1); + + /*find v1 and v2*/ + v1 = vert_array[mf->v1]; + v2 = vert_array[mf->v2]; + + f = bmesh_make_ngon(bm, v1, v2, edar, len, 0); + f->mat_nr = mf->mat_nr; + if(mf->flag & 1) bmesh_set_flag(f, BMESH_SELECT); + if(mf->flag & ME_HIDE) bmesh_set_flag(f, BMESH_HIDDEN); + CustomData_to_bmesh_block(data, &bm->pdata, index, &f->data); + + return f; +} + +/* + * DERIVEDMESH TO BMESH + * + * Converts a derived mesh to a bmesh. + * +*/ + +BMMesh *derivedmesh_to_bmesh(DerivedMesh *dm) +{ + + BMMesh *bm; + BMVert **vert_array; + BMFace *f=NULL; + + MVert *mvert, *mv; + MEdge *medge, *me; + MFace *mface, *mf; + + int totface,totedge,totvert,i,len, numTex, numCol; + int allocsize[4] = {512,512,2048,512}; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + /*allocate a new bmesh*/ + bm = bmesh_make_mesh(allocsize); + + /*copy custom data layout*/ + CustomData_copy(&dm->vertData, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->edgeData, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&dm->faceData, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*copy face corner data*/ + CustomData_to_bmeshpoly(&dm->faceData, &bm->pdata, &bm->ldata); + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); + CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); + + /*needed later*/ + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + totvert = dm->getNumVerts(dm); + totedge = dm->getNumEdges(dm); + totface = dm->getNumTessFaces(dm); + mvert = dm->getVertArray(dm); + medge = dm->getEdgeArray(dm); + mface = dm->getTessFaceArray(dm); + + vert_array = MEM_mallocN(sizeof(BMVert *)* totvert,"derivedmesh to bmesh vertex pointer array"); + + bmesh_begin_edit(bm); + /*add verts*/ + for(i=0, mv = mvert; i < totvert; i++, mv++) + mvert_to_bmeshvert(bm, vert_array, i, mv, &dm->vertData); + + /*add edges*/ + for(i=0, me = medge; i < totedge; i++, me++) + medge_to_bmeshedge(bm, vert_array, i, me, &dm->edgeData, edge_hash); + + /*add faces.*/ + for(i=0, mf = mface; i < totface; i++, mf++){ + f = mface_to_bmeshface(bm, vert_array, mf, &dm->faceData, edge_hash); + if(f) DMcorners_to_loops(bm, &dm->faceData, i, f, numCol, numTex); + } + + bmesh_end__edit(bm); + BLI_edgehash_free(edge_hash, NULL); + MEM_freeN(vert_array); + return bm; +} + +static void bmeshvert_to_mvert(BMMesh *bm, BMVert *v, MVert *mv, int index, CustomData *data) +{ + copy_v3_v3(mv->co, v->co); + if(bmesh_test_flag(v, BMESH_SELECT)) mv->flag |= 1; + if(bmesh_test_flag(v, BMESH_HIDDEN)) mv->flag |= ME_HIDE; + mv->bweight = (char)(255.0*v1->bweight); + CustomData_from_bmesh_block(&bm->vdata, data, &v1->data, index); +} + +static int bmeshedge_to_medge(BMMesh *bm, BMEdge *e, MEdge *me, int index, CustomData *data) +{ + if(e->head.eflag2){ + if(e->v1->head.eflag1 < e->v2->head.eflag1){ + me->v1 = e->v1->head.eflag1; + me->v2 = e->v2->head.eflag1; + } + else{ + me->v1 = e->v2->head.eflag1; + me->v2 = e->v1->eflag1; + } + + me->crease = (char)(255.0*e->crease); + me->bweight = (char)(255.0*e->bweight); + if(bmesh_test_flag(e, BMESH_SELECT)) me->flag |= 1; + if(bmesh_test_flag(e, BMESH_HIDDEN)) me->flag |= ME_HIDE; + CustomData_from_bmesh_block(&bm->edata, data, &e->data, index); + return 1; + } + return 0; +} + +static int bmeshface_to_mface(BMMesh *bm, BMFace *f, MFace *mf, int index, CustomData *data) +{ + if(f->len==3 || f->len==4){ + mf->v1 = f->loopbase->v->head.eflag1; + mf->v2 = f->loopbase->next->v->head.eflag1; + mf->v3 = f->loopbase->next->next->v->head.eflag1; + if(len == 4){ + mf->v4 = f->loopbase->prev->v->head.eflag1; + } + /* test and rotate indexes if necessary so that verts 3 and 4 aren't index 0 */ + if(mf->v3 == 0 || (f->len == 4 && mf->v4 == 0)){ + test_index_face(mf, NULL, index, f->len); + } + mf->mat_nr = (unsigned char)f->mat_nr; + if(bmesh_test_flag(f, BMESH_SELECT)) mf->flag |= 1; + if(bmesh_test_flag(f, BMESH_HIDDEN)) mf->flag |= ME_HIDE; + CustomData_from_bmesh_block(&bm->pdata, data, &f->data, index); + return TRUE; + } + return FALSE; +} + +/* + * BMESH TO DERIVEDMESH + * + * Converts a bmesh to a derived mesh. + * +*/ + +DerivedMesh *bmesh_to_derivedmesh(BMMesh *bm, DerivedMesh *dm) +{ + MFace *mface = NULL, *mf = NULL; + MEdge *medge = NULL, *me = NULL; + MVert *mvert = NULL, *mv = NULL; + DerivedMesh *result = NULL; + + BMVert *v=NULL; + BMEdge *e=NULL, *oe=NULL; + BMFace *f=NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + + int totface = 0,totedge = 0,totvert = 0,i = 0, numTex, numCol; + + EdgeHash *edge_hash = BLI_edgehash_new(); + + /*get element counts*/ + totvert = bmesh_count_element(bm, BMESH_VERT); + + /*store element indices. Note that the abuse of eflag here should NOT be duplicated!*/ + for(i=0, v = bmeshIterator_init(verts, BM_VERTS, bm, 0); v; v = bmeshIterator_step(verts), i++) + v->head.eflag1 = i; + + /*we cannot have double edges in a derived mesh!*/ + for(e = bmeshIterator_init(edges, BM_EDGES, bm, 0); e; e = bmeshIterator_step(edges)){ + oe = BLI_edgehash_lookup(edge_hash,e->v1->head.eflag1, e->v2->head.eflag1); + if(!oe){ + totedge++; + BLI_edgehash_insert(edge_hash,e->v1->head.eflag1,e->v2->head.eflag1,e); + e->head.eflag2 = 1; + } + else{ + e->head.eflag2 = 0; + } + } + + /*count quads and tris*/ + for(f = bmeshIterator_init(faces, BM_FACES, bm, 0); f; f = bmeshIterator_step(faces)){ + if(f->len == 3 || f->len == 4) totface++; + } + + /*Allocate derivedmesh and copy custom data*/ + result = CDDM_from_template(dm,totvert,totedge,totface); + CustomData_merge(&bm->vdata, &result->vertData, CD_MASK_BMESH, CD_CALLOC, totvert); + CustomData_merge(&bm->edata, &result->edgeData, CD_MASK_BMESH, CD_CALLOC, totedge); + CustomData_merge(&bm->pdata, &result->faceData, CD_MASK_BMESH, CD_CALLOC, totface); + CustomData_from_bmeshpoly(&result->faceData, &bm->pdata, &bm->ldata,totface); + numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); + + /*Make Verts*/ + mvert = CDDM_get_verts(result); + for(i = 0, v = bmeshIterator_init(verts, BM_VERTS, bm, 0); v; v = bmeshIterator_step(verts), i++, mv++){ + bmeshvert_to_mvert(bm,v,mv,i,&result->vertData); + } + + /*Make Edges*/ + medge = CDDM_get_edges(result); + i=0; + for(e = bmeshIterator_init(edges, BM_EDGES, bm, 0); e; e = bmeshIterator_step(edges)){ + me = &medge[i]; + if(bmeshedge_to_medge(bm, e, me, i, &result->edgeData){ + me++; + i++; + } + } + /*Make Faces*/ + if(totface){ + mface = CDDM_get_faces(result); + i=0; + for(f = bmeshIterator_init(faces, BM_FACES, bm, 0); f; f = bmeshIterator_step(faces)){ + mf = &mface[i]; + if(bmeshface_to_mface(bm, f, mf, i, &result->faceData)){ + loops_to_DMcorners(bm, &result->faceData, i, f, numCol, numTex); + i++; + } + } + } + BLI_edgehash_free(edge_hash, NULL); + return result; +} + +#endif |