Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-02-19 22:31:04 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-02-19 22:31:04 +0400
commitafc56a0b10b52d2c8bdfd44257624d776db72f79 (patch)
tree25d194e29b2708ac1143b3dde6dfbeec7ef1d876 /source/blender/bmesh/intern
parentd6deca4e9d6bc7faff98644286571c7934324a9d (diff)
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh/intern')
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c773
-rw-r--r--source/blender/bmesh/intern/bmesh_eulers.c1215
-rw-r--r--source/blender/bmesh/intern/bmesh_inline.c71
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c984
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.c417
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators_inline.c160
-rw-r--r--source/blender/bmesh/intern/bmesh_marking.c910
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c625
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c769
-rw-r--r--source/blender/bmesh/intern/bmesh_newcore.c2024
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c1145
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.c1376
-rw-r--r--source/blender/bmesh/intern/bmesh_operators_private.h106
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c1069
-rw-r--r--source/blender/bmesh/intern/bmesh_private.h98
-rw-r--r--source/blender/bmesh/intern/bmesh_queries.c658
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.c1103
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.h106
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers.c266
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_impl.c911
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_private.h89
-rw-r--r--source/blender/bmesh/intern/in-progress/BME_conversions.c479
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