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
parentd6deca4e9d6bc7faff98644286571c7934324a9d (diff)
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh')
-rw-r--r--source/blender/bmesh/CMakeLists.txt137
-rw-r--r--source/blender/bmesh/SConscript40
-rw-r--r--source/blender/bmesh/bmesh.h380
-rw-r--r--source/blender/bmesh/bmesh_class.h190
-rw-r--r--source/blender/bmesh/bmesh_error.h72
-rw-r--r--source/blender/bmesh/bmesh_iterators.h136
-rw-r--r--source/blender/bmesh/bmesh_marking.h75
-rw-r--r--source/blender/bmesh/bmesh_operator_api.h555
-rw-r--r--source/blender/bmesh/bmesh_operators.h105
-rw-r--r--source/blender/bmesh/bmesh_queries.h123
-rw-r--r--source/blender/bmesh/bmesh_walkers.h139
-rw-r--r--source/blender/bmesh/editmesh_tools.c6384
-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
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c881
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c414
-rw-r--r--source/blender/bmesh/operators/bmo_create.c1412
-rw-r--r--source/blender/bmesh/operators/bmo_dissolve.c559
-rw-r--r--source/blender/bmesh/operators/bmo_dupe.c512
-rw-r--r--source/blender/bmesh/operators/bmo_edgesplit.c426
-rw-r--r--source/blender/bmesh/operators/bmo_extrude.c591
-rw-r--r--source/blender/bmesh/operators/bmo_join_triangles.c373
-rw-r--r--source/blender/bmesh/operators/bmo_mesh_conv.c906
-rw-r--r--source/blender/bmesh/operators/bmo_mirror.c126
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c734
-rw-r--r--source/blender/bmesh/operators/bmo_removedoubles.c590
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.c1104
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.h66
-rw-r--r--source/blender/bmesh/operators/bmo_triangulate.c219
-rw-r--r--source/blender/bmesh/operators/bmo_utils.c1297
-rw-r--r--source/blender/bmesh/tools/BME_bevel.c1011
-rw-r--r--source/blender/bmesh/tools/BME_dupe_ops.c322
-rw-r--r--source/blender/bmesh/tools/BME_duplicate.c310
-rw-r--r--source/blender/bmesh/tools/BME_weld.c341
54 files changed, 35884 insertions, 0 deletions
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
new file mode 100644
index 00000000000..d8349012a5f
--- /dev/null
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -0,0 +1,137 @@
+# $Id: CMakeLists.txt 31746 2010-09-04 05:31:25Z joeedh $
+# ***** 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) 2006, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Jacques Beaurain.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ intern
+ operators
+ ../avi
+ ../blenfont
+ ../blenkernel
+ ../blenlib
+ ../blenloader
+ ../editors/include
+ ../editors/mesh
+ ../gpu
+ ../ikplugin
+ ../imbuf
+ ../makesdna
+ ../makesrna
+ ../modifiers
+ ../nodes
+ ../render/extern/include
+ ../../../extern/glew/include
+ ../../../intern/audaspace/intern
+ ../../../intern/bsp/extern
+ ../../../intern/decimation/extern
+ ../../../intern/elbeem/extern
+ ../../../intern/guardedalloc
+ ../../../intern/iksolver/extern
+ ../../../intern/memutil
+ ../../../intern/mikktspace
+ ../../../intern/opennl/extern
+ ../../../intern/smoke/extern
+ # XXX - BAD LEVEL CALL WM_api.h
+ ../../../source/blender/windowmanager
+)
+
+set(INC_SYS
+ ${ZLIB_INCLUDE_DIRS}
+)
+
+set(SRC
+ operators/bmo_bevel.c
+ operators/bmo_connect.c
+ operators/bmo_create.c
+ operators/bmo_dissolve.c
+ operators/bmo_dupe.c
+ operators/bmo_edgesplit.c
+ operators/bmo_extrude.c
+ operators/bmo_join_triangles.c
+ operators/bmo_mesh_conv.c
+ operators/bmo_mirror.c
+ operators/bmo_primitive.c
+ operators/bmo_removedoubles.c
+ operators/bmo_subdivide.c
+ operators/bmo_subdivide.h
+ operators/bmo_triangulate.c
+ operators/bmo_utils.c
+
+ intern/bmesh_newcore.c
+ intern/bmesh_interp.c
+ intern/bmesh_iterators.c
+ intern/bmesh_iterators_inline.c
+ intern/bmesh_marking.c
+ intern/bmesh_mesh.c
+ intern/bmesh_mods.c
+ intern/bmesh_structure.h
+ intern/bmesh_construct.c
+ intern/bmesh_operators_private.h
+ intern/bmesh_structure.c
+ intern/bmesh_polygon.c
+ intern/bmesh_queries.c
+ intern/bmesh_opdefines.c
+ intern/bmesh_eulers.c
+ intern/bmesh_operators.c
+ intern/bmesh_private.h
+ intern/bmesh_walkers.c
+ intern/bmesh_walkers_impl.c
+ intern/bmesh_walkers_private.h
+ intern/bmesh_inline.c
+
+ tools/BME_bevel.c
+ bmesh.h
+ bmesh_class.h
+ bmesh_error.h
+ bmesh_iterators.h
+ bmesh_marking.h
+ bmesh_operator_api.h
+ bmesh_operators.h
+ bmesh_queries.h
+ bmesh_walkers.h
+)
+
+add_definitions(-DGLEW_STATIC)
+
+if(WITH_LZO)
+ add_definitions(-DWITH_LZO)
+ list(APPEND INC_SYS
+ ../../../extern/lzo/minilzo
+ )
+endif()
+
+if(WITH_LZMA)
+ add_definitions(-DWITH_LZMA)
+ list(APPEND INC_SYS
+ ../../../extern/lzma
+ )
+endif()
+
+if(MSVC)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
+endif()
+
+blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/bmesh/SConscript b/source/blender/bmesh/SConscript
new file mode 100644
index 00000000000..91b7a72bb1f
--- /dev/null
+++ b/source/blender/bmesh/SConscript
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+Import ('env')
+
+cflags=''
+"""
+sources = ['intern/bmesh_eulers.c']
+sources.append('intern/bmesh_mesh.c')
+sources.append('intern/bmesh_polygon.c')
+sources.append('intern/bmesh_structure.c')
+sources.append('intern/bmesh_marking.c')
+
+sources.append('intern/bmesh_construct.c')
+sources.append('intern/bmesh_interp.c')
+sources.append('intern/bmesh_filters.c')
+sources.append('intern/bmesh_iterators.c')
+sources.append('intern/bmesh_mods.c')
+sources.append('intern/bmesh_queries.c')
+sources.append('intern/bmesh_operators.c')
+"""
+#sources.append('api/BME_walkers.c')
+
+
+sources = env.Glob('intern/*.c')
+sources += env.Glob('operators/*.c')
+
+#sources += env.Glob('tools/*.c')
+
+incs = ['#/intern/guardedalloc']
+incs.append('../blenlib')
+incs.append('../blenloader')
+incs.append('../makesdna')
+incs.append('../makesrna')
+incs.append('../blenkernel')
+incs.append('./')
+incs.append('./intern')
+incs.append('../editors/mesh')
+incs.append('../editors/include')
+
+defs = []
+env.BlenderLib ( libname = 'bf_bmesh', sources = sources, includes = Split(incs), libtype = 'core', defines=defs, priority=100, compileflags=cflags )
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
new file mode 100644
index 00000000000..ae85c40e270
--- /dev/null
+++ b/source/blender/bmesh/bmesh.h
@@ -0,0 +1,380 @@
+/*
+ * ***** 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, Levi Schooley.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_H__
+#define __BMESH_H__
+
+/** \file blender/bmesh/bmesh.h
+ * \ingroup bmesh
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "DNA_listBase.h"
+#include "DNA_customdata_types.h"
+
+#include "BLI_utildefines.h"
+
+#include "bmesh_class.h"
+
+/*
+ * short introduction:
+ *
+ * the bmesh structure is a boundary representation, supporting non-manifold
+ * locally modifiable topology. the API is designed to allow clean, maintainable
+ * code, that never (or almost never) directly inspects the underlying structure.
+ *
+ * The API includes iterators, including many useful topological iterators;
+ * walkers, which walk over a mesh, without the risk of hitting the recursion
+ * limit; operators, which are logical, reusable mesh modules; topological
+ * modification functions (like split face, join faces, etc), which are used for
+ * topological manipulations; and some (not yet finished) geometric utility
+ * functions.
+ *
+ * some definitions:
+ *
+ * tool flags: private flags for tools. each operator has it's own private
+ * tool flag "layer", which it can use to flag elements.
+ * tool flags are also used by various other parts of the api.
+ * header flags: stores persistent flags, such as selection state, hide state,
+ * etc. be careful of touching these.
+ */
+
+/*forward declarations*/
+struct BMesh;
+struct BMVert;
+struct BMEdge;
+struct BMFace;
+struct BMLoop;
+struct BMOperator;
+struct Mesh;
+struct EditMesh;
+
+/*
+ * BMHeader
+ *
+ * All mesh elements begin with a BMHeader. This structure
+ * hold several types of data
+ *
+ * 1: The type of the element (vert, edge, loop or face)
+ * 2: Persistant "header" flags/markings (sharp, seam, select, hidden, ect)
+ note that this is different from the "tool" flags.
+ * 3: Unique ID in the bmesh.
+ * 4: some elements for internal record keeping.
+ *
+*/
+
+/* BMHeader->htype (char) */
+#define BM_VERT 1
+#define BM_EDGE 2
+#define BM_LOOP 4
+#define BM_FACE 8
+#define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)
+
+/* BMHeader->hflag (char) */
+#define BM_ELEM_SELECT (1 << 0)
+#define BM_ELEM_HIDDEN (1 << 1)
+#define BM_ELEM_SEAM (1 << 2)
+#define BM_ELEM_SMOOTH (1 << 3) /* used for faces and edges, note from the user POV,
+ * this is a sharp edge when disabled */
+
+#define BM_ELEM_TAG (1 << 4) /* internal flag, used for ensuring correct normals
+ * during multires interpolation, and any other time
+ * when temp tagging is handy.
+ * always assume dirty & clear before use. */
+
+/* we have 3 spare flags which is awesome but since we're limited to 8
+ * only add new flags with care! - campbell */
+/* #define BM_ELEM_SPARE (1<<5) */
+/* #define BM_ELEM_SPARE (1<<6) */
+/* #define BM_ELEM_NONORMCALC (1<<7) */ /* UNUSED */
+
+/* stub */
+void bmesh_error(void);
+
+/* Mesh Level Ops */
+extern int bm_mesh_allocsize_default[4];
+
+/* ob is needed by multires */
+BMesh *BM_mesh_create(struct Object *ob, const int allocsize[4]);
+BMesh *BM_mesh_copy(BMesh *bmold);
+void BM_mesh_free(BMesh *bm);
+
+/* frees mesh, but not actual BMesh struct */
+void BM_mesh_data_free(BMesh *bm);
+void BM_mesh_normals_update(BMesh *bm);
+
+/* Construction */
+BMVert *BM_vert_create(BMesh *bm, const float co[3], const BMVert *example);
+BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *example, int nodouble);
+BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, int nodouble);
+
+BMFace *BM_face_create_quad_tri_v(BMesh *bm,
+ BMVert **verts, int len,
+ const BMFace *example, const int nodouble);
+
+/* easier to use version of BM_face_create_quad_tri_v.
+ * creates edges if necassary. */
+BMFace *BM_face_create_quad_tri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
+ const BMFace *example, const int nodouble);
+
+/* makes an ngon from an unordered list of edges. v1 and v2 must be the verts
+ * defining edges[0], and define the winding of the new face. */
+BMFace *BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble);
+
+/* stuff for dealing with header flags */
+BM_INLINE char BM_elem_flag_test(const void *element, const char hflag);
+
+/* stuff for dealing with header flags */
+BM_INLINE void BM_elem_flag_enable(void *element, const char hflag);
+
+/* stuff for dealing with header flags */
+BM_INLINE void BM_elem_flag_disable(void *element, const char hflag);
+
+/* stuff for dealing BM_elem_flag_toggle header flags */
+BM_INLINE void BM_elem_flag_toggle(void *element, const char hflag);
+BM_INLINE void BM_elem_flag_merge(void *element_a, void *element_b);
+
+/* notes on BM_elem_index_set(...) usage,
+ * Set index is sometimes abused as temp storage, other times we cant be
+ * sure if the index values are valid because certain operations have modified
+ * the mesh structure.
+ *
+ * To set the elements to valid indicies 'BM_mesh_elem_index_ensure' should be used
+ * rather then adding inline loops, however there are cases where we still
+ * set the index directly
+ *
+ * In an attempt to manage this, here are 3 tags Im adding to uses of
+ * 'BM_elem_index_set'
+ *
+ * - 'set_inline' -- since the data is already being looped over set to a
+ * valid value inline.
+ *
+ * - 'set_dirty!' -- intentionally sets the index to an invalid value,
+ * flagging 'bm->elem_index_dirty' so we dont use it.
+ *
+ * - 'set_ok' -- this is valid use since the part of the code is low level.
+ *
+ * - 'set_ok_invalid' -- set to -1 on purpose since this should not be
+ * used without a full array re-index, do this on
+ * adding new vert/edge/faces since they may be added at
+ * the end of the array.
+ *
+ * - 'set_loop' -- currently loop index values are not used used much so
+ * assume each case they are dirty.
+ * - campbell */
+
+BM_INLINE void BM_elem_index_set(void *element, const int index);
+BM_INLINE int BM_elem_index_get(const void *element);
+
+/* todo */
+BMFace *BM_face_copy(BMesh *bm, BMFace *f, int copyedges, int copyverts);
+
+/* copies loop data from adjacent faces */
+void BM_face_copy_shared(BMesh *bm, BMFace *f);
+
+/* copies attributes, e.g. customdata, header flags, etc, from one element
+ * to another of the same type.*/
+void BM_elem_attrs_copy(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target);
+
+/* Modification */
+/* join two adjacent faces together along an edge. note that
+ * the faces must only be joined by on edge. e is the edge you
+ * wish to dissolve.*/
+BMFace *BM_faces_join_pair(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e);
+
+/* generic, flexible join faces function; note that most everything uses
+ * this, including BM_faces_join_pair */
+BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface);
+
+/* split a face along two vertices. returns the newly made face, and sets
+ * the nl member to a loop in the newly created edge.*/
+BMFace *BM_face_split(BMesh *bm, BMFace *f,
+ BMVert *v1, BMVert *v2,
+ struct BMLoop **nl, BMEdge *example);
+
+/* these 2 functions are very similar */
+BMEdge* BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, const int join_faces);
+BMEdge* BM_vert_collapse_edges(BMesh *bm, BMEdge *ke, BMVert *kv);
+
+
+/* splits an edge. ne is set to the new edge created. */
+BMVert *BM_edge_split(BMesh *bm, BMVert *v, BMEdge *e, BMEdge **ne, float percent);
+
+/* split an edge multiple times evenly */
+BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts);
+
+/* connect two verts together, through a face they share. this function may
+ * be removed in the future. */
+BMEdge *BM_verts_connect(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **nf);
+
+/* rotates an edge topologically, either clockwise (if ccw=0) or counterclockwise
+ * (if ccw is 1). */
+BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw);
+
+/* Rip a single face from a vertex fan */
+BMVert *BM_vert_rip(BMesh *bm, BMFace *sf, BMVert *sv);
+
+/*updates a face normal*/
+void BM_face_normal_update(BMesh *bm, BMFace *f);
+void BM_face_normal_update_vcos(BMesh *bm, BMFace *f, float no[3], float (*vertexCos)[3]);
+
+/*updates face and vertex normals incident on an edge*/
+void BM_edge_normals_update(BMesh *bm, BMEdge *e);
+
+/*update a vert normal (but not the faces incident on it)*/
+void BM_vert_normal_update(BMesh *bm, BMVert *v);
+void BM_vert_normal_update_all(BMesh *bm, BMVert *v);
+
+void BM_face_normal_flip(BMesh *bm, BMFace *f);
+
+/*dissolves all faces around a vert, and removes it.*/
+int BM_disk_dissolve(BMesh *bm, BMVert *v);
+
+/* dissolves vert, in more situations then BM_disk_dissolve
+ * (e.g. if the vert is part of a wire edge, etc).*/
+int BM_vert_dissolve(BMesh *bm, BMVert *v);
+
+/* 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]);
+
+/* Interpolation */
+
+/* projects target onto source for customdata interpolation. note: only
+ * does loop customdata. multires is handled. */
+void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source);
+
+/* projects a single loop, target, onto source for customdata interpolation. multires is handled.
+ * if do_vertex is true, target's vert data will also get interpolated.*/
+void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
+ int do_vertex, int do_multires);
+
+/* smoothes boundaries between multires grids, including some borders in adjacent faces */
+void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f);
+
+/* project the multires grid in target onto source's set of multires grids */
+void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source);
+void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source);
+
+void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac);
+void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, struct BMEdge *e1, const float fac);
+void BM_data_layer_add(BMesh *em, CustomData *data, int type);
+void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name);
+void BM_data_layer_free(BMesh *em, CustomData *data, int type);
+void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n);
+float BM_elem_float_data_get(struct CustomData *cd, void *element, int type);
+void BM_elem_float_data_set(struct CustomData *cd, void *element, int type, const float val);
+
+/* get the area of the face */
+float BM_face_area_calc(BMesh *bm, BMFace *f);
+/* computes the centroid of a face, using the center of the bounding box */
+void BM_face_center_bounds_calc(BMesh *bm, BMFace *f, float center[3]);
+/* computes the centroid of a face, using the mean average */
+void BM_face_center_mean_calc(BMesh *bm, BMFace *f, float center[3]);
+
+void BM_mesh_select_mode_flush(BMesh *bm);
+
+/* mode independant flushing up/down */
+void BM_mesh_deselect_flush(BMesh *bm);
+void BM_mesh_select_flush(BMesh *bm);
+
+/* flag conversion funcs */
+char BM_face_flag_from_mflag(const char mflag);
+char BM_edge_flag_from_mflag(const short mflag);
+char BM_vert_flag_from_mflag(const char mflag);
+/* reverse */
+char BM_face_flag_to_mflag(BMFace *f);
+short BM_edge_flag_to_mflag(BMEdge *e);
+char BM_vert_flag_to_mflag(BMVert *v);
+
+
+/* convert MLoop*** in a bmface to mtface and mcol in
+ * an MFace*/
+void BM_loops_to_corners(BMesh *bm, struct Mesh *me, int findex,
+ BMFace *f, int numTex, int numCol);
+
+void BM_loop_kill(BMesh *bm, BMLoop *l);
+void BM_face_kill(BMesh *bm, BMFace *f);
+void BM_edge_kill(BMesh *bm, BMEdge *e);
+void BM_vert_kill(BMesh *bm, BMVert *v);
+
+/* kills all edges associated with f, along with any other faces containing
+ * those edges*/
+void BM_face_edges_kill(BMesh *bm, BMFace *f);
+
+/* kills all verts associated with f, along with any other faces containing
+ * those vertices*/
+void BM_face_verts_kill(BMesh *bm, BMFace *f);
+
+/*clear all data in bm*/
+void BM_mesh_clear(BMesh *bm);
+
+void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag);
+
+void BM_mesh_elem_index_validate(BMesh *bm, const char *location, const char *func,
+ const char *msg_a, const char *msg_b);
+
+BMVert *BM_vert_at_index(BMesh *bm, const int index);
+BMEdge *BM_edge_at_index(BMesh *bm, const int index);
+BMFace *BM_face_at_index(BMesh *bm, const int index);
+
+/*start/stop edit*/
+void bmesh_begin_edit(BMesh *bm, int flag);
+void bmesh_end_edit(BMesh *bm, int flag);
+
+
+#ifdef USE_BMESH_HOLES
+# define BM_FACE_FIRST_LOOP(p) (((BMLoopList *)((p)->loops.first))->first)
+#else
+# define BM_FACE_FIRST_LOOP(p) ((p)->l_first)
+#endif
+
+/* size to use for static arrays when dealing with NGons,
+ * alloc after this limit is reached.
+ * this value is rather arbitrary */
+#define BM_NGON_STACK_SIZE 32
+
+/* avoid inf loop, this value is arbtrary
+ * but should not error on valid cases */
+#define BM_LOOP_RADIAL_MAX 10000
+#define BM_NGON_MAX 100000
+
+/* include the rest of the API */
+#include "bmesh_marking.h"
+#include "bmesh_operator_api.h"
+#include "bmesh_operators.h"
+#include "bmesh_error.h"
+#include "bmesh_queries.h"
+#include "bmesh_iterators.h"
+#include "bmesh_walkers.h"
+#include "intern/bmesh_inline.c"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BMESH_H__ */
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
new file mode 100644
index 00000000000..3a62eaa2eeb
--- /dev/null
+++ b/source/blender/bmesh/bmesh_class.h
@@ -0,0 +1,190 @@
+/*
+ * ***** 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, Levi Schooley, Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_CLASS_H__
+#define __BMESH_CLASS_H__
+
+/** \file blender/bmesh/bmesh_class.h
+ * \ingroup bmesh
+ */
+
+/* bmesh data structures */
+
+/* dissable holes for now, these are ifdef'd because they use more memory and cant be saved in DNA currently */
+// define USE_BMESH_HOLES
+
+struct BMesh;
+struct BMVert;
+struct BMEdge;
+struct BMLoop;
+struct BMFace;
+struct BMFlagLayer;
+struct BMLayerType;
+struct BMSubClassLayer;
+
+struct BLI_mempool;
+struct Object;
+
+/*note: it is very important for BMHeader to start with two
+ pointers. this is a requirement of mempool's method of
+ iteration.
+*/
+typedef struct BMHeader {
+ void *data; /* customdata layers */
+ int index; /* notes:
+ * - Use BM_elem_index_get/SetIndex macros for index
+ * - Unitialized to -1 so we can easily tell its not set.
+ * - Used for edge/vert/face, check BMesh.elem_index_dirty for valid index values,
+ * this is abused by various tools which set it dirty.
+ * - For loops this is used for sorting during tesselation. */
+
+ char htype; /* element geometric type (verts/edges/loops/faces) */
+ char hflag; /* this would be a CD layer, see below */
+} BMHeader;
+
+/* note: need some way to specify custom locations for custom data layers. so we can
+ * make them point directly into structs. and some way to make it only happen to the
+ * active layer, and properly update when switching active layers.*/
+
+typedef struct BMVert {
+ BMHeader head;
+ struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
+
+ float co[3];
+ float no[3];
+ struct BMEdge *e;
+} BMVert;
+
+/* disk link structure, only used by edges */
+typedef struct BMDiskLink {
+ struct BMEdge *next, *prev;
+} BMDiskLink;
+
+typedef struct BMEdge {
+ BMHeader head;
+ struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
+
+ struct BMVert *v1, *v2;
+ struct BMLoop *l;
+
+ /* disk cycle pointers */
+ BMDiskLink v1_disk_link, v2_disk_link;
+} BMEdge;
+
+typedef struct BMLoop {
+ BMHeader head;
+ /* notice no flags layer */
+
+ struct BMVert *v;
+ struct BMEdge *e;
+ struct BMFace *f;
+
+ struct BMLoop *radial_next, *radial_prev;
+
+ /* these were originally commented as private but are used all over the code */
+ /* can't use ListBase API, due to head */
+ struct BMLoop *next, *prev;
+} BMLoop;
+
+/* can cast BMFace/BMEdge/BMVert, but NOT BMLoop, since these dont have a flag layer */
+typedef struct BMElemF {
+ BMHeader head;
+ struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */
+} BMElemF;
+
+#ifdef USE_BMESH_HOLES
+/* eventually, this structure will be used for supporting holes in faces */
+typedef struct BMLoopList {
+ struct BMLoopList *next, *prev;
+ struct BMLoop *first, *last;
+} BMLoopList;
+#endif
+
+typedef struct BMFace {
+ BMHeader head;
+ struct BMFlagLayer *oflags; /* an array of flags, mostly used by the operator stack */
+
+ int len; /*includes all boundary loops*/
+#ifdef USE_BMESH_HOLES
+ int totbounds; /*total boundaries, is one plus the number of holes in the face*/
+ ListBase loops;
+#else
+ BMLoop *l_first;
+#endif
+ float no[3]; /*yes, we do store this here*/
+ short mat_nr;
+} BMFace;
+
+typedef struct BMFlagLayer {
+ short f, pflag; /* flags */
+} BMFlagLayer;
+
+typedef struct BMesh {
+ int totvert, totedge, totloop, totface;
+ int totvertsel, totedgesel, totfacesel;
+
+ /* flag index arrays as being dirty so we can check if they are clean and
+ * avoid looping over the entire vert/edge/face array in those cases.
+ * valid flags are - BM_VERT | BM_EDGE | BM_FACE.
+ * BM_LOOP isnt handled so far. */
+ char elem_index_dirty;
+
+ /*element pools*/
+ struct BLI_mempool *vpool, *epool, *lpool, *fpool;
+
+ /*operator api stuff*/
+ struct BLI_mempool *toolflagpool;
+ int stackdepth;
+ struct BMOperator *currentop;
+
+ CustomData vdata, edata, ldata, pdata;
+
+#ifdef USE_BMESH_HOLES
+ struct BLI_mempool *looplistpool;
+#endif
+
+ /* should be copy of scene select mode */
+ /* stored in BMEditMesh too, this is a bit confusing,
+ * make sure the're in sync!
+ * Only use when the edit mesh cant be accessed - campbell */
+ short selectmode;
+
+ /*ID of the shape key this bmesh came from*/
+ int shapenr;
+
+ int walkers, totflags;
+ ListBase selected, error_stack;
+
+ BMFace *act_face;
+
+ ListBase errorstack;
+ struct Object *ob; /* owner object */
+
+ int opflag; /* current operator flag */
+} BMesh;
+
+#define BM_VERT 1
+#define BM_EDGE 2
+#define BM_LOOP 4
+#define BM_FACE 8
+
+#endif /* __BMESH_CLASS_H__ */
diff --git a/source/blender/bmesh/bmesh_error.h b/source/blender/bmesh/bmesh_error.h
new file mode 100644
index 00000000000..26f499afd22
--- /dev/null
+++ b/source/blender/bmesh/bmesh_error.h
@@ -0,0 +1,72 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_ERROR_H__
+#define __BMESH_ERROR_H__
+
+/** \file blender/bmesh/bmesh_error.h
+ * \ingroup bmesh
+ */
+
+/*----------- bmop error system ----------*/
+
+/* pushes an error onto the bmesh error stack.
+ * if msg is null, then the default message for the errorcode is used.*/
+void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg);
+
+/* gets the topmost error from the stack.
+ * returns error code or 0 if no error.*/
+int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op);
+int BMO_error_occurred(BMesh *bm);
+
+/* same as geterror, only pops the error off the stack as well */
+int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op);
+void BMO_error_clear(BMesh *bm);
+
+#if 0
+//this is meant for handling errors, like self-intersection test failures.
+//it's dangerous to handle errors in general though, so disabled for now.
+
+/* catches an error raised by the op pointed to by catchop.
+ * errorcode is either the errorcode, or BMERR_ALL for any
+ * error.*/
+int BMO_error_catch_op(BMesh *bm, BMOperator *catchop, int errorcode, char **msg);
+#endif
+
+#define BM_ELEM_INDEX_VALIDATE(_bm, _msg_a, _msg_b) \
+ BM_mesh_elem_index_validate(_bm, __FILE__ ":" STRINGIFY(__LINE__), __func__, _msg_a, _msg_b)
+
+/*------ error code defines -------*/
+
+/*error messages*/
+#define BMERR_SELF_INTERSECTING 1
+#define BMERR_DISSOLVEDISK_FAILED 2
+#define BMERR_CONNECTVERT_FAILED 3
+#define BMERR_WALKER_FAILED 4
+#define BMERR_DISSOLVEFACES_FAILED 5
+#define BMERR_DISSOLVEVERTS_FAILED 6
+#define BMERR_TESSELATION 7
+#define BMERR_NONMANIFOLD 8
+#define BMERR_INVALID_SELECTION 9
+#define BMERR_MESH_ERROR 10
+
+#endif /* __BMESH_ERROR_H__ */
diff --git a/source/blender/bmesh/bmesh_iterators.h b/source/blender/bmesh/bmesh_iterators.h
new file mode 100644
index 00000000000..51df9460e8b
--- /dev/null
+++ b/source/blender/bmesh/bmesh_iterators.h
@@ -0,0 +1,136 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_ITERATORS_H__
+#define __BMESH_ITERATORS_H__
+
+/** \file blender/bmesh/bmesh_iterators.h
+ * \ingroup bmesh
+ */
+
+/*
+ * BMESH ITERATORS
+ *
+ * The functions and structures in this file
+ * provide a unified method for iterating over
+ * the elements of a mesh and answering simple
+ * adjacency queries. Tool authors should use
+ * the iterators provided in this file instead
+ * of inspecting the structure directly.
+ *
+*/
+
+#include "BLI_mempool.h"
+
+/* Defines for passing to BM_iter_new.
+ *
+ * "OF" can be substituted for "around"
+ * so BM_VERTS_OF_FACE means "vertices
+ * around a face."
+ */
+
+/* these iterator over all elements of a specific
+ * type in the mesh.*/
+#define BM_VERTS_OF_MESH 1
+#define BM_EDGES_OF_MESH 2
+#define BM_FACES_OF_MESH 3
+
+/*these are topological iterators.*/
+#define BM_EDGES_OF_VERT 4
+#define BM_FACES_OF_VERT 5
+#define BM_LOOPS_OF_VERT 6
+#define BM_FACES_OF_EDGE 7
+#define BM_VERTS_OF_FACE 8
+#define BM_EDGES_OF_FACE 9
+#define BM_LOOPS_OF_FACE 10
+/* returns elements from all boundaries, and returns
+ * the first element at the end to flag that we're entering
+ * a different face hole boundary*/
+#define BM_ALL_LOOPS_OF_FACE 11
+
+/* iterate through loops around this loop, which are fetched
+ * from the other faces in the radial cycle surrounding the
+ * input loop's edge.*/
+#define BM_LOOPS_OF_LOOP 12
+#define BM_LOOPS_OF_EDGE 13
+
+#define BM_ITER(ele, iter, bm, itype, data) \
+ ele = BM_iter_new(iter, bm, itype, data); \
+ for ( ; ele; ele=BM_iter_step(iter))
+
+#define BM_ITER_INDEX(ele, iter, bm, itype, data, indexvar) \
+ ele = BM_iter_new(iter, bm, itype, data); \
+ for (indexvar=0; ele; indexvar++, ele=BM_iter_step(iter))
+
+/*Iterator Structure*/
+typedef struct BMIter {
+ BLI_mempool_iter pooliter;
+
+ struct BMVert *firstvert, *nextvert, *vdata;
+ struct BMEdge *firstedge, *nextedge, *edata;
+ struct BMLoop *firstloop, *nextloop, *ldata, *l;
+ struct BMFace *firstpoly, *nextpoly, *pdata;
+ struct BMesh *bm;
+ void (*begin)(struct BMIter *iter);
+ void *(*step)(struct BMIter *iter);
+ union {
+ void *p;
+ int i;
+ long l;
+ float f;
+ } filter;
+ int count;
+ char itype;
+} BMIter;
+
+void *BM_iter_at_index(struct BMesh *bm, const char htype, void *data, int index);
+int BM_iter_as_array(struct BMesh *bm, const char htype, void *data, void **array, const int len);
+
+/* private for bmesh_iterators_inline.c */
+void bmiter__vert_of_mesh_begin(struct BMIter *iter);
+void *bmiter__vert_of_mesh_step(struct BMIter *iter);
+void bmiter__edge_of_mesh_begin(struct BMIter *iter);
+void *bmiter__edge_of_mesh_step(struct BMIter *iter);
+void bmiter__face_of_mesh_begin(struct BMIter *iter);
+void *bmiter__face_of_mesh_step(struct BMIter *iter);
+void bmiter__edge_of_vert_begin(struct BMIter *iter);
+void *bmiter__edge_of_vert_step(struct BMIter *iter);
+void bmiter__face_of_vert_begin(struct BMIter *iter);
+void *bmiter__face_of_vert_step(struct BMIter *iter);
+void bmiter__loop_of_vert_begin(struct BMIter *iter);
+void *bmiter__loop_of_vert_step(struct BMIter *iter);
+void bmiter__loops_of_edge_begin(struct BMIter *iter);
+void *bmiter__loops_of_edge_step(struct BMIter *iter);
+void bmiter__loops_of_loop_begin(struct BMIter *iter);
+void *bmiter__loops_of_loop_step(struct BMIter *iter);
+void bmiter__face_of_edge_begin(struct BMIter *iter);
+void *bmiter__face_of_edge_step(struct BMIter *iter);
+void bmiter__vert_of_face_begin(struct BMIter *iter);
+void *bmiter__vert_of_face_step(struct BMIter *iter);
+void bmiter__edge_of_face_begin(struct BMIter *iter);
+void *bmiter__edge_of_face_step(struct BMIter *iter);
+void bmiter__loop_of_face_begin(struct BMIter *iter);
+void *bmiter__loop_of_face_step(struct BMIter *iter);
+
+#include "intern/bmesh_iterators_inline.c"
+
+#endif /* __BMESH_ITERATORS_H__ */
diff --git a/source/blender/bmesh/bmesh_marking.h b/source/blender/bmesh/bmesh_marking.h
new file mode 100644
index 00000000000..9b45ac10394
--- /dev/null
+++ b/source/blender/bmesh/bmesh_marking.h
@@ -0,0 +1,75 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_MARKING_H__
+#define __BMESH_MARKING_H__
+
+/** \file blender/bmesh/bmesh_marking.h
+ * \ingroup bmesh
+ */
+
+typedef struct BMEditSelection
+{
+ struct BMEditSelection *next, *prev;
+ void *data;
+ char htype;
+} BMEditSelection;
+
+/* geometry hiding code */
+void BM_elem_hide_set(BMesh *bm, void *element, int hide);
+void BM_vert_hide_set(BMesh *bm, BMVert *v, int hide);
+void BM_edge_hide_set(BMesh *bm, BMEdge *e, int hide);
+void BM_face_hide_set(BMesh *bm, BMFace *f, int hide);
+
+/* Selection code */
+void BM_elem_select_set(struct BMesh *bm, void *element, int select);
+
+/* use BM_elem_flag_test(ele, BM_ELEM_SELECT) to test selection */
+
+void BM_mesh_elem_flag_enable_all(BMesh *bm, const char htype, const char hflag);
+void BM_mesh_elem_flag_disable_all(BMesh *bm, const char htype, const char hflag);
+
+/* individual element select functions, BM_elem_select_set is a shortcut for these
+ * that automatically detects which one to use*/
+void BM_vert_select_set(struct BMesh *bm, struct BMVert *v, int select);
+void BM_edge_select_set(struct BMesh *bm, struct BMEdge *e, int select);
+void BM_face_select_set(struct BMesh *bm, struct BMFace *f, int select);
+
+void BM_select_mode_set(struct BMesh *bm, int selectmode);
+
+/* counts number of elements with flag set */
+int BM_mesh_count_flag(struct BMesh *bm, const char htype, const char hflag, int respecthide);
+
+/* edit selection stuff */
+void BM_active_face_set(BMesh *em, BMFace *f);
+BMFace *BM_active_face_get(BMesh *bm, int sloppy);
+void BM_editselection_center(BMesh *bm, float r_center[3], BMEditSelection *ese);
+void BM_editselection_normal(float r_normal[3], BMEditSelection *ese);
+void BM_editselection_plane(BMesh *bm, float r_plane[3], BMEditSelection *ese);
+
+int BM_select_history_check(BMesh *bm, void *data);
+void BM_select_history_remove(BMesh *bm, void *data);
+void BM_select_history_store(BMesh *bm, void *data);
+void BM_select_history_validate(BMesh *bm);
+void BM_select_history_clear(BMesh *em);
+
+#endif /* __BMESH_MARKING_H__ */
diff --git a/source/blender/bmesh/bmesh_operator_api.h b/source/blender/bmesh/bmesh_operator_api.h
new file mode 100644
index 00000000000..7bbb579685d
--- /dev/null
+++ b/source/blender/bmesh/bmesh_operator_api.h
@@ -0,0 +1,555 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_OPERATOR_API_H__
+#define __BMESH_OPERATOR_API_H__
+
+/** \file blender/bmesh/bmesh_operator_api.h
+ * \ingroup bmesh
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "BLI_memarena.h"
+#include "BLI_ghash.h"
+
+#include "BKE_utildefines.h"
+
+#include <stdarg.h>
+#include <string.h> /* for memcpy */
+
+/*
+ * operators represent logical, executable mesh modules. all topological
+ * operations involving a bmesh has to go through them.
+ *
+ * operators are nested, as are tool flags, which are private to an operator
+ * when it's executed. tool flags are allocated in layers, one per operator
+ * execution, and are used for all internal flagging a tool needs to do.
+ *
+ * each operator has a series of "slots," which can be of the following types:
+ * - simple numerical types
+ * - arrays of elements (e.g. arrays of faces).
+ * - hash mappings.
+ *
+ * each slot is identified by a slot code, as are each operator.
+ * operators, and their slots, are defined in bmesh_opdefines.c (with their
+ * execution functions prototyped in bmesh_operators_private.h), with all their
+ * operator code and slot codes defined in bmesh_operators.h. see
+ * bmesh_opdefines.c and the BMOpDefine struct for how to define new operators.
+ *
+ * in general, operators are fed arrays of elements, created using either
+ * BM_HeaderFlag_To_Slot or BM_Flag_To_Slot (or through one of the format
+ * specifyers in BMO_op_callf or BMO_op_initf). Note that multiple element
+ * types (e.g. faces and edges) can be fed to the same slot array. Operators
+ * act on this data, and possibly spit out data into output slots.
+ *
+ * some notes:
+ * - operators should never read from header flags (e.g. element->head.flag). for
+ * example, if you want an operator to only operate on selected faces, you
+ * should use BM_HeaderFlag_To_Slot to put the selected elements into a slot.
+ * - when you read from an element slot array or mapping, you can either tool-flag
+ * all the elements in it, or read them using an iterator APi (which is
+ * semantically similar to the iterator api in bmesh_iterators.h).
+ */
+
+struct GHashIterator;
+
+/* slot type arrays are terminated by the last member
+ * having a slot type of 0.*/
+#define BMO_OP_SLOT_SENTINEL 0
+#define BMO_OP_SLOT_INT 1
+#define BMO_OP_SLOT_FLT 2
+#define BMO_OP_SLOT_PNT 3
+#define BMO_OP_SLOT_MAT 4
+#define BMO_OP_SLOT_VEC 7
+
+/* after BMO_OP_SLOT_VEC, everything is
+
+ * dynamically allocated arrays. we
+ * leave a space in the identifiers
+ * for future growth.
+ */
+//it's very important this remain a power of two
+#define BMO_OP_SLOT_ELEMENT_BUF 8
+#define BMO_OP_SLOT_MAPPING 9
+/* #define BMO_OP_SLOT_TOTAL_TYPES 10 */ /* not used yet */
+
+/* please ignore all these structures, don't touch them in tool code, except
+ * for when your defining an operator with BMOpDefine.*/
+
+typedef struct BMOpSlot{
+ int slottype;
+ int len;
+ int flag;
+ int index; /* index within slot array */
+ union {
+ int i;
+ float f;
+ void *p;
+ float vec[3];
+ void *buf;
+ GHash *ghash;
+ } data;
+} BMOpSlot;
+
+#define BMO_OP_MAX_SLOTS 16 /* way more than probably needed */
+
+#ifdef slots
+#undef slots
+#endif
+
+typedef struct BMOperator {
+ int type;
+ int slottype;
+ int needflag;
+ int flag;
+ struct BMOpSlot slots[BMO_OP_MAX_SLOTS];
+ void (*exec)(struct BMesh *bm, struct BMOperator *op);
+ MemArena *arena;
+} BMOperator;
+
+#define MAX_SLOTNAME 32
+
+typedef struct BMOSlotType {
+ int type;
+ char name[MAX_SLOTNAME];
+} BMOSlotType;
+
+typedef struct BMOpDefine {
+ const char *name;
+ BMOSlotType slottypes[BMO_OP_MAX_SLOTS];
+ void (*exec)(BMesh *bm, BMOperator *op);
+ int flag;
+} BMOpDefine;
+
+/* BMOpDefine->flag */
+#define BMO_OP_FLAG_UNTAN_MULTIRES 1 /*switch from multires tangent space to absolute coordinates*/
+
+/* ensures consistent normals before operator execution,
+ * restoring the original ones windings/normals afterwards.
+ * keep in mind, this won't work if the input mesh isn't
+ * manifold.*/
+#define BMO_OP_FLAG_RATIONALIZE_NORMALS 2
+
+/*------------- Operator API --------------*/
+
+/* data types that use pointers (arrays, etc) should never
+ * have it set directly. and never use BMO_slot_ptr_set to
+ * pass in a list of edges or any arrays, really.*/
+
+void BMO_op_init(struct BMesh *bm, struct BMOperator *op, const char *opname);
+
+/* executes an operator, pushing and popping a new tool flag
+ * layer as appropriate.*/
+void BMO_op_exec(struct BMesh *bm, struct BMOperator *op);
+
+/* finishes an operator (though note the operator's tool flag is removed
+ * after it finishes executing in BMO_op_exec).*/
+void BMO_op_finish(struct BMesh *bm, struct BMOperator *op);
+
+
+/* tool flag API. never, ever ever should tool code put junk in
+ * header flags (element->head.flag), nor should they use
+ * element->head.eflag1/eflag2. instead, use this api to set
+ * flags.
+ *
+ * if you need to store a value per element, use a
+ * ghash or a mapping slot to do it. */
+
+/* flags 15 and 16 (1<<14 and 1<<15) are reserved for bmesh api use */
+#define BMO_elem_flag_test(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f & (oflag))
+#define BMO_elem_flag_enable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f |= (oflag))
+#define BMO_elem_flag_disable(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f &= ~(oflag))
+#define BMO_elem_flag_toggle(bm, element, oflag) ((element)->oflags[bm->stackdepth-1].f ^= (oflag))
+
+/* profiling showed a significant amount of time spent in BMO_elem_flag_test */
+#if 0
+void BMO_elem_flag_enable(struct BMesh *bm, void *element, const short oflag);
+void BMO_elem_flag_disable(struct BMesh *bm, void *element, const short oflag);
+int BMO_elem_flag_test(struct BMesh *bm, void *element, const short oflag);
+#endif
+
+/* count the number of elements with a specific flag.
+ * type can be a bitmask of BM_FACE, BM_EDGE, or BM_FACE. */
+int BMO_mesh_flag_count(struct BMesh *bm, const short oflag, const char htype);
+
+/*---------formatted operator initialization/execution-----------*/
+/*
+ * this system is used to execute or initialize an operator,
+ * using a formatted-string system.
+ *
+ * for example, BMO_op_callf(bm, "del geom=%hf context=%d", BM_ELEM_SELECT, DEL_FACES);
+ * . . .will execute the delete operator, feeding in selected faces, deleting them.
+ *
+ * the basic format for the format string is:
+ * [operatorname] [slotname]=%[code] [slotname]=%[code]
+ *
+ * as in printf, you pass in one additional argument to the function
+ * for every code.
+ *
+ * the formatting codes are:
+ * %d - put int in slot
+ * %f - put float in slot
+ * %p - put pointer in slot
+ * %h[f/e/v] - put elements with a header flag in slot.
+ * the letters after %h define which element types to use,
+ * so e.g. %hf will do faces, %hfe will do faces and edges,
+ * %hv will do verts, etc. must pass in at least one
+ * element type letter.
+ * %f[f/e/v] - same as %h, except it deals with tool flags instead of
+ * header flags.
+ * %a[f/e/v] - pass all elements (of types specified by f/e/v) to the
+ * slot.
+ * %e - pass in a single element.
+ * %v - pointer to a float vector of length 3.
+ * %m[3/4] - matrix, 3/4 refers to the matrix size, 3 or 4. the
+ * corrusponding argument must be a pointer to
+ * a float matrix.
+ * %s - copy a slot from another op, instead of mapping to one
+ * argument, it maps to two, a pointer to an operator and
+ * a slot name.
+ */
+void BMO_push(BMesh *bm, BMOperator *op);
+void BMO_pop(BMesh *bm);
+
+/*executes an operator*/
+int BMO_op_callf(BMesh *bm, const char *fmt, ...);
+
+/* initializes, but doesn't execute an operator. this is so you can
+ * gain access to the outputs of the operator. note that you have
+ * to execute/finitsh (BMO_op_exec and BMO_op_finish) yourself. */
+int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...);
+
+/* va_list version, used to implement the above two functions,
+ * plus EDBM_CallOpf in bmeshutils.c. */
+int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *fmt, va_list vlist);
+
+/* test whether a named slot exists */
+int BMO_slot_exists(struct BMOperator *op, const char *slotname);
+
+/* get a pointer to a slot. this may be removed layer on from the public API. */
+BMOpSlot *BMO_slot_get(struct BMOperator *op, const char *slotname);
+
+/* copies the data of a slot from one operator to another. src and dst are the
+ * source/destination slot codes, respectively. */
+void BMO_slot_copy(struct BMOperator *source_op, struct BMOperator *dest_op,
+ const char *src, const char *dst);
+
+/* remove tool flagged elements */
+void BMO_remove_tagged_faces(struct BMesh *bm, const short oflag);
+void BMO_remove_tagged_edges(struct BMesh *bm, const short oflag);
+void BMO_remove_tagged_verts(struct BMesh *bm, const short oflag);
+
+/* take care, uses operator flag DEL_WIREVERT */
+void BMO_remove_tagged_context(BMesh *bm, const short oflag, const int type);
+
+/* del "context" slot values, used for operator too */
+enum {
+ DEL_VERTS = 1,
+ DEL_EDGES,
+ DEL_ONLYFACES,
+ DEL_EDGESFACES,
+ DEL_FACES,
+ DEL_ALL ,
+ DEL_ONLYTAGGED
+};
+
+void BMO_op_flag_enable(struct BMesh *bm, struct BMOperator *op, const int op_flag);
+void BMO_op_flag_disable(struct BMesh *bm, struct BMOperator *op, const int op_flag);
+
+void BMO_slot_float_set(struct BMOperator *op, const char *slotname, const float f);
+float BMO_slot_float_get(BMOperator *op, const char *slotname);
+void BMO_slot_int_set(struct BMOperator *op, const char *slotname, const int i);
+int BMO_slot_int_get(BMOperator *op, const char *slotname);
+
+/* don't pass in arrays that are supposed to map to elements this way.
+ *
+ * so, e.g. passing in list of floats per element in another slot is bad.
+ * passing in, e.g. pointer to an editmesh for the conversion operator is fine
+ * though. */
+void BMO_slot_ptr_set(struct BMOperator *op, const char *slotname, void *p);
+void *BMO_slot_ptr_get(BMOperator *op, const char *slotname);
+void BMO_slot_vec_set(struct BMOperator *op, const char *slotname, const float vec[3]);
+void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3]);
+
+/* only supports square mats */
+/* size must be 3 or 4; this api is meant only for transformation matrices.
+ * note that internally the matrix is stored in 4x4 form, and it's safe to
+ * call whichever BMO_Get_Mat* function you want. */
+void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size);
+void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4]);
+void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3]);
+
+void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *op, const char htype, const short oflag);
+
+/* puts every element of type type (which is a bitmask) with tool flag flag,
+ * into a slot. */
+void BMO_slot_from_flag(struct BMesh *bm, struct BMOperator *op, const char *slotname,
+ const short oflag, const char htype);
+
+/* tool-flags all elements inside an element slot array with flag flag. */
+void BMO_slot_buffer_flag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
+ const short oflag, const char htype);
+/* clears tool-flag flag from all elements inside a slot array. */
+void BMO_slot_buffer_flag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
+ const short oflag, const char htype);
+
+/* tool-flags all elements inside an element slot array with flag flag. */
+void BMO_slot_buffer_hflag_enable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
+ const char hflag, const char htype);
+/* clears tool-flag flag from all elements inside a slot array. */
+void BMO_slot_buffer_hflag_disable(struct BMesh *bm, struct BMOperator *op, const char *slotname,
+ const char hflag, const char htype);
+
+/* puts every element of type type (which is a bitmask) with header flag
+ * flag, into a slot. note: ignores hidden elements (e.g. elements with
+ * header flag BM_ELEM_HIDDEN set).*/
+void BMO_slot_from_hflag(struct BMesh *bm, struct BMOperator *op, const char *slotname,
+ const char hflag, const char htype);
+
+/* counts number of elements inside a slot array. */
+int BMO_slot_buf_count(struct BMesh *bm, struct BMOperator *op, const char *slotname);
+int BMO_slot_map_count(struct BMesh *bm, struct BMOperator *op, const char *slotname);
+
+/* Counts the number of edges with tool flag toolflag around
+ */
+int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag);
+
+/* inserts a key/value mapping into a mapping slot. note that it copies the
+ * value, it doesn't store a reference to it. */
+
+#if 0
+
+BM_INLINE void BMO_slot_map_insert(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element, void *data, int len);
+
+/* inserts a key/float mapping pair into a mapping slot. */
+BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element, float val);
+
+/* returns 1 if the specified pointer is in the map. */
+BM_INLINE int BMO_slot_map_contains(BMesh *bm, BMOperator *op, const char *slotname, void *element);
+
+/* returns a point to the value of a specific key. */
+BM_INLINE void *BMO_slot_map_data_get(BMesh *bm, BMOperator *op, const char *slotname, void *element);
+
+/* returns the float part of a key/float pair. */
+BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname, void *element);
+
+#endif
+
+/* flags all elements in a mapping. note that the mapping must only have
+ * bmesh elements in it.*/
+void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op,
+ const char *slotname, const short oflag);
+
+/* pointer versoins of BMO_slot_map_float_get and BMO_slot_map_float_insert.
+ *
+ * do NOT use these for non-operator-api-allocated memory! instead
+ * use BMO_slot_map_data_get and BMO_slot_map_insert, which copies the data. */
+
+#if 0
+BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname, void *key, void *val);
+BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname, void *key);
+#endif
+
+/* this part of the API is used to iterate over element buffer or
+ * mapping slots.
+ *
+ * for example, iterating over the faces in a slot is:
+ *
+ * BMOIter oiter;
+ * BMFace *f;
+ *
+ * f = BMO_iter_new(&oiter, bm, some_operator, "slotname", BM_FACE);
+ * for (; f; f=BMO_iter_step(&oiter)) {
+ * /do something with the face
+ * }
+ *
+ * another example, iterating over a mapping:
+ * BMOIter oiter;
+ * void *key;
+ * void *val;
+ *
+ * key = BMO_iter_new(&oiter, bm, some_operator, "slotname", 0);
+ * for (; key; key=BMO_iter_step(&oiter)) {
+ * val = BMO_iter_map_value(&oiter);
+ * //do something with the key/val pair
+ * //note that val is a pointer to the val data,
+ * //whether it's a float, pointer, whatever.
+ * //
+ * // so to get a pointer, for example, use:
+ * // *((void**)BMO_iter_map_value(&oiter));
+ * //or something like that.
+ * }
+ */
+
+/* contents of this structure are private,
+ * don't directly access. */
+typedef struct BMOIter {
+ BMOpSlot *slot;
+ int cur; //for arrays
+ struct GHashIterator giter;
+ void *val;
+ char restrictmask; /* bitwise '&' with BMHeader.htype */
+} BMOIter;
+
+void *BMO_slot_elem_first(BMOperator *op, const char *slotname);
+
+/* restrictmask restricts the iteration to certain element types
+ * (e.g. combination of BM_VERT, BM_EDGE, BM_FACE), if iterating
+ * over an element buffer (not a mapping).*/
+void *BMO_iter_new(BMOIter *iter, BMesh *bm, BMOperator *op,
+ const char *slotname, const char restrictmask);
+void *BMO_iter_step(BMOIter *iter);
+
+/* returns a pointer to the key value when iterating over mappings.
+ * remember for pointer maps this will be a pointer to a pointer.*/
+void *BMO_iter_map_value(BMOIter *iter);
+
+/* use this for pointer mappings */
+void *BMO_iter_map_value_p(BMOIter *iter);
+
+/* use this for float mappings */
+float BMO_iter_map_value_f(BMOIter *iter);
+
+#define BMO_ITER(ele, iter, bm, op, slotname, restrict) \
+ ele = BMO_iter_new(iter, bm, op, slotname, restrict); \
+ for ( ; ele; ele=BMO_iter_step(iter))
+
+/******************* Inlined Functions********************/
+typedef void (*opexec)(struct BMesh *bm, struct BMOperator *op);
+
+/* mappings map elements to data, which
+ * follows the mapping struct in memory. */
+typedef struct BMOElemMapping {
+ BMHeader *element;
+ int len;
+} BMOElemMapping;
+
+extern const int BMO_OPSLOT_TYPEINFO[];
+
+BM_INLINE void BMO_slot_map_insert(BMesh *UNUSED(bm), BMOperator *op, const char *slotname,
+ void *element, void *data, int len)
+{
+ BMOElemMapping *mapping;
+ BMOpSlot *slot = BMO_slot_get(op, slotname);
+
+ /*sanity check*/
+ if (slot->slottype != BMO_OP_SLOT_MAPPING) {
+ return;
+ }
+
+ mapping = (BMOElemMapping *) BLI_memarena_alloc(op->arena, sizeof(*mapping) + len);
+
+ mapping->element = (BMHeader*) element;
+ mapping->len = len;
+ memcpy(mapping + 1, data, len);
+
+ if (!slot->data.ghash) {
+ slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash,
+ BLI_ghashutil_ptrcmp, "bmesh op");
+ }
+
+ BLI_ghash_insert(slot->data.ghash, element, mapping);
+}
+
+BM_INLINE void BMO_slot_map_int_insert(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element, int val)
+{
+ BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(int));
+}
+
+BM_INLINE void BMO_slot_map_float_insert(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element, float val)
+{
+ BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(float));
+}
+
+BM_INLINE void BMO_slot_map_ptr_insert(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element, void *val)
+{
+ BMO_slot_map_insert(bm, op, slotname, element, &val, sizeof(void*));
+}
+
+BM_INLINE int BMO_slot_map_contains(BMesh *UNUSED(bm), BMOperator *op, const char *slotname, void *element)
+{
+ BMOpSlot *slot = BMO_slot_get(op, slotname);
+
+ /*sanity check*/
+ if (slot->slottype != BMO_OP_SLOT_MAPPING) return 0;
+ if (!slot->data.ghash) return 0;
+
+ return BLI_ghash_haskey(slot->data.ghash, element);
+}
+
+BM_INLINE void *BMO_slot_map_data_get(BMesh *UNUSED(bm), BMOperator *op, const char *slotname,
+ void *element)
+{
+ BMOElemMapping *mapping;
+ BMOpSlot *slot = BMO_slot_get(op, slotname);
+
+ /*sanity check*/
+ if (slot->slottype != BMO_OP_SLOT_MAPPING) return NULL;
+ if (!slot->data.ghash) return NULL;
+
+ mapping = (BMOElemMapping *)BLI_ghash_lookup(slot->data.ghash, element);
+
+ if (!mapping) return NULL;
+
+ return mapping + 1;
+}
+
+BM_INLINE float BMO_slot_map_float_get(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element)
+{
+ float *val = (float*) BMO_slot_map_data_get(bm, op, slotname, element);
+ if (val) return *val;
+
+ return 0.0f;
+}
+
+BM_INLINE int BMO_slot_map_int_get(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element)
+{
+ int *val = (int*) BMO_slot_map_data_get(bm, op, slotname, element);
+ if (val) return *val;
+
+ return 0;
+}
+
+BM_INLINE void *BMO_slot_map_ptr_get(BMesh *bm, BMOperator *op, const char *slotname,
+ void *element)
+{
+ void **val = (void**) BMO_slot_map_data_get(bm, op, slotname, element);
+ if (val) return *val;
+
+ return NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BMESH_OPERATOR_API_H__ */
diff --git a/source/blender/bmesh/bmesh_operators.h b/source/blender/bmesh/bmesh_operators.h
new file mode 100644
index 00000000000..de92f57bc23
--- /dev/null
+++ b/source/blender/bmesh/bmesh_operators.h
@@ -0,0 +1,105 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_OPERATORS_H__
+#define __BMESH_OPERATORS_H__
+
+/** \file blender/bmesh/bmesh_operators.h
+ * \ingroup bmesh
+ */
+
+/*see comments in intern/bmesh_opdefines.c for documentation of specific operators*/
+
+/*--------defines/enumerations for specific operators-------*/
+
+/*quad innervert values*/
+enum {
+ SUBD_INNERVERT,
+ SUBD_PATH,
+ SUBD_FAN,
+ SUBD_STRAIGHT_CUT
+};
+
+/* similar face selection slot values */
+enum {
+ SIMFACE_MATERIAL = 201,
+ SIMFACE_IMAGE,
+ SIMFACE_AREA,
+ SIMFACE_PERIMETER,
+ SIMFACE_NORMAL,
+ SIMFACE_COPLANAR
+};
+
+/* similar edge selection slot values */
+enum {
+ SIMEDGE_LENGTH = 101,
+ SIMEDGE_DIR,
+ SIMEDGE_FACE,
+ SIMEDGE_FACE_ANGLE,
+ SIMEDGE_CREASE,
+ SIMEDGE_SEAM,
+ SIMEDGE_SHARP
+};
+
+/* similar vertex selection slot values */
+enum {
+ SIMVERT_NORMAL = 0,
+ SIMVERT_FACE,
+ SIMVERT_VGROUP
+};
+
+enum {
+ OPUVC_AXIS_X = 1,
+ OPUVC_AXIS_Y
+};
+
+enum {
+ DIRECTION_CW = 1,
+ DIRECTION_CCW
+};
+
+/* vertex path selection values */
+enum {
+ VPATH_SELECT_EDGE_LENGTH = 0,
+ VPATH_SELECT_TOPOLOGICAL
+};
+
+extern BMOpDefine *opdefines[];
+extern int bmesh_total_ops;
+
+/*------specific operator helper functions-------*/
+
+/* executes the duplicate operation, feeding elements of
+ * type flag etypeflag and header flag flag to it. note,
+ * to get more useful information (such as the mapping from
+ * original to new elements) you should run the dupe op manually.*/
+struct Object;
+struct EditMesh;
+
+#if 0
+void BMO_dupe_from_flag(struct BMesh *bm, int etypeflag, const char hflag);
+#endif
+void BM_mesh_esubdivideflag(struct Object *obedit, BMesh *bm, int flag, float smooth,
+ float fractal, int beauty, int numcuts, int seltype,
+ int cornertype, int singleedge, int gridfill, int seed);
+
+#endif /* __BMESH_OPERATORS_H__ */
diff --git a/source/blender/bmesh/bmesh_queries.h b/source/blender/bmesh/bmesh_queries.h
new file mode 100644
index 00000000000..1ae469eb663
--- /dev/null
+++ b/source/blender/bmesh/bmesh_queries.h
@@ -0,0 +1,123 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_QUERIES_H__
+#define __BMESH_QUERIES_H__
+
+/** \file blender/bmesh/bmesh_queries.h
+ * \ingroup bmesh
+ */
+
+#include <stdio.h>
+
+/* Queries */
+
+/* counts number of elements of type type are in the mesh. */
+int BM_mesh_elem_count(struct BMesh *bm, const char htype);
+
+/*returns true if v is in f*/
+int BM_vert_in_face(struct BMFace *f, struct BMVert *v);
+
+// int BM_verts_in_face(struct BMFace *f, struct BMVert **varr, int len);
+int BM_verts_in_face(struct BMesh *bm, struct BMFace *f, struct BMVert **varr, int len);
+
+int BM_edge_in_face(struct BMFace *f, struct BMEdge *e);
+
+int BM_vert_in_edge(struct BMEdge *e, struct BMVert *v);
+
+int BM_verts_in_edge(struct BMVert *v1, struct BMVert *v2, struct BMEdge *e);
+
+/*get opposing vert from v in edge e.*/
+struct BMVert *BM_edge_other_vert(struct BMEdge *e, struct BMVert *v);
+
+/*finds other loop that shares v with e's loop in f.*/
+struct BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v);
+
+/*returns the edge existing between v1 and v2, or NULL if there isn't one.*/
+struct BMEdge *BM_edge_exists(struct BMVert *v1, struct BMVert *v2);
+
+
+/*returns number of edges aroudn a vert*/
+int BM_vert_edge_count(struct BMVert *v);
+
+/*returns number of faces around an edge*/
+int BM_edge_face_count(struct BMEdge *e);
+
+/*returns number of faces around a vert.*/
+int BM_vert_face_count(struct BMVert *v);
+
+
+/*returns true if v is a wire vert*/
+int BM_vert_is_wire(struct BMesh *bm, struct BMVert *v);
+
+/*returns true if e is a wire edge*/
+int BM_edge_is_wire(struct BMesh *bm, struct BMEdge *e);
+
+/* returns FALSE if v is part of a non-manifold edge in the mesh,
+ * I believe this includes if it's part of both a wire edge and
+ * a face.*/
+int BM_vert_is_manifold(struct BMesh *bm, struct BMVert *v);
+
+/* returns FALSE if e is shared by more then two faces. */
+int BM_edge_is_manifold(struct BMesh *bm, struct BMEdge *e);
+
+/* returns true if e is a boundary edge, e.g. has only 1 face bordering it. */
+int BM_edge_is_boundry(struct BMEdge *e);
+
+
+/* returns angle of two faces surrounding an edge. note there must be
+ * exactly two faces sharing the edge.*/
+float BM_edge_face_angle(struct BMesh *bm, struct BMEdge *e);
+
+/* returns angle of two faces surrounding edges. note there must be
+ * exactly two edges sharing the vertex.*/
+float BM_vert_edge_angle(struct BMesh *bm, struct BMVert *v);
+
+/* checks overlapping of existing faces with the verts in varr. */
+int BM_face_exists_overlap(struct BMesh *bm, struct BMVert **varr, int len, struct BMFace **existface);
+
+/* checks if a face defined by varr already exists. */
+int BM_face_exists(BMesh *bm, BMVert **varr, int len, BMFace **existface);
+
+
+/* returns number of edges f1 and f2 share. */
+int BM_face_share_edges(struct BMFace *f1, struct BMFace *f2);
+
+/* returns number of faces e1 and e2 share. */
+int BM_edge_share_faces(struct BMEdge *e1, struct BMEdge *e2);
+
+/* returns bool 1/0 if the edges share a vertex */
+int BM_edge_share_vert(struct BMEdge *e1, struct BMEdge *e2);
+
+/* edge verts in winding order from face */
+void BM_edge_ordered_verts(struct BMEdge *edge, struct BMVert **r_v1, struct BMVert **r_v2);
+
+/* checks if a face is valid in the data structure */
+int BM_face_validate(BMesh *bm, BMFace *face, FILE *err);
+
+/* 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.*/
+void BM_face_legal_splits(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len);
+
+#endif /* __BMESH_QUERIES_H__ */
diff --git a/source/blender/bmesh/bmesh_walkers.h b/source/blender/bmesh/bmesh_walkers.h
new file mode 100644
index 00000000000..a9515dbd3e7
--- /dev/null
+++ b/source/blender/bmesh/bmesh_walkers.h
@@ -0,0 +1,139 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_WALKERS_H__
+#define __BMESH_WALKERS_H__
+
+/** \file blender/bmesh/bmesh_walkers.h
+ * \ingroup bmesh
+ */
+
+#include "BLI_ghash.h"
+
+/*
+ NOTE: do NOT modify topology while walking a mesh!
+*/
+
+typedef enum {
+ BMW_DEPTH_FIRST,
+ BMW_BREADTH_FIRST
+} BMWOrder;
+
+/*Walkers*/
+typedef struct BMWalker {
+ void (*begin) (struct BMWalker *walker, void *start);
+ void *(*step) (struct BMWalker *walker);
+ void *(*yield)(struct BMWalker *walker);
+ int structsize;
+ BMWOrder order;
+ int valid_mask;
+
+ /* runtime */
+ int layer;
+
+ BMesh *bm;
+ BLI_mempool *worklist;
+ ListBase states;
+
+ short mask_vert;
+ short mask_edge;
+ short mask_loop;
+ short mask_face;
+
+ GHash *visithash;
+ int depth;
+} BMWalker;
+
+/* define to make BMW_init more clear */
+#define BMW_MASK_NOP 0
+
+/* initialize a walker. searchmask restricts some (not all) walkers to
+ * elements with a specific tool flag set. flags is specific to each walker.*/
+void BMW_init(struct BMWalker *walker, BMesh *bm, int type,
+ short mask_vert, short mask_edge, short mask_loop, short mask_face,
+ int layer);
+void *BMW_begin(BMWalker *walker, void *start);
+void *BMW_step(struct BMWalker *walker);
+void BMW_end(struct BMWalker *walker);
+int BMW_current_depth(BMWalker *walker);
+
+/*these are used by custom walkers*/
+void *BMW_current_state(BMWalker *walker);
+void *BMW_state_add(BMWalker *walker);
+void BMW_state_remove(BMWalker *walker);
+void *BMW_walk(BMWalker *walker);
+void BMW_reset(BMWalker *walker);
+
+/*
+example of usage, walking over an island of tool flagged faces:
+
+BMWalker walker;
+BMFace *f;
+
+BMW_init(&walker, bm, BMW_ISLAND, SOME_OP_FLAG);
+f = BMW_begin(&walker, some_start_face);
+for (; f; f=BMW_step(&walker)) {
+ //do something with f
+}
+BMW_end(&walker);
+*/
+
+enum {
+ /* walk over connected geometry. can restrict to a search flag,
+ * or not, it's optional.
+ *
+ * takes a vert as an arugment, and spits out edges, restrict flag acts
+ * on the edges as well. */
+ BMW_SHELL,
+ /*walk over an edge loop. search flag doesn't do anything.*/
+ BMW_LOOP,
+ BMW_FACELOOP,
+ BMW_EDGERING,
+ /* #define BMW_RING 2 */
+ /* walk over uv islands; takes a loop as input. restrict flag
+ * restricts the walking to loops whose vert has restrict flag set as a
+ * tool flag.
+ *
+ * the flag parameter to BMW_init maps to a loop customdata layer index.
+ */
+ BMW_LOOPDATA_ISLAND,
+ /* walk over an island of flagged faces. note, that this doesn't work on
+ * non-manifold geometry. it might be better to rewrite this to extract
+ * boundary info from the island walker, rather then directly walking
+ * over the boundary. raises an error if it encouters nonmanifold
+ * geometry. */
+ BMW_ISLANDBOUND,
+ /* walk over all faces in an island of tool flagged faces. */
+ BMW_ISLAND,
+ /* walk from a vertex to all connected vertices. */
+ BMW_CONNECTED_VERTEX,
+ /* end of array index enum vals */
+
+ /* do not intitialze function pointers and struct size in BMW_init */
+ BMW_CUSTOM,
+ BMW_MAXWALKERS
+};
+
+/* use with BMW_init, so as not to confuse with restrict flags */
+#define BMW_NIL_LAY 0
+
+#endif /* __BMESH_WALKERS_H__ */
diff --git a/source/blender/bmesh/editmesh_tools.c b/source/blender/bmesh/editmesh_tools.c
new file mode 100644
index 00000000000..93be1ac5422
--- /dev/null
+++ b/source/blender/bmesh/editmesh_tools.c
@@ -0,0 +1,6384 @@
+#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 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Johnny Matthews, Geoffrey Bantle.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/*
+
+editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BMF_Api.h"
+#include "DNA_mesh_types.h"
+#include "DNA_material_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_key_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_editVert.h"
+#include "BLI_rand.h"
+#include "BLI_ghash.h"
+#include "BLI_linklist.h"
+#include "BLI_heap.h"
+
+#include "BKE_depsgraph.h"
+#include "BKE_customdata.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_utildefines.h"
+#include "BKE_bmesh.h"
+
+#ifdef WITH_VERSE
+#include "BKE_verse.h"
+#endif
+
+#include "BIF_cursors.h"
+#include "BIF_editmesh.h"
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+#include "BIF_graphics.h"
+#include "BIF_interface.h"
+#include "BIF_mywindow.h"
+#include "BIF_screen.h"
+#include "BIF_space.h"
+#include "BIF_resources.h"
+#include "BIF_toolbox.h"
+#include "BIF_transform.h"
+#include "transform.h"
+
+#ifdef WITH_VERSE
+#include "BIF_verse.h"
+#endif
+
+#include "BDR_drawobject.h"
+#include "BDR_editobject.h"
+
+#include "BSE_view.h"
+#include "BSE_edit.h"
+
+#include "blendef.h"
+#include "multires.h"
+#include "mydevice.h"
+
+#include "editmesh.h"
+
+#include "MTC_vectorops.h"
+
+#include "PIL_time.h"
+
+#include "BLO_sys_types.h" // for intptr_t support
+
+/* local prototypes ---------------*/
+void bevel_menu(void);
+static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa);
+
+/********* qsort routines *********/
+
+
+typedef struct xvertsort {
+ float x;
+ EditVert *v1;
+} xvertsort;
+
+static int vergxco(const void *v1, const void *v2)
+{
+ const xvertsort *x1=v1, *x2=v2;
+
+ if( x1->x > x2->x ) return 1;
+ else if( x1->x < x2->x) return -1;
+ return 0;
+}
+
+struct facesort {
+ uintptr_t x;
+ struct EditFace *efa;
+};
+
+
+static int vergface(const void *v1, const void *v2)
+{
+ const struct facesort *x1=v1, *x2=v2;
+
+ if( x1->x > x2->x ) return 1;
+ else if( x1->x < x2->x) return -1;
+ return 0;
+}
+
+
+/* *********************************** */
+
+void convert_to_triface(int direction)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa, *efan, *next;
+ float fac;
+
+ if(multires_test()) return;
+
+ efa= em->faces.last;
+ while(efa) {
+ next= efa->prev;
+ if(efa->v4) {
+ if(efa->f & SELECT) {
+ /* choose shortest diagonal for split */
+ fac= len_v3v3(efa->v1->co, efa->v3->co) - len_v3v3(efa->v2->co, efa->v4->co);
+ /* this makes sure exact squares get split different in both cases */
+ if( (direction==0 && fac<FLT_EPSILON) || (direction && fac>0.0f) ) {
+ efan= EM_face_from_faces(efa, NULL, 0, 1, 2, -1);
+ if(efa->f & SELECT) EM_select_face(efan, 1);
+ efan= EM_face_from_faces(efa, NULL, 0, 2, 3, -1);
+ if(efa->f & SELECT) EM_select_face(efan, 1);
+ }
+ else {
+ efan= EM_face_from_faces(efa, NULL, 0, 1, 3, -1);
+ if(efa->f & SELECT) EM_select_face(efan, 1);
+ efan= EM_face_from_faces(efa, NULL, 1, 2, 3, -1);
+ if(efa->f & SELECT) EM_select_face(efan, 1);
+ }
+
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ }
+ efa= next;
+ }
+
+ EM_fgon_flags(); // redo flags and indices for fgons
+
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+ BIF_undo_push("Convert Quads to Triangles");
+
+}
+
+int removedoublesflag(short flag, short automerge, float limit) /* return amount */
+{
+ /*
+ flag - Test with vert->flags
+ automerge - Alternative operation, merge unselected into selected.
+ Used for "Auto Weld" mode. warning.
+ limit - Quick manhattan distance between verts.
+ */
+
+ EditMesh *em = G.editMesh;
+ /* all verts with (flag & 'flag') are being evaluated */
+ EditVert *eve, *v1, *nextve;
+ EditEdge *eed, *e1, *nexted;
+ EditFace *efa, *nextvl;
+ xvertsort *sortblock, *sb, *sb1;
+ struct facesort *vlsortblock, *vsb, *vsb1;
+ int a, b, test, amount;
+
+ if(multires_test()) return 0;
+
+
+ /* flag 128 is cleared, count */
+
+ /* Normal non weld operation */
+ eve= em->verts.first;
+ amount= 0;
+ while(eve) {
+ eve->f &= ~128;
+ if(eve->h==0 && (automerge || (eve->f & flag))) amount++;
+ eve= eve->next;
+ }
+ if(amount==0) return 0;
+
+ /* allocate memory and qsort */
+ sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub");
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->h==0 && (automerge || (eve->f & flag))) {
+ sb->x= eve->co[0]+eve->co[1]+eve->co[2];
+ sb->v1= eve;
+ sb++;
+ }
+ eve= eve->next;
+ }
+ qsort(sortblock, amount, sizeof(xvertsort), vergxco);
+
+
+ /* test for doubles */
+ sb= sortblock;
+ if (automerge) {
+ for(a=0; a<amount; a++, sb++) {
+ eve= sb->v1;
+ if( (eve->f & 128)==0 ) {
+ sb1= sb+1;
+ for(b=a+1; b<amount && (eve->f & 128)==0; b++, sb1++) {
+ if(sb1->x - sb->x > limit) break;
+
+ /* when automarge, only allow unselected->selected */
+ v1= sb1->v1;
+ if( (v1->f & 128)==0 ) {
+ if ((eve->f & flag)==0 && (v1->f & flag)==1) {
+ if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
+ (float)fabs(v1->co[1]-eve->co[1])<=limit &&
+ (float)fabs(v1->co[2]-eve->co[2])<=limit)
+ { /* unique bit */
+ eve->f|= 128;
+ eve->tmp.v = v1;
+ }
+ } else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) {
+ if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
+ (float)fabs(v1->co[1]-eve->co[1])<=limit &&
+ (float)fabs(v1->co[2]-eve->co[2])<=limit)
+ { /* unique bit */
+ v1->f|= 128;
+ v1->tmp.v = eve;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ for(a=0; a<amount; a++, sb++) {
+ eve= sb->v1;
+ if( (eve->f & 128)==0 ) {
+ sb1= sb+1;
+ for(b=a+1; b<amount; b++, sb1++) {
+ /* first test: simpel dist */
+ if(sb1->x - sb->x > limit) break;
+ v1= sb1->v1;
+
+ /* second test: is vertex allowed */
+ if( (v1->f & 128)==0 ) {
+ if( (float)fabs(v1->co[0]-eve->co[0])<=limit &&
+ (float)fabs(v1->co[1]-eve->co[1])<=limit &&
+ (float)fabs(v1->co[2]-eve->co[2])<=limit)
+ {
+ v1->f|= 128;
+ v1->tmp.v = eve;
+ }
+ }
+ }
+ }
+ }
+ }
+ MEM_freeN(sortblock);
+
+ if (!automerge)
+ for(eve = em->verts.first; eve; eve=eve->next)
+ if((eve->f & flag) && (eve->f & 128))
+ EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f);
+
+ /* test edges and insert again */
+ eed= em->edges.first;
+ while(eed) {
+ eed->f2= 0;
+ eed= eed->next;
+ }
+ eed= em->edges.last;
+ while(eed) {
+ nexted= eed->prev;
+
+ if(eed->f2==0) {
+ if( (eed->v1->f & 128) || (eed->v2->f & 128) ) {
+ remedge(eed);
+
+ if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v;
+ if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v;
+ e1= addedgelist(eed->v1, eed->v2, eed);
+
+ if(e1) {
+ e1->f2= 1;
+ if(eed->f & SELECT)
+ e1->f |= SELECT;
+ }
+ if(e1!=eed) free_editedge(eed);
+ }
+ }
+ eed= nexted;
+ }
+
+ /* first count amount of test faces */
+ efa= (struct EditFace *)em->faces.first;
+ amount= 0;
+ while(efa) {
+ efa->f1= 0;
+ if(efa->v1->f & 128) efa->f1= 1;
+ else if(efa->v2->f & 128) efa->f1= 1;
+ else if(efa->v3->f & 128) efa->f1= 1;
+ else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1;
+
+ if(efa->f1==1) amount++;
+ efa= efa->next;
+ }
+
+ /* test faces for double vertices, and if needed remove them */
+ efa= (struct EditFace *)em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ if(efa->f1==1) {
+
+ if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v;
+ if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v;
+ if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v;
+ if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v;
+
+ test= 0;
+ if(efa->v1==efa->v2) test+=1;
+ if(efa->v2==efa->v3) test+=2;
+ if(efa->v3==efa->v1) test+=4;
+ if(efa->v4==efa->v1) test+=8;
+ if(efa->v3==efa->v4) test+=16;
+ if(efa->v2==efa->v4) test+=32;
+
+ if(test) {
+ if(efa->v4) {
+ if(test==1 || test==2) {
+ efa->v2= efa->v3;
+ efa->v3= efa->v4;
+ efa->v4= 0;
+
+ EM_data_interp_from_faces(efa, NULL, efa, 0, 2, 3, 3);
+
+ test= 0;
+ }
+ else if(test==8 || test==16) {
+ efa->v4= 0;
+ test= 0;
+ }
+ else {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ amount--;
+ }
+ }
+ else {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ amount--;
+ }
+ }
+
+ if(test==0) {
+ /* set edge pointers */
+ efa->e1= findedgelist(efa->v1, efa->v2);
+ efa->e2= findedgelist(efa->v2, efa->v3);
+ if(efa->v4==0) {
+ efa->e3= findedgelist(efa->v3, efa->v1);
+ efa->e4= 0;
+ }
+ else {
+ efa->e3= findedgelist(efa->v3, efa->v4);
+ efa->e4= findedgelist(efa->v4, efa->v1);
+ }
+ }
+ }
+ efa= nextvl;
+ }
+
+ /* double faces: sort block */
+ /* count again, now all selected faces */
+ amount= 0;
+ efa= em->faces.first;
+ while(efa) {
+ efa->f1= 0;
+ if(faceselectedOR(efa, 1)) {
+ efa->f1= 1;
+ amount++;
+ }
+ efa= efa->next;
+ }
+
+ if(amount) {
+ /* double faces: sort block */
+ vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub");
+ efa= em->faces.first;
+ while(efa) {
+ if(efa->f1 & 1) {
+ if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4);
+ else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3);
+
+ vsb->efa= efa;
+ vsb++;
+ }
+ efa= efa->next;
+ }
+
+ qsort(vlsortblock, amount, sizeof(struct facesort), vergface);
+
+ vsb= vlsortblock;
+ for(a=0; a<amount; a++) {
+ efa= vsb->efa;
+ if( (efa->f1 & 128)==0 ) {
+ vsb1= vsb+1;
+
+ for(b=a+1; b<amount; b++) {
+
+ /* first test: same pointer? */
+ if(vsb->x != vsb1->x) break;
+
+ /* second test: is test permitted? */
+ efa= vsb1->efa;
+ if( (efa->f1 & 128)==0 ) {
+ if( compareface(efa, vsb->efa)) efa->f1 |= 128;
+
+ }
+ vsb1++;
+ }
+ }
+ vsb++;
+ }
+
+ MEM_freeN(vlsortblock);
+
+ /* remove double faces */
+ efa= (struct EditFace *)em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ if(efa->f1 & 128) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ efa= nextvl;
+ }
+ }
+
+ /* remove double vertices */
+ a= 0;
+ eve= (struct EditVert *)em->verts.first;
+ while(eve) {
+ nextve= eve->next;
+ if(automerge || eve->f & flag) {
+ if(eve->f & 128) {
+ a++;
+ BLI_remlink(&em->verts, eve);
+ free_editvert(eve);
+ }
+ }
+ eve= nextve;
+ }
+
+#ifdef WITH_VERSE
+ if((a>0) && (G.editMesh->vnode)) {
+ sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode);
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+ }
+#endif
+
+ return a; /* amount */
+}
+
+/* called from buttons */
+static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index)
+{
+ xvertsort *sortblock = userData;
+
+ sortblock[index].x = x;
+}
+void xsortvert_flag(int flag)
+{
+ EditMesh *em = G.editMesh;
+ /* all verts with (flag & 'flag') are sorted */
+ EditVert *eve;
+ xvertsort *sortblock;
+ ListBase tbase;
+ int i, amount = BLI_countlist(&em->verts);
+
+ if(multires_test()) return;
+
+ sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort");
+ for (i=0,eve=em->verts.first; eve; i++,eve=eve->next)
+ if(eve->f & flag)
+ sortblock[i].v1 = eve;
+ mesh_foreachScreenVert(xsortvert_flag__doSetX, sortblock, V3D_CLIP_TEST_OFF);
+ qsort(sortblock, amount, sizeof(xvertsort), vergxco);
+
+ /* make temporal listbase */
+ tbase.first= tbase.last= 0;
+ for (i=0; i<amount; i++) {
+ eve = sortblock[i].v1;
+
+ if (eve) {
+ BLI_remlink(&em->verts, eve);
+ BLI_addtail(&tbase, eve);
+ }
+ }
+
+ addlisttolist(&em->verts, &tbase);
+
+ MEM_freeN(sortblock);
+
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+
+ BIF_undo_push("Xsort");
+
+}
+
+/* called from buttons */
+void hashvert_flag(int flag)
+{
+ /* switch vertex order using hash table */
+ EditMesh *em = G.editMesh;
+ EditVert *eve;
+ struct xvertsort *sortblock, *sb, onth, *newsort;
+ ListBase tbase;
+ int amount, a, b;
+
+ if(multires_test()) return;
+
+ /* count */
+ eve= em->verts.first;
+ amount= 0;
+ while(eve) {
+ if(eve->f & flag) amount++;
+ eve= eve->next;
+ }
+ if(amount==0) return;
+
+ /* allocate memory */
+ sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub");
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->f & flag) {
+ sb->v1= eve;
+ sb++;
+ }
+ eve= eve->next;
+ }
+
+ BLI_srand(1);
+
+ sb= sortblock;
+ for(a=0; a<amount; a++, sb++) {
+ b= (int)(amount*BLI_drand());
+ if(b>=0 && b<amount) {
+ newsort= sortblock+b;
+ onth= *sb;
+ *sb= *newsort;
+ *newsort= onth;
+ }
+ }
+
+ /* make temporal listbase */
+ tbase.first= tbase.last= 0;
+ sb= sortblock;
+ while(amount--) {
+ eve= sb->v1;
+ BLI_remlink(&em->verts, eve);
+ BLI_addtail(&tbase, eve);
+ sb++;
+ }
+
+ addlisttolist(&em->verts, &tbase);
+
+ MEM_freeN(sortblock);
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+ BIF_undo_push("Hash");
+
+}
+
+/* generic extern called extruder */
+void extrude_mesh(void)
+{
+ float nor[3]= {0.0, 0.0, 0.0};
+ short nr, transmode= 0;
+
+ TEST_EDITMESH
+ if(multires_test()) return;
+
+ if(G.scene->selectmode & SCE_SELECT_VERTEX) {
+ if(G.totvertsel==0) nr= 0;
+ else if(G.totvertsel==1) nr= 4;
+ else if(G.totedgesel==0) nr= 4;
+ else if(G.totfacesel==0)
+ nr= pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
+ else if(G.totfacesel==1)
+ nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
+ else
+ nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
+ }
+ else if(G.scene->selectmode & SCE_SELECT_EDGE) {
+ if (G.totedgesel==0) nr = 0;
+ else if (G.totedgesel==1) nr = 3;
+ else if(G.totfacesel==0) nr = 3;
+ else if(G.totfacesel==1)
+ nr= pupmenu("Extrude %t|Region %x1|Only Edges%x3");
+ else
+ nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
+ }
+ else {
+ if (G.totfacesel == 0) nr = 0;
+ else if (G.totfacesel == 1) nr = 1;
+ else
+ nr= pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
+ }
+
+ if(nr<1) return;
+
+ if(nr==1) transmode= extrudeflag(SELECT, nor);
+ else if(nr==4) transmode= extrudeflag_verts_indiv(SELECT, nor);
+ else if(nr==3) transmode= extrudeflag_edges_indiv(SELECT, nor);
+ else transmode= extrudeflag_face_indiv(SELECT, nor);
+
+ if(transmode==0) {
+ error("No valid selection");
+ }
+ else {
+ EM_fgon_flags();
+ countall();
+
+ /* We need to force immediate calculation here because
+ * transform may use derived objects (which are now stale).
+ *
+ * This shouldn't be necessary, derived queries should be
+ * automatically building this data if invalid. Or something.
+ */
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ object_handle_update(G.obedit);
+
+ /* individual faces? */
+ BIF_TransformSetUndo("Extrude");
+ if(nr==2) {
+ initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR);
+ Transform();
+ }
+ else {
+ initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR);
+ if(transmode=='n') {
+ mul_m4_v3(G.obedit->obmat, nor);
+ sub_v3_v3v3(nor, nor, G.obedit->obmat[3]);
+ BIF_setSingleAxisConstraint(nor, "along normal");
+ }
+ Transform();
+ }
+ }
+
+}
+
+void split_mesh(void)
+{
+
+ TEST_EDITMESH
+ if(multires_test()) return;
+
+ if(okee(" Split ")==0) return;
+
+ waitcursor(1);
+
+ /* make duplicate first */
+ adduplicateflag(SELECT);
+ /* old faces have flag 128 set, delete them */
+ delfaceflag(128);
+ recalc_editnormals();
+
+ waitcursor(0);
+
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+
+ BIF_undo_push("Split");
+
+}
+
+void extrude_repeat_mesh(int steps, float offs)
+{
+ float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0};
+ short a;
+
+ TEST_EDITMESH
+ if(multires_test()) return;
+
+ /* dvec */
+ dvec[0]= G.vd->persinv[2][0];
+ dvec[1]= G.vd->persinv[2][1];
+ dvec[2]= G.vd->persinv[2][2];
+ normalize_v3(dvec);
+ dvec[0]*= offs;
+ dvec[1]*= offs;
+ dvec[2]*= offs;
+
+ /* base correction */
+ copy_m3_m4(bmat, G.obedit->obmat);
+ invert_m3_m3(tmat, bmat);
+ mul_m3_v3(tmat, dvec);
+
+ for(a=0; a<steps; a++) {
+ extrudeflag(SELECT, nor);
+ translateflag(SELECT, dvec);
+ }
+
+ recalc_editnormals();
+
+ EM_fgon_flags();
+ countall();
+
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+
+ BIF_undo_push("Extrude Repeat");
+}
+
+void spin_mesh(int steps, float degr, float *dvec, int mode)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *eve,*nextve;
+ float nor[3]= {0.0, 0.0, 0.0};
+ float *curs, si,n[3],q[4],cmat[3][3],imat[3][3], tmat[3][3];
+ float cent[3],bmat[3][3];
+ float phi;
+ short a,ok;
+
+ TEST_EDITMESH
+ if(multires_test()) return;
+
+ /* imat and center and size */
+ copy_m3_m4(bmat, G.obedit->obmat);
+ invert_m3_m3(imat,bmat);
+
+ curs= give_cursor();
+ copy_v3_v3(cent, curs);
+ cent[0]-= G.obedit->obmat[3][0];
+ cent[1]-= G.obedit->obmat[3][1];
+ cent[2]-= G.obedit->obmat[3][2];
+ mul_m3_v3(imat, cent);
+
+ phi= degr*M_PI/360.0;
+ phi/= steps;
+ if(G.scene->toolsettings->editbutflag & B_CLOCKWISE) phi= -phi;
+
+ if(dvec) {
+ n[0]= G.vd->viewinv[1][0];
+ n[1]= G.vd->viewinv[1][1];
+ n[2]= G.vd->viewinv[1][2];
+ } else {
+ n[0]= G.vd->viewinv[2][0];
+ n[1]= G.vd->viewinv[2][1];
+ n[2]= G.vd->viewinv[2][2];
+ }
+ normalize_v3(n);
+
+ q[0]= (float)cos(phi);
+ si= (float)sin(phi);
+ q[1]= n[0]*si;
+ q[2]= n[1]*si;
+ q[3]= n[2]*si;
+ quat_to_mat3( cmat,q);
+
+ mul_m3_m3m3(tmat,cmat,bmat);
+ mul_m3_m3m3(bmat,imat,tmat);
+
+ if(mode==0) if(G.scene->toolsettings->editbutflag & B_KEEPORIG) adduplicateflag(1);
+ ok= 1;
+
+ for(a=0;a<steps;a++) {
+ if(mode==0) ok= extrudeflag(SELECT, nor);
+ else adduplicateflag(SELECT);
+ if(ok==0) {
+ error("No valid vertices are selected");
+ break;
+ }
+ rotateflag(SELECT, cent, bmat);
+ if(dvec) {
+ mul_m3_v3(bmat,dvec);
+ translateflag(SELECT, dvec);
+ }
+ }
+
+ if(ok==0) {
+ /* no vertices or only loose ones selected, remove duplicates */
+ eve= em->verts.first;
+ while(eve) {
+ nextve= eve->next;
+ if(eve->f & SELECT) {
+ BLI_remlink(&em->verts,eve);
+ free_editvert(eve);
+ }
+ eve= nextve;
+ }
+ }
+ recalc_editnormals();
+
+ EM_fgon_flags();
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+
+
+ if(dvec==NULL) BIF_undo_push("Spin");
+}
+
+void screw_mesh(int steps, int turns)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *eve,*v1=0,*v2=0;
+ EditEdge *eed;
+ float dvec[3], nor[3];
+
+ TEST_EDITMESH
+ if(multires_test()) return;
+
+ /* clear flags */
+ eve= em->verts.first;
+ while(eve) {
+ eve->f1= 0;
+ eve= eve->next;
+ }
+ /* edges set flags in verts */
+ eed= em->edges.first;
+ while(eed) {
+ if(eed->v1->f & SELECT) {
+ if(eed->v2->f & SELECT) {
+ /* watch: f1 is a byte */
+ if(eed->v1->f1<2) eed->v1->f1++;
+ if(eed->v2->f1<2) eed->v2->f1++;
+ }
+ }
+ eed= eed->next;
+ }
+ /* find two vertices with eve->f1==1, more or less is wrong */
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->f1==1) {
+ if(v1==0) v1= eve;
+ else if(v2==0) v2= eve;
+ else {
+ v1=0;
+ break;
+ }
+ }
+ eve= eve->next;
+ }
+ if(v1==0 || v2==0) {
+ error("You have to select a string of connected vertices too");
+ return;
+ }
+
+ /* calculate dvec */
+ dvec[0]= ( (v1->co[0]- v2->co[0]) )/(steps);
+ dvec[1]= ( (v1->co[1]- v2->co[1]) )/(steps);
+ dvec[2]= ( (v1->co[2]- v2->co[2]) )/(steps);
+
+ copy_v3_v3(nor, G.obedit->obmat[2]);
+
+ if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) {
+ dvec[0]= -dvec[0];
+ dvec[1]= -dvec[1];
+ dvec[2]= -dvec[2];
+ }
+
+ spin_mesh(turns*steps, turns*360, dvec, 0);
+
+ BIF_undo_push("Spin");
+}
+
+
+static void erase_edges(ListBase *l)
+{
+ EditEdge *ed, *nexted;
+
+ ed = (EditEdge *) l->first;
+ while(ed) {
+ nexted= ed->next;
+ if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) {
+ remedge(ed);
+ free_editedge(ed);
+ }
+ ed= nexted;
+ }
+}
+
+static void erase_faces(ListBase *l)
+{
+ EditFace *f, *nextf;
+
+ f = (EditFace *) l->first;
+
+ while(f) {
+ nextf= f->next;
+ if( faceselectedOR(f, SELECT) ) {
+ BLI_remlink(l, f);
+ free_editface(f);
+ }
+ f = nextf;
+ }
+}
+
+static void erase_vertices(ListBase *l)
+{
+ EditVert *v, *nextv;
+
+ v = (EditVert *) l->first;
+ while(v) {
+ nextv= v->next;
+ if(v->f & 1) {
+ BLI_remlink(l, v);
+ free_editvert(v);
+ }
+ v = nextv;
+ }
+}
+
+void delete_mesh(void)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa, *nextvl;
+ EditVert *eve,*nextve;
+ EditEdge *eed,*nexted;
+ short event;
+ int count;
+ char *str="Erase";
+
+ TEST_EDITMESH
+ if(multires_test()) return;
+
+ event= pupmenu("Erase %t|Vertices%x10|Edges%x1|Faces%x2|All%x3|Edges & Faces%x4|Only Faces%x5|Edge Loop%x6");
+ if(event<1) return;
+
+ if(event==10 ) {
+ str= "Erase Vertices";
+ erase_edges(&em->edges);
+ erase_faces(&em->faces);
+ erase_vertices(&em->verts);
+ }
+ else if(event==6) {
+ if(!EdgeLoopDelete())
+ return;
+
+ str= "Erase Edge Loop";
+ }
+ else if(event==4) {
+ str= "Erase Edges & Faces";
+ efa= em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ /* delete only faces with 1 or more edges selected */
+ count= 0;
+ if(efa->e1->f & SELECT) count++;
+ if(efa->e2->f & SELECT) count++;
+ if(efa->e3->f & SELECT) count++;
+ if(efa->e4 && (efa->e4->f & SELECT)) count++;
+ if(count) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ efa= nextvl;
+ }
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+ if(eed->f & SELECT) {
+ remedge(eed);
+ free_editedge(eed);
+ }
+ eed= nexted;
+ }
+ efa= em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ event=0;
+ if( efa->v1->f & SELECT) event++;
+ if( efa->v2->f & SELECT) event++;
+ if( efa->v3->f & SELECT) event++;
+ if(efa->v4 && (efa->v4->f & SELECT)) event++;
+
+ if(event>1) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ efa= nextvl;
+ }
+ }
+ else if(event==1) {
+ str= "Erase Edges";
+ // faces first
+ efa= em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ event=0;
+ if( efa->e1->f & SELECT) event++;
+ if( efa->e2->f & SELECT) event++;
+ if( efa->e3->f & SELECT) event++;
+ if(efa->e4 && (efa->e4->f & SELECT)) event++;
+
+ if(event) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ efa= nextvl;
+ }
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+ if(eed->f & SELECT) {
+ remedge(eed);
+ free_editedge(eed);
+ }
+ eed= nexted;
+ }
+ /* to remove loose vertices: */
+ eed= em->edges.first;
+ while(eed) {
+ if( eed->v1->f & SELECT) eed->v1->f-=SELECT;
+ if( eed->v2->f & SELECT) eed->v2->f-=SELECT;
+ eed= eed->next;
+ }
+ eve= em->verts.first;
+ while(eve) {
+ nextve= eve->next;
+ if(eve->f & SELECT) {
+ BLI_remlink(&em->verts,eve);
+ free_editvert(eve);
+ }
+ eve= nextve;
+ }
+
+ }
+ else if(event==2) {
+ str="Erase Faces";
+ delfaceflag(SELECT);
+ }
+ else if(event==3) {
+ str= "Erase All";
+ if(em->verts.first) free_vertlist(&em->verts);
+ if(em->edges.first) free_edgelist(&em->edges);
+ if(em->faces.first) free_facelist(&em->faces);
+ if(em->selected.first) BLI_freelistN(&(em->selected));
+ }
+ else if(event==5) {
+ str= "Erase Only Faces";
+ efa= em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ if(efa->f & SELECT) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ efa= nextvl;
+ }
+ }
+
+ EM_fgon_flags(); // redo flags and indices for fgons
+
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ BIF_undo_push(str);
+}
+
+
+/* Got this from scanfill.c. You will need to juggle around the
+ * callbacks for the scanfill.c code a bit for this to work. */
+void fill_mesh(void)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *eve,*v1;
+ EditEdge *eed,*e1,*nexted;
+ EditFace *efa,*nextvl, *efan;
+ short ok;
+
+ if(G.obedit==0 || (G.obedit->type!=OB_MESH)) return;
+ if(multires_test()) return;
+
+ waitcursor(1);
+
+ /* copy all selected vertices */
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->f & SELECT) {
+ v1= BLI_addfillvert(eve->co);
+ eve->tmp.v= v1;
+ v1->tmp.v= eve;
+ v1->xs= 0; // used for counting edges
+ }
+ eve= eve->next;
+ }
+ /* copy all selected edges */
+ eed= em->edges.first;
+ while(eed) {
+ if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) {
+ e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v);
+ e1->v1->xs++;
+ e1->v2->xs++;
+ }
+ eed= eed->next;
+ }
+ /* from all selected faces: remove vertices and edges to prevent doubles */
+ /* all edges add values, faces subtract,
+ then remove edges with vertices ->xs<2 */
+ efa= em->faces.first;
+ ok= 0;
+ while(efa) {
+ nextvl= efa->next;
+ if( faceselectedAND(efa, 1) ) {
+ efa->v1->tmp.v->xs--;
+ efa->v2->tmp.v->xs--;
+ efa->v3->tmp.v->xs--;
+ if(efa->v4) efa->v4->tmp.v->xs--;
+ ok= 1;
+
+ }
+ efa= nextvl;
+ }
+ if(ok) { /* there are faces selected */
+ eed= filledgebase.first;
+ while(eed) {
+ nexted= eed->next;
+ if(eed->v1->xs<2 || eed->v2->xs<2) {
+ BLI_remlink(&filledgebase,eed);
+ }
+ eed= nexted;
+ }
+ }
+
+ if(BLI_edgefill(0, (G.obedit && G.obedit->actcol)?(G.obedit->actcol-1):0)) {
+ efa= fillfacebase.first;
+ while(efa) {
+ /* normals default pointing up */
+ efan= addfacelist(efa->v3->tmp.v, efa->v2->tmp.v,
+ efa->v1->tmp.v, 0, NULL, NULL);
+ if(efan) EM_select_face(efan, 1);
+ efa= efa->next;
+ }
+ }
+
+ BLI_end_edgefill();
+
+ waitcursor(0);
+ EM_select_flush();
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+
+ BIF_undo_push("Fill");
+}
+/*GB*/
+/*-------------------------------------------------------------------------------*/
+/*--------------------------- Edge Based Subdivide ------------------------------*/
+
+#define EDGENEW 2
+#define FACENEW 2
+#define EDGEINNER 4
+#define EDGEOLD 8
+
+/*used by faceloop cut to select only edges valid for edge slide*/
+#define DOUBLEOPFILL 16
+
+/* calculates offset for co, based on fractal, sphere or smooth settings */
+static void alter_co(float *co, EditEdge *edge, float rad, int beauty, float perc)
+{
+ float vec1[3], fac;
+
+ if(beauty & B_SMOOTH) {
+ /* we calculate an offset vector vec1[], to be added to *co */
+ float len, fac, nor[3], nor1[3], nor2[3];
+
+ sub_v3_v3v3(nor, edge->v1->co, edge->v2->co);
+ len= 0.5f*normalize_v3(nor);
+
+ copy_v3_v3(nor1, edge->v1->no);
+ copy_v3_v3(nor2, edge->v2->no);
+
+ /* cosine angle */
+ fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ;
+
+ vec1[0]= fac*nor1[0];
+ vec1[1]= fac*nor1[1];
+ vec1[2]= fac*nor1[2];
+
+ /* cosine angle */
+ fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ;
+
+ vec1[0]+= fac*nor2[0];
+ vec1[1]+= fac*nor2[1];
+ vec1[2]+= fac*nor2[2];
+
+ vec1[0]*= rad*len;
+ vec1[1]*= rad*len;
+ vec1[2]*= rad*len;
+
+ co[0] += vec1[0];
+ co[1] += vec1[1];
+ co[2] += vec1[2];
+ }
+ else {
+ if(rad > 0.0) { /* subdivide sphere */
+ normalize_v3(co);
+ co[0]*= rad;
+ co[1]*= rad;
+ co[2]*= rad;
+ }
+ else if(rad< 0.0) { /* fractal subdivide */
+ fac= rad* len_v3v3(edge->v1->co, edge->v2->co);
+ vec1[0]= fac*(float)(0.5-BLI_drand());
+ vec1[1]= fac*(float)(0.5-BLI_drand());
+ vec1[2]= fac*(float)(0.5-BLI_drand());
+ add_v3_v3v3(co, co, vec1);
+ }
+
+ }
+}
+
+/* assumes in the edge is the correct interpolated vertices already */
+/* percent defines the interpolation, rad and beauty are for special options */
+/* results in new vertex with correct coordinate, vertex normal and weight group info */
+static EditVert *subdivide_edge_addvert(EditEdge *edge, float rad, int beauty, float percent)
+{
+ EditVert *ev;
+ float co[3];
+
+ co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0];
+ co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1];
+ co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2];
+
+ /* offset for smooth or sphere or fractal */
+ alter_co(co, edge, rad, beauty, percent);
+
+ /* clip if needed by mirror modifier */
+ if (edge->v1->f2) {
+ if ( edge->v1->f2 & edge->v2->f2 & 1) {
+ co[0]= 0.0f;
+ }
+ if ( edge->v1->f2 & edge->v2->f2 & 2) {
+ co[1]= 0.0f;
+ }
+ if ( edge->v1->f2 & edge->v2->f2 & 4) {
+ co[2]= 0.0f;
+ }
+ }
+
+ ev = addvertlist(co, NULL);
+
+ /* vert data (vgroups, ..) */
+ EM_data_interp_from_verts(edge->v1, edge->v2, ev, percent);
+
+ /* normal */
+ ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0];
+ ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1];
+ ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2];
+ normalize_v3(ev->no);
+
+ return ev;
+}
+
+static void flipvertarray(EditVert** arr, short size)
+{
+ EditVert *hold;
+ int i;
+
+ for(i=0; i<size/2; i++) {
+ hold = arr[i];
+ arr[i] = arr[size-i-1];
+ arr[size-i-1] = hold;
+ }
+}
+
+static void facecopy(EditFace *source, EditFace *target)
+{
+ EditMesh *em= G.editMesh;
+ float *v1 = source->v1->co, *v2 = source->v2->co, *v3 = source->v3->co;
+ float *v4 = source->v4? source->v4->co: NULL;
+ float w[4][4];
+
+ CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data);
+
+ target->mat_nr = source->mat_nr;
+ target->flag = source->flag;
+ target->h = source->h;
+
+ interp_weights_face_v3( w[0],v1, v2, v3, v4, target->v1->co);
+ interp_weights_face_v3( w[1],v1, v2, v3, v4, target->v2->co);
+ interp_weights_face_v3( w[2],v1, v2, v3, v4, target->v3->co);
+ if (target->v4)
+ interp_weights_face_v3( w[3],v1, v2, v3, v4, target->v4->co);
+
+ CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data);
+}
+
+static void fill_quad_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype)
+{
+ EditEdge *cedge=NULL;
+ EditVert *v[4], **verts;
+ EditFace *hold;
+ short start=0, end, left, right, vertsize,i;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
+ else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
+ else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
+ else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;}
+
+ // Point verts to the array of new verts for cedge
+ verts = BLI_ghash_lookup(gh, cedge);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);}
+ end = (start+1)%4;
+ left = (start+2)%4;
+ right = (start+3)%4;
+
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ |---*---*---|
+ | |
+ | |
+ | |
+ -------------
+ left right
+
+ where start,end,left, right are indexes of EditFace->v1, etc (stored in v)
+ and 0,1,2... are the indexes of the new verts stored in verts
+
+ We will fill this case like this or this depending on even or odd cuts
+
+ |---*---*---| |---*---|
+ | / \ | | / \ |
+ | / \ | | / \ |
+ |/ \| |/ \|
+ ------------- ---------
+ */
+
+ // Make center face
+ if(vertsize % 2 == 0) {
+ hold = addfacelist(verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ hold->e4->f2 |= EDGEINNER;
+ }else{
+ hold = addfacelist(verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL);
+ hold->e1->f2 |= EDGEINNER;
+ hold->e3->f2 |= EDGEINNER;
+ }
+ facecopy(efa,hold);
+
+ // Make side faces
+ for(i=0;i<(vertsize-1)/2;i++) {
+ hold = addfacelist(verts[i],verts[i+1],v[right],NULL,NULL,NULL);
+ facecopy(efa,hold);
+ if(i+1 != (vertsize-1)/2) {
+ if(seltype == SUBDIV_SELECT_INNER) {
+ hold->e2->f2 |= EDGEINNER;
+ }
+ }
+ hold = addfacelist(verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL);
+ facecopy(efa,hold);
+ if(i+1 != (vertsize-1)/2) {
+ if(seltype == SUBDIV_SELECT_INNER) {
+ hold->e3->f2 |= EDGEINNER;
+ }
+ }
+ }
+}
+
+static void fill_tri_single(EditFace *efa, struct GHash *gh, int numcuts, int seltype)
+{
+ EditEdge *cedge=NULL;
+ EditVert *v[3], **verts;
+ EditFace *hold;
+ short start=0, end, op, vertsize,i;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+
+ if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
+ else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
+ else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
+
+ // Point verts to the array of new verts for cedge
+ verts = BLI_ghash_lookup(gh, cedge);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);}
+ end = (start+1)%3;
+ op = (start+2)%3;
+
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ |---*---*---|
+ \ |
+ \ |
+ \ |
+ \ |
+ \ |
+ \ |
+ |op
+
+ where start,end,op are indexes of EditFace->v1, etc (stored in v)
+ and 0,1,2... are the indexes of the new verts stored in verts
+
+ We will fill this case like this or this depending on even or odd cuts
+
+ 3 2 1 0
+ |---*---*---|
+ \ \ \ |
+ \ \ \ |
+ \ \ \ |
+ \ \ \|
+ \ \\|
+ \ |
+ |op
+ */
+
+ // Make side faces
+ for(i=0;i<(vertsize-1);i++) {
+ hold = addfacelist(verts[i],verts[i+1],v[op],NULL,NULL,NULL);
+ if(i+1 != vertsize-1) {
+ if(seltype == SUBDIV_SELECT_INNER) {
+ hold->e2->f2 |= EDGEINNER;
+ }
+ }
+ facecopy(efa,hold);
+ }
+}
+
+static void fill_quad_double_op(EditFace *efa, struct GHash *gh, int numcuts)
+{
+ EditEdge *cedge[2]={NULL, NULL};
+ EditVert *v[4], **verts[2];
+ EditFace *hold;
+ short start=0, end, left, right, vertsize,i;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;}
+ else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;}
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, cedge[0]);
+ verts[1] = BLI_ghash_lookup(gh, cedge[1]);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
+ end = (start+1)%4;
+ left = (start+2)%4;
+ right = (start+3)%4;
+ if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);}
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ |---*---*---|
+ | |
+ | |
+ | |
+ |---*---*---|
+ 0 1 2 3
+ left right
+
+ We will fill this case like this or this depending on even or odd cuts
+
+ |---*---*---|
+ | | | |
+ | | | |
+ | | | |
+ |---*---*---|
+ */
+
+ // Make side faces
+ for(i=0;i<vertsize-1;i++) {
+ hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-2-i],verts[1][vertsize-1-i],NULL,NULL);
+ if(i < vertsize-2) {
+ hold->e2->f2 |= EDGEINNER;
+ hold->e2->f2 |= DOUBLEOPFILL;
+ }
+ facecopy(efa,hold);
+ }
+}
+
+static void fill_quad_double_adj_path(EditFace *efa, struct GHash *gh, int numcuts)
+{
+ EditEdge *cedge[2]={NULL, NULL};
+ EditVert *v[4], **verts[2];
+ EditFace *hold;
+ short start=0, start2=0, vertsize,i;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;}
+ if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;}
+ if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;}
+ if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;}
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, cedge[0]);
+ verts[1] = BLI_ghash_lookup(gh, cedge[1]);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ start2 0|---*---*---|
+ | |
+ 1* |
+ | |
+ 2* |
+ | |
+ end2 3|-----------|
+
+ We will fill this case like this or this depending on even or odd cuts
+ |---*---*---|
+ | / / / |
+ * / / |
+ | / / |
+ * / |
+ | / |
+ |-----------|
+ */
+
+ // Make outside tris
+ hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
+ /* when ctrl is depressed, only want verts on the cutline selected */
+ if (G.qual != LR_CTRLKEY)
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ hold = addfacelist(verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL);
+ /* when ctrl is depressed, only want verts on the cutline selected */
+ if (G.qual != LR_CTRLKEY)
+ hold->e1->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
+ // hold->e1->h |= EM_FGON;
+ //}
+ // Make side faces
+
+ for(i=0;i<numcuts;i++) {
+ hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ }
+ //EM_fgon_flags();
+
+}
+
+static void fill_quad_double_adj_fan(EditFace *efa, struct GHash *gh, int numcuts)
+{
+ EditEdge *cedge[2]={NULL, NULL};
+ EditVert *v[4], *op=NULL, **verts[2];
+ EditFace *hold;
+ short start=0, start2=0, vertsize,i;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;}
+ if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;}
+ if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;}
+ if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;}
+
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, cedge[0]);
+ verts[1] = BLI_ghash_lookup(gh, cedge[1]);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ start2 0|---*---*---|
+ | |
+ 1* |
+ | |
+ 2* |
+ | |
+ end2 3|-----------|op
+
+ We will fill this case like this or this (warning horrible ascii art follows)
+ |---*---*---|
+ | \ \ \ |
+ *---\ \ \ |
+ | \ \ \ \|
+ *---- \ \ \ |
+ | --- \\\|
+ |-----------|
+ */
+
+ for(i=0;i<=numcuts;i++) {
+ hold = addfacelist(op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL);
+ hold->e1->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+
+ hold = addfacelist(op,verts[0][i],verts[0][i+1],NULL,NULL,NULL);
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ }
+}
+
+static void fill_quad_double_adj_inner(EditFace *efa, struct GHash *gh, int numcuts)
+{
+ EditEdge *cedge[2]={NULL, NULL};
+ EditVert *v[4], *op=NULL, **verts[2],**inner;
+ EditFace *hold;
+ short start=0, start2=0, vertsize,i;
+ float co[3];
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;}
+ if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;}
+ if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;}
+ if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;}
+
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, cedge[0]);
+ verts[1] = BLI_ghash_lookup(gh, cedge[1]);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ start2 0|---*---*---|
+ | |
+ 1* |
+ | |
+ 2* |
+ | |
+ end2 3|-----------|op
+
+ We will fill this case like this or this (warning horrible ascii art follows)
+ |---*-----*---|
+ | * / |
+ * \ / |
+ | * |
+ | / \ |
+ * \ |
+ | \ |
+ |-------------|
+ */
+
+ // Add Inner Vert(s)
+ inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts");
+
+ for(i=0;i<numcuts;i++) {
+ co[0] = (verts[0][numcuts-i]->co[0] + verts[1][i+1]->co[0] ) / 2 ;
+ co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ;
+ co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ;
+ inner[i] = addvertlist(co, NULL);
+ inner[i]->f2 |= EDGEINNER;
+
+ EM_data_interp_from_verts(verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f);
+ }
+
+ // Add Corner Quad
+ hold = addfacelist(verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ // Add Bottom Quads
+ hold = addfacelist(verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+
+ hold = addfacelist(op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+
+ //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
+ // hold->e1->h |= EM_FGON;
+ //}
+ // Add Fill Quads (if # cuts > 1)
+
+ for(i=0;i<numcuts-1;i++) {
+ hold = addfacelist(inner[i],verts[1][i+1],verts[1][i+2],inner[i+1],NULL,NULL);
+ hold->e1->f2 |= EDGEINNER;
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+
+ hold = addfacelist(inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ hold->e4->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+
+ //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
+ // hold->e1->h |= EM_FGON;
+ //}
+ }
+
+ //EM_fgon_flags();
+
+ MEM_freeN(inner);
+}
+
+static void fill_tri_double(EditFace *efa, struct GHash *gh, int numcuts)
+{
+ EditEdge *cedge[2]={NULL, NULL};
+ EditVert *v[3], **verts[2];
+ EditFace *hold;
+ short start=0, start2=0, vertsize,i;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+
+ if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;}
+ if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;}
+ if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;}
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, cedge[0]);
+ verts[1] = BLI_ghash_lookup(gh, cedge[1]);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
+ /*
+ We should have something like this now
+
+ end start
+ 3 2 1 0
+ start2 0|---*---*---|
+ | /
+ 1* /
+ | /
+ 2* /
+ | /
+ end2 3|
+
+ We will fill this case like this or this depending on even or odd cuts
+ |---*---*---|
+ | / / /
+ * / /
+ | / /
+ * /
+ | /
+ |
+ */
+
+ // Make outside tri
+ hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ // Make side faces
+
+ for(i=0;i<numcuts;i++) {
+ hold = addfacelist(verts[0][i],verts[0][i+1],verts[1][vertsize-1-(i+1)],verts[1][vertsize-1-i],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ }
+}
+
+static void fill_quad_triple(EditFace *efa, struct GHash *gh, int numcuts)
+{
+ EditEdge *cedge[3]={0};
+ EditVert *v[4], **verts[3];
+ EditFace *hold;
+ short start=0, start2=0, start3=0, vertsize, i, repeats;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(!(efa->e1->f & SELECT)) {
+ cedge[0] = efa->e2;
+ cedge[1] = efa->e3;
+ cedge[2] = efa->e4;
+ start = 1;start2 = 2;start3 = 3;
+ }
+ if(!(efa->e2->f & SELECT)) {
+ cedge[0] = efa->e3;
+ cedge[1] = efa->e4;
+ cedge[2] = efa->e1;
+ start = 2;start2 = 3;start3 = 0;
+ }
+ if(!(efa->e3->f & SELECT)) {
+ cedge[0] = efa->e4;
+ cedge[1] = efa->e1;
+ cedge[2] = efa->e2;
+ start = 3;start2 = 0;start3 = 1;
+ }
+ if(!(efa->e4->f & SELECT)) {
+ cedge[0] = efa->e1;
+ cedge[1] = efa->e2;
+ cedge[2] = efa->e3;
+ start = 0;start2 = 1;start3 = 2;
+ }
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, cedge[0]);
+ verts[1] = BLI_ghash_lookup(gh, cedge[1]);
+ verts[2] = BLI_ghash_lookup(gh, cedge[2]);
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);}
+ if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);}
+ /*
+ We should have something like this now
+
+ start2
+ 3 2 1 0
+ start3 0|---*---*---|3
+ | |
+ 1* *2
+ | |
+ 2* *1
+ | |
+ 3|-----------|0 start
+
+ We will fill this case like this or this depending on even or odd cuts
+ there are a couple of differences. For odd cuts, there is a tri in the
+ middle as well as 1 quad at the bottom (not including the extra quads
+ for odd cuts > 1
+
+ For even cuts, there is a quad in the middle and 2 quads on the bottom
+
+ they are numbered here for clarity
+
+ 1 outer tris and bottom quads
+ 2 inner tri or quad
+ 3 repeating quads
+
+ |---*---*---*---|
+ |1/ / \ \ 1|
+ |/ 3 / \ 3 \|
+ * / 2 \ *
+ | / \ |
+ |/ \ |
+ *---------------*
+ | 3 |
+ | |
+ *---------------*
+ | |
+ | 1 |
+ | |
+ |---------------|
+
+ |---*---*---*---*---|
+ | 1/ / \ \ 1|
+ | / / \ \ |
+ |/ 3 / \ 3 \|
+ * / \ *
+ | / \ |
+ | / 2 \ |
+ |/ \|
+ *-------------------*
+ | |
+ | 3 |
+ | |
+ *-------------------*
+ | |
+ | 1 |
+ | |
+ *-------------------*
+ | |
+ | 1 |
+ | |
+ |-------------------|
+
+ */
+
+ // Make outside tris
+ hold = addfacelist(verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL);
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ hold = addfacelist(verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL);
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ // Make bottom quad
+ hold = addfacelist(verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ //If it is even cuts, add the 2nd lower quad
+ if(numcuts % 2 == 0) {
+ hold = addfacelist(verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ // Also Make inner quad
+ hold = addfacelist(verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL);
+ hold->e3->f2 |= EDGEINNER;
+ //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
+ // hold->e3->h |= EM_FGON;
+ //}
+ facecopy(efa,hold);
+ repeats = (numcuts / 2) -1;
+ } else {
+ // Make inner tri
+ hold = addfacelist(verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ //if(G.scene->toolsettings->editbutflag & B_AUTOFGON) {
+ // hold->e2->h |= EM_FGON;
+ //}
+ facecopy(efa,hold);
+ repeats = ((numcuts+1) / 2)-1;
+ }
+
+ // cuts for 1 and 2 do not have the repeating quads
+ if(numcuts < 3) {repeats = 0;}
+ for(i=0;i<repeats;i++) {
+ //Make side repeating Quads
+ hold = addfacelist(verts[1][i+1],verts[1][i+2],verts[0][vertsize-i-3],verts[0][vertsize-i-2],NULL,NULL);
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ hold = addfacelist(verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL);
+ hold->e4->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ }
+ // Do repeating bottom quads
+ for(i=0;i<repeats;i++) {
+ if(numcuts % 2 == 1) {
+ hold = addfacelist(verts[0][1+i],verts[0][2+i],verts[2][vertsize-3-i],verts[2][vertsize-2-i],NULL,NULL);
+ } else {
+ hold = addfacelist(verts[0][2+i],verts[0][3+i],verts[2][vertsize-4-i],verts[2][vertsize-3-i],NULL,NULL);
+ }
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa,hold);
+ }
+ //EM_fgon_flags();
+}
+
+static void fill_quad_quadruple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty)
+{
+ EditVert **verts[4], ***innerverts;
+ EditFace *hold;
+ EditEdge temp;
+ short vertsize, i, j;
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, efa->e1);
+ verts[1] = BLI_ghash_lookup(gh, efa->e2);
+ verts[2] = BLI_ghash_lookup(gh, efa->e3);
+ verts[3] = BLI_ghash_lookup(gh, efa->e4);
+
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);}
+ if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);}
+ if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);}
+ /*
+ We should have something like this now
+ 1
+
+ 3 2 1 0
+ 0|---*---*---|0
+ | |
+ 1* *1
+ 2 | | 4
+ 2* *2
+ | |
+ 3|---*---*---|3
+ 3 2 1 0
+
+ 3
+ // we will fill a 2 dim array of editvert*s to make filling easier
+ // the innervert order is shown
+
+ 0 0---1---2---3
+ | | | |
+ 1 0---1---2---3
+ | | | |
+ 2 0---1---2---3
+ | | | |
+ 3 0---1---2---3
+
+ */
+ innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array");
+ for(i=0;i<numcuts+2;i++) {
+ innerverts[i] = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts inner array");
+ }
+
+ // first row is e1 last row is e3
+ for(i=0;i<numcuts+2;i++) {
+ innerverts[0][i] = verts[0][(numcuts+1)-i];
+ innerverts[numcuts+1][i] = verts[2][(numcuts+1)-i];
+ }
+
+ for(i=1;i<=numcuts;i++) {
+ /* we create a fake edge for the next loop */
+ temp.v2 = innerverts[i][0] = verts[1][i];
+ temp.v1 = innerverts[i][numcuts+1] = verts[3][i];
+
+ for(j=1;j<=numcuts;j++) {
+ float percent= (float)j/(float)(numcuts+1);
+
+ innerverts[i][(numcuts+1)-j]= subdivide_edge_addvert(&temp, rad, beauty, percent);
+ }
+ }
+ // Fill with faces
+ for(i=0;i<numcuts+1;i++) {
+ for(j=0;j<numcuts+1;j++) {
+ hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],innerverts[i+1][j+1],NULL,NULL);
+ hold->e1->f2 = EDGENEW;
+ hold->e2->f2 = EDGENEW;
+ hold->e3->f2 = EDGENEW;
+ hold->e4->f2 = EDGENEW;
+
+ if(i != 0) { hold->e1->f2 |= EDGEINNER; }
+ if(j != 0) { hold->e2->f2 |= EDGEINNER; }
+ if(i != numcuts) { hold->e3->f2 |= EDGEINNER; }
+ if(j != numcuts) { hold->e4->f2 |= EDGEINNER; }
+
+ facecopy(efa,hold);
+ }
+ }
+ // Clean up our dynamic multi-dim array
+ for(i=0;i<numcuts+2;i++) {
+ MEM_freeN(innerverts[i]);
+ }
+ MEM_freeN(innerverts);
+}
+
+static void fill_tri_triple(EditFace *efa, struct GHash *gh, int numcuts, float rad, int beauty)
+{
+ EditVert **verts[3], ***innerverts;
+ short vertsize, i, j;
+ EditFace *hold;
+ EditEdge temp;
+
+ // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1]
+ verts[0] = BLI_ghash_lookup(gh, efa->e1);
+ verts[1] = BLI_ghash_lookup(gh, efa->e2);
+ verts[2] = BLI_ghash_lookup(gh, efa->e3);
+
+ //This is the index size of the verts array
+ vertsize = numcuts+2;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);}
+ if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);}
+ if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);}
+ /*
+ We should have something like this now
+ 3
+
+ 3 2 1 0
+ 0|---*---*---|3
+ | /
+ 1 1* *2
+ | /
+ 2* *1 2
+ | /
+ 3|/
+ 0
+
+ we will fill a 2 dim array of editvert*s to make filling easier
+
+ 3
+
+ 0 0---1---2---3---4
+ | / | / |/ | /
+ 1 0---1----2---3
+ 1 | / | / | /
+ 2 0----1---2 2
+ | / | /
+ |/ |/
+ 3 0---1
+ | /
+ |/
+ 4 0
+
+ */
+
+ innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array");
+ for(i=0;i<numcuts+2;i++) {
+ innerverts[i] = MEM_mallocN(sizeof(EditVert*)*((numcuts+2)-i),"tri-tri subdiv inner verts inner array");
+ }
+ //top row is e3 backwards
+ for(i=0;i<numcuts+2;i++) {
+ innerverts[0][i] = verts[2][(numcuts+1)-i];
+ }
+
+ for(i=1;i<=numcuts+1;i++) {
+ //fake edge, first vert is from e1, last is from e2
+ temp.v1= innerverts[i][0] = verts[0][i];
+ temp.v2= innerverts[i][(numcuts+1)-i] = verts[1][(numcuts+1)-i];
+
+ for(j=1;j<(numcuts+1)-i;j++) {
+ float percent= (float)j/(float)((numcuts+1)-i);
+
+ innerverts[i][((numcuts+1)-i)-j]= subdivide_edge_addvert(&temp, rad, beauty, 1-percent);
+ }
+ }
+
+ // Now fill the verts with happy little tris :)
+ for(i=0;i<=numcuts+1;i++) {
+ for(j=0;j<(numcuts+1)-i;j++) {
+ //We always do the first tri
+ hold = addfacelist(innerverts[i][j+1],innerverts[i][j],innerverts[i+1][j],NULL,NULL,NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ if(i != 0) { hold->e1->f2 |= EDGEINNER; }
+ if(j != 0) { hold->e2->f2 |= EDGEINNER; }
+ if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;}
+
+ facecopy(efa,hold);
+ //if there are more to come, we do the 2nd
+ if(j+1 <= numcuts-i) {
+ hold = addfacelist(innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL);
+ facecopy(efa,hold);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ }
+ }
+ }
+
+ // Clean up our dynamic multi-dim array
+ for(i=0;i<numcuts+2;i++) {
+ MEM_freeN(innerverts[i]);
+ }
+ MEM_freeN(innerverts);
+}
+
+//Next two fill types are for knife exact only and are provided to allow for knifing through vertices
+//This means there is no multicut!
+static void fill_quad_doublevert(EditFace *efa, int v1, int v2)
+{
+ EditFace *hold;
+ /*
+ Depending on which two vertices have been knifed through (v1 and v2), we
+ triangulate like the patterns below.
+ X-------| |-------X
+ | \ | | / |
+ | \ | | / |
+ | \ | | / |
+ --------X X--------
+ */
+
+ if(v1 == 1 && v2 == 3){
+ hold= addfacelist(efa->v1, efa->v2, efa->v3, 0, efa, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+
+ hold= addfacelist(efa->v1, efa->v3, efa->v4, 0, efa, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e1->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+ }
+ else{
+ hold= addfacelist(efa->v1, efa->v2, efa->v4, 0, efa, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+
+ hold= addfacelist(efa->v2, efa->v3, efa->v4, 0, efa, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+ }
+}
+
+static void fill_quad_singlevert(EditFace *efa, struct GHash *gh)
+{
+ EditEdge *cedge=NULL;
+ EditVert *v[4], **verts;
+ EditFace *hold;
+ short start=0, end, left, right, vertsize;
+
+ v[0] = efa->v1;
+ v[1] = efa->v2;
+ v[2] = efa->v3;
+ v[3] = efa->v4;
+
+ if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;}
+ else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;}
+ else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;}
+ else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;}
+
+ // Point verts to the array of new verts for cedge
+ verts = BLI_ghash_lookup(gh, cedge);
+ //This is the index size of the verts array
+ vertsize = 3;
+
+ // Is the original v1 the same as the first vert on the selected edge?
+ // if not, the edge is running the opposite direction in this face so flip
+ // the array to the correct direction
+
+ if(verts[0] != v[start]) {flipvertarray(verts,3);}
+ end = (start+1)%4;
+ left = (start+2)%4;
+ right = (start+3)%4;
+
+/*
+ We should have something like this now
+
+ end start
+ 2 1 0
+ |-----*-----|
+ | |
+ | |
+ | |
+ -------------
+ left right
+
+ where start,end,left, right are indexes of EditFace->v1, etc (stored in v)
+ and 0,1,2 are the indexes of the new verts stored in verts. We fill like
+ this, depending on whether its vertex 'left' or vertex 'right' thats
+ been knifed through...
+
+ |---*---| |---*---|
+ | / | | \ |
+ | / | | \ |
+ |/ | | \|
+ X-------- --------X
+*/
+
+ if(v[left]->f1) {
+ //triangle is composed of cutvert, end and left
+ hold = addfacelist(verts[1],v[end],v[left],NULL, NULL,NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+
+ //quad is composed of cutvert, left, right and start
+ hold = addfacelist(verts[1],v[left],v[right],v[start], NULL, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e4->f2 |= EDGENEW;
+ hold->e1->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+ }
+ else if(v[right]->f1) {
+ //triangle is composed of cutvert, right and start
+ hold = addfacelist(verts[1],v[right],v[start], NULL, NULL, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e1->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+ //quad is composed of cutvert, end, left, right
+ hold = addfacelist(verts[1],v[end], v[left], v[right], NULL, NULL);
+ hold->e1->f2 |= EDGENEW;
+ hold->e2->f2 |= EDGENEW;
+ hold->e3->f2 |= EDGENEW;
+ hold->e4->f2 |= EDGENEW;
+ hold->e4->f2 |= EDGEINNER;
+ facecopy(efa, hold);
+ }
+
+}
+
+// This function takes an example edge, the current point to create and
+// the total # of points to create, then creates the point and return the
+// editvert pointer to it.
+static EditVert *subdivideedgenum(EditEdge *edge, int curpoint, int totpoint, float rad, int beauty)
+{
+ EditVert *ev;
+ float percent;
+
+ if (beauty & (B_PERCENTSUBD) && totpoint == 1)
+ //percent=(float)(edge->tmp.l)/32768.0f;
+ percent= edge->tmp.fp;
+ else
+ percent= (float)curpoint/(float)(totpoint+1);
+
+ ev= subdivide_edge_addvert(edge, rad, beauty, percent);
+ ev->f = edge->v1->f;
+
+ return ev;
+}
+
+void esubdivideflag(int flag, float rad, int beauty, int numcuts, int seltype)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *ef;
+ EditEdge *eed, *cedge, *sort[4];
+ EditVert *eve, **templist;
+ struct GHash *gh;
+ float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3];
+ int i, j, edgecount, touchcount, facetype,hold;
+ ModifierData *md= G.obedit->modifiers.first;
+
+ if(multires_test()) return;
+
+ //Set faces f1 to 0 cause we need it later
+ for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0;
+ for(eve=em->verts.first; eve; eve=eve->next) {
+ if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */
+ eve->f1 = 0;
+ eve->f2 = 0;
+ }
+
+ for (; md; md=md->next) {
+ if ((md->type==eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
+ MirrorModifierData *mmd = (MirrorModifierData*) md;
+
+ if(mmd->flag & MOD_MIR_CLIPPING) {
+ for (eve= em->verts.first; eve; eve= eve->next) {
+ eve->f2= 0;
+ switch(mmd->axis) {
+ case 0:
+ if (fabs(eve->co[0]) < mmd->tolerance)
+ eve->f2 |= 1;
+ break;
+ case 1:
+ if (fabs(eve->co[1]) < mmd->tolerance)
+ eve->f2 |= 2;
+ break;
+ case 2:
+ if (fabs(eve->co[2]) < mmd->tolerance)
+ eve->f2 |= 4;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ //Flush vertex flags upward to the edges
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ //if(eed->f & flag && eed->v1->f == eed->v2->f) {
+ // eed->f |= eed->v1->f;
+ // }
+ eed->f2 = 0;
+ if(eed->f & flag) {
+ eed->f2 |= EDGEOLD;
+ }
+ }
+
+ // We store an array of verts for each edge that is subdivided,
+ // we put this array as a value in a ghash which is keyed by the EditEdge*
+
+ // Now for beauty subdivide deselect edges based on length
+ if(beauty & B_BEAUTY) {
+ for(ef = em->faces.first;ef;ef = ef->next) {
+ if(!ef->v4) {
+ continue;
+ }
+ if(ef->f & SELECT) {
+ copy_v3_v3(v1mat, ef->v1->co);
+ copy_v3_v3(v2mat, ef->v2->co);
+ copy_v3_v3(v3mat, ef->v3->co);
+ copy_v3_v3(v4mat, ef->v4->co);
+ mul_mat3_m4_v3(G.obedit->obmat, v1mat);
+ mul_mat3_m4_v3(G.obedit->obmat, v2mat);
+ mul_mat3_m4_v3(G.obedit->obmat, v3mat);
+ mul_mat3_m4_v3(G.obedit->obmat, v4mat);
+
+ length[0] = len_v3v3(v1mat, v2mat);
+ length[1] = len_v3v3(v2mat, v3mat);
+ length[2] = len_v3v3(v3mat, v4mat);
+ length[3] = len_v3v3(v4mat, v1mat);
+ sort[0] = ef->e1;
+ sort[1] = ef->e2;
+ sort[2] = ef->e3;
+ sort[3] = ef->e4;
+
+
+ // Beauty Short Edges
+ if(beauty & B_BEAUTY_SHORT) {
+ for(j=0;j<2;j++) {
+ hold = -1;
+ for(i=0;i<4;i++) {
+ if(length[i] < 0) {
+ continue;
+ } else if(hold == -1) {
+ hold = i;
+ } else {
+ if(length[hold] < length[i]) {
+ hold = i;
+ }
+ }
+ }
+ sort[hold]->f &= ~SELECT;
+ sort[hold]->f2 |= EDGENEW;
+ length[hold] = -1;
+ }
+ }
+
+ // Beauty Long Edges
+ else {
+ for(j=0;j<2;j++) {
+ hold = -1;
+ for(i=0;i<4;i++) {
+ if(length[i] < 0) {
+ continue;
+ } else if(hold == -1) {
+ hold = i;
+ } else {
+ if(length[hold] > length[i]) {
+ hold = i;
+ }
+ }
+ }
+ sort[hold]->f &= ~SELECT;
+ sort[hold]->f2 |= EDGENEW;
+ length[hold] = -1;
+ }
+ }
+ }
+ }
+ }
+
+ gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+ // If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut
+ if(beauty & B_KNIFE) {
+ for(eed= em->edges.first;eed;eed=eed->next) {
+ if( eed->tmp.fp == 0 ) {
+ EM_select_edge(eed,0);
+ }
+ }
+ }
+ // So for each edge, if it is selected, we allocate an array of size cuts+2
+ // so we can have a place for the v1, the new verts and v2
+ for(eed=em->edges.first;eed;eed = eed->next) {
+ if(eed->f & flag) {
+ templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist");
+ templist[0] = eed->v1;
+ for(i=0;i<numcuts;i++) {
+ // This function creates the new vert and returns it back
+ // to the array
+ templist[i+1] = subdivideedgenum(eed, i+1, numcuts, rad, beauty);
+ //while we are here, we can copy edge info from the original edge
+ cedge = addedgelist(templist[i],templist[i+1],eed);
+ // Also set the edge f2 to EDGENEW so that we can use this info later
+ cedge->f2 = EDGENEW;
+ }
+ templist[i+1] = eed->v2;
+ //Do the last edge too
+ cedge = addedgelist(templist[i],templist[i+1],eed);
+ cedge->f2 = EDGENEW;
+ // Now that the edge is subdivided, we can put its verts in the ghash
+ BLI_ghash_insert(gh, eed, templist);
+ }
+ }
+
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ // Now for each face in the mesh we need to figure out How many edges were cut
+ // and which filling method to use for that face
+ for(ef = em->faces.first;ef;ef = ef->next) {
+ edgecount = 0;
+ facetype = 3;
+ if(ef->e1->f & flag) {edgecount++;}
+ if(ef->e2->f & flag) {edgecount++;}
+ if(ef->e3->f & flag) {edgecount++;}
+ if(ef->v4) {
+ facetype = 4;
+ if(ef->e4->f & flag) {edgecount++;}
+ }
+ if(facetype == 4) {
+ switch(edgecount) {
+ case 0:
+ if(beauty & B_KNIFE && numcuts == 1){
+ /*Test for when knifing through two opposite verts but no edges*/
+ touchcount = 0;
+ if(ef->v1->f1) touchcount++;
+ if(ef->v2->f1) touchcount++;
+ if(ef->v3->f1) touchcount++;
+ if(ef->v4->f1) touchcount++;
+ if(touchcount == 2){
+ if(ef->v1->f1 && ef->v3->f1){
+ ef->f1 = SELECT;
+ fill_quad_doublevert(ef, 1, 3);
+ }
+ else if(ef->v2->f1 && ef->v4->f1){
+ ef->f1 = SELECT;
+ fill_quad_doublevert(ef, 2, 4);
+ }
+ }
+ }
+ break;
+
+ case 1:
+ if(beauty & B_KNIFE && numcuts == 1){
+ /*Test for when knifing through an edge and one vert*/
+ touchcount = 0;
+ if(ef->v1->f1) touchcount++;
+ if(ef->v2->f1) touchcount++;
+ if(ef->v3->f1) touchcount++;
+ if(ef->v4->f1) touchcount++;
+
+ if(touchcount == 1){
+ if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) ||
+ (ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) ||
+ (ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) ||
+ (ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){
+
+ ef->f1 = SELECT;
+ fill_quad_singlevert(ef, gh);
+ }
+ else{
+ ef->f1 = SELECT;
+ fill_quad_single(ef, gh, numcuts, seltype);
+ }
+ }
+ else{
+ ef->f1 = SELECT;
+ fill_quad_single(ef, gh, numcuts, seltype);
+ }
+ }
+ else{
+ ef->f1 = SELECT;
+ fill_quad_single(ef, gh, numcuts, seltype);
+ }
+ break;
+ case 2: ef->f1 = SELECT;
+ // if there are 2, we check if edge 1 and 3 are either both on or off that way
+ // we can tell if the selected pair is Adjacent or Opposite of each other
+ if((ef->e1->f & flag && ef->e3->f & flag) ||
+ (ef->e2->f & flag && ef->e4->f & flag)) {
+ fill_quad_double_op(ef, gh, numcuts);
+ }else{
+ switch(G.scene->toolsettings->cornertype) {
+ case 0: fill_quad_double_adj_path(ef, gh, numcuts); break;
+ case 1: fill_quad_double_adj_inner(ef, gh, numcuts); break;
+ case 2: fill_quad_double_adj_fan(ef, gh, numcuts); break;
+ }
+
+ }
+ break;
+ case 3: ef->f1 = SELECT;
+ fill_quad_triple(ef, gh, numcuts);
+ break;
+ case 4: ef->f1 = SELECT;
+ fill_quad_quadruple(ef, gh, numcuts, rad, beauty);
+ break;
+ }
+ } else {
+ switch(edgecount) {
+ case 0: break;
+ case 1: ef->f1 = SELECT;
+ fill_tri_single(ef, gh, numcuts, seltype);
+ break;
+ case 2: ef->f1 = SELECT;
+ fill_tri_double(ef, gh, numcuts);
+ break;
+ case 3: ef->f1 = SELECT;
+ fill_tri_triple(ef, gh, numcuts, rad, beauty);
+ break;
+ }
+ }
+ }
+
+ // Delete Old Edges and Faces
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ if(BLI_ghash_haskey(gh,eed)) {
+ eed->f1 = SELECT;
+ } else {
+ eed->f1 = 0;
+ }
+ }
+ free_tagged_edges_faces(em->edges.first, em->faces.first);
+
+ if(seltype == SUBDIV_SELECT_ORIG && G.qual != LR_CTRLKEY) {
+ /* bugfix: vertex could get flagged as "not-selected"
+ // solution: clear flags before, not at the same time as setting SELECT flag -dg
+ */
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) {
+ eed->f &= !flag;
+ EM_select_edge(eed,0);
+ }
+ }
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) {
+ eed->f |= flag;
+ EM_select_edge(eed,1);
+ }
+ }
+ } else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| G.qual == LR_CTRLKEY) {
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ if(eed->f2 & EDGEINNER) {
+ eed->f |= flag;
+ EM_select_edge(eed,1);
+ if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT;
+ if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT;
+ }else{
+ eed->f &= !flag;
+ EM_select_edge(eed,0);
+ }
+ }
+ } else if(seltype == SUBDIV_SELECT_LOOPCUT){
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ if(eed->f2 & DOUBLEOPFILL){
+ eed->f |= flag;
+ EM_select_edge(eed,1);
+ }else{
+ eed->f &= !flag;
+ EM_select_edge(eed,0);
+ }
+ }
+ }
+ if(G.scene->selectmode & SCE_SELECT_VERTEX) {
+ for(eed = em->edges.first;eed;eed = eed->next) {
+ if(eed->f & SELECT) {
+ eed->v1->f |= SELECT;
+ eed->v2->f |= SELECT;
+ }
+ }
+ }
+
+ //fix hide flags for edges. First pass, hide edges of hidden faces
+ for(ef=em->faces.first; ef; ef=ef->next){
+ if(ef->h){
+ ef->e1->h |= 1;
+ ef->e2->h |= 1;
+ ef->e3->h |= 1;
+ if(ef->e4) ef->e4->h |= 1;
+ }
+ }
+ //second pass: unhide edges of visible faces adjacent to hidden faces
+ for(ef=em->faces.first; ef; ef=ef->next){
+ if(ef->h == 0){
+ ef->e1->h &= ~1;
+ ef->e2->h &= ~1;
+ ef->e3->h &= ~1;
+ if(ef->e4) ef->e4->h &= ~1;
+ }
+ }
+
+ // Free the ghash and call MEM_freeN on all the value entries to return
+ // that memory
+ BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN);
+
+ EM_selectmode_flush();
+ for(ef=em->faces.first;ef;ef = ef->next) {
+ if(ef->e4) {
+ if( (ef->e1->f & SELECT && ef->e2->f & SELECT) &&
+ (ef->e3->f & SELECT && ef->e4->f & SELECT) ) {
+ ef->f |= SELECT;
+ }
+ } else {
+ if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) {
+ ef->f |= SELECT;
+ }
+ }
+ }
+
+ recalc_editnormals();
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+}
+
+static int count_selected_edges(EditEdge *ed)
+{
+ int totedge = 0;
+ while(ed) {
+ ed->tmp.p = 0;
+ if( ed->f & SELECT ) totedge++;
+ ed= ed->next;
+ }
+ return totedge;
+}
+
+/* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */
+typedef EditFace *EVPtr;
+typedef EVPtr EVPTuple[2];
+
+/** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces)
+ sharing one edge.
+ arguments: selected edge list, face list.
+ Edges will also be tagged accordingly (see eed->f2) */
+
+static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa)
+{
+ EditEdge *e1, *e2, *e3;
+ EVPtr *evp;
+ int i = 0;
+
+ /* run through edges, if selected, set pointer edge-> facearray */
+ while(eed) {
+ eed->f2= 0;
+ eed->f1= 0;
+ if( eed->f & SELECT ) {
+ eed->tmp.p = (EditVert *) (&efaa[i]);
+ i++;
+ }
+ else eed->tmp.p = NULL;
+
+ eed= eed->next;
+ }
+
+
+ /* find edges pointing to 2 faces by procedure:
+
+ - run through faces and their edges, increase
+ face counter e->f1 for each face
+ */
+
+ while(efa) {
+ efa->f1= 0;
+ if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */
+ e1= efa->e1;
+ e2= efa->e2;
+ e3= efa->e3;
+ if(e1->f2<3 && e1->tmp.p) {
+ if(e1->f2<2) {
+ evp= (EVPtr *) e1->tmp.p;
+ evp[(int)e1->f2] = efa;
+ }
+ e1->f2+= 1;
+ }
+ if(e2->f2<3 && e2->tmp.p) {
+ if(e2->f2<2) {
+ evp= (EVPtr *) e2->tmp.p;
+ evp[(int)e2->f2]= efa;
+ }
+ e2->f2+= 1;
+ }
+ if(e3->f2<3 && e3->tmp.p) {
+ if(e3->f2<2) {
+ evp= (EVPtr *) e3->tmp.p;
+ evp[(int)e3->f2]= efa;
+ }
+ e3->f2+= 1;
+ }
+ }
+ else {
+ /* set to 3 to make sure these are not flipped or joined */
+ efa->e1->f2= 3;
+ efa->e2->f2= 3;
+ efa->e3->f2= 3;
+ if (efa->e4) efa->e4->f2= 3;
+ }
+
+ efa= efa->next;
+ }
+ return i;
+}
+
+
+/* returns vertices of two adjacent triangles forming a quad
+ - can be righthand or lefthand
+
+ 4-----3
+ |\ |
+ | \ 2 | <- efa1
+ | \ |
+ efa-> | 1 \ |
+ | \|
+ 1-----2
+
+*/
+#define VTEST(face, num, other) \
+ (face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3)
+
+static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex)
+{
+ if VTEST(efa, 1, efa1) {
+ *v1= efa->v1;
+ *v2= efa->v2;
+ vindex[0]= 0;
+ vindex[1]= 1;
+ }
+ else if VTEST(efa, 2, efa1) {
+ *v1= efa->v2;
+ *v2= efa->v3;
+ vindex[0]= 1;
+ vindex[1]= 2;
+ }
+ else if VTEST(efa, 3, efa1) {
+ *v1= efa->v3;
+ *v2= efa->v1;
+ vindex[0]= 2;
+ vindex[1]= 0;
+ }
+
+ if VTEST(efa1, 1, efa) {
+ *v3= efa1->v1;
+ *v4= efa1->v2;
+ vindex[2]= 0;
+ vindex[3]= 1;
+ }
+ else if VTEST(efa1, 2, efa) {
+ *v3= efa1->v2;
+ *v4= efa1->v3;
+ vindex[2]= 1;
+ vindex[3]= 2;
+ }
+ else if VTEST(efa1, 3, efa) {
+ *v3= efa1->v3;
+ *v4= efa1->v1;
+ vindex[2]= 2;
+ vindex[3]= 0;
+ }
+ else
+ *v3= *v4= NULL;
+}
+
+/* Helper functions for edge/quad edit features*/
+static void untag_edges(EditFace *f)
+{
+ f->e1->f1 = 0;
+ f->e2->f1 = 0;
+ f->e3->f1 = 0;
+ if (f->e4) f->e4->f1 = 0;
+}
+
+/** remove and free list of tagged edges and faces */
+static void free_tagged_edges_faces(EditEdge *eed, EditFace *efa)
+{
+ EditMesh *em= G.editMesh;
+ EditEdge *nexted;
+ EditFace *nextvl;
+
+ while(efa) {
+ nextvl= efa->next;
+ if(efa->f1) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(efa);
+ }
+ else
+ /* avoid deleting edges that are still in use */
+ untag_edges(efa);
+ efa= nextvl;
+ }
+
+ while(eed) {
+ nexted= eed->next;
+ if(eed->f1) {
+ remedge(eed);
+ free_editedge(eed);
+ }
+ eed= nexted;
+ }
+}
+
+/* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the
+ edge/face flags, with very mixed results.... */
+void beauty_fill(void)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *v1, *v2, *v3, *v4;
+ EditEdge *eed, *nexted;
+ EditEdge dia1, dia2;
+ EditFace *efa, *w;
+ // void **efaar, **efaa;
+ EVPTuple *efaar;
+ EVPtr *efaa;
+ float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2;
+ int totedge, ok, notbeauty=8, onedone, vindex[4];
+
+ if(multires_test()) return;
+
+ /* - all selected edges with two faces
+ * - find the faces: store them in edges (using datablock)
+ * - per edge: - test convex
+ * - test edge: flip?
+ * - if true: remedge, addedge, all edges at the edge get new face pointers
+ */
+
+ EM_selectmode_set(); // makes sure in selectmode 'face' the edges of selected faces are selected too
+
+ totedge = count_selected_edges(em->edges.first);
+ if(totedge==0) return;
+
+ /* temp block with face pointers */
+ efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill");
+
+ while (notbeauty) {
+ notbeauty--;
+
+ ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
+
+ /* there we go */
+ onedone= 0;
+
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+
+ /* f2 is set in collect_quadedges() */
+ if(eed->f2==2 && eed->h==0) {
+
+ efaa = (EVPtr *) eed->tmp.p;
+
+ /* none of the faces should be treated before, nor be part of fgon */
+ ok= 1;
+ efa= efaa[0];
+ if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
+ if(efa->fgonf) ok= 0;
+ efa= efaa[1];
+ if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
+ if(efa->fgonf) ok= 0;
+
+ if(ok) {
+ /* test convex */
+ givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
+ if(v1 && v2 && v3 && v4) {
+ if( convex(v1->co, v2->co, v3->co, v4->co) ) {
+
+ /* test edges */
+ if( (v1) > (v3) ) {
+ dia1.v1= v3;
+ dia1.v2= v1;
+ }
+ else {
+ dia1.v1= v1;
+ dia1.v2= v3;
+ }
+
+ if( (v2) > (v4) ) {
+ dia2.v1= v4;
+ dia2.v2= v2;
+ }
+ else {
+ dia2.v1= v2;
+ dia2.v2= v4;
+ }
+
+ /* testing rule:
+ * the area divided by the total edge lengths
+ */
+
+ len1= len_v3v3(v1->co, v2->co);
+ len2= len_v3v3(v2->co, v3->co);
+ len3= len_v3v3(v3->co, v4->co);
+ len4= len_v3v3(v4->co, v1->co);
+ len5= len_v3v3(v1->co, v3->co);
+ len6= len_v3v3(v2->co, v4->co);
+
+ opp1= area_tri_v3(v1->co, v2->co, v3->co);
+ opp2= area_tri_v3(v1->co, v3->co, v4->co);
+
+ fac1= opp1/(len1+len2+len5) + opp2/(len3+len4+len5);
+
+ opp1= area_tri_v3(v2->co, v3->co, v4->co);
+ opp2= area_tri_v3(v2->co, v4->co, v1->co);
+
+ fac2= opp1/(len2+len3+len6) + opp2/(len4+len1+len6);
+
+ ok= 0;
+ if(fac1 > fac2) {
+ if(dia2.v1==eed->v1 && dia2.v2==eed->v2) {
+ eed->f1= 1;
+ efa= efaa[0];
+ efa->f1= 1;
+ efa= efaa[1];
+ efa->f1= 1;
+
+ w= EM_face_from_faces(efaa[0], efaa[1],
+ vindex[0], vindex[1], 4+vindex[2], -1);
+ w->f |= SELECT;
+
+
+ w= EM_face_from_faces(efaa[0], efaa[1],
+ vindex[0], 4+vindex[2], 4+vindex[3], -1);
+ w->f |= SELECT;
+
+ onedone= 1;
+ }
+ }
+ else if(fac1 < fac2) {
+ if(dia1.v1==eed->v1 && dia1.v2==eed->v2) {
+ eed->f1= 1;
+ efa= efaa[0];
+ efa->f1= 1;
+ efa= efaa[1];
+ efa->f1= 1;
+
+
+ w= EM_face_from_faces(efaa[0], efaa[1],
+ vindex[1], 4+vindex[2], 4+vindex[3], -1);
+ w->f |= SELECT;
+
+
+ w= EM_face_from_faces(efaa[0], efaa[1],
+ vindex[0], 4+vindex[1], 4+vindex[3], -1);
+ w->f |= SELECT;
+
+ onedone= 1;
+ }
+ }
+ }
+ }
+ }
+
+ }
+ eed= nexted;
+ }
+
+ free_tagged_edges_faces(em->edges.first, em->faces.first);
+
+ if(onedone==0) break;
+
+ EM_selectmode_set(); // new edges/faces were added
+ }
+
+ MEM_freeN(efaar);
+
+ EM_select_flush();
+
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+ BIF_undo_push("Beauty Fill");
+}
+
+
+/* ******************** BEGIN TRIANGLE TO QUAD ************************************* */
+static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit)
+{
+
+ /*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/
+ /*Note: this is more complicated than it needs to be and should be cleaned up...*/
+ float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff,
+ edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff,
+ minarea, maxarea, areaA, areaB;
+
+ /*First Test: Normal difference*/
+ normal_tri_v3( noA1,v1->co, v2->co, v3->co);
+ normal_tri_v3( noA2,v1->co, v3->co, v4->co);
+
+ if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0;
+ else normalADiff = angle_v2v2(noA1, noA2);
+ //if(!normalADiff) normalADiff = 179;
+ normal_tri_v3( noB1,v2->co, v3->co, v4->co);
+ normal_tri_v3( noB2,v4->co, v1->co, v2->co);
+
+ if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0;
+ else normalBDiff = angle_v2v2(noB1, noB2);
+ //if(!normalBDiff) normalBDiff = 179;
+
+ measure += (normalADiff/360) + (normalBDiff/360);
+ if(measure > limit) return measure;
+
+ /*Second test: Colinearity*/
+ sub_v3_v3v3(edgeVec1, v1->co, v2->co);
+ sub_v3_v3v3(edgeVec2, v2->co, v3->co);
+ sub_v3_v3v3(edgeVec3, v3->co, v4->co);
+ sub_v3_v3v3(edgeVec4, v4->co, v1->co);
+
+ diff = 0.0;
+
+ diff = (
+ fabs(angle_v2v2(edgeVec1, edgeVec2) - 90) +
+ fabs(angle_v2v2(edgeVec2, edgeVec3) - 90) +
+ fabs(angle_v2v2(edgeVec3, edgeVec4) - 90) +
+ fabs(angle_v2v2(edgeVec4, edgeVec1) - 90)) / 360;
+ if(!diff) return 0.0;
+
+ measure += diff;
+ if(measure > limit) return measure;
+
+ /*Third test: Concavity*/
+ areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co);
+ areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co);
+
+ if(areaA <= areaB) minarea = areaA;
+ else minarea = areaB;
+
+ if(areaA >= areaB) maxarea = areaA;
+ else maxarea = areaB;
+
+ if(!maxarea) measure += 1;
+ else measure += (1 - (minarea / maxarea));
+
+ return measure;
+}
+
+#define T2QUV_LIMIT 0.005
+#define T2QCOL_LIMIT 3
+static int compareFaceAttribs(EditFace *f1, EditFace *f2, EditEdge *eed)
+{
+ /*Test to see if the per-face attributes for the joining edge match within limit*/
+ MTFace *tf1, *tf2;
+ unsigned int *col1, *col2;
+ short i,attrok=0, flag = G.scene->toolsettings->editbutflag, fe1[2], fe2[2];
+
+ tf1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MTFACE);
+ tf2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MTFACE);
+
+ col1 = CustomData_em_get(&G.editMesh->fdata, f1->data, CD_MCOL);
+ col2 = CustomData_em_get(&G.editMesh->fdata, f2->data, CD_MCOL);
+
+ /*store indices for faceedges*/
+ f1->v1->f1 = 0;
+ f1->v2->f1 = 1;
+ f1->v3->f1 = 2;
+
+ fe1[0] = eed->v1->f1;
+ fe1[1] = eed->v2->f1;
+
+ f2->v1->f1 = 0;
+ f2->v2->f1 = 1;
+ f2->v3->f1 = 2;
+
+ fe2[0] = eed->v1->f1;
+ fe2[1] = eed->v2->f1;
+
+ /*compare faceedges for each face attribute. Additional per face attributes can be added later*/
+ /*do UVs*/
+ if(flag & B_JOINTRIA_UV){
+
+ if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV;
+ else if(tf1->tpage != tf2->tpage); /*do nothing*/
+ else{
+ for(i = 0; i < 2; i++){
+ if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] &&
+ tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV;
+ }
+ }
+ }
+
+ /*do VCOLs*/
+ if(flag & B_JOINTRIA_VCOL){
+ if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL;
+ else{
+ char *f1vcol, *f2vcol;
+ for(i = 0; i < 2; i++){
+ f1vcol = (char *)&(col1[fe1[i]]);
+ f2vcol = (char *)&(col2[fe2[i]]);
+
+ /*compare f1vcol with f2vcol*/
+ if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] &&
+ f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] &&
+ f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL;
+ }
+ }
+ }
+
+ if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1;
+ return 0;
+}
+
+static int fplcmp(const void *v1, const void *v2)
+{
+ const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2);
+
+ if( e1->crease > e2->crease) return 1;
+ else if( e1->crease < e2->crease) return -1;
+
+ return 0;
+}
+
+/*Bitflags for edges.*/
+#define T2QDELETE 1
+#define T2QCOMPLEX 2
+#define T2QJOIN 4
+void join_triangles(void)
+{
+ EditMesh *em=G.editMesh;
+ EditVert *v1, *v2, *v3, *v4, *eve;
+ EditEdge *eed, **edsortblock = NULL, **edb = NULL;
+ EditFace *efa;
+ EVPTuple *efaar = NULL;
+ EVPtr *efaa = NULL;
+ float *creases = NULL;
+ float measure; /*Used to set tolerance*/
+ float limit = G.scene->toolsettings->jointrilimit;
+ int i, ok, totedge=0, totseledge=0, complexedges, vindex[4];
+
+ /*test for multi-resolution data*/
+ if(multires_test()) return;
+
+ /*if we take a long time on very dense meshes we want waitcursor to display*/
+ waitcursor(1);
+
+ totseledge = count_selected_edges(em->edges.first);
+ if(totseledge==0) return;
+
+ /*abusing crease value to store weights for edge pairs. Nasty*/
+ for(eed=em->edges.first; eed; eed=eed->next) totedge++;
+ if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array");
+ for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){
+ creases[i] = eed->crease;
+ eed->crease = 0.0;
+ }
+
+ /*clear temp flags*/
+ for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0;
+ for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0;
+ for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0;
+
+ /*For every selected 2 manifold edge, create pointers to its two faces.*/
+ efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad");
+ ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
+ complexedges = 0;
+
+ if(ok){
+
+
+ /*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/
+ for(eed=em->edges.first; eed; eed=eed->next){
+ /* eed->f2 is 2 only if this edge is part of exactly two
+ triangles, and both are selected, and it has EVPTuple assigned */
+ if(eed->f2 == 2){
+ efaa= (EVPtr *) eed->tmp.p;
+ efaa[0]->tmp.l++;
+ efaa[1]->tmp.l++;
+ }
+ }
+
+ for(eed=em->edges.first; eed; eed=eed->next){
+ if(eed->f2 == 2){
+ efaa= (EVPtr *) eed->tmp.p;
+ v1 = v2 = v3 = v4 = NULL;
+ givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
+ if(v1 && v2 && v3 && v4){
+ /*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/
+ if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){
+ if( convex(v1->co, v2->co, v3->co, v4->co) ){
+ eed->f1 |= T2QJOIN;
+ efaa[0]->f1 = 1; //mark for join
+ efaa[1]->f1 = 1; //mark for join
+ }
+ }
+ else{
+
+ /* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved.
+ Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria:
+
+ 1: the two faces do not share the same material
+ 2: the edge joining the two faces is marked as sharp.
+ 3: the two faces UV's do not make a good match
+ 4: the two faces Vertex colors do not make a good match
+
+ If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function.
+ This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user
+ the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the
+ same faces in the current pair later.
+
+ This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of
+ the python scripts bundled with Blender releases.
+ */
+
+ if(G.scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/
+ else if(G.scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/
+ else if(((G.scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (G.scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) &&
+ compareFaceAttribs(efaa[0], efaa[1], eed) == 0); /*do nothing*/
+ else{
+ measure = measure_facepair(v1, v2, v3, v4, limit);
+ if(measure < limit){
+ complexedges++;
+ eed->f1 |= T2QCOMPLEX;
+ eed->crease = measure; /*we dont mark edges for join yet*/
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*Quicksort the complex edges according to their weighting*/
+ if(complexedges){
+ edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array");
+ for(eed = em->edges.first; eed; eed=eed->next){
+ if(eed->f1 & T2QCOMPLEX){
+ *edb = eed;
+ edb++;
+ }
+ }
+ qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp);
+ /*now go through and mark the edges who get the highest weighting*/
+ for(edb=edsortblock, i=0; i < complexedges; edb++, i++){
+ efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/
+ if( !efaa[0]->f1 && !efaa[1]->f1){
+ efaa[0]->f1 = 1; //mark for join
+ efaa[1]->f1 = 1; //mark for join
+ (*edb)->f1 |= T2QJOIN;
+ }
+ }
+ }
+
+ /*finally go through all edges marked for join (simple and complex) and create new faces*/
+ for(eed=em->edges.first; eed; eed=eed->next){
+ if(eed->f1 & T2QJOIN){
+ efaa= (EVPtr *)eed->tmp.p;
+ v1 = v2 = v3 = v4 = NULL;
+ givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
+ if((v1 && v2 && v3 && v4) && (exist_face(v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be adressed.*/
+ /*flag for delete*/
+ eed->f1 |= T2QDELETE;
+ /*create new quad and select*/
+ efa = EM_face_from_faces(efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]);
+ EM_select_face(efa,1);
+ }
+ else{
+ efaa[0]->f1 = 0;
+ efaa[1]->f1 = 0;
+ }
+ }
+ }
+ }
+
+ /*free data and cleanup*/
+ if(creases){
+ for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i];
+ MEM_freeN(creases);
+ }
+ for(eed=em->edges.first; eed; eed=eed->next){
+ if(eed->f1 & T2QDELETE) eed->f1 = 1;
+ else eed->f1 = 0;
+ }
+ free_tagged_edges_faces(em->edges.first, em->faces.first);
+ if(efaar) MEM_freeN(efaar);
+ if(edsortblock) MEM_freeN(edsortblock);
+
+ EM_selectmode_flush();
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ #ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+ #endif
+ waitcursor(0);
+ BIF_undo_push("Convert Triangles to Quads");
+}
+/* ******************** END TRIANGLE TO QUAD ************************************* */
+
+#define FACE_MARKCLEAR(f) (f->f1 = 1)
+
+/* quick hack, basically a copy of beauty_fill */
+void edge_flip(void)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *v1, *v2, *v3, *v4;
+ EditEdge *eed, *nexted;
+ EditFace *efa, *w;
+ //void **efaar, **efaa;
+ EVPTuple *efaar;
+ EVPtr *efaa;
+ int totedge, ok, vindex[4];
+
+ /* - all selected edges with two faces
+ * - find the faces: store them in edges (using datablock)
+ * - per edge: - test convex
+ * - test edge: flip?
+ - if true: remedge, addedge, all edges at the edge get new face pointers
+ */
+
+ EM_selectmode_flush(); // makes sure in selectmode 'face' the edges of selected faces are selected too
+
+ totedge = count_selected_edges(em->edges.first);
+ if(totedge==0) return;
+
+ /* temporary array for : edge -> face[1], face[2] */
+ efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip");
+
+ ok = collect_quadedges(efaar, em->edges.first, em->faces.first);
+
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+
+ if(eed->f2==2) { /* points to 2 faces */
+
+ efaa= (EVPtr *) eed->tmp.p;
+
+ /* don't do it if flagged */
+
+ ok= 1;
+ efa= efaa[0];
+ if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
+ efa= efaa[1];
+ if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0;
+
+ if(ok) {
+ /* test convex */
+ givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex);
+
+/*
+ 4-----3 4-----3
+ |\ | | /|
+ | \ 1 | | 1 / |
+ | \ | -> | / |
+ | 0 \ | | / 0 |
+ | \| |/ |
+ 1-----2 1-----2
+*/
+ /* make new faces */
+ if (v1 && v2 && v3) {
+ if( convex(v1->co, v2->co, v3->co, v4->co) ) {
+ if(exist_face(v1, v2, v3, v4)==0) {
+ /* outch this may break seams */
+ w= EM_face_from_faces(efaa[0], efaa[1], vindex[0],
+ vindex[1], 4+vindex[2], -1);
+
+ EM_select_face(w, 1);
+
+ /* outch this may break seams */
+ w= EM_face_from_faces(efaa[0], efaa[1], vindex[0],
+ 4+vindex[2], 4+vindex[3], -1);
+
+ EM_select_face(w, 1);
+ }
+ /* tag as to-be-removed */
+ FACE_MARKCLEAR(efaa[1]);
+ FACE_MARKCLEAR(efaa[0]);
+ eed->f1 = 1;
+
+ } /* endif test convex */
+ }
+ }
+ }
+ eed= nexted;
+ }
+
+ /* clear tagged edges and faces: */
+ free_tagged_edges_faces(em->edges.first, em->faces.first);
+
+ MEM_freeN(efaar);
+
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+ BIF_undo_push("Flip Triangle Edges");
+
+}
+
+static void edge_rotate(EditEdge *eed,int dir)
+{
+ EditMesh *em = G.editMesh;
+ EditVert **verts[2];
+ EditFace *face[2], *efa, *newFace[2];
+ EditEdge **edges[2], **hiddenedges, *srchedge;
+ int facecount, p1, p2, p3, p4, fac1, fac2, i, j;
+ int numhidden, numshared, p[2][4];
+
+ /* check to make sure that the edge is only part of 2 faces */
+ facecount = 0;
+ for(efa = em->faces.first;efa;efa = efa->next) {
+ if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) {
+ if(facecount >= 2) {
+ /* more than two faces with this edge */
+ return;
+ }
+ else {
+ face[facecount] = efa;
+ facecount++;
+ }
+ }
+ }
+
+ if(facecount < 2)
+ return;
+
+ /* how many edges does each face have */
+ if(face[0]->e4) fac1= 4;
+ else fac1= 3;
+
+ if(face[1]->e4) fac2= 4;
+ else fac2= 3;
+
+ /* make a handy array for verts and edges */
+ verts[0]= &face[0]->v1;
+ edges[0]= &face[0]->e1;
+ verts[1]= &face[1]->v1;
+ edges[1]= &face[1]->e1;
+
+ /* we don't want to rotate edges between faces that share more than one edge */
+ numshared= 0;
+ for(i=0; i<fac1; i++)
+ for(j=0; j<fac2; j++)
+ if (edges[0][i] == edges[1][j])
+ numshared++;
+
+ if(numshared > 1)
+ return;
+
+ /* coplaner faces only please */
+ if(dot_v3v3(face[0]->n,face[1]->n) <= 0.000001)
+ return;
+
+ /* we want to construct an array of vertex indicis in both faces, starting at
+ the last vertex of the edge being rotated.
+ - first we find the two vertices that lie on the rotating edge
+ - then we make sure they are ordered according to the face vertex order
+ - and then we construct the array */
+ p1= p2= p3= p4= 0;
+
+ for(i=0; i<4; i++) {
+ if(eed->v1 == verts[0][i]) p1 = i;
+ if(eed->v2 == verts[0][i]) p2 = i;
+ if(eed->v1 == verts[1][i]) p3 = i;
+ if(eed->v2 == verts[1][i]) p4 = i;
+ }
+
+ if((p1+1)%fac1 == p2)
+ SWAP(int, p1, p2);
+ if((p3+1)%fac2 == p4)
+ SWAP(int, p3, p4);
+
+ for (i = 0; i < 4; i++) {
+ p[0][i]= (p1 + i)%fac1;
+ p[1][i]= (p3 + i)%fac2;
+ }
+
+ /* create an Array of the Edges who have h set prior to rotate */
+ numhidden = 0;
+ for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next)
+ if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT)))
+ numhidden++;
+
+ hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts");
+ if(!hiddenedges) {
+ error("Malloc Was not happy!");
+ return;
+ }
+
+ numhidden = 0;
+ for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next)
+ if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT))
+ hiddenedges[numhidden++] = srchedge;
+
+ /* create the 2 new faces */
+ if(fac1 == 3 && fac2 == 3) {
+ /* no need of reverse setup */
+
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1);
+ newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1);
+ }
+ else if(fac1 == 4 && fac2 == 3) {
+ if(dir == 1) {
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]);
+ newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1);
+ } else if (dir == 2) {
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]);
+ newFace[1]= EM_face_from_faces(face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1);
+
+ verts[0][p[0][2]]->f |= SELECT;
+ verts[1][p[1][1]]->f |= SELECT;
+ }
+ }
+ else if(fac1 == 3 && fac2 == 4) {
+ if(dir == 1) {
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1);
+ newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]);
+ } else if (dir == 2) {
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1);
+ newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]);
+
+ verts[0][p[0][1]]->f |= SELECT;
+ verts[1][p[1][2]]->f |= SELECT;
+ }
+
+ }
+ else if(fac1 == 4 && fac2 == 4) {
+ if(dir == 1) {
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]);
+ newFace[1]= EM_face_from_faces(face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]);
+ } else if (dir == 2) {
+ newFace[0]= EM_face_from_faces(face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]);
+ newFace[1]= EM_face_from_faces(face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]);
+
+ verts[0][p[0][2]]->f |= SELECT;
+ verts[1][p[1][2]]->f |= SELECT;
+ }
+ }
+ else
+ return; /* This should never happen */
+
+ if(dir == 1 || (fac1 == 3 && fac2 == 3)) {
+ verts[0][p[0][1]]->f |= SELECT;
+ verts[1][p[1][1]]->f |= SELECT;
+ }
+
+ /* copy old edge's flags to new center edge*/
+ for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) {
+ if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) {
+ srchedge->f = eed->f;
+ srchedge->h = eed->h;
+ srchedge->dir = eed->dir;
+ srchedge->seam = eed->seam;
+ srchedge->crease = eed->crease;
+ srchedge->bweight = eed->bweight;
+ }
+ }
+
+ /* resetting hidden flag */
+ for(numhidden--; numhidden>=0; numhidden--)
+ hiddenedges[numhidden]->h= 1;
+
+ /* check for orhphan edges */
+ for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next)
+ srchedge->f1= -1;
+
+ /* cleanup */
+ MEM_freeN(hiddenedges);
+
+ /* get rid of the old edge and faces*/
+ remedge(eed);
+ free_editedge(eed);
+ BLI_remlink(&em->faces, face[0]);
+ free_editface(face[0]);
+ BLI_remlink(&em->faces, face[1]);
+ free_editface(face[1]);
+}
+
+/* only accepts 1 selected edge, or 2 selected faces */
+void edge_rotate_selected(int dir)
+{
+ EditEdge *eed;
+ EditFace *efa;
+ short edgeCount = 0;
+
+ /*clear new flag for new edges, count selected edges */
+ for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
+ eed->f1= 0;
+ eed->f2 &= ~2;
+ if(eed->f & SELECT) edgeCount++;
+ }
+
+ if(edgeCount>1) {
+ /* more selected edges, check faces */
+ for(efa= G.editMesh->faces.first; efa; efa= efa->next) {
+ if(efa->f & SELECT) {
+ efa->e1->f1++;
+ efa->e2->f1++;
+ efa->e3->f1++;
+ if(efa->e4) efa->e4->f1++;
+ }
+ }
+ edgeCount= 0;
+ for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
+ if(eed->f1==2) edgeCount++;
+ }
+ if(edgeCount==1) {
+ for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
+ if(eed->f1==2) {
+ edge_rotate(eed,dir);
+ break;
+ }
+ }
+ }
+ else error("Select one edge or two adjacent faces");
+ }
+ else if(edgeCount==1) {
+ for(eed= G.editMesh->edges.first; eed; eed= eed->next) {
+ if(eed->f & SELECT) {
+ EM_select_edge(eed, 0);
+ edge_rotate(eed,dir);
+ break;
+ }
+ }
+ }
+ else error("Select one edge or two adjacent faces");
+
+
+ /* flush selected vertices (again) to edges/faces */
+ EM_select_flush();
+
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode)
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+#endif
+
+ BIF_undo_push("Rotate Edge");
+}
+
+/******************* BEVEL CODE STARTS HERE ********************/
+
+static void bevel_displace_vec(float *midvec, float *v1, float *v2, float *v3, float d, float no[3])
+{
+ float a[3], c[3], n_a[3], n_c[3], mid[3], ac, ac2, fac;
+
+ sub_v3_v3v3(a, v1, v2);
+ sub_v3_v3v3(c, v3, v2);
+
+ cross_v3_v3v3(n_a, a, no);
+ normalize_v3(n_a);
+ cross_v3_v3v3(n_c, no, c);
+ normalize_v3(n_c);
+
+ normalize_v3(a);
+ normalize_v3(c);
+ ac = dot_v3v3(a, c);
+
+ if (ac == 1 || ac == -1) {
+ midvec[0] = midvec[1] = midvec[2] = 0;
+ return;
+ }
+ ac2 = ac * ac;
+ fac = (float)sqrt((ac2 + 2*ac + 1)/(1 - ac2) + 1);
+ add_v3_v3v3(mid, n_c, n_a);
+ normalize_v3(mid);
+ mul_v3_fl(mid, d * fac);
+ add_v3_v3v3(mid, mid, v2);
+ copy_v3_v3(midvec, mid);
+}
+
+/* Finds the new point using the sinus law to extrapolate a triangle
+ Lots of sqrts which would not be good for a real time algo
+ Using the mid point of the extrapolation of both sides
+ Useless for coplanar quads, but that doesn't happen too often */
+static void fix_bevel_wrap(float *midvec, float *v1, float *v2, float *v3, float *v4, float d, float no[3])
+{
+ float a[3], b[3], c[3], l_a, l_b, l_c, s_a, s_b, s_c, Pos1[3], Pos2[3], Dir[3];
+
+ sub_v3_v3v3(a, v3, v2);
+ l_a = normalize_v3(a);
+ sub_v3_v3v3(b, v4, v3);
+ normalize_v3(b);
+ sub_v3_v3v3(c, v1, v2);
+ normalize_v3(c);
+
+ s_b = dot_v3v3(a, c);
+ s_b = (float)sqrt(1 - (s_b * s_b));
+ s_a = dot_v3v3(b, c);
+ s_a = (float)sqrt(1 - (s_a * s_a));
+ mul_v3_fl(a, -1);
+ s_c = dot_v3v3(a, b);
+ s_c = (float)sqrt(1 - (s_c * s_c));
+
+ l_b = s_b * l_a / s_a;
+ l_c = s_c * l_a / s_a;
+
+ mul_v3_fl(b, l_b);
+ mul_v3_fl(c, l_c);
+
+ add_v3_v3v3(Pos1, v2, c);
+ add_v3_v3v3(Pos2, v3, b);
+
+ add_v3_v3v3(Dir, Pos1, Pos2);
+ mul_v3_fl(Dir, 0.5);
+
+ bevel_displace_vec(midvec, v3, Dir, v2, d, no);
+
+}
+
+
+static char detect_wrap(float *o_v1, float *o_v2, float *v1, float *v2, float *no)
+{
+ float o_a[3], a[3], o_c[3], c[3];
+
+ sub_v3_v3v3(o_a, o_v1, o_v2);
+ sub_v3_v3v3(a, v1, v2);
+
+ cross_v3_v3v3(o_c, o_a, no);
+ cross_v3_v3v3(c, a, no);
+
+ if (dot_v3v3(c, o_c) <= 0)
+ return 1;
+ else
+ return 0;
+}
+
+// Detects and fix a quad wrapping after the resize
+// Arguments are the orginal verts followed by the final verts and then the bevel size and the normal
+static void fix_bevel_quad_wrap(float *o_v1, float *o_v2, float *o_v3, float *o_v4, float *v1, float *v2, float *v3, float *v4, float d, float *no)
+{
+ float vec[3];
+ char wrap[4];
+
+ // Quads can wrap partially. Watch out
+ wrap[0] = detect_wrap(o_v1, o_v2, v1, v2, no); // Edge 1-2
+ wrap[1] = detect_wrap(o_v2, o_v3, v2, v3, no); // Edge 2-3
+ wrap[2] = detect_wrap(o_v3, o_v4, v3, v4, no); // Edge 3-4
+ wrap[3] = detect_wrap(o_v4, o_v1, v4, v1, no); // Edge 4-1
+
+ // Edge 1 inverted
+ if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 0) {
+ fix_bevel_wrap(vec, o_v2, o_v3, o_v4, o_v1, d, no);
+ copy_v3_v3(v1, vec);
+ copy_v3_v3(v2, vec);
+ }
+ // Edge 2 inverted
+ else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 0) {
+ fix_bevel_wrap(vec, o_v3, o_v4, o_v1, o_v2, d, no);
+ copy_v3_v3(v2, vec);
+ copy_v3_v3(v3, vec);
+ }
+ // Edge 3 inverted
+ else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) {
+ fix_bevel_wrap(vec, o_v4, o_v1, o_v2, o_v3, d, no);
+ copy_v3_v3(v3, vec);
+ copy_v3_v3(v4, vec);
+ }
+ // Edge 4 inverted
+ else if (wrap[0] == 0 && wrap[1] == 0 && wrap[2] == 0 && wrap[3] == 1) {
+ fix_bevel_wrap(vec, o_v1, o_v2, o_v3, o_v4, d, no);
+ copy_v3_v3(v4, vec);
+ copy_v3_v3(v1, vec);
+ }
+ // Edge 2 and 4 inverted
+ else if (wrap[0] == 0 && wrap[1] == 1 && wrap[2] == 0 && wrap[3] == 1) {
+ add_v3_v3v3(vec, v2, v3);
+ mul_v3_fl(vec, 0.5);
+ copy_v3_v3(v2, vec);
+ copy_v3_v3(v3, vec);
+ add_v3_v3v3(vec, v1, v4);
+ mul_v3_fl(vec, 0.5);
+ copy_v3_v3(v1, vec);
+ copy_v3_v3(v4, vec);
+ }
+ // Edge 1 and 3 inverted
+ else if (wrap[0] == 1 && wrap[1] == 0 && wrap[2] == 1 && wrap[3] == 0) {
+ add_v3_v3v3(vec, v1, v2);
+ mul_v3_fl(vec, 0.5);
+ copy_v3_v3(v1, vec);
+ copy_v3_v3(v2, vec);
+ add_v3_v3v3(vec, v3, v4);
+ mul_v3_fl(vec, 0.5);
+ copy_v3_v3(v3, vec);
+ copy_v3_v3(v4, vec);
+ }
+ // Totally inverted
+ else if (wrap[0] == 1 && wrap[1] == 1 && wrap[2] == 1 && wrap[3] == 1) {
+ add_v3_v3v3(vec, v1, v2);
+ add_v3_v3v3(vec, vec, v3);
+ add_v3_v3v3(vec, vec, v4);
+ mul_v3_fl(vec, 0.25);
+ copy_v3_v3(v1, vec);
+ copy_v3_v3(v2, vec);
+ copy_v3_v3(v3, vec);
+ copy_v3_v3(v4, vec);
+ }
+
+}
+
+// Detects and fix a tri wrapping after the resize
+// Arguments are the orginal verts followed by the final verts and the normal
+// Triangles cannot wrap partially (not in this situation
+static void fix_bevel_tri_wrap(float *o_v1, float *o_v2, float *o_v3, float *v1, float *v2, float *v3, float *no)
+{
+ if (detect_wrap(o_v1, o_v2, v1, v2, no)) {
+ float vec[3];
+ add_v3_v3v3(vec, o_v1, o_v2);
+ add_v3_v3v3(vec, vec, o_v3);
+ mul_v3_fl(vec, 1.0f/3.0f);
+ copy_v3_v3(v1, vec);
+ copy_v3_v3(v2, vec);
+ copy_v3_v3(v3, vec);
+ }
+}
+
+static void bevel_shrink_faces(float d, int flag)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ float vec[3], no[3], v1[3], v2[3], v3[3], v4[3];
+
+ /* move edges of all faces with efa->f1 & flag closer towards their centers */
+ efa= em->faces.first;
+ while (efa) {
+ if (efa->f1 & flag) {
+ copy_v3_v3(v1, efa->v1->co);
+ copy_v3_v3(v2, efa->v2->co);
+ copy_v3_v3(v3, efa->v3->co);
+ copy_v3_v3(no, efa->n);
+ if (efa->v4 == NULL) {
+ bevel_displace_vec(vec, v1, v2, v3, d, no);
+ copy_v3_v3(efa->v2->co, vec);
+ bevel_displace_vec(vec, v2, v3, v1, d, no);
+ copy_v3_v3(efa->v3->co, vec);
+ bevel_displace_vec(vec, v3, v1, v2, d, no);
+ copy_v3_v3(efa->v1->co, vec);
+
+ fix_bevel_tri_wrap(v1, v2, v3, efa->v1->co, efa->v2->co, efa->v3->co, no);
+ } else {
+ copy_v3_v3(v4, efa->v4->co);
+ bevel_displace_vec(vec, v1, v2, v3, d, no);
+ copy_v3_v3(efa->v2->co, vec);
+ bevel_displace_vec(vec, v2, v3, v4, d, no);
+ copy_v3_v3(efa->v3->co, vec);
+ bevel_displace_vec(vec, v3, v4, v1, d, no);
+ copy_v3_v3(efa->v4->co, vec);
+ bevel_displace_vec(vec, v4, v1, v2, d, no);
+ copy_v3_v3(efa->v1->co, vec);
+
+ fix_bevel_quad_wrap(v1, v2, v3, v4, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, d, no);
+ }
+ }
+ efa= efa->next;
+ }
+}
+
+static void bevel_shrink_draw(float d, int flag)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ float vec[3], no[3], v1[3], v2[3], v3[3], v4[3], fv1[3], fv2[3], fv3[3], fv4[3];
+
+ /* move edges of all faces with efa->f1 & flag closer towards their centers */
+ efa= em->faces.first;
+ while (efa) {
+ copy_v3_v3(v1, efa->v1->co);
+ copy_v3_v3(v2, efa->v2->co);
+ copy_v3_v3(v3, efa->v3->co);
+ copy_v3_v3(no, efa->n);
+ if (efa->v4 == NULL) {
+ bevel_displace_vec(vec, v1, v2, v3, d, no);
+ copy_v3_v3(fv2, vec);
+ bevel_displace_vec(vec, v2, v3, v1, d, no);
+ copy_v3_v3(fv3, vec);
+ bevel_displace_vec(vec, v3, v1, v2, d, no);
+ copy_v3_v3(fv1, vec);
+
+ fix_bevel_tri_wrap(v1, v2, v3, fv1, fv2, fv3, no);
+
+ glBegin(GL_LINES);
+ glVertex3fv(fv1);
+ glVertex3fv(fv2);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3fv(fv2);
+ glVertex3fv(fv3);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3fv(fv1);
+ glVertex3fv(fv3);
+ glEnd();
+ } else {
+ copy_v3_v3(v4, efa->v4->co);
+ bevel_displace_vec(vec, v4, v1, v2, d, no);
+ copy_v3_v3(fv1, vec);
+ bevel_displace_vec(vec, v1, v2, v3, d, no);
+ copy_v3_v3(fv2, vec);
+ bevel_displace_vec(vec, v2, v3, v4, d, no);
+ copy_v3_v3(fv3, vec);
+ bevel_displace_vec(vec, v3, v4, v1, d, no);
+ copy_v3_v3(fv4, vec);
+
+ fix_bevel_quad_wrap(v1, v2, v3, v4, fv1, fv2, fv3, fv4, d, no);
+
+ glBegin(GL_LINES);
+ glVertex3fv(fv1);
+ glVertex3fv(fv2);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3fv(fv2);
+ glVertex3fv(fv3);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3fv(fv3);
+ glVertex3fv(fv4);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3fv(fv1);
+ glVertex3fv(fv4);
+ glEnd();
+ }
+ efa= efa->next;
+ }
+}
+
+static void bevel_mesh(float bsize, int allfaces)
+{
+ EditMesh *em = G.editMesh;
+//#define BEV_DEBUG
+/* Enables debug printfs and assigns material indices: */
+/* 2 = edge quad */
+/* 3 = fill polygon (vertex clusters) */
+
+ EditFace *efa, *example; //, *nextvl;
+ EditEdge *eed, *eed2;
+ EditVert *neweve[1024], *eve, *eve2, *eve3, *v1, *v2, *v3, *v4; //, *eve4;
+ //short found4, search;
+ //float f1, f2, f3, f4;
+ float cent[3], min[3], max[3];
+ int a, b, c;
+ float limit= 0.001f;
+
+ if(multires_test()) return;
+
+ waitcursor(1);
+
+ removedoublesflag(1, 0, limit);
+
+ /* tag all original faces */
+ efa= em->faces.first;
+ while (efa) {
+ efa->f1= 0;
+ if (faceselectedAND(efa, 1)||allfaces) {
+ efa->f1= 1;
+ efa->v1->f |= 128;
+ efa->v2->f |= 128;
+ efa->v3->f |= 128;
+ if (efa->v4) efa->v4->f |= 128;
+ }
+ efa->v1->f &= ~64;
+ efa->v2->f &= ~64;
+ efa->v3->f &= ~64;
+ if (efa->v4) efa->v4->f &= ~64;
+
+ efa= efa->next;
+ }
+
+#ifdef BEV_DEBUG
+ fprintf(stderr,"bevel_mesh: split\n");
+#endif
+
+ efa= em->faces.first;
+ while (efa) {
+ if (efa->f1 & 1) {
+ efa->f1-= 1;
+ v1= addvertlist(efa->v1->co, efa->v1);
+ v1->f= efa->v1->f & ~128;
+ efa->v1->tmp.v = v1;
+
+ v1= addvertlist(efa->v2->co, efa->v2);
+ v1->f= efa->v2->f & ~128;
+ efa->v2->tmp.v = v1;
+
+ v1= addvertlist(efa->v3->co, efa->v3);
+ v1->f= efa->v3->f & ~128;
+ efa->v3->tmp.v = v1;
+
+ if (efa->v4) {
+ v1= addvertlist(efa->v4->co, efa->v4);
+ v1->f= efa->v4->f & ~128;
+ efa->v4->tmp.v = v1;
+ }
+
+ /* Needs better adaption of creases? */
+ addedgelist(efa->e1->v1->tmp.v,
+ efa->e1->v2->tmp.v,
+ efa->e1);
+ addedgelist(efa->e2->v1->tmp.v,
+ efa->e2->v2->tmp.v,
+ efa->e2);
+ addedgelist(efa->e3->v1->tmp.v,
+ efa->e3->v2->tmp.v,
+ efa->e3);
+ if (efa->e4) addedgelist(efa->e4->v1->tmp.v,
+ efa->e4->v2->tmp.v,
+ efa->e4);
+
+ if(efa->v4) {
+ v1 = efa->v1->tmp.v;
+ v2 = efa->v2->tmp.v;
+ v3 = efa->v3->tmp.v;
+ v4 = efa->v4->tmp.v;
+ addfacelist(v1, v2, v3, v4, efa,NULL);
+ } else {
+ v1= efa->v1->tmp.v;
+ v2= efa->v2->tmp.v;
+ v3= efa->v3->tmp.v;
+ addfacelist(v1, v2, v3, 0, efa,NULL);
+ }
+
+ efa= efa-> next;
+ } else {
+ efa= efa->next;
+ }
+ }
+
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if( (efa->v1->f & 128) && (efa->v2->f & 128) && (efa->v3->f & 128) ) {
+ if(efa->v4==NULL || (efa->v4->f & 128)) efa->f |= 128;
+ }
+ }
+
+ delfaceflag(128); // works with face flag now
+
+ /* tag all faces for shrink*/
+ efa= em->faces.first;
+ while (efa) {
+ if (faceselectedAND(efa, 1)||allfaces) {
+ efa->f1= 2;
+ }
+ efa= efa->next;
+ }
+
+#ifdef BEV_DEBUG
+ fprintf(stderr,"bevel_mesh: make edge quads\n");
+#endif
+
+ /* find edges that are on each other and make quads between them */
+
+ eed= em->edges.first;
+ while(eed) {
+ eed->f2= eed->f1= 0;
+ if ( ((eed->v1->f & eed->v2->f) & 1) || allfaces)
+ eed->f1 |= 4; /* original edges */
+ eed->tmp.v = 0;
+ eed= eed->next;
+ }
+
+ eed= em->edges.first;
+ while (eed) {
+ if ( ((eed->f1 & 2)==0) && (eed->f1 & 4) ) {
+ eed2= em->edges.first;
+ while (eed2) {
+ if ( (eed2 != eed) && ((eed2->f1 & 2)==0) && (eed->f1 & 4) ) {
+ if (
+ (eed->v1 != eed2->v1) &&
+ (eed->v1 != eed2->v2) &&
+ (eed->v2 != eed2->v1) &&
+ (eed->v2 != eed2->v2) && (
+ ( compare_v3v3(eed->v1->co, eed2->v1->co, limit) &&
+ compare_v3v3(eed->v2->co, eed2->v2->co, limit) ) ||
+ ( compare_v3v3(eed->v1->co, eed2->v2->co, limit) &&
+ compare_v3v3(eed->v2->co, eed2->v1->co, limit) ) ) )
+ {
+
+#ifdef BEV_DEBUG
+ fprintf(stderr, "bevel_mesh: edge quad\n");
+#endif
+
+ eed->f1 |= 2; /* these edges are finished */
+ eed2->f1 |= 2;
+
+ example= NULL;
+ efa= em->faces.first; /* search example face (for mat_nr, ME_SMOOTH, ...) */
+ while (efa) {
+ if ( (efa->e1 == eed) ||
+ (efa->e2 == eed) ||
+ (efa->e3 == eed) ||
+ (efa->e4 && (efa->e4 == eed)) ) {
+ example= efa;
+ efa= NULL;
+ }
+ if (efa) efa= efa->next;
+ }
+
+ neweve[0]= eed->v1; neweve[1]= eed->v2;
+ neweve[2]= eed2->v1; neweve[3]= eed2->v2;
+
+ if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) {
+ efa= NULL;
+
+ if (compare_v3v3(eed->v1->co, eed2->v2->co, limit)) {
+ efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], example,NULL);
+ } else {
+ efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], example,NULL);
+ }
+
+ if(efa) {
+ float inp;
+ normal_tri_v3( efa->n,efa->v1->co, efa->v2->co, efa->v3->co);
+ inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2];
+ if(inp < 0.0) flipface(efa);
+#ifdef BEV_DEBUG
+ efa->mat_nr= 1;
+#endif
+ } else fprintf(stderr,"bevel_mesh: error creating face\n");
+ }
+ eed2= NULL;
+ }
+ }
+ if (eed2) eed2= eed2->next;
+ }
+ }
+ eed= eed->next;
+ }
+
+ eed= em->edges.first;
+ while(eed) {
+ eed->f2= eed->f1= 0;
+ eed->f1= 0;
+ eed->v1->f1 &= ~1;
+ eed->v2->f1 &= ~1;
+ eed->tmp.v = 0;
+ eed= eed->next;
+ }
+
+#ifdef BEV_DEBUG
+ fprintf(stderr,"bevel_mesh: find clusters\n");
+#endif
+
+ /* Look for vertex clusters */
+
+ eve= em->verts.first;
+ while (eve) {
+ eve->f &= ~(64|128);
+ eve->tmp.v = NULL;
+ eve= eve->next;
+ }
+
+ /* eve->f: 128: first vertex in a list (->tmp.v) */
+ /* 64: vertex is in a list */
+
+ eve= em->verts.first;
+ while (eve) {
+ eve2= em->verts.first;
+ eve3= NULL;
+ while (eve2) {
+ if ((eve2 != eve) && ((eve2->f & (64|128))==0)) {
+ if (compare_v3v3(eve->co, eve2->co, limit)) {
+ if ((eve->f & (128|64)) == 0) {
+ /* fprintf(stderr,"Found vertex cluster:\n *\n *\n"); */
+ eve->f |= 128;
+ eve->tmp.v = eve2;
+ eve3= eve2;
+ } else if ((eve->f & 64) == 0) {
+ /* fprintf(stderr," *\n"); */
+ if (eve3) eve3->tmp.v = eve2;
+ eve2->f |= 64;
+ eve3= eve2;
+ }
+ }
+ }
+ eve2= eve2->next;
+ if (!eve2) {
+ if (eve3) eve3->tmp.v = NULL;
+ }
+ }
+ eve= eve->next;
+ }
+
+#ifdef BEV_DEBUG
+ fprintf(stderr,"bevel_mesh: shrink faces\n");
+#endif
+
+ bevel_shrink_faces(bsize, 2);
+
+#ifdef BEV_DEBUG
+ fprintf(stderr,"bevel_mesh: fill clusters\n");
+#endif
+
+ /* Make former vertex clusters faces */
+
+ eve= em->verts.first;
+ while (eve) {
+ eve->f &= ~64;
+ eve= eve->next;
+ }
+
+ eve= em->verts.first;
+ while (eve) {
+ if (eve->f & 128) {
+ eve->f &= ~128;
+ a= 0;
+ neweve[a]= eve;
+ eve2 = eve->tmp.v;
+ while (eve2) {
+ a++;
+ neweve[a]= eve2;
+ eve2 = eve2->tmp.v;
+ }
+ a++;
+ efa= NULL;
+ if (a>=3) {
+ example= NULL;
+ efa= em->faces.first; /* search example face */
+ while (efa) {
+ if ( (efa->v1 == neweve[0]) ||
+ (efa->v2 == neweve[0]) ||
+ (efa->v3 == neweve[0]) ||
+ (efa->v4 && (efa->v4 == neweve[0])) ) {
+ example= efa;
+ efa= NULL;
+ }
+ if (efa) efa= efa->next;
+ }
+#ifdef BEV_DEBUG
+ fprintf(stderr,"bevel_mesh: Making %d-gon\n", a);
+#endif
+ if (a>4) {
+ cent[0]= cent[1]= cent[2]= 0.0;
+ INIT_MINMAX(min, max);
+ for (b=0; b<a; b++) {
+ add_v3_v3v3(cent, cent, neweve[b]->co);
+ DO_MINMAX(neweve[b]->co, min, max);
+ }
+ cent[0]= (min[0]+max[0])/2;
+ cent[1]= (min[1]+max[1])/2;
+ cent[2]= (min[2]+max[2])/2;
+ eve2= addvertlist(cent, NULL);
+ eve2->f |= 1;
+ eed= em->edges.first;
+ while (eed) {
+ c= 0;
+ for (b=0; b<a; b++)
+ if ((neweve[b]==eed->v1) || (neweve[b]==eed->v2)) c++;
+ if (c==2) {
+ if(exist_face(eed->v1, eed->v2, eve2, 0)==0) {
+ efa= addfacelist(eed->v1, eed->v2, eve2, 0, example,NULL);
+#ifdef BEV_DEBUG
+ efa->mat_nr= 2;
+#endif
+ }
+ }
+ eed= eed->next;
+ }
+ } else if (a==4) {
+ if(exist_face(neweve[0], neweve[1], neweve[2], neweve[3])==0) {
+ /* the order of vertices can be anything, three cases to check */
+ if( convex(neweve[0]->co, neweve[1]->co, neweve[2]->co, neweve[3]->co) ) {
+ efa= addfacelist(neweve[0], neweve[1], neweve[2], neweve[3], NULL, NULL);
+ }
+ else if( convex(neweve[0]->co, neweve[2]->co, neweve[3]->co, neweve[1]->co) ) {
+ efa= addfacelist(neweve[0], neweve[2], neweve[3], neweve[1], NULL, NULL);
+ }
+ else if( convex(neweve[0]->co, neweve[2]->co, neweve[1]->co, neweve[3]->co) ) {
+ efa= addfacelist(neweve[0], neweve[2], neweve[1], neweve[3], NULL, NULL);
+ }
+ }
+ }
+ else if (a==3) {
+ if(exist_face(neweve[0], neweve[1], neweve[2], 0)==0)
+ efa= addfacelist(neweve[0], neweve[1], neweve[2], 0, example, NULL);
+ }
+ if(efa) {
+ float inp;
+ normal_tri_v3( efa->n,neweve[0]->co, neweve[1]->co, neweve[2]->co);
+ inp= efa->n[0]*G.vd->viewmat[0][2] + efa->n[1]*G.vd->viewmat[1][2] + efa->n[2]*G.vd->viewmat[2][2];
+ if(inp < 0.0) flipface(efa);
+#ifdef BEV_DEBUG
+ efa->mat_nr= 2;
+#endif
+ }
+ }
+ }
+ eve= eve->next;
+ }
+
+ eve= em->verts.first;
+ while (eve) {
+ eve->f1= 0;
+ eve->f &= ~(128|64);
+ eve->tmp.v= NULL;
+ eve= eve->next;
+ }
+
+ recalc_editnormals();
+ waitcursor(0);
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+
+ removedoublesflag(1, 0, limit);
+
+ /* flush selected vertices to edges/faces */
+ EM_select_flush();
+
+#undef BEV_DEBUG
+}
+
+static void bevel_mesh_recurs(float bsize, short recurs, int allfaces)
+{
+ float d;
+ short nr;
+
+ d= bsize;
+ for (nr=0; nr<recurs; nr++) {
+ bevel_mesh(d, allfaces);
+ if (nr==0) d /= 3; else d /= 2;
+ }
+}
+
+void bevel_menu(void)
+{
+ BME_Mesh *bm;
+ BME_TransData_Head *td;
+ TransInfo *t;
+ int options, res, gbm_free = 0;
+
+ t = BIF_GetTransInfo();
+ if (!G.editBMesh) {
+ G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh");
+ gbm_free = 1;
+ }
+
+ G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT;
+ G.editBMesh->res = 1;
+
+ while(G.editBMesh->options & BME_BEVEL_RUNNING) {
+ options = G.editBMesh->options;
+ res = G.editBMesh->res;
+ bm = BME_editmesh_to_bmesh(G.editMesh);
+ BIF_undo_push("Pre-Bevel");
+ free_editMesh(G.editMesh);
+ BME_bevel(bm,0.1f,res,options,0,0,&td);
+ BME_bmesh_to_editmesh(bm, td);
+ EM_selectmode_flush();
+ G.editBMesh->bm = bm;
+ G.editBMesh->td = td;
+ initTransform(TFM_BEVEL,CTX_BMESH);
+ Transform();
+ BME_free_transdata(td);
+ BME_free_mesh(bm);
+ if (t->state != TRANS_CONFIRM) {
+ BIF_undo();
+ }
+ if (options == G.editBMesh->options) {
+ G.editBMesh->options &= ~BME_BEVEL_RUNNING;
+ }
+ }
+
+ if (gbm_free) {
+ MEM_freeN(G.editBMesh);
+ G.editBMesh = NULL;
+ }
+}
+
+
+void bevel_menu_old()
+{
+ char Finished = 0, Canceled = 0, str[100], Recalc = 0;
+ short mval[2], oval[2], curval[2], event = 0, recurs = 1, nr;
+ float vec[3], d, drawd=0.0, center[3], fac = 1;
+
+ getmouseco_areawin(mval);
+ oval[0] = mval[0]; oval[1] = mval[1];
+
+ // Silly hackish code to initialise the variable (warning if not done)
+ // while still drawing in the first iteration (and without using another variable)
+ curval[0] = mval[0] + 1; curval[1] = mval[1] + 1;
+
+ // Init grabz for window to vec conversions
+ initgrabz(-G.vd->ofs[0], -G.vd->ofs[1], -G.vd->ofs[2]);
+ window_to_3d(center, mval[0], mval[1]);
+
+ if(button(&recurs, 1, 4, "Recursion:")==0) return;
+
+ for (nr=0; nr<recurs-1; nr++) {
+ if (nr==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * nr * 2.0f);
+ }
+
+ EM_set_flag_all(SELECT);
+
+ SetBlenderCursor(SYSCURSOR);
+
+ while (Finished == 0)
+ {
+ getmouseco_areawin(mval);
+ if (mval[0] != curval[0] || mval[1] != curval[1] || (Recalc == 1))
+ {
+ Recalc = 0;
+ curval[0] = mval[0];
+ curval[1] = mval[1];
+
+ window_to_3d(vec, mval[0]-oval[0], mval[1]-oval[1]);
+ d = normalize_v3(vec) / 10;
+
+
+ drawd = d * fac;
+ if (G.qual & LR_CTRLKEY)
+ drawd = (float) floor(drawd * 10.0f)/10.0f;
+ if (G.qual & LR_SHIFTKEY)
+ drawd /= 10;
+
+ /*------------- Preview lines--------------- */
+
+ /* uses callback mechanism to draw it all in current area */
+ scrarea_do_windraw(curarea);
+
+ /* set window matrix to perspective, default an area returns with buttons transform */
+ persp(PERSP_VIEW);
+ /* make a copy, for safety */
+ glPushMatrix();
+ /* multiply with the object transformation */
+ mymultmatrix(G.obedit->obmat);
+
+ glColor3ub(255, 255, 0);
+
+ // PREVIEW CODE GOES HERE
+ bevel_shrink_draw(drawd, 2);
+
+ /* restore matrix transform */
+ glPopMatrix();
+
+ sprintf(str, "Bevel Size: %.4f LMB to confirm, RMB to cancel, SPACE to input directly.", drawd);
+ headerprint(str);
+
+ /* this also verifies other area/windows for clean swap */
+ screen_swapbuffers();
+
+ persp(PERSP_WIN);
+
+ glDrawBuffer(GL_FRONT);
+
+ BIF_ThemeColor(TH_WIRE);
+
+ setlinestyle(3);
+ glBegin(GL_LINE_STRIP);
+ glVertex2sv(mval);
+ glVertex2sv(oval);
+ glEnd();
+ setlinestyle(0);
+
+ persp(PERSP_VIEW);
+ bglFlush(); // flush display for frontbuffer
+ glDrawBuffer(GL_BACK);
+ }
+ while(qtest()) {
+ short val=0;
+ event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle
+
+ /* val==0 on key-release event */
+ if(val && (event==ESCKEY || event==RIGHTMOUSE || event==LEFTMOUSE || event==RETKEY || event==ESCKEY)) {
+ if (event==RIGHTMOUSE || event==ESCKEY)
+ Canceled = 1;
+ Finished = 1;
+ }
+ else if (val && event==SPACEKEY) {
+ if (fbutton(&d, 0.000, 10.000, 10, 0, "Width:")!=0) {
+ drawd = d * fac;
+ Finished = 1;
+ }
+ }
+ else if (val) {
+ /* On any other keyboard event, recalc */
+ Recalc = 1;
+ }
+
+ }
+ }
+ if (Canceled==0) {
+ SetBlenderCursor(BC_WAITCURSOR);
+ bevel_mesh_recurs(drawd/fac, recurs, 1);
+ righthandfaces(1);
+ SetBlenderCursor(SYSCURSOR);
+ BIF_undo_push("Bevel");
+ }
+}
+
+/* -------------------- More tools ------------------ */
+
+void mesh_set_face_flags(short mode)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ MTFace *tface;
+ short m_tex=0, m_tiles=0, m_shared=0,
+ m_light=0, m_invis=0, m_collision=0,
+ m_twoside=0, m_obcolor=0, m_halo=0,
+ m_billboard=0, m_shadow=0, m_text=0,
+ m_sort=0;
+ short flag = 0, change = 0;
+
+ if (!EM_texFaceCheck()) {
+ error("not a mesh with uv/image layers");
+ return;
+ }
+
+ add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL);
+ add_numbut(1, TOG|SHO, "Tiles", 0, 0, &m_tiles, NULL);
+ add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL);
+ add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL);
+ add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL);
+ add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL);
+ add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL);
+ add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL);
+ add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL);
+ add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL);
+ add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL);
+ add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL);
+ add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL);
+
+ if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW))
+ return;
+
+ /* these 2 cant both be on */
+ if (mode) /* are we seeting*/
+ if (m_halo)
+ m_billboard = 0;
+
+ if (m_tex) flag |= TF_TEX;
+ if (m_tiles) flag |= TF_TILES;
+ if (m_shared) flag |= TF_SHAREDCOL;
+ if (m_light) flag |= TF_LIGHT;
+ if (m_invis) flag |= TF_INVISIBLE;
+ if (m_collision) flag |= TF_DYNAMIC;
+ if (m_twoside) flag |= TF_TWOSIDE;
+ if (m_obcolor) flag |= TF_OBCOL;
+ if (m_halo) flag |= TF_BILLBOARD;
+ if (m_billboard) flag |= TF_BILLBOARD2;
+ if (m_shadow) flag |= TF_SHADOW;
+ if (m_text) flag |= TF_BMFONT;
+ if (m_sort) flag |= TF_ALPHASORT;
+
+ if (flag==0)
+ return;
+
+ efa= em->faces.first;
+ while(efa) {
+ if(efa->f & SELECT) {
+ tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ if (mode) tface->mode |= flag;
+ else tface->mode &= ~flag;
+ change = 1;
+ }
+ efa= efa->next;
+ }
+
+ if (change) {
+ BIF_undo_push((mode ? "Set Flags" : "Clear Flags"));
+
+ allqueue(REDRAWIMAGE, 0);
+ allqueue(REDRAWVIEW3D, 0);
+ }
+}
+
+void mesh_set_smooth_faces(short event)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+
+ if(G.obedit==0) return;
+
+ if(G.obedit->type != OB_MESH) return;
+
+ efa= em->faces.first;
+ while(efa) {
+ if(efa->f & SELECT) {
+ if(event==1) efa->flag |= ME_SMOOTH;
+ else if(event==0) efa->flag &= ~ME_SMOOTH;
+ }
+ efa= efa->next;
+ }
+
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+
+ if(event==1) BIF_undo_push("Set Smooth");
+ else if(event==0) BIF_undo_push("Set Solid");
+}
+
+/* helper to find edge for edge_rip */
+static float mesh_rip_edgedist(float mat[][4], float *co1, float *co2, short *mval)
+{
+ float vec1[3], vec2[3], mvalf[2];
+
+ view3d_project_float(curarea, co1, vec1, mat);
+ view3d_project_float(curarea, co2, vec2, mat);
+ mvalf[0]= (float)mval[0];
+ mvalf[1]= (float)mval[1];
+
+ return dist_to_line_segment_v2(mvalf, vec1, vec2);
+}
+
+/* helper for below */
+static void mesh_rip_setface(EditFace *sefa)
+{
+ /* put new vertices & edges in best face */
+ if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v;
+ if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v;
+ if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v;
+ if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v;
+
+ sefa->e1= addedgelist(sefa->v1, sefa->v2, sefa->e1);
+ sefa->e2= addedgelist(sefa->v2, sefa->v3, sefa->e2);
+ if(sefa->v4) {
+ sefa->e3= addedgelist(sefa->v3, sefa->v4, sefa->e3);
+ sefa->e4= addedgelist(sefa->v4, sefa->v1, sefa->e4);
+ }
+ else
+ sefa->e3= addedgelist(sefa->v3, sefa->v1, sefa->e3);
+
+}
+
+/* based on mouse cursor position, it defines how is being ripped */
+void mesh_rip(void)
+{
+ extern void faceloop_select(EditEdge *startedge, int select);
+ EditMesh *em = G.editMesh;
+ EditVert *eve, *nextve;
+ EditEdge *eed, *seed= NULL;
+ EditFace *efa, *sefa= NULL;
+ float projectMat[4][4], viewMat[4][4], vec[3], dist, mindist;
+ short doit= 1, mval[2],propmode,prop;
+
+ propmode = G.scene->prop_mode;
+ G.scene->prop_mode = 0;
+ prop = G.scene->proportional;
+ G.scene->proportional = 0;
+
+ /* select flush... vertices are important */
+ EM_selectmode_set();
+
+ getmouseco_areawin(mval);
+ view3d_get_object_project_mat(curarea, G.obedit, projectMat, viewMat);
+
+ /* find best face, exclude triangles and break on face select or faces with 2 edges select */
+ mindist= 1000000.0f;
+ for(efa= em->faces.first; efa; efa=efa->next) {
+ if( efa->f & 1)
+ break;
+ if(efa->v4 && faceselectedOR(efa, SELECT) ) {
+ int totsel=0;
+
+ if(efa->e1->f & SELECT) totsel++;
+ if(efa->e2->f & SELECT) totsel++;
+ if(efa->e3->f & SELECT) totsel++;
+ if(efa->e4->f & SELECT) totsel++;
+
+ if(totsel>1)
+ break;
+ view3d_project_float(curarea, efa->cent, vec, projectMat);
+ dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) );
+ if(dist<mindist) {
+ mindist= dist;
+ sefa= efa;
+ }
+ }
+ }
+
+ if(efa) {
+ error("Can't perform ripping with faces selected this way");
+ return;
+ }
+ if(sefa==NULL) {
+ error("No proper selection or faces included");
+ return;
+ }
+
+
+ /* duplicate vertices, new vertices get selected */
+ for(eve = em->verts.last; eve; eve= eve->prev) {
+ eve->tmp.v = NULL;
+ if(eve->f & SELECT) {
+ eve->tmp.v = addvertlist(eve->co, eve);
+ eve->f &= ~SELECT;
+ eve->tmp.v->f |= SELECT;
+ }
+ }
+
+ /* find the best candidate edge */
+ /* or one of sefa edges is selected... */
+ if(sefa->e1->f & SELECT) seed= sefa->e2;
+ if(sefa->e2->f & SELECT) seed= sefa->e1;
+ if(sefa->e3->f & SELECT) seed= sefa->e2;
+ if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3;
+
+ /* or we do the distance trick */
+ if(seed==NULL) {
+ mindist= 1000000.0f;
+ if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) {
+ dist = mesh_rip_edgedist(projectMat,
+ sefa->e1->v1->co,
+ sefa->e1->v2->co, mval);
+ if(dist<mindist) {
+ seed= sefa->e1;
+ mindist= dist;
+ }
+ }
+ if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) {
+ dist = mesh_rip_edgedist(projectMat,
+ sefa->e2->v1->co,
+ sefa->e2->v2->co, mval);
+ if(dist<mindist) {
+ seed= sefa->e2;
+ mindist= dist;
+ }
+ }
+ if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) {
+ dist= mesh_rip_edgedist(projectMat,
+ sefa->e3->v1->co,
+ sefa->e3->v2->co, mval);
+ if(dist<mindist) {
+ seed= sefa->e3;
+ mindist= dist;
+ }
+ }
+ if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) {
+ dist= mesh_rip_edgedist(projectMat,
+ sefa->e4->v1->co,
+ sefa->e4->v2->co, mval);
+ if(dist<mindist) {
+ seed= sefa->e4;
+ mindist= dist;
+ }
+ }
+ }
+
+ if(seed==NULL) { // never happens?
+ error("No proper edge found to start");
+ return;
+ }
+
+ faceloop_select(seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1
+
+ /* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */
+ for(eed = em->edges.last; eed; eed= eed->prev) {
+ eed->tmp.v = NULL;
+ if((eed->v1->tmp.v) || (eed->v2->tmp.v)) {
+ EditEdge *newed;
+
+ newed= addedgelist(eed->v1->tmp.v?eed->v1->tmp.v:eed->v1,
+ eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed);
+ if(eed->f & SELECT) {
+ eed->f &= ~SELECT;
+ newed->f |= SELECT;
+ }
+ eed->tmp.v = (EditVert *)newed;
+ }
+ }
+
+ /* first clear edges to help finding neighbours */
+ for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0;
+
+ /* put new vertices & edges && flag in best face */
+ mesh_rip_setface(sefa);
+
+ /* starting with neighbours of best face, we loop over the seam */
+ sefa->f1= 2;
+ doit= 1;
+ while(doit) {
+ doit= 0;
+
+ for(efa= em->faces.first; efa; efa=efa->next) {
+ /* new vert in face */
+ if (efa->v1->tmp.v || efa->v2->tmp.v ||
+ efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) {
+ /* face is tagged with loop */
+ if(efa->f1==1) {
+ mesh_rip_setface(efa);
+ efa->f1= 2;
+ doit= 1;
+ }
+ }
+ }
+ }
+
+ /* remove loose edges, that were part of a ripped face */
+ for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0;
+ for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0;
+ for(efa= em->faces.first; efa; efa=efa->next) {
+ efa->e1->f1= 1;
+ efa->e2->f1= 1;
+ efa->e3->f1= 1;
+ if(efa->e4) efa->e4->f1= 1;
+ }
+
+ for(eed = em->edges.last; eed; eed= seed) {
+ seed= eed->prev;
+ if(eed->f1==0) {
+ if(eed->v1->tmp.v || eed->v2->tmp.v ||
+ (eed->v1->f & SELECT) || (eed->v2->f & SELECT)) {
+ remedge(eed);
+ free_editedge(eed);
+ eed= NULL;
+ }
+ }
+ if(eed) {
+ eed->v1->f1= 1;
+ eed->v2->f1= 1;
+ }
+ }
+
+ /* and remove loose selected vertices, that got duplicated accidentally */
+ for(eve = em->verts.first; eve; eve= nextve) {
+ nextve= eve->next;
+ if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) {
+ BLI_remlink(&em->verts,eve);
+ free_editvert(eve);
+ }
+ }
+
+ countall(); // apparently always needed when adding stuff, derived mesh
+
+#ifdef WITH_VERSE
+ if(G.editMesh->vnode) {
+ sync_all_verseverts_with_editverts((VNode*)G.editMesh->vnode);
+ sync_all_versefaces_with_editfaces((VNode*)G.editMesh->vnode);
+ }
+#endif
+
+ BIF_TransformSetUndo("Rip");
+ initTransform(TFM_TRANSLATION, 0);
+ Transform();
+
+ G.scene->prop_mode = propmode;
+ G.scene->proportional = prop;
+}
+
+void shape_propagate(void)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *ev = NULL;
+ Mesh* me = (Mesh*)G.obedit->data;
+ Key* ky = NULL;
+ KeyBlock* kb = NULL;
+ Base* base=NULL;
+
+
+ if(me->key){
+ ky = me->key;
+ } else {
+ error("Object Has No Key");
+ return;
+ }
+
+ if(ky->block.first){
+ for(ev = em->verts.first; ev ; ev = ev->next){
+ if(ev->f & SELECT){
+ for(kb=ky->block.first;kb;kb = kb->next){
+ float *data;
+ data = kb->data;
+ copy_v3_v3(data+(ev->keyindex*3),ev->co);
+ }
+ }
+ }
+ } else {
+ error("Object Has No Blendshapes");
+ return;
+ }
+
+ //TAG Mesh Objects that share this data
+ for(base = G.scene->base.first; base; base = base->next){
+ if(base->object && base->object->data == me){
+ base->object->recalc = OB_RECALC_DATA;
+ }
+ }
+
+ BIF_undo_push("Propagate Blendshape Verts");
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ return;
+}
+
+void shape_copy_from_lerp(KeyBlock* thisBlock, KeyBlock* fromBlock)
+{
+ EditMesh *em = G.editMesh;
+ EditVert *ev = NULL;
+ short mval[2], curval[2], event = 0, finished = 0, canceled = 0, fullcopy=0 ;
+ float perc = 0;
+ char str[64];
+ float *data, *odata;
+
+ data = fromBlock->data;
+ odata = thisBlock->data;
+
+ getmouseco_areawin(mval);
+ curval[0] = mval[0] + 1; curval[1] = mval[1] + 1;
+
+ while (finished == 0)
+ {
+ getmouseco_areawin(mval);
+ if (mval[0] != curval[0] || mval[1] != curval[1])
+ {
+
+ if(mval[0] > curval[0])
+ perc += 0.1;
+ else if(mval[0] < curval[0])
+ perc -= 0.1;
+
+ if(perc < 0) perc = 0;
+ if(perc > 1) perc = 1;
+
+ curval[0] = mval[0];
+ curval[1] = mval[1];
+
+ if(fullcopy == 1){
+ perc = 1;
+ }
+
+ for(ev = em->verts.first; ev ; ev = ev->next){
+ if(ev->f & SELECT){
+ interp_v3_v3v3(ev->co,odata+(ev->keyindex*3),data+(ev->keyindex*3),perc);
+ }
+ }
+ sprintf(str,"Blending at %d%c MMB to Copy at 100%c",(int)(perc*100),'%','%');
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ headerprint(str);
+ force_draw(0);
+
+ if(fullcopy == 1){
+ break;
+ }
+
+ } else {
+ PIL_sleep_ms(10);
+ }
+
+ while(qtest()) {
+ short val=0;
+ event= extern_qread(&val);
+ if(val){
+ if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)){
+ finished = 1;
+ }
+ else if (event == MIDDLEMOUSE){
+ fullcopy = 1;
+ }
+ else if (ELEM3(event,ESCKEY,RIGHTMOUSE,RIGHTMOUSE)){
+ canceled = 1;
+ finished = 1;
+ }
+ }
+ }
+ }
+ if(!canceled)
+ BIF_undo_push("Copy Blendshape Verts");
+ else
+ for(ev = em->verts.first; ev ; ev = ev->next){
+ if(ev->f & SELECT){
+ copy_v3_v3(ev->co, odata+(ev->keyindex*3));
+ }
+ }
+ return;
+}
+
+
+
+void shape_copy_select_from()
+{
+ Mesh* me = (Mesh*)G.obedit->data;
+ EditMesh *em = G.editMesh;
+ EditVert *ev = NULL;
+ int totverts = 0,curshape = G.obedit->shapenr;
+
+ Key* ky = NULL;
+ KeyBlock *kb = NULL,*thisBlock = NULL;
+ int maxlen=32, nr=0, a=0;
+ char *menu;
+
+ if(me->key){
+ ky = me->key;
+ } else {
+ error("Object Has No Key");
+ return;
+ }
+
+ if(ky->block.first){
+ for(kb=ky->block.first;kb;kb = kb->next){
+ maxlen += 40; // Size of a block name
+ if(a == curshape-1){
+ thisBlock = kb;
+ }
+
+ a++;
+ }
+ a=0;
+ menu = MEM_callocN(maxlen, "Copy Shape Menu Text");
+ strcpy(menu, "Copy Vert Positions from Shape %t|");
+ for(kb=ky->block.first;kb;kb = kb->next){
+ if(a != curshape-1){
+ sprintf(menu,"%s %s %cx%d|",menu,kb->name,'%',a);
+ }
+ a++;
+ }
+ nr = pupmenu_col(menu, 20);
+ MEM_freeN(menu);
+ } else {
+ error("Object Has No Blendshapes");
+ return;
+ }
+
+ a = 0;
+
+ for(kb=ky->block.first;kb;kb = kb->next){
+ if(a == nr){
+
+ for(ev = em->verts.first;ev;ev = ev->next){
+ totverts++;
+ }
+
+ if(me->totvert != totverts){
+ error("Shape Has had Verts Added/Removed, please cycle editmode before copying");
+ return;
+ }
+ shape_copy_from_lerp(thisBlock,kb);
+
+ return;
+ }
+ a++;
+ }
+ return;
+}
+
+/* Collection Routines|Currently used by the improved merge code*/
+/* buildEdge_collection() creates a list of lists*/
+/* these lists are filled with edges that are topologically connected.*/
+/* This whole tool needs to be redone, its rather poorly implemented...*/
+
+typedef struct Collection{
+ struct Collection *next, *prev;
+ int index;
+ ListBase collectionbase;
+} Collection;
+
+typedef struct CollectedEdge{
+ struct CollectedEdge *next, *prev;
+ EditEdge *eed;
+} CollectedEdge;
+
+#define MERGELIMIT 0.000001
+
+static void build_edgecollection(ListBase *allcollections)
+{
+ EditEdge *eed;
+ Collection *edgecollection, *newcollection;
+ CollectedEdge *newedge;
+
+ int currtag = 1;
+ short ebalanced = 0;
+ short collectionfound = 0;
+
+ for (eed=G.editMesh->edges.first; eed; eed = eed->next){
+ eed->tmp.l = 0;
+ eed->v1->tmp.l = 0;
+ eed->v2->tmp.l = 0;
+ }
+
+ /*1st pass*/
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next){
+ if(eed->f&SELECT){
+ eed->v1->tmp.l = currtag;
+ eed->v2->tmp.l = currtag;
+ currtag +=1;
+ }
+ }
+
+ /*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */
+ while(ebalanced == 0){
+ ebalanced = 1;
+ for(eed=G.editMesh->edges.first; eed; eed = eed->next){
+ if(eed->f&SELECT){
+ if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{
+ if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l;
+ else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l;
+ ebalanced = 0;
+ }
+ }
+ }
+ }
+
+ /*3rd pass, set all the edge flags (unnessecary?)*/
+ for(eed=G.editMesh->edges.first; eed; eed = eed->next){
+ if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l;
+ }
+
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next){
+ if(eed->f&SELECT){
+ if(allcollections->first){
+ for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){
+ if(edgecollection->index == eed->tmp.l){
+ newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge");
+ newedge->eed = eed;
+ BLI_addtail(&(edgecollection->collectionbase), newedge);
+ collectionfound = 1;
+ break;
+ }
+ else collectionfound = 0;
+ }
+ }
+ if(allcollections->first == NULL || collectionfound == 0){
+ newcollection = MEM_mallocN(sizeof(Collection), "element collection");
+ newcollection->index = eed->tmp.l;
+ newcollection->collectionbase.first = 0;
+ newcollection->collectionbase.last = 0;
+
+ newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge");
+ newedge->eed = eed;
+
+ BLI_addtail(&(newcollection->collectionbase), newedge);
+ BLI_addtail(allcollections, newcollection);
+ }
+ }
+
+ }
+}
+
+static void freecollections(ListBase *allcollections)
+{
+ struct Collection *curcollection;
+
+ for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next)
+ BLI_freelistN(&(curcollection->collectionbase));
+ BLI_freelistN(allcollections);
+}
+
+/*Begin UV Edge Collapse Code
+ Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail
+ in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it.
+ The welded UV edges can then be sorted and collapsed.
+*/
+typedef struct wUV{
+ struct wUV *next, *prev;
+ ListBase nodes;
+ float u, v; /*cached copy of UV coordinates pointed to by nodes*/
+ EditVert *eve;
+ int f;
+} wUV;
+
+typedef struct wUVNode{
+ struct wUVNode *next, *prev;
+ float *u; /*pointer to original tface data*/
+ float *v; /*pointer to original tface data*/
+} wUVNode;
+
+typedef struct wUVEdge{
+ struct wUVEdge *next, *prev;
+ float v1uv[2], v2uv[2]; /*nasty.*/
+ struct wUV *v1, *v2; /*oriented same as editedge*/
+ EditEdge *eed;
+ int f;
+} wUVEdge;
+
+typedef struct wUVEdgeCollect{ /*used for grouping*/
+ struct wUVEdgeCollect *next, *prev;
+ wUVEdge *uved;
+ int id;
+} wUVEdgeCollect;
+
+static void append_weldedUV(EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts)
+{
+ wUV *curwvert, *newwvert;
+ wUVNode *newnode;
+ int found;
+ MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
+
+ found = 0;
+
+ for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
+ if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){
+ newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node");
+ newnode->u = &(tf->uv[tfindex][0]);
+ newnode->v = &(tf->uv[tfindex][1]);
+ BLI_addtail(&(curwvert->nodes), newnode);
+ found = 1;
+ break;
+ }
+ }
+
+ if(!found){
+ newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node");
+ newnode->u = &(tf->uv[tfindex][0]);
+ newnode->v = &(tf->uv[tfindex][1]);
+
+ newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert");
+ newwvert->u = *(newnode->u);
+ newwvert->v = *(newnode->v);
+ newwvert->eve = eve;
+
+ BLI_addtail(&(newwvert->nodes), newnode);
+ BLI_addtail(uvverts, newwvert);
+
+ }
+}
+
+static void build_weldedUVs(ListBase *uvverts)
+{
+ EditFace *efa;
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ if(efa->v1->f1) append_weldedUV(efa, efa->v1, 0, uvverts);
+ if(efa->v2->f1) append_weldedUV(efa, efa->v2, 1, uvverts);
+ if(efa->v3->f1) append_weldedUV(efa, efa->v3, 2, uvverts);
+ if(efa->v4 && efa->v4->f1) append_weldedUV(efa, efa->v4, 3, uvverts);
+ }
+}
+
+static void append_weldedUVEdge(EditFace *efa, EditEdge *eed, ListBase *uvedges)
+{
+ wUVEdge *curwedge, *newwedge;
+ int v1tfindex, v2tfindex, found;
+ MTFace *tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
+
+ found = 0;
+
+ if(eed->v1 == efa->v1) v1tfindex = 0;
+ else if(eed->v1 == efa->v2) v1tfindex = 1;
+ else if(eed->v1 == efa->v3) v1tfindex = 2;
+ else /* if(eed->v1 == efa->v4) */ v1tfindex = 3;
+
+ if(eed->v2 == efa->v1) v2tfindex = 0;
+ else if(eed->v2 == efa->v2) v2tfindex = 1;
+ else if(eed->v2 == efa->v3) v2tfindex = 2;
+ else /* if(eed->v2 == efa->v4) */ v2tfindex = 3;
+
+ for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){
+ if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){
+ found = 1;
+ break; //do nothing, we don't need another welded uv edge
+ }
+ }
+
+ if(!found){
+ newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge");
+ newwedge->v1uv[0] = tf->uv[v1tfindex][0];
+ newwedge->v1uv[1] = tf->uv[v1tfindex][1];
+ newwedge->v2uv[0] = tf->uv[v2tfindex][0];
+ newwedge->v2uv[1] = tf->uv[v2tfindex][1];
+ newwedge->eed = eed;
+
+ BLI_addtail(uvedges, newwedge);
+ }
+}
+
+static void build_weldedUVEdges(ListBase *uvedges, ListBase *uvverts)
+{
+ wUV *curwvert;
+ wUVEdge *curwedge;
+ EditFace *efa;
+
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ if(efa->e1->f1) append_weldedUVEdge(efa, efa->e1, uvedges);
+ if(efa->e2->f1) append_weldedUVEdge(efa, efa->e2, uvedges);
+ if(efa->e3->f1) append_weldedUVEdge(efa, efa->e3, uvedges);
+ if(efa->e4 && efa->e4->f1) append_weldedUVEdge(efa, efa->e4, uvedges);
+ }
+
+
+ //link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers
+ for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){
+ for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
+ if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){
+ curwedge->v1 = curwvert;
+ break;
+ }
+ }
+ for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){
+ if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){
+ curwedge->v2 = curwvert;
+ break;
+ }
+ }
+ }
+}
+
+static void free_weldedUVs(ListBase *uvverts)
+{
+ wUV *curwvert;
+ for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes));
+ BLI_freelistN(uvverts);
+}
+
+static void collapse_edgeuvs(void)
+{
+ ListBase uvedges, uvverts, allcollections;
+ wUVEdge *curwedge;
+ wUVNode *curwnode;
+ wUVEdgeCollect *collectedwuve, *newcollectedwuve;
+ Collection *wuvecollection, *newcollection;
+ int curtag, balanced, collectionfound= 0, vcount;
+ float avg[2];
+
+ if (!EM_texFaceCheck())
+ return;
+
+ uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL;
+
+ build_weldedUVs(&uvverts);
+ build_weldedUVEdges(&uvedges, &uvverts);
+
+ curtag = 0;
+
+ for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
+ curwedge->v1->f = curtag;
+ curwedge->v2->f = curtag;
+ curtag +=1;
+ }
+
+ balanced = 0;
+ while(!balanced){
+ balanced = 1;
+ for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
+ if(curwedge->v1->f != curwedge->v2->f){
+ if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f;
+ else curwedge->v2->f = curwedge->v1->f;
+ balanced = 0;
+ }
+ }
+ }
+
+ for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f;
+
+
+ for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){
+ if(allcollections.first){
+ for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){
+ if(wuvecollection->index == curwedge->f){
+ newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge");
+ newcollectedwuve->uved = curwedge;
+ BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve);
+ collectionfound = 1;
+ break;
+ }
+
+ else collectionfound = 0;
+ }
+ }
+ if(allcollections.first == NULL || collectionfound == 0){
+ newcollection = MEM_callocN(sizeof(Collection), "element collection");
+ newcollection->index = curwedge->f;
+ newcollection->collectionbase.first = 0;
+ newcollection->collectionbase.last = 0;
+
+ newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge");
+ newcollectedwuve->uved = curwedge;
+
+ BLI_addtail(&(newcollection->collectionbase), newcollectedwuve);
+ BLI_addtail(&allcollections, newcollection);
+ }
+ }
+
+ for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){
+
+ vcount = avg[0] = avg[1] = 0;
+
+ for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){
+ avg[0] += collectedwuve->uved->v1uv[0];
+ avg[1] += collectedwuve->uved->v1uv[1];
+
+ avg[0] += collectedwuve->uved->v2uv[0];
+ avg[1] += collectedwuve->uved->v2uv[1];
+
+ vcount +=2;
+
+ }
+
+ avg[0] /= vcount; avg[1] /= vcount;
+
+ for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){
+ for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){
+ *(curwnode->u) = avg[0];
+ *(curwnode->v) = avg[1];
+ }
+ for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){
+ *(curwnode->u) = avg[0];
+ *(curwnode->v) = avg[1];
+ }
+ }
+ }
+
+ free_weldedUVs(&uvverts);
+ BLI_freelistN(&uvedges);
+ freecollections(&allcollections);
+}
+
+/*End UV Edge collapse code*/
+
+static void collapseuvs(EditVert *mergevert)
+{
+ EditFace *efa;
+ MTFace *tf;
+ int uvcount;
+ float uvav[2];
+
+ if (!EM_texFaceCheck())
+ return;
+
+ uvcount = 0;
+ uvav[0] = 0;
+ uvav[1] = 0;
+
+ for(efa = G.editMesh->faces.first; efa; efa=efa->next){
+ tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
+
+ if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) {
+ uvav[0] += tf->uv[0][0];
+ uvav[1] += tf->uv[0][1];
+ uvcount += 1;
+ }
+ if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){
+ uvav[0] += tf->uv[1][0];
+ uvav[1] += tf->uv[1][1];
+ uvcount += 1;
+ }
+ if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){
+ uvav[0] += tf->uv[2][0];
+ uvav[1] += tf->uv[2][1];
+ uvcount += 1;
+ }
+ if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){
+ uvav[0] += tf->uv[3][0];
+ uvav[1] += tf->uv[3][1];
+ uvcount += 1;
+ }
+ }
+
+ if(uvcount > 0) {
+ uvav[0] /= uvcount;
+ uvav[1] /= uvcount;
+
+ for(efa = G.editMesh->faces.first; efa; efa=efa->next){
+ tf = CustomData_em_get(&G.editMesh->fdata, efa->data, CD_MTFACE);
+
+ if(efa->v1->f1){
+ tf->uv[0][0] = uvav[0];
+ tf->uv[0][1] = uvav[1];
+ }
+ if(efa->v2->f1){
+ tf->uv[1][0] = uvav[0];
+ tf->uv[1][1] = uvav[1];
+ }
+ if(efa->v3->f1){
+ tf->uv[2][0] = uvav[0];
+ tf->uv[2][1] = uvav[1];
+ }
+ if(efa->v4 && efa->v4->f1){
+ tf->uv[3][0] = uvav[0];
+ tf->uv[3][1] = uvav[1];
+ }
+ }
+ }
+}
+
+int collapseEdges(void)
+{
+ EditVert *eve;
+ EditEdge *eed;
+
+ ListBase allcollections;
+ CollectedEdge *curredge;
+ Collection *edgecollection;
+
+ int totedges, groupcount, mergecount,vcount;
+ float avgcount[3];
+
+ allcollections.first = 0;
+ allcollections.last = 0;
+
+ mergecount = 0;
+
+ if(multires_test()) return 0;
+
+ build_edgecollection(&allcollections);
+ groupcount = BLI_countlist(&allcollections);
+
+
+ for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){
+ totedges = BLI_countlist(&(edgecollection->collectionbase));
+ mergecount += totedges;
+ avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0;
+
+ vcount = 0;
+
+ for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
+ avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0];
+ avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1];
+ avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2];
+
+ avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0];
+ avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1];
+ avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2];
+
+ vcount +=2;
+ }
+
+ avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount;
+
+ for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
+ copy_v3_v3(((EditEdge*)curredge->eed)->v1->co,avgcount);
+ copy_v3_v3(((EditEdge*)curredge->eed)->v2->co,avgcount);
+ }
+
+ if (EM_texFaceCheck()) {
+ /*uv collapse*/
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
+ for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){
+ curredge->eed->v1->f1 = 1;
+ curredge->eed->v2->f1 = 1;
+ curredge->eed->f1 = 1;
+ }
+ collapse_edgeuvs();
+ }
+
+ }
+ freecollections(&allcollections);
+ removedoublesflag(1, 0, MERGELIMIT);
+ /*get rid of this!*/
+ countall();
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ if (EM_texFaceCheck())
+ allqueue(REDRAWIMAGE, 0);
+ return mergecount;
+}
+
+int merge_firstlast(int first, int uvmerge)
+{
+ EditVert *eve,*mergevert;
+ EditSelection *ese;
+
+ if(multires_test()) return 0;
+
+ /* do sanity check in mergemenu in edit.c ?*/
+ if(first == 0){
+ ese = G.editMesh->selected.last;
+ mergevert= (EditVert*)ese->data;
+ }
+ else{
+ ese = G.editMesh->selected.first;
+ mergevert = (EditVert*)ese->data;
+ }
+
+ if(mergevert->f&SELECT){
+ for (eve=G.editMesh->verts.first; eve; eve=eve->next){
+ if (eve->f&SELECT)
+ copy_v3_v3(eve->co,mergevert->co);
+ }
+ }
+
+ if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){
+
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next){
+ if(eve->f&SELECT) eve->f1 = 1;
+ }
+ collapseuvs(mergevert);
+ }
+
+ countall();
+ return removedoublesflag(1, 0, MERGELIMIT);
+}
+
+int merge_target(int target, int uvmerge)
+{
+ EditVert *eve;
+
+ if(multires_test()) return 0;
+
+ if(target) snap_sel_to_curs();
+ else snap_to_center();
+
+ if(uvmerge && CustomData_has_layer(&G.editMesh->fdata, CD_MTFACE)){
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next) eve->f1 = 0;
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next){
+ if(eve->f&SELECT) eve->f1 = 1;
+ }
+ collapseuvs(NULL);
+ }
+
+ countall();
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ return removedoublesflag(1, 0, MERGELIMIT);
+
+}
+#undef MERGELIMIT
+
+typedef struct PathNode{
+ int u;
+ int visited;
+ ListBase edges;
+} PathNode;
+
+typedef struct PathEdge{
+ struct PathEdge *next, *prev;
+ int v;
+ float w;
+} PathEdge;
+
+void pathselect(void)
+{
+ EditVert *eve, *s, *t;
+ EditEdge *eed;
+ EditSelection *ese;
+ PathEdge *newpe, *currpe;
+ PathNode *currpn;
+ PathNode *Q;
+ int v, *previous, pathvert, pnindex; /*pnindex redundant?*/
+ int unbalanced, totnodes;
+ short physical;
+ float *cost;
+ Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/
+
+ s = t = NULL;
+
+ countall(); /*paranoid?*/
+
+ ese = ((EditSelection*)G.editMesh->selected.last);
+ if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT){
+ physical= pupmenu("Distance Method? %t|Edge Length%x1|Topological%x0");
+
+ t = (EditVert*)ese->data;
+ s = (EditVert*)ese->prev->data;
+
+ /*need to find out if t is actually reachable by s....*/
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next){
+ eve->f1 = 0;
+ }
+
+ s->f1 = 1;
+
+ unbalanced = 1;
+ totnodes = 1;
+ while(unbalanced){
+ unbalanced = 0;
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next){
+ if(!eed->h){
+ if(eed->v1->f1 && !eed->v2->f1){
+ eed->v2->f1 = 1;
+ totnodes++;
+ unbalanced = 1;
+ }
+ else if(eed->v2->f1 && !eed->v1->f1){
+ eed->v1->f1 = 1;
+ totnodes++;
+ unbalanced = 1;
+ }
+ }
+ }
+ }
+
+
+
+ if(s->f1 && t->f1){ /*t can be reached by s*/
+ Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes");
+ totnodes = 0;
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next){
+ if(eve->f1){
+ Q[totnodes].u = totnodes;
+ Q[totnodes].edges.first = 0;
+ Q[totnodes].edges.last = 0;
+ Q[totnodes].visited = 0;
+ eve->tmp.p = &(Q[totnodes]);
+ totnodes++;
+ }
+ else eve->tmp.p = NULL;
+ }
+
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next){
+ if(!eed->h){
+ if(eed->v1->f1){
+ currpn = ((PathNode*)eed->v1->tmp.p);
+
+ newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge");
+ newpe->v = ((PathNode*)eed->v2->tmp.p)->u;
+ if(physical){
+ newpe->w = len_v3v3(eed->v1->co, eed->v2->co);
+ }
+ else newpe->w = 1;
+ newpe->next = 0;
+ newpe->prev = 0;
+ BLI_addtail(&(currpn->edges), newpe);
+ }
+ if(eed->v2->f1){
+ currpn = ((PathNode*)eed->v2->tmp.p);
+ newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge");
+ newpe->v = ((PathNode*)eed->v1->tmp.p)->u;
+ if(physical){
+ newpe->w = len_v3v3(eed->v1->co, eed->v2->co);
+ }
+ else newpe->w = 1;
+ newpe->next = 0;
+ newpe->prev = 0;
+ BLI_addtail(&(currpn->edges), newpe);
+ }
+ }
+ }
+
+ heap = BLI_heap_new();
+ cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs");
+ previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices");
+
+ for(v=0; v < totnodes; v++){
+ cost[v] = 1000000;
+ previous[v] = -1; /*array of indices*/
+ }
+
+ pnindex = ((PathNode*)s->tmp.p)->u;
+ cost[pnindex] = 0;
+ BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex));
+
+ while( !BLI_heap_empty(heap) ){
+
+ pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap));
+ currpn = &(Q[pnindex]);
+
+ if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/
+ break;
+
+ for(currpe=currpn->edges.first; currpe; currpe=currpe->next){
+ if(!Q[currpe->v].visited){
+ if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){
+ cost[currpe->v] = cost[currpn->u] + currpe->w;
+ previous[currpe->v] = currpn->u;
+ Q[currpe->v].visited = 1;
+ BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v));
+ }
+ }
+ }
+ }
+
+ pathvert = ((PathNode*)t->tmp.p)->u;
+ while(pathvert != -1){
+ for(eve=G.editMesh->verts.first; eve; eve=eve->next){
+ if(eve->f1){
+ if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT;
+ }
+ }
+ pathvert = previous[pathvert];
+ }
+
+ for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges));
+ MEM_freeN(Q);
+ MEM_freeN(cost);
+ MEM_freeN(previous);
+ BLI_heap_free(heap, NULL);
+ EM_select_flush();
+ countall();
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ if (EM_texFaceCheck())
+ allqueue(REDRAWIMAGE, 0);
+ }
+ }
+ else{
+ error("Path Selection requires that exactly two vertices be selected");
+ return;
+ }
+}
+
+void region_to_loop(void)
+{
+ EditEdge *eed;
+ EditFace *efa;
+
+ if(G.totfacesel){
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
+
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ if(efa->f&SELECT){
+ efa->e1->f1++;
+ efa->e2->f1++;
+ efa->e3->f1++;
+ if(efa->e4)
+ efa->e4->f1++;
+ }
+ }
+
+ EM_clear_flag_all(SELECT);
+
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next){
+ if(eed->f1 == 1) EM_select_edge(eed, 1);
+ }
+
+ G.scene->selectmode = SCE_SELECT_EDGE;
+ EM_selectmode_set();
+ countall();
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ if (EM_texFaceCheck())
+ allqueue(REDRAWIMAGE, 0);
+ BIF_undo_push("Face Region to Edge Loop");
+
+ }
+}
+
+static int validate_loop(Collection *edgecollection)
+{
+ EditEdge *eed;
+ EditFace *efa;
+ CollectedEdge *curredge;
+
+ /*1st test*/
+ for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
+ curredge->eed->v1->f1 = 0;
+ curredge->eed->v2->f1 = 0;
+ }
+ for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
+ curredge->eed->v1->f1++;
+ curredge->eed->v2->f1++;
+ }
+ for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
+ if(curredge->eed->v1->f1 > 2) return(0); else
+ if(curredge->eed->v2->f1 > 2) return(0);
+ }
+
+ /*2nd test*/
+ for(eed = G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = 0;
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ efa->e1->f1++;
+ efa->e2->f1++;
+ efa->e3->f1++;
+ if(efa->e4) efa->e4->f1++;
+ }
+ for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){
+ if(curredge->eed->f1 > 2) return(0);
+ }
+ return(1);
+}
+
+static int loop_bisect(Collection *edgecollection)
+{
+
+ EditFace *efa, *sf1, *sf2;
+ EditEdge *eed, *sed;
+ CollectedEdge *curredge;
+ int totsf1, totsf2, unbalanced,balancededges;
+
+ for(eed=G.editMesh->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0;
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next) efa->f1 = 0;
+
+ for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1;
+
+ sf1 = sf2 = NULL;
+ sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed;
+
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ if(sf2) break;
+ else if(sf1){
+ if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa;
+ }
+ else{
+ if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa;
+ }
+ }
+
+ if(sf1==NULL || sf2==NULL)
+ return(-1);
+
+ if(!(sf1->e1->f1)) sf1->e1->f2 = 1;
+ if(!(sf1->e2->f1)) sf1->e2->f2 = 1;
+ if(!(sf1->e3->f1)) sf1->e3->f2 = 1;
+ if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1;
+ sf1->f1 = 1;
+ totsf1 = 1;
+
+ if(!(sf2->e1->f1)) sf2->e1->f2 = 2;
+ if(!(sf2->e2->f1)) sf2->e2->f2 = 2;
+ if(!(sf2->e3->f1)) sf2->e3->f2 = 2;
+ if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2;
+ sf2->f1 = 2;
+ totsf2 = 1;
+
+ /*do sf1*/
+ unbalanced = 1;
+ while(unbalanced){
+ unbalanced = 0;
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ balancededges = 0;
+ if(efa->f1 == 0){
+ if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){
+ balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1;
+ balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1;
+ balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1;
+ if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1;
+ if(balancededges){
+ unbalanced = 1;
+ efa->f1 = 1;
+ totsf1++;
+ }
+ }
+ }
+ }
+ }
+
+ /*do sf2*/
+ unbalanced = 1;
+ while(unbalanced){
+ unbalanced = 0;
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ balancededges = 0;
+ if(efa->f1 == 0){
+ if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){
+ balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2;
+ balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2;
+ balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2;
+ if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2;
+ if(balancededges){
+ unbalanced = 1;
+ efa->f1 = 2;
+ totsf2++;
+ }
+ }
+ }
+ }
+ }
+
+ if(totsf1 < totsf2) return(1);
+ else return(2);
+}
+
+void loop_to_region(void)
+{
+ EditFace *efa;
+ ListBase allcollections={NULL,NULL};
+ Collection *edgecollection;
+ int testflag;
+
+ build_edgecollection(&allcollections);
+
+ for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){
+ if(validate_loop(edgecollection)){
+ testflag = loop_bisect(edgecollection);
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){
+ if(efa->f1 == testflag){
+ if(efa->f&SELECT) EM_select_face(efa, 0);
+ else EM_select_face(efa,1);
+ }
+ }
+ }
+ }
+
+ for(efa=G.editMesh->faces.first; efa; efa=efa->next){ /*fix this*/
+ if(efa->f&SELECT) EM_select_face(efa,1);
+ }
+
+ countall();
+ freecollections(&allcollections);
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ if (EM_texFaceCheck())
+ allqueue(REDRAWIMAGE, 0);
+ BIF_undo_push("Edge Loop to Face Region");
+}
+
+
+/* texface and vertex color editmode tools for the face menu */
+
+void mesh_rotate_uvs(void)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ short change = 0, ccw;
+ MTFace *tf;
+ float u1, v1;
+
+ if (!EM_texFaceCheck()) {
+ error("mesh has no uv/image layers");
+ return;
+ }
+
+ ccw = (G.qual == LR_SHIFTKEY);
+
+ for(efa=em->faces.first; efa; efa=efa->next) {
+ if (efa->f & SELECT) {
+ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ u1= tf->uv[0][0];
+ v1= tf->uv[0][1];
+
+ if (ccw) {
+ if(efa->v4) {
+ tf->uv[0][0]= tf->uv[3][0];
+ tf->uv[0][1]= tf->uv[3][1];
+
+ tf->uv[3][0]= tf->uv[2][0];
+ tf->uv[3][1]= tf->uv[2][1];
+ } else {
+ tf->uv[0][0]= tf->uv[2][0];
+ tf->uv[0][1]= tf->uv[2][1];
+ }
+
+ tf->uv[2][0]= tf->uv[1][0];
+ tf->uv[2][1]= tf->uv[1][1];
+
+ tf->uv[1][0]= u1;
+ tf->uv[1][1]= v1;
+ } else {
+ tf->uv[0][0]= tf->uv[1][0];
+ tf->uv[0][1]= tf->uv[1][1];
+
+ tf->uv[1][0]= tf->uv[2][0];
+ tf->uv[1][1]= tf->uv[2][1];
+
+ if(efa->v4) {
+ tf->uv[2][0]= tf->uv[3][0];
+ tf->uv[2][1]= tf->uv[3][1];
+
+ tf->uv[3][0]= u1;
+ tf->uv[3][1]= v1;
+ }
+ else {
+ tf->uv[2][0]= u1;
+ tf->uv[2][1]= v1;
+ }
+ }
+ change = 1;
+ }
+ }
+
+ if (change) {
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ BIF_undo_push("Rotate UV face");
+ }
+}
+
+void mesh_mirror_uvs(void)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ short change = 0, altaxis;
+ MTFace *tf;
+ float u1, v1;
+
+ if (!EM_texFaceCheck()) {
+ error("mesh has no uv/image layers");
+ return;
+ }
+
+ altaxis = (G.qual == LR_SHIFTKEY);
+
+ for(efa=em->faces.first; efa; efa=efa->next) {
+ if (efa->f & SELECT) {
+ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ if (altaxis) {
+ u1= tf->uv[1][0];
+ v1= tf->uv[1][1];
+ if(efa->v4) {
+
+ tf->uv[1][0]= tf->uv[2][0];
+ tf->uv[1][1]= tf->uv[2][1];
+
+ tf->uv[2][0]= u1;
+ tf->uv[2][1]= v1;
+
+ u1= tf->uv[3][0];
+ v1= tf->uv[3][1];
+
+ tf->uv[3][0]= tf->uv[0][0];
+ tf->uv[3][1]= tf->uv[0][1];
+
+ tf->uv[0][0]= u1;
+ tf->uv[0][1]= v1;
+ }
+ else {
+ tf->uv[1][0]= tf->uv[2][0];
+ tf->uv[1][1]= tf->uv[2][1];
+ tf->uv[2][0]= u1;
+ tf->uv[2][1]= v1;
+ }
+
+ } else {
+ u1= tf->uv[0][0];
+ v1= tf->uv[0][1];
+ if(efa->v4) {
+
+ tf->uv[0][0]= tf->uv[1][0];
+ tf->uv[0][1]= tf->uv[1][1];
+
+ tf->uv[1][0]= u1;
+ tf->uv[1][1]= v1;
+
+ u1= tf->uv[3][0];
+ v1= tf->uv[3][1];
+
+ tf->uv[3][0]= tf->uv[2][0];
+ tf->uv[3][1]= tf->uv[2][1];
+
+ tf->uv[2][0]= u1;
+ tf->uv[2][1]= v1;
+ }
+ else {
+ tf->uv[0][0]= tf->uv[1][0];
+ tf->uv[0][1]= tf->uv[1][1];
+ tf->uv[1][0]= u1;
+ tf->uv[1][1]= v1;
+ }
+ }
+ change = 1;
+ }
+ }
+
+ if (change) {
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ BIF_undo_push("Mirror UV face");
+ }
+}
+
+void mesh_rotate_colors(void)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ short change = 0, ccw;
+ MCol tmpcol, *mcol;
+ if (!EM_vertColorCheck()) {
+ error("mesh has no color layers");
+ return;
+ }
+
+ ccw = (G.qual == LR_SHIFTKEY);
+
+ for(efa=em->faces.first; efa; efa=efa->next) {
+ if (efa->f & SELECT) {
+ mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
+ tmpcol= mcol[0];
+
+ if (ccw) {
+ if(efa->v4) {
+ mcol[0]= mcol[3];
+ mcol[3]= mcol[2];
+ } else {
+ mcol[0]= mcol[2];
+ }
+ mcol[2]= mcol[1];
+ mcol[1]= tmpcol;
+ } else {
+ mcol[0]= mcol[1];
+ mcol[1]= mcol[2];
+
+ if(efa->v4) {
+ mcol[2]= mcol[3];
+ mcol[3]= tmpcol;
+ }
+ else
+ mcol[2]= tmpcol;
+ }
+ change = 1;
+ }
+ }
+
+ if (change) {
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ BIF_undo_push("Rotate Color face");
+ }
+}
+
+void mesh_mirror_colors(void)
+{
+ EditMesh *em = G.editMesh;
+ EditFace *efa;
+ short change = 0, altaxis;
+ MCol tmpcol, *mcol;
+ if (!EM_vertColorCheck()) {
+ error("mesh has no color layers");
+ return;
+ }
+
+ altaxis = (G.qual == LR_SHIFTKEY);
+
+ for(efa=em->faces.first; efa; efa=efa->next) {
+ if (efa->f & SELECT) {
+ mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
+ if (altaxis) {
+ tmpcol= mcol[1];
+ mcol[1]= mcol[2];
+ mcol[2]= tmpcol;
+
+ if(efa->v4) {
+ tmpcol= mcol[0];
+ mcol[0]= mcol[3];
+ mcol[3]= tmpcol;
+ }
+ } else {
+ tmpcol= mcol[0];
+ mcol[0]= mcol[1];
+ mcol[1]= tmpcol;
+
+ if(efa->v4) {
+ tmpcol= mcol[2];
+ mcol[2]= mcol[3];
+ mcol[3]= tmpcol;
+ }
+ }
+ change = 1;
+ }
+ }
+
+ if (change) {
+ DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
+ allqueue(REDRAWVIEW3D, 0);
+ BIF_undo_push("Mirror Color face");
+ }
+}
+
+#endif
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
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
new file mode 100644
index 00000000000..98e9a510126
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -0,0 +1,881 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+#include "BLI_smallhash.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+
+#define BEVEL_FLAG 1
+#define BEVEL_DEL 2
+#define FACE_NEW 4
+#define EDGE_OLD 8
+#define FACE_OLD 16
+#define VERT_OLD 32
+#define FACE_SPAN 64
+#define FACE_HOLE 128
+
+typedef struct LoopTag {
+ BMVert *newv;
+} LoopTag;
+
+typedef struct EdgeTag {
+ BMVert *newv1, *newv2;
+} EdgeTag;
+
+static void calc_corner_co(BMesh *bm, BMLoop *l, const float fac, float r_co[3],
+ const short do_dist, const short do_even)
+{
+ float no[3], l_vec_prev[3], l_vec_next[3], l_co_prev[3], l_co[3], l_co_next[3], co_ofs[3];
+ int is_concave;
+
+ /* first get the prev/next verts */
+ if (l->f->len > 2) {
+ copy_v3_v3(l_co_prev, l->prev->v->co);
+ copy_v3_v3(l_co, l->v->co);
+ copy_v3_v3(l_co_next, l->next->v->co);
+
+ /* calculate norma */
+ sub_v3_v3v3(l_vec_prev, l_co_prev, l_co);
+ sub_v3_v3v3(l_vec_next, l_co_next, l_co);
+
+ cross_v3_v3v3(no, l_vec_prev, l_vec_next);
+ is_concave = dot_v3v3(no, l->f->no) > 0.0f;
+ }
+ else {
+ BMIter iter;
+ BMLoop *l2;
+ float up[3] = {0.0f, 0.0f, 1.0f};
+
+ copy_v3_v3(l_co_prev, l->prev->v->co);
+ copy_v3_v3(l_co, l->v->co);
+
+ BM_ITER(l2, &iter, bm, BM_LOOPS_OF_VERT, l->v) {
+ if (l2->f != l->f) {
+ copy_v3_v3(l_co_next, BM_edge_other_vert(l2->e, l2->next->v)->co);
+ break;
+ }
+ }
+
+ sub_v3_v3v3(l_vec_prev, l_co_prev, l_co);
+ sub_v3_v3v3(l_vec_next, l_co_next, l_co);
+
+ cross_v3_v3v3(no, l_vec_prev, l_vec_next);
+ if (dot_v3v3(no, no) == 0.0f) {
+ no[0] = no[1] = 0.0f; no[2] = -1.0f;
+ }
+
+ is_concave = dot_v3v3(no, up) < 0.0f;
+ }
+
+
+ /* now calculate the new location */
+ if (do_dist) { /* treat 'fac' as distance */
+
+ normalize_v3(l_vec_prev);
+ normalize_v3(l_vec_next);
+
+ add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next);
+ if (UNLIKELY(normalize_v3(co_ofs) == 0.0f)) { /* edges form a straignt line */
+ cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no);
+ }
+
+ if (do_even) {
+ negate_v3(l_vec_next);
+ mul_v3_fl(co_ofs, fac * shell_angle_to_dist(0.5f * angle_normalized_v3v3(l_vec_prev, l_vec_next)));
+ /* negate_v3(l_vec_next); */ /* no need unless we use again */
+ }
+ else {
+ mul_v3_fl(co_ofs, fac);
+ }
+ }
+ else { /* treat as 'fac' as a factor (0 - 1) */
+
+ /* not strictly necessary, balance vectors
+ * so the longer edge doesn't skew the result,
+ * gives nicer, move even output.
+ *
+ * Use the minimum rather then the middle value so skinny faces don't flip along the short axis */
+ float min_fac = minf(normalize_v3(l_vec_prev), normalize_v3(l_vec_next));
+ float angle;
+
+ if (do_even) {
+ negate_v3(l_vec_next);
+ angle = angle_normalized_v3v3(l_vec_prev, l_vec_next);
+ negate_v3(l_vec_next); /* no need unless we use again */
+ }
+ else {
+ angle = 0.0f;
+ }
+
+ mul_v3_fl(l_vec_prev, min_fac);
+ mul_v3_fl(l_vec_next, min_fac);
+
+ add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next);
+
+ if (UNLIKELY(is_zero_v3(co_ofs))) {
+ cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no);
+ normalize_v3(co_ofs);
+ mul_v3_fl(co_ofs, min_fac);
+ }
+
+ /* done */
+ if (do_even) {
+ mul_v3_fl(co_ofs, (fac * 0.5) * shell_angle_to_dist(0.5f * angle));
+ }
+ else {
+ mul_v3_fl(co_ofs, fac * 0.5);
+ }
+ }
+
+ /* apply delta vec */
+ if (is_concave)
+ negate_v3(co_ofs);
+
+ add_v3_v3v3(r_co, co_ofs, l->v->co);
+}
+
+
+#define ETAG_SET(e, v, nv) ( \
+ (v) == (e)->v1 ? \
+ (etags[BM_elem_index_get((e))].newv1 = (nv)) : \
+ (etags[BM_elem_index_get((e))].newv2 = (nv)) \
+ )
+
+#define ETAG_GET(e, v) ( \
+ (v) == (e)->v1 ? \
+ (etags[BM_elem_index_get((e))].newv1) : \
+ (etags[BM_elem_index_get((e))].newv2) \
+ )
+
+void bmesh_bevel_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMEdge *e;
+ BMVert *v;
+ BMFace **faces = NULL, *f;
+ LoopTag *tags = NULL, *tag;
+ EdgeTag *etags = NULL;
+ BMVert **verts = NULL;
+ BMEdge **edges = NULL;
+ BLI_array_declare(faces);
+ BLI_array_declare(tags);
+ BLI_array_declare(etags);
+ BLI_array_declare(verts);
+ BLI_array_declare(edges);
+ SmallHash hash;
+ float fac = BMO_slot_float_get(op, "percent");
+ const short do_even = BMO_slot_int_get(op, "use_even");
+ const short do_dist = BMO_slot_int_get(op, "use_dist");
+ int i, li, has_elens, HasMDisps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
+
+ has_elens = CustomData_has_layer(&bm->edata, CD_PROP_FLT) && BMO_slot_int_get(op, "use_lengths");
+ if (has_elens) {
+ li = BMO_slot_int_get(op, "lengthlayer");
+ }
+
+ BLI_smallhash_init(&hash);
+
+ BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) {
+ BMO_elem_flag_enable(bm, e, BEVEL_FLAG|BEVEL_DEL);
+ BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL);
+ BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL);
+
+ if (BM_edge_face_count(e) < 2) {
+ BMO_elem_flag_disable(bm, e, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL);
+ }
+#if 0
+ if (BM_edge_face_count(e) == 0) {
+ BMVert *verts[2] = {e->v1, e->v2};
+ BMEdge *edges[2] = {e, BM_edge_create(bm, e->v1, e->v2, e, 0)};
+
+ BMO_elem_flag_enable(bm, edges[1], BEVEL_FLAG);
+ BM_face_create(bm, verts, edges, 2, FALSE);
+ }
+#endif
+ }
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, v, VERT_OLD);
+ }
+
+#if 0
+ //a bit of cleaner code that, alas, doens't work.
+ /* build edge tag */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e->v1, BEVEL_FLAG) || BMO_elem_flag_test(bm, e->v2, BEVEL_FLAG)) {
+ BMIter liter;
+ BMLoop *l;
+
+ if (!BMO_elem_flag_test(bm, e, EDGE_OLD)) {
+ BM_elem_index_set(e, BLI_array_count(etags)); /* set_dirty! */
+ BLI_array_growone(etags);
+
+ BMO_elem_flag_enable(bm, e, EDGE_OLD);
+ }
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
+ BMLoop *l2;
+ BMIter liter2;
+
+ if (BMO_elem_flag_test(bm, l->f, BEVEL_FLAG))
+ continue;
+
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) {
+ BM_elem_index_set(l2, BLI_array_count(tags)); /* set_loop */
+ BLI_array_growone(tags);
+
+ if (!BMO_elem_flag_test(bm, l2->e, EDGE_OLD)) {
+ BM_elem_index_set(l2->e, BLI_array_count(etags)); /* set_dirty! */
+ BLI_array_growone(etags);
+
+ BMO_elem_flag_enable(bm, l2->e, EDGE_OLD);
+ }
+ }
+
+ BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG);
+ BLI_array_append(faces, l->f);
+ }
+ }
+ else {
+ BM_elem_index_set(e, -1); /* set_dirty! */
+ }
+ }
+#endif
+
+ /* create and assign looptag structure */
+ BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) {
+ BMLoop *l;
+ BMIter liter;
+
+ BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL);
+ BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL);
+
+ if (BM_edge_face_count(e) < 2) {
+ BMO_elem_flag_disable(bm, e, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL);
+ }
+
+ if (!BLI_smallhash_haskey(&hash, (intptr_t)e)) {
+ BLI_array_growone(etags);
+ BM_elem_index_set(e, BLI_array_count(etags) - 1); /* set_dirty! */
+ BLI_smallhash_insert(&hash, (intptr_t)e, NULL);
+ BMO_elem_flag_enable(bm, e, EDGE_OLD);
+ }
+
+ /* find all faces surrounding e->v1 and, e->v2 */
+ for (i = 0; i < 2; i++) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, i ? e->v2:e->v1) {
+ BMLoop *l2;
+ BMIter liter2;
+
+ /* see if we've already processed this loop's fac */
+ if (BLI_smallhash_haskey(&hash, (intptr_t)l->f))
+ continue;
+
+ /* create tags for all loops in l-> */
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) {
+ BLI_array_growone(tags);
+ BM_elem_index_set(l2, BLI_array_count(tags) - 1); /* set_loop */
+
+ if (!BLI_smallhash_haskey(&hash, (intptr_t)l2->e)) {
+ BLI_array_growone(etags);
+ BM_elem_index_set(l2->e, BLI_array_count(etags) - 1); /* set_dirty! */
+ BLI_smallhash_insert(&hash, (intptr_t)l2->e, NULL);
+ BMO_elem_flag_enable(bm, l2->e, EDGE_OLD);
+ }
+ }
+
+ BLI_smallhash_insert(&hash, (intptr_t)l->f, NULL);
+ BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG);
+ BLI_array_append(faces, l->f);
+ }
+ }
+ }
+
+ bm->elem_index_dirty |= BM_EDGE;
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMIter eiter;
+
+ if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG))
+ continue;
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && !ETAG_GET(e, v)) {
+ BMVert *v2;
+ float co[3];
+
+ v2 = BM_edge_other_vert(e, v);
+ sub_v3_v3v3(co, v2->co, v->co);
+ if (has_elens) {
+ float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, e->head.data, CD_PROP_FLT, li);
+
+ normalize_v3(co);
+ mul_v3_fl(co, elen);
+ }
+
+ mul_v3_fl(co, fac);
+ add_v3_v3(co, v->co);
+
+ v2 = BM_vert_create(bm, co, v);
+ ETAG_SET(e, v, v2);
+ }
+ }
+ }
+
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+
+ BMO_elem_flag_enable(bm, faces[i], FACE_OLD);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
+ float co[3];
+
+ if (BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) {
+ if (BMO_elem_flag_test(bm, l->prev->e, BEVEL_FLAG))
+ {
+ tag = tags + BM_elem_index_get(l);
+ calc_corner_co(bm, l, fac, co, do_dist, do_even);
+ tag->newv = BM_vert_create(bm, co, l->v);
+ }
+ else {
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = ETAG_GET(l->prev->e, l->v);
+
+ if (!tag->newv) {
+ sub_v3_v3v3(co, l->prev->v->co, l->v->co);
+ if (has_elens) {
+ float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data,
+ CD_PROP_FLT, li);
+
+ normalize_v3(co);
+ mul_v3_fl(co, elen);
+ }
+
+ mul_v3_fl(co, fac);
+ add_v3_v3(co, l->v->co);
+
+ tag->newv = BM_vert_create(bm, co, l->v);
+
+ ETAG_SET(l->prev->e, l->v, tag->newv);
+ }
+ }
+ }
+ else if (BMO_elem_flag_test(bm, l->v, BEVEL_FLAG)) {
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = ETAG_GET(l->e, l->v);
+
+ if (!tag->newv) {
+ sub_v3_v3v3(co, l->next->v->co, l->v->co);
+ if (has_elens) {
+ float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->e->head.data, CD_PROP_FLT, li);
+
+ normalize_v3(co);
+ mul_v3_fl(co, elen);
+ }
+
+ mul_v3_fl(co, fac);
+ add_v3_v3(co, l->v->co);
+
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = BM_vert_create(bm, co, l->v);
+
+ ETAG_SET(l->e, l->v, tag->newv);
+ }
+ }
+ else {
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = l->v;
+ BMO_elem_flag_disable(bm, l->v, BEVEL_DEL);
+ }
+ }
+ }
+
+ /* create new faces inset from original face */
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+ BMFace *f;
+ BMVert *lastv = NULL, *firstv = NULL;
+
+ BMO_elem_flag_enable(bm, faces[i], BEVEL_DEL);
+
+ BLI_array_empty(verts);
+ BLI_array_empty(edges);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
+ BMVert *v2;
+
+ tag = tags + BM_elem_index_get(l);
+ BLI_array_append(verts, tag->newv);
+
+ if (!firstv)
+ firstv = tag->newv;
+
+ if (lastv) {
+ e = BM_edge_create(bm, lastv, tag->newv, l->e, TRUE);
+ BM_elem_attrs_copy(bm, bm, l->prev->e, e);
+ BLI_array_append(edges, e);
+ }
+ lastv = tag->newv;
+
+ v2 = ETAG_GET(l->e, l->next->v);
+
+ tag = &tags[BM_elem_index_get(l->next)];
+ if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG) && v2 && v2 != tag->newv) {
+ BLI_array_append(verts, v2);
+
+ e = BM_edge_create(bm, lastv, v2, l->e, TRUE);
+ BM_elem_attrs_copy(bm, bm, l->e, e);
+
+ BLI_array_append(edges, e);
+ lastv = v2;
+ }
+ }
+
+ e = BM_edge_create(bm, firstv, lastv, BM_FACE_FIRST_LOOP(faces[i])->e, TRUE);
+ if (BM_FACE_FIRST_LOOP(faces[i])->prev->e != e) {
+ BM_elem_attrs_copy(bm, bm, BM_FACE_FIRST_LOOP(faces[i])->prev->e, e);
+ }
+ BLI_array_append(edges, e);
+
+ f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), FALSE);
+ if (!f) {
+ printf("%s: could not make face!\n", __func__);
+ continue;
+ }
+
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+ int j;
+
+ /* create quad spans between split edge */
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
+ BMVert *v1 = NULL, *v2 = NULL, *v3 = NULL, *v4 = NULL;
+
+ if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG))
+ continue;
+
+ v1 = tags[BM_elem_index_get(l)].newv;
+ v2 = tags[BM_elem_index_get(l->next)].newv;
+ if (l->radial_next != l) {
+ v3 = tags[BM_elem_index_get(l->radial_next)].newv;
+ if (l->radial_next->next->v == l->next->v) {
+ v4 = v3;
+ v3 = tags[BM_elem_index_get(l->radial_next->next)].newv;
+ }
+ else {
+ v4 = tags[BM_elem_index_get(l->radial_next->next)].newv;
+ }
+ }
+ else {
+ /* the loop is on a boundar */
+ v3 = l->next->v;
+ v4 = l->v;
+
+ for (j = 0; j < 2; j++) {
+ BMIter eiter;
+ BMVert *v = j ? v4 : v3;
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BM_vert_in_edge(e, v3) || !BM_vert_in_edge(e, v4))
+ continue;
+
+ if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && BMO_elem_flag_test(bm, e, EDGE_OLD)) {
+ BMVert *vv;
+
+ vv = ETAG_GET(e, v);
+ if (!vv || BMO_elem_flag_test(bm, vv, BEVEL_FLAG))
+ continue;
+
+ if (j)
+ v1 = vv;
+ else
+ v2 = vv;
+ break;
+ }
+ }
+ }
+
+ BMO_elem_flag_disable(bm, v3, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, v4, BEVEL_DEL);
+ }
+
+ if (v1 != v2 && v2 != v3 && v3 != v4) {
+ BMIter liter2;
+ BMLoop *l2, *l3;
+ BMEdge *e1, *e2;
+ float d1, d2, *d3;
+
+ f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, l->f, TRUE);
+
+ e1 = BM_edge_exists(v4, v3);
+ e2 = BM_edge_exists(v2, v1);
+ BM_elem_attrs_copy(bm, bm, l->e, e1);
+ BM_elem_attrs_copy(bm, bm, l->e, e2);
+
+ /* set edge lengths of cross edges as the average of the cross edges they're based o */
+ if (has_elens) {
+ /* angle happens not to be used. why? - not sure it just isnt - campbell.
+ * leave this in incase we need to use it later */
+#if 0
+ float ang;
+#endif
+ e1 = BM_edge_exists(v1, v4);
+ e2 = BM_edge_exists(v2, v3);
+
+ if (l->radial_next->v == l->v) {
+ l2 = l->radial_next->prev;
+ l3 = l->radial_next->next;
+ }
+ else {
+ l2 = l->radial_next->next;
+ l3 = l->radial_next->prev;
+ }
+
+ d3 = CustomData_bmesh_get_n(&bm->edata, e1->head.data, CD_PROP_FLT, li);
+ d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li);
+ d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l2->e->head.data, CD_PROP_FLT, li);
+#if 0
+ ang = angle_v3v3v3(l->prev->v->co, l->v->co, BM_edge_other_vert(l2->e, l->v)->co);
+#endif
+ *d3 = (d1 + d2) * 0.5f;
+
+ d3 = CustomData_bmesh_get_n(&bm->edata, e2->head.data, CD_PROP_FLT, li);
+ d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->next->e->head.data, CD_PROP_FLT, li);
+ d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l3->e->head.data, CD_PROP_FLT, li);
+#if 0
+ ang = angle_v3v3v3(BM_edge_other_vert(l->next->e, l->next->v)->co, l->next->v->co,
+ BM_edge_other_vert(l3->e, l->next->v)->co);
+#endif
+ *d3 = (d1 + d2) * 0.5f;
+ }
+
+ if (!f) {
+ fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__);
+ continue;
+ }
+
+ BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_SPAN);
+
+ /* un-tag edges in f for deletio */
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, f) {
+ BMO_elem_flag_disable(bm, l2->e, BEVEL_DEL);
+ }
+ }
+ else {
+ f = NULL;
+ }
+ }
+ }
+
+ /* fill in holes at vertices */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMIter eiter;
+ BMVert *vv, *vstart = NULL, *lastv = NULL;
+ SmallHash tmphash;
+ int rad, insorig = 0, err = 0;
+
+ BLI_smallhash_init(&tmphash);
+
+ if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG))
+ continue;
+
+ BLI_array_empty(verts);
+ BLI_array_empty(edges);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ BMIter liter;
+ BMVert *v1 = NULL, *v2 = NULL;
+ BMLoop *l;
+
+ if (BM_edge_face_count(e) < 2)
+ insorig = 1;
+
+ if (BM_elem_index_get(e) == -1)
+ continue;
+
+ rad = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
+ if (!BMO_elem_flag_test(bm, l->f, FACE_OLD))
+ continue;
+
+ rad++;
+
+ tag = tags + BM_elem_index_get((l->v == v) ? l : l->next);
+
+ if (!v1)
+ v1 = tag->newv;
+ else if (!v2)
+ v2 = tag->newv;
+ }
+
+ if (rad < 2)
+ insorig = 1;
+
+ if (!v1)
+ v1 = ETAG_GET(e, v);
+ if (!v2 || v1 == v2)
+ v2 = ETAG_GET(e, v);
+
+ if (v1) {
+ if (!BLI_smallhash_haskey(&tmphash, (intptr_t)v1)) {
+ BLI_array_append(verts, v1);
+ BLI_smallhash_insert(&tmphash, (intptr_t)v1, NULL);
+ }
+
+ if (v2 && v1 != v2 && !BLI_smallhash_haskey(&tmphash, (intptr_t)v2)) {
+ BLI_array_append(verts, v2);
+ BLI_smallhash_insert(&tmphash, (intptr_t)v2, NULL);
+ }
+ }
+ }
+
+ if (!BLI_array_count(verts))
+ continue;
+
+ if (insorig) {
+ BLI_array_append(verts, v);
+ BLI_smallhash_insert(&tmphash, (intptr_t)v, NULL);
+ }
+
+ /* find edges that exist between vertices in verts. this is basically
+ * a topological walk of the edges connecting them */
+ vstart = vstart ? vstart : verts[0];
+ vv = vstart;
+ do {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) {
+ BMVert *vv2 = BM_edge_other_vert(e, vv);
+
+ if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) {
+ /* if we've go over the same vert twice, break out of outer loop */
+ if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) {
+ e = NULL;
+ err = 1;
+ break;
+ }
+
+ /* use self pointer as ta */
+ BLI_smallhash_remove(&tmphash, (intptr_t)vv2);
+ BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2);
+
+ lastv = vv;
+ BLI_array_append(edges, e);
+ vv = vv2;
+ break;
+ }
+ }
+
+ if (e == NULL) {
+ break;
+ }
+ } while (vv != vstart);
+
+ if (err) {
+ continue;
+ }
+
+ /* there may not be a complete loop of edges, so start again and make
+ * final edge afterwards. in this case, the previous loop worked to
+ * find one of the two edges at the extremes. */
+ if (vv != vstart) {
+ /* undo previous taggin */
+ for (i = 0; i < BLI_array_count(verts); i++) {
+ BLI_smallhash_remove(&tmphash, (intptr_t)verts[i]);
+ BLI_smallhash_insert(&tmphash, (intptr_t)verts[i], NULL);
+ }
+
+ vstart = vv;
+ lastv = NULL;
+ BLI_array_empty(edges);
+ do {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) {
+ BMVert *vv2 = BM_edge_other_vert(e, vv);
+
+ if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) {
+ /* if we've go over the same vert twice, break out of outer loo */
+ if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) {
+ e = NULL;
+ err = 1;
+ break;
+ }
+
+ /* use self pointer as ta */
+ BLI_smallhash_remove(&tmphash, (intptr_t)vv2);
+ BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2);
+
+ lastv = vv;
+ BLI_array_append(edges, e);
+ vv = vv2;
+ break;
+ }
+ }
+ if (e == NULL)
+ break;
+ } while (vv != vstart);
+
+ if (!err) {
+ e = BM_edge_create(bm, vv, vstart, NULL, TRUE);
+ BLI_array_append(edges, e);
+ }
+ }
+
+ if (err)
+ continue;
+
+ if (BLI_array_count(edges) >= 3) {
+ BMFace *f;
+
+ if (BM_face_exists(bm, verts, BLI_array_count(verts), &f))
+ continue;
+
+ f = BM_face_create_ngon(bm, lastv, vstart, edges, BLI_array_count(edges), FALSE);
+ if (!f) {
+ fprintf(stderr, "%s: in bevel vert fill! (bmesh internal error)\n", __func__);
+ }
+ else {
+ BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_HOLE);
+ }
+ }
+ BLI_smallhash_release(&tmphash);
+ }
+
+ /* copy over customdat */
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+ BMFace *f = faces[i];
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BMLoop *l2;
+ BMIter liter2;
+
+ tag = tags + BM_elem_index_get(l);
+ if (!tag->newv)
+ continue;
+
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_VERT, tag->newv) {
+ if (!BMO_elem_flag_test(bm, l2->f, FACE_NEW) || (l2->v != tag->newv && l2->v != l->v))
+ continue;
+
+ if (tag->newv != l->v || HasMDisps) {
+ BM_elem_attrs_copy(bm, bm, l->f, l2->f);
+ BM_loop_interp_from_face(bm, l2, l->f, TRUE, TRUE);
+ }
+ else {
+ BM_elem_attrs_copy(bm, bm, l->f, l2->f);
+ BM_elem_attrs_copy(bm, bm, l, l2);
+ }
+
+ if (HasMDisps) {
+ BMLoop *l3;
+ BMIter liter3;
+
+ BM_ITER(l3, &liter3, bm, BM_LOOPS_OF_FACE, l2->f) {
+ BM_loop_interp_multires(bm, l3, l->f);
+ }
+ }
+ }
+ }
+ }
+
+ /* handle vertices along boundary edge */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_OLD) &&
+ BMO_elem_flag_test(bm, v, BEVEL_FLAG) &&
+ !BMO_elem_flag_test(bm, v, BEVEL_DEL))
+ {
+ BMLoop *l;
+ BMLoop *lorig = NULL;
+ BMIter liter;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
+ // BMIter liter2;
+ // BMLoop *l2 = l->v == v ? l : l->next, *l3;
+
+ if (BMO_elem_flag_test(bm, l->f, FACE_OLD)) {
+ lorig = l;
+ break;
+ }
+ }
+
+ if (!lorig)
+ continue;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
+ BMLoop *l2 = l->v == v ? l : l->next;
+
+ BM_elem_attrs_copy(bm, bm, lorig->f, l2->f);
+ BM_elem_attrs_copy(bm, bm, lorig, l2);
+ }
+ }
+ }
+#if 0
+ /* clean up any remaining 2-edged face */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (f->len == 2) {
+ BMFace *faces[2] = {f, BM_FACE_FIRST_LOOP(f)->radial_next->f};
+
+ if (faces[0] == faces[1])
+ BM_face_kill(bm, f);
+ else
+ BM_faces_join(bm, faces, 2);
+ }
+ }
+#endif
+
+ BMO_op_callf(bm, "del geom=%fv context=%i", BEVEL_DEL, DEL_VERTS);
+
+ /* clean up any edges that might not get properly delete */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e, EDGE_OLD) && !e->l)
+ BMO_elem_flag_enable(bm, e, BEVEL_DEL);
+ }
+
+ BMO_op_callf(bm, "del geom=%fe context=%i", BEVEL_DEL, DEL_EDGES);
+ BMO_op_callf(bm, "del geom=%ff context=%i", BEVEL_DEL, DEL_FACES);
+
+ BLI_smallhash_release(&hash);
+ BLI_array_free(tags);
+ BLI_array_free(etags);
+ BLI_array_free(verts);
+ BLI_array_free(edges);
+ BLI_array_free(faces);
+
+ BMO_slot_from_flag(bm, op, "face_spans", FACE_SPAN, BM_FACE);
+ BMO_slot_from_flag(bm, op, "face_holes", FACE_HOLE, BM_FACE);
+}
diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c
new file mode 100644
index 00000000000..6ab6d28008b
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_connect.c
@@ -0,0 +1,414 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+
+#include "bmesh.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#define VERT_INPUT 1
+#define EDGE_OUT 1
+#define FACE_NEW 2
+#define EDGE_MARK 4
+#define EDGE_DONE 8
+
+void connectverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter, liter;
+ BMFace *f, *nf;
+ BMLoop **loops = NULL, *lastl = NULL;
+ BLI_array_declare(loops);
+ BMLoop *l, *nl;
+ BMVert **verts = NULL;
+ BLI_array_declare(verts);
+ int i;
+
+ BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_INPUT, BM_VERT);
+
+ for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
+ BLI_array_empty(loops);
+ BLI_array_empty(verts);
+
+ if (BMO_elem_flag_test(bm, f, FACE_NEW)) continue;
+
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ lastl = NULL;
+ for ( ; l; l = BM_iter_step(&liter)) {
+ if (BMO_elem_flag_test(bm, l->v, VERT_INPUT)) {
+ if (!lastl) {
+ lastl = l;
+ continue;
+ }
+
+ if (lastl != l->prev && lastl != l->next) {
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = lastl;
+
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = l;
+
+ }
+ lastl = l;
+ }
+ }
+
+ if (BLI_array_count(loops) == 0) continue;
+
+ if (BLI_array_count(loops) > 2) {
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = loops[BLI_array_count(loops) - 2];
+
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = loops[0];
+ }
+
+ BM_face_legal_splits(bm, f, (BMLoop *(*)[2])loops, BLI_array_count(loops) / 2);
+
+ for (i = 0; i < BLI_array_count(loops) / 2; i++) {
+ if (loops[i * 2] == NULL) continue;
+
+ BLI_array_growone(verts);
+ verts[BLI_array_count(verts) - 1] = loops[i * 2]->v;
+
+ BLI_array_growone(verts);
+ verts[BLI_array_count(verts) - 1] = loops[i * 2 + 1]->v;
+ }
+
+ for (i = 0; i < BLI_array_count(verts) / 2; i++) {
+ nf = BM_face_split(bm, f, verts[i * 2], verts[i * 2 + 1], &nl, NULL);
+ f = nf;
+
+ if (!nl || !nf) {
+ BMO_error_raise(bm, op, BMERR_CONNECTVERT_FAILED, NULL);
+ BLI_array_free(loops);
+ return;
+ }
+ BMO_elem_flag_enable(bm, nf, FACE_NEW);
+ BMO_elem_flag_enable(bm, nl->e, EDGE_OUT);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", EDGE_OUT, BM_EDGE);
+
+ BLI_array_free(loops);
+ BLI_array_free(verts);
+}
+
+static BMVert *get_outer_vert(BMesh *bm, BMEdge *e)
+{
+ BMIter iter;
+ BMEdge *e2;
+ int i;
+
+ i = 0;
+ BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, e->v1) {
+ if (BMO_elem_flag_test(bm, e2, EDGE_MARK)) {
+ i++;
+ }
+ }
+
+ return (i == 2) ? e->v2 : e->v1;
+}
+
+/* Clamp x to the interval {0..len-1}, with wrap-around */
+static int clamp_index(const int x, const int len)
+{
+ return (x < 0) ? (len - (-x % len)) : (x % len);
+}
+
+/* There probably is a better way to swap BLI_arrays, or if there
+ * isn't there should be... */
+#define ARRAY_SWAP(elemtype, arr1, arr2) \
+ { \
+ int i; \
+ elemtype *arr_tmp = NULL; \
+ BLI_array_declare(arr_tmp); \
+ for (i = 0; i < BLI_array_count(arr1); i++) { \
+ BLI_array_append(arr_tmp, arr1[i]); \
+ } \
+ BLI_array_empty(arr1); \
+ for (i = 0; i < BLI_array_count(arr2); i++) { \
+ BLI_array_append(arr1, arr2[i]); \
+ } \
+ BLI_array_empty(arr2); \
+ for (i = 0; i < BLI_array_count(arr_tmp); i++) { \
+ BLI_array_append(arr2, arr_tmp[i]); \
+ } \
+ BLI_array_free(arr_tmp); \
+ }
+
+void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op)
+{
+ BMEdge **ee1 = NULL, **ee2 = NULL;
+ BMVert **vv1 = NULL, **vv2 = NULL;
+ BLI_array_declare(ee1);
+ BLI_array_declare(ee2);
+ BLI_array_declare(vv1);
+ BLI_array_declare(vv2);
+ BMOIter siter;
+ BMIter iter;
+ BMEdge *e, *nexte;
+ int c = 0, cl1 = 0, cl2 = 0;
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_DONE)) {
+ BMVert *v, *ov;
+ /* BMEdge *e2, *e3, *oe = e; */ /* UNUSED */
+ BMEdge *e2, *e3;
+
+ if (c > 2) {
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Select only two edge loops");
+ goto cleanup;
+ }
+
+ e2 = e;
+ v = e->v1;
+ do {
+ v = BM_edge_other_vert(e2, v);
+ nexte = NULL;
+ BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK)) {
+ if (nexte == NULL) {
+ nexte = e3;
+ }
+ else {
+ /* edges do not form a loop: there is a disk
+ * with more than two marked edges. */
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION,
+ "Selection must only contain edges from two edge loops");
+ goto cleanup;
+ }
+ }
+ }
+
+ if (nexte)
+ e2 = nexte;
+ } while (nexte && e2 != e);
+
+ if (!e2)
+ e2 = e;
+
+ e = e2;
+ ov = v;
+ do {
+ if (c == 0) {
+ BLI_array_append(ee1, e2);
+ BLI_array_append(vv1, v);
+ }
+ else {
+ BLI_array_append(ee2, e2);
+ BLI_array_append(vv2, v);
+ }
+
+ BMO_elem_flag_enable(bm, e2, EDGE_DONE);
+
+ v = BM_edge_other_vert(e2, v);
+ BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK) && !BMO_elem_flag_test(bm, e3, EDGE_DONE)) {
+ break;
+ }
+ }
+ if (e3)
+ e2 = e3;
+ } while (e3 && e2 != e);
+
+ if (v && !e3) {
+ if (c == 0) {
+ if (BLI_array_count(vv1) && v == vv1[BLI_array_count(vv1) - 1]) {
+ printf("%s: internal state waning *TODO DESCRIPTION!*\n", __func__);
+ }
+ BLI_array_append(vv1, v);
+ }
+ else {
+ BLI_array_append(vv2, v);
+ }
+ }
+
+ /* test for connected loops, and set cl1 or cl2 if so */
+ if (v == ov) {
+ if (c == 0) {
+ cl1 = 1;
+ }
+ else {
+ cl2 = 1;
+ }
+ }
+
+ c++;
+ }
+ }
+
+ if (ee1 && ee2) {
+ int i, j;
+ BMVert *v1, *v2, *v3, *v4;
+ int starti = 0, dir1 = 1, wdir = 0, lenv1, lenv2;
+
+ /* Simplify code below by avoiding the (!cl1 && cl2) case */
+ if (!cl1 && cl2) {
+ SWAP(int, cl1, cl2);
+ ARRAY_SWAP(BMVert *, vv1, vv2);
+ ARRAY_SWAP(BMEdge *, ee1, ee2);
+ }
+
+ lenv1 = lenv2 = BLI_array_count(vv1);
+
+ /* Below code assumes vv1/vv2 each have at least two verts. should always be
+ * a safe assumption, since ee1/ee2 are non-empty and an edge has two verts. */
+ BLI_assert((lenv1 > 1) && (lenv2 > 1));
+
+ /* BMESH_TODO: Would be nice to handle cases where the edge loops
+ * have different edge counts by generating triangles & quads for
+ * the bridge instead of quads only. */
+ if (BLI_array_count(ee1) != BLI_array_count(ee2)) {
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION,
+ "Selected loops must have equal edge counts");
+ goto cleanup;
+ }
+
+ j = 0;
+ if (vv1[0] == vv1[lenv1 - 1]) {
+ lenv1--;
+ }
+ if (vv2[0] == vv2[lenv2 - 1]) {
+ lenv2--;
+ }
+
+ /* Find starting point and winding direction for two unclosed loops */
+ if (!cl1 && !cl2) {
+ /* First point of loop 1 */
+ v1 = get_outer_vert(bm, ee1[0]);
+ /* Last point of loop 1 */
+ v2 = get_outer_vert(bm, ee1[clamp_index(-1, BLI_array_count(ee1))]);
+ /* First point of loop 2 */
+ v3 = get_outer_vert(bm, ee2[0]);
+ /* Last point of loop 2 */
+ v4 = get_outer_vert(bm, ee2[clamp_index(-1, BLI_array_count(ee2))]);
+
+ /* If v1 is a better match for v4 than v3, AND v2 is a better match
+ * for v3 than v4, the loops are in opposite directions, so reverse
+ * the order of reads from vv1. We can avoid sqrt for comparison */
+ if (len_squared_v3v3(v1->co, v3->co) > len_squared_v3v3(v1->co, v4->co) &&
+ len_squared_v3v3(v2->co, v4->co) > len_squared_v3v3(v2->co, v3->co))
+ {
+ dir1 = -1;
+ starti = clamp_index(-1, lenv1);
+ }
+ }
+
+ /* Find the shortest distance from a vert in vv1 to vv2[0]. Use that
+ * vertex in vv1 as a starting point in the first loop, while starting
+ * from vv2[0] in the second loop. This is a simplistic attempt to get
+ * a better edge-to-edge match between the two loops. */
+ if (cl1) {
+ int previ, nexti;
+ float min = 1e32;
+
+ /* BMESH_TODO: Would be nice to do a more thorough analysis of all
+ * the vertices in both loops to find a more accurate match for the
+ * starting point and winding direction of the bridge generation. */
+
+ for (i = 0; i < BLI_array_count(vv1); i++) {
+ if (len_v3v3(vv1[i]->co, vv2[0]->co) < min) {
+ min = len_v3v3(vv1[i]->co, vv2[0]->co);
+ starti = i;
+ }
+ }
+
+ /* Reverse iteration order for the first loop if the distance of
+ * the (starti - 1) vert from vv1 is a better match for vv2[1] than
+ * the (starti + 1) vert.
+ *
+ * This is not always going to be right, but it will work better in
+ * the average case.
+ */
+ previ = clamp_index(starti - 1, lenv1);
+ nexti = clamp_index(starti + 1, lenv1);
+
+ /* avoid sqrt for comparison */
+ if (len_squared_v3v3(vv1[nexti]->co, vv2[1]->co) > len_squared_v3v3(vv1[previ]->co, vv2[1]->co)) {
+ /* reverse direction for reading vv1 (1 is forward, -1 is backward) */
+ dir1 = -1;
+ }
+ }
+
+ /* Vert rough attempt to determine proper winding for the bridge quads:
+ * just uses the first loop it finds for any of the edges of ee2 or ee1 */
+ if (wdir == 0) {
+ for (i = 0; i < BLI_array_count(ee2); i++) {
+ if (ee2[i]->l) {
+ wdir = (ee2[i]->l->v == vv2[i]) ? (-1) : (1);
+ break;
+ }
+ }
+ }
+ if (wdir == 0) {
+ for (i = 0; i < BLI_array_count(ee1); i++) {
+ j = clamp_index((i * dir1) + starti, BLI_array_count(ee1));
+ if (ee1[j]->l && ee2[j]->l) {
+ wdir = (ee2[j]->l->v == vv2[j]) ? (1) : (-1);
+ break;
+ }
+ }
+ }
+
+ /* Generate the bridge quads */
+ for (i = 0; i < BLI_array_count(ee1) && i < BLI_array_count(ee2); i++) {
+ BMFace *f;
+ int i1, i1next, i2, i2next;
+
+ i1 = clamp_index(i * dir1 + starti, lenv1);
+ i1next = clamp_index((i + 1) * dir1 + starti, lenv1);
+ i2 = i;
+ i2next = clamp_index(i + 1, lenv2);
+
+ if (vv1[i1] == vv1[i1next]) {
+ continue;
+ }
+
+ if (wdir < 0) {
+ SWAP(int, i1, i1next);
+ SWAP(int, i2, i2next);
+ }
+
+ f = BM_face_create_quad_tri(bm,
+ vv1[i1],
+ vv2[i2],
+ vv2[i2next],
+ vv1[i1next],
+ NULL, TRUE);
+ if (!f || f->len != 4) {
+ fprintf(stderr, "%s: in bridge! (bmesh internal error)\n", __func__);
+ }
+ }
+ }
+
+cleanup:
+ BLI_array_free(ee1);
+ BLI_array_free(ee2);
+ BLI_array_free(vv1);
+ BLI_array_free(vv2);
+}
diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c
new file mode 100644
index 00000000000..d90e5c36c80
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_create.c
@@ -0,0 +1,1412 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_heap.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_smallhash.h"
+#include "BLI_rand.h"
+
+#include "bmesh.h"
+
+
+#define EDGE_MARK 1
+#define EDGE_VIS 2
+
+#define FACE_NEW 1
+
+#define ELE_NEW 1
+#define ELE_OUT 2
+#define ELE_ORIG 4
+
+#define FACE_IGNORE 16
+
+typedef struct EPathNode {
+ struct EPathNode *next, *prev;
+ BMVert *v;
+ BMEdge *e;
+ BMEdge *cure;
+} EPathNode;
+
+typedef struct EPath {
+ ListBase nodes;
+ float weight;
+ int group;
+} EPath;
+
+typedef struct PathBase {
+ BLI_mempool *nodepool, *pathpool;
+} PathBase;
+
+typedef struct EdgeData {
+ int tag;
+ int ftag;
+ BMDiskLink v1_disk_link, v2_disk_link;
+} EdgeData;
+
+typedef struct VertData {
+ BMEdge *e;
+ float no[3], offco[3], sco[3]; /* offco is vertex coordinate slightly offset randoml */
+ int tag;
+} VertData;
+
+static int count_edge_faces(BMesh *bm, BMEdge *e);
+
+/**** rotation system code * */
+
+#define RS_GET_EDGE_LINK(e, v, e_data) ( \
+ (v) == ((BMEdge *)(e))->v1 ? \
+ &(((EdgeData *)(e_data))->v1_disk_link) : \
+ &(((EdgeData *)(e_data))->v2_disk_link) \
+ )
+
+
+static int rotsys_append_edge(struct BMEdge *e, struct BMVert *v,
+ EdgeData *edata, VertData *vdata)
+{
+ EdgeData *ed = &edata[BM_elem_index_get(e)];
+ VertData *vd = &vdata[BM_elem_index_get(v)];
+
+ if (!vd->e) {
+ Link *e1 = (Link *)RS_GET_EDGE_LINK(e, v, ed);
+
+ vd->e = e;
+ e1->next = e1->prev = (Link *)e;
+ }
+ else {
+ BMDiskLink *dl1, *dl2, *dl3;
+ EdgeData *ved = &edata[BM_elem_index_get(vd->e)];
+
+ dl1 = RS_GET_EDGE_LINK(e, v, ed);
+ dl2 = RS_GET_EDGE_LINK(vd->e, v, ved);
+ dl3 = dl2->prev ? RS_GET_EDGE_LINK(dl2->prev, v, &edata[BM_elem_index_get(dl2->prev)]) : NULL;
+
+ dl1->next = vd->e;
+ dl1->prev = dl2->prev;
+
+ dl2->prev = e;
+ if (dl3) {
+ dl3->next = e;
+ }
+ }
+
+ return TRUE;
+}
+
+static void UNUSED_FUNCTION(rotsys_remove_edge)(struct BMEdge *e, struct BMVert *v,
+ EdgeData *edata, VertData *vdata)
+{
+ EdgeData *ed = edata + BM_elem_index_get(e);
+ VertData *vd = vdata + BM_elem_index_get(v);
+ BMDiskLink *e1, *e2;
+
+ e1 = RS_GET_EDGE_LINK(e, v, ed);
+ if (e1->prev) {
+ e2 = RS_GET_EDGE_LINK(e1->prev, v, ed);
+ e2->next = e1->next;
+ }
+
+ if (e1->next) {
+ e2 = RS_GET_EDGE_LINK(e1->next, v, ed);
+ e2->prev = e1->prev;
+ }
+
+ if (vd->e == e)
+ vd->e = (e != (BMEdge *)e1->next) ? (BMEdge *)e1->next : NULL;
+
+ e1->next = e1->prev = NULL;
+}
+
+static struct BMEdge *rotsys_nextedge(struct BMEdge *e, struct BMVert *v,
+ EdgeData *edata, VertData *UNUSED(vdata))
+{
+ if (v == e->v1)
+ return edata[BM_elem_index_get(e)].v1_disk_link.next;
+ if (v == e->v2)
+ return edata[BM_elem_index_get(e)].v2_disk_link.next;
+ return NULL;
+}
+
+static BMEdge *rotsys_prevedge(BMEdge *e, BMVert *v,
+ EdgeData *edata, VertData *UNUSED(vdata))
+{
+ if (v == e->v1)
+ return edata[BM_elem_index_get(e)].v1_disk_link.prev;
+ if (v == e->v2)
+ return edata[BM_elem_index_get(e)].v2_disk_link.prev;
+ return NULL;
+}
+
+static void rotsys_reverse(struct BMEdge *UNUSED(e), struct BMVert *v, EdgeData *edata, VertData *vdata)
+{
+ BMEdge **edges = NULL;
+ BMEdge *e_first;
+ BMEdge *e;
+ BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
+ int i, totedge;
+
+ e = e_first = vdata[BM_elem_index_get(v)].e;
+ do {
+ BLI_array_append(edges, e);
+ e = rotsys_nextedge(e, v, edata, vdata);
+ } while (e != e_first);
+
+ totedge = BLI_array_count(edges);
+ for (i = 0; i < totedge / 2; i++) {
+ SWAP(BMEdge *, edges[i], edges[totedge - 1 - i]);
+ }
+
+ vdata[BM_elem_index_get(v)].e = NULL;
+ for (i = 0; i < totedge; i++) {
+ rotsys_append_edge(edges[i], v, edata, vdata);
+ }
+
+ BLI_array_free(edges);
+}
+
+static int UNUSED_FUNCTION(rotsys_count)(struct BMVert *v, EdgeData *edata, VertData *vdata)
+{
+ BMEdge *e = vdata[BM_elem_index_get(v)].e;
+ int i = 0;
+
+ if (!e)
+ return 0;
+
+ do {
+ if (!e)
+ return 0;
+ e = rotsys_nextedge(e, v, edata, vdata);
+
+ if (i >= (1 << 20)) {
+ printf("bmesh error: infinite loop in disk cycle!\n");
+ return 0;
+ }
+
+ i += 1;
+ } while (e != vdata[BM_elem_index_get(v)].e);
+
+ return i;
+}
+
+static int UNUSED_FUNCTION(rotsys_fill_faces)(BMesh *bm, EdgeData *edata, VertData *vdata)
+{
+ BMIter iter;
+ BMEdge *e, **edges = NULL;
+ BLI_array_declare(edges);
+ BMVert *v, **verts = NULL;
+ BMFace *f;
+ BLI_array_declare(verts);
+ SmallHash visithash, *hash = &visithash;
+ int i;
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMEdge *e2, *starte;
+ BMVert *startv;
+ int rad, ok;
+
+ rad = count_edge_faces(bm, e);
+
+ if (rad < 2)
+ starte = e;
+ else
+ continue;
+
+ /* do two passes, going forward then backwar */
+ for (i = 0; i < 2; i++) {
+ BLI_smallhash_init(hash);
+
+ BLI_array_empty(verts);
+ BLI_array_empty(edges);
+
+ startv = v = starte->v1;
+ e2 = starte;
+ ok = 1;
+ if (!v || !e2)
+ continue;
+
+ do {
+ if (BLI_smallhash_haskey(hash, (intptr_t)e2) ||
+ BLI_smallhash_haskey(hash, (intptr_t)v))
+ {
+ ok = 0;
+ break;
+ }
+
+ BLI_array_append(verts, v);
+ BLI_array_append(edges, e2);
+
+ BLI_smallhash_insert(hash, (intptr_t)e2, NULL);
+
+ v = BM_edge_other_vert(e2, v);
+ e2 = i ? rotsys_prevedge(e2, v, edata, vdata) : rotsys_nextedge(e2, v, edata, vdata);
+ } while (e2 != starte && v != startv);
+
+ BLI_smallhash_release(hash);
+
+ if (!ok || BLI_array_count(edges) < 3)
+ continue;
+
+ f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), TRUE);
+ if (!f)
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static void rotsys_make_consistent(BMesh *bm, EdgeData *edata, VertData *vdata)
+{
+ BMIter iter;
+ BMEdge *e;
+ BMVert *v, **stack = NULL;
+ BLI_array_declare(stack);
+ int i;
+
+ for (i = 0; i < bm->totvert; i++) {
+ vdata[i].tag = 0;
+ }
+
+ while (1) {
+ VertData *vd;
+ BMVert *startv = NULL;
+ float dis;
+
+ v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ for (i = 0; i < bm->totvert; i++, BM_iter_step(&iter)) {
+ vd = vdata + BM_elem_index_get(v);
+
+ if (vd->tag)
+ continue;
+
+ if (!startv || dot_v3v3(vd->offco, vd->offco) > dis) {
+ dis = dot_v3v3(vd->offco, vd->offco);
+ startv = v;
+ }
+ }
+
+ if (!startv)
+ break;
+
+ vd = vdata + BM_elem_index_get(startv);
+
+ BLI_array_empty(stack);
+ BLI_array_append(stack, startv);
+
+ vd->tag = 1;
+
+ while (BLI_array_count(stack)) {
+ v = BLI_array_pop(stack);
+ vd = vdata + BM_elem_index_get(v);
+
+ if (!vd->e)
+ continue;
+
+ e = vd->e;
+ do {
+ BMVert *v2 = BM_edge_other_vert(e, v);
+ VertData *vd2 = vdata + BM_elem_index_get(v2);
+
+ if (dot_v3v3(vd->no, vd2->no) < 0.0f + FLT_EPSILON * 2) {
+ rotsys_reverse(e, v2, edata, vdata);
+ mul_v3_fl(vd2->no, -1.0f);
+ }
+
+ if (!vd2->tag) {
+ BLI_array_append(stack, v2);
+ vd2->tag = 1;
+ }
+
+ e = rotsys_nextedge(e, v, edata, vdata);
+ } while (e != vd->e);
+ }
+ }
+
+ BLI_array_free(stack);
+}
+
+static void init_rotsys(BMesh *bm, EdgeData *edata, VertData *vdata)
+{
+ BMIter iter;
+ BMEdge *e;
+ BMEdge **edges = NULL;
+ BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
+ BMVert *v;
+ /* BMVert **verts = NULL; */
+ /* BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); */ /* UNUSE */
+ int i;
+
+#define SIGN(n) ((n)<0.0f)
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMIter eiter;
+ float no[3], cent[3];
+ int j, k = 0, totedge = 0;
+
+ if (BM_elem_index_get(v) == -1)
+ continue;
+
+ BLI_array_empty(edges);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ BLI_array_append(edges, e);
+ totedge++;
+ }
+ }
+
+ copy_v3_v3(cent, v->co);
+
+ zero_v3(no);
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1, *e2;
+ float cno[3], vec1[3], vec2[3];
+
+ e1 = edges[i];
+ e2 = edges[(i + 1) % totedge];
+
+ sub_v3_v3v3(vec1, (BM_edge_other_vert(e1, v))->co, v->co);
+ sub_v3_v3v3(vec2, (BM_edge_other_vert(e2, v))->co, v->co);
+
+ cross_v3_v3v3(cno, vec1, vec2);
+ normalize_v3(cno);
+
+ if (i && dot_v3v3(cno, no) < 0.0f + FLT_EPSILON * 10)
+ mul_v3_fl(cno, -1.0f);
+
+ add_v3_v3(no, cno);
+ normalize_v3(no);
+ }
+
+ /* generate plane-flattened coordinate */
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1;
+ BMVert *v2;
+ float cvec[3], vec1[3];
+
+ e1 = edges[i];
+ v2 = BM_edge_other_vert(e1, v);
+
+ sub_v3_v3v3(vec1, v2->co, v->co);
+
+ cross_v3_v3v3(cvec, vec1, no);
+ cross_v3_v3v3(vec1, cvec, no);
+ normalize_v3(vec1);
+
+ mul_v3_fl(vec1, len_v3v3(v2->co, v->co));
+ add_v3_v3(vec1, v->co);
+
+ copy_v3_v3(vdata[BM_elem_index_get(v2)].sco, vec1);
+ }
+
+ BLI_srandom(0);
+
+ /* first, ensure no 0 or 180 angles between adjacent
+ * (and that adjacent's adjacent) edges */
+ for (i = 0, k = 0; i < totedge; i++) {
+ BMEdge *e1, *e2, *e3 = NULL;
+ BMVert *v1, *v2, *v3;
+ VertData *vd1, *vd2, *vd3;
+ float vec1[3], vec2[3], vec3[3], size;
+ int s1, s2, s3;
+
+ if (totedge < 3)
+ continue;
+
+ e1 = edges[(i + totedge - 1) % totedge];
+ e2 = edges[i];
+ e3 = edges[(i + 1) % totedge];
+
+ v1 = BM_edge_other_vert(e1, v);
+ v2 = BM_edge_other_vert(e2, v);
+ v3 = BM_edge_other_vert(e3, v);
+
+ vd1 = vdata + BM_elem_index_get(v1);
+ vd2 = vdata + BM_elem_index_get(v2);
+ vd3 = vdata + BM_elem_index_get(v3);
+
+ sub_v3_v3v3(vec1, vd1->sco, cent);
+ sub_v3_v3v3(vec2, vd2->sco, cent);
+ sub_v3_v3v3(vec3, vd3->sco, cent);
+
+ size = (len_v3(vec1) + len_v3(vec3)) * 0.01f;
+ normalize_v3(vec1); normalize_v3(vec2); normalize_v3(vec3);
+
+#ifdef STRAIGHT
+#undef STRAIGHT
+#endif
+#define STRAIGHT(vec11, vec22) (fabsf(dot_v3v3((vec11), (vec22))) > 1.0f - ((float)FLT_EPSILON * 1000.0f))
+
+ s1 = STRAIGHT(vec1, vec2); s2 = STRAIGHT(vec2, vec3); s3 = STRAIGHT(vec1, vec3);
+
+ if (s1 || s2 || s3) {
+ copy_v3_v3(cent, v->co);
+
+ for (j = 0; j < 3; j++) {
+ float fac = (BLI_frand() - 0.5f)*size;
+ cent[j] += fac;
+ }
+
+ if (k < 2000) {
+ i = 0;
+ k++;
+ continue;
+ }
+ else {
+ k++;
+ continue;
+ }
+
+ }
+ }
+
+ copy_v3_v3(vdata[BM_elem_index_get(v)].offco, cent);
+ //copy_v3_v3(v->co, cent);
+
+ /* now, sort edges so the triangle fan of all edges
+ * has a consistent normal. this is the same as
+ * sorting by polar coordinates along a group normal */
+ for (j = 0; j < totedge; j++) {
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1, *e2, *e3 = NULL;
+ BMVert *v1, *v2, *v3;
+ VertData *vd1, *vd2, *vd3;
+ float vec1[3], vec2[3], vec3[3], n1[3], n2[3], n3[3];
+ int s1, s2, s3;
+
+ e1 = edges[(i + totedge - 1) % totedge];
+ e2 = edges[i];
+ e3 = edges[(i + 1) % totedge];
+
+ v1 = BM_edge_other_vert(e1, v);
+ v2 = BM_edge_other_vert(e2, v);
+ v3 = BM_edge_other_vert(e3, v);
+
+ vd1 = vdata + BM_elem_index_get(v1);
+ vd2 = vdata + BM_elem_index_get(v2);
+ vd3 = vdata + BM_elem_index_get(v3);
+
+ sub_v3_v3v3(vec1, vd1->sco, cent);
+ sub_v3_v3v3(vec2, vd2->sco, cent);
+ sub_v3_v3v3(vec3, vd3->sco, cent);
+
+ cross_v3_v3v3(n1, vec1, vec2);
+ cross_v3_v3v3(n2, vec2, vec3);
+ cross_v3_v3v3(n3, vec1, vec3);
+
+ /* Other way to determine if two vectors approach are (nearly) parallel: the
+ * cross product of the two vectors will approach zero */
+ s1 = (dot_v3v3(n1, n1) < (0.0f + FLT_EPSILON * 10));
+ s2 = (dot_v3v3(n2, n2) < (0.0f + FLT_EPSILON * 10));
+ s3 = (totedge < 3) ? 0 : (dot_v3v3(n3, n3) < (0.0f + FLT_EPSILON * 10));
+
+ normalize_v3(n1); normalize_v3(n2); normalize_v3(n3);
+
+ if (s1 || s2 || s3) {
+ fprintf(stderr, "%s: s1: %d, s2: %d, s3: %dx (bmesh internal error)\n", __func__, s1, s2, s3);
+ }
+ if (dot_v3v3(n1, n2) < 0.0f) {
+ if (dot_v3v3(n1, n3) >= 0.0f + FLT_EPSILON * 10) {
+ SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
+ }
+ else {
+ SWAP(BMEdge *, edges[(i + totedge - 1) % totedge], edges[(i + 1) % totedge]);
+ SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
+ }
+ }
+ }
+ }
+
+#undef STRAIGHT
+
+ zero_v3(no);
+
+ /* yay, edges are sorted */
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1 = edges[i], *e2 = edges[(i + 1) % totedge];
+ float eno[3];
+
+ normal_tri_v3(eno, BM_edge_other_vert(e1, v)->co, v->co, BM_edge_other_vert(e2, v)->co);
+ add_v3_v3(no, eno);
+
+ rotsys_append_edge(edges[i], v, edata, vdata);
+ }
+
+ normalize_v3(no);
+ copy_v3_v3(vdata[BM_elem_index_get(v)].no, no);
+ }
+
+ /* now, make sure rotation system is topologically consistent
+ * (e.g. vert normals consistently point either inside or outside) */
+ rotsys_make_consistent(bm, edata, vdata);
+
+ //rotsys_fill_faces(bm, edata, vdata);
+
+#if 0
+ /* create visualizing geometr */
+ BMVert *lastv;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMVert *v2;
+ BMFace *f;
+ int totedge = BM_vert_edge_count(v);
+
+ if (BM_elem_index_get(v) == -1)
+ continue;
+
+ //cv = BM_vert_create(bm, cent, v);
+ //BM_elem_index_set(cv, -1); /* set_dirty! */
+ i = 0;
+ e = vdata[BM_elem_index_get(v)].e;
+ lastv = NULL;
+ do {
+ BMEdge *e2;
+ BMVert *v2;
+ float f = ((float)i / (float)totedge) * 0.35 + 0.05;
+ float co[3];
+
+ if (!e)
+ break;
+
+ if (!BM_edge_other_vert(e, v))
+ continue;
+
+ sub_v3_v3v3(co, (BM_edge_other_vert(e, v))->co, vdata[BM_elem_index_get(v)].offco);
+ mul_v3_fl(co, f);
+ add_v3_v3(co, vdata[BM_elem_index_get(v)].offco);
+
+ v2 = BM_vert_create(bm, co, NULL);
+ BM_elem_index_set(v2, -1); /* set_dirty! */
+ //BM_edge_create(bm, cv, v2, NULL, FALSE);
+
+ BM_elem_select_set(bm, v2, TRUE);
+ if (lastv) {
+ e2 = BM_edge_create(bm, lastv, v2, NULL, FALSE);
+ BM_elem_select_set(bm, e2, TRUE);
+ }
+
+ lastv = v2;
+
+ e = rotsys_nextedge(e, v, edata, vdata);
+ i++;
+ } while (e != vdata[BM_elem_index_get(v)].e);
+ }
+#endif
+
+ BLI_array_free(edges);
+}
+
+static PathBase *edge_pathbase_new(void)
+{
+ PathBase *pb = MEM_callocN(sizeof(PathBase), "PathBase");
+
+ pb->nodepool = BLI_mempool_create(sizeof(EPathNode), 1, 512, TRUE, FALSE);
+ pb->pathpool = BLI_mempool_create(sizeof(EPath), 1, 512, TRUE, FALSE);
+
+ return pb;
+}
+
+static void edge_pathbase_free(PathBase *pathbase)
+{
+ BLI_mempool_destroy(pathbase->nodepool);
+ BLI_mempool_destroy(pathbase->pathpool);
+ MEM_freeN(pathbase);
+}
+
+static EPath *edge_copy_add_path(PathBase *pb, EPath *path, BMVert *appendv, BMEdge *e)
+{
+ EPath *path2;
+ EPathNode *node, *node2;
+
+ path2 = BLI_mempool_alloc(pb->pathpool);
+ path2->nodes.first = path2->nodes.last = NULL;
+ path2->weight = 0.0f;
+ path2->group = path->group;
+
+ for (node = path->nodes.first; node; node = node->next) {
+ node2 = BLI_mempool_alloc(pb->nodepool);
+ *node2 = *node;
+ BLI_addtail(&path2->nodes, node2);
+ }
+
+ node2 = BLI_mempool_alloc(pb->nodepool);
+ node2->v = appendv;
+ node2->e = e;
+ node2->cure = NULL;
+
+ BLI_addtail(&path2->nodes, node2);
+
+ return path2;
+}
+
+static EPath *edge_path_new(PathBase *pb, BMVert *start, BMEdge *starte)
+{
+ EPath *path;
+ EPathNode *node;
+
+ path = BLI_mempool_alloc(pb->pathpool);
+ node = BLI_mempool_alloc(pb->nodepool);
+
+ path->nodes.first = path->nodes.last = NULL;
+
+ node->v = start;
+ node->e = starte;
+ node->cure = NULL;
+
+ BLI_addtail(&path->nodes, node);
+ path->weight = 0.0f;
+
+ return path;
+}
+
+static float edge_weight_path(EPath *path, EdgeData *edata, VertData *UNUSED(vdata))
+{
+ EPathNode *node, *first = path->nodes.first;
+ float w = 0.0;
+
+ for (node = path->nodes.first; node; node = node->next) {
+ if (node->e && node != path->nodes.first) {
+ w += edata[BM_elem_index_get(node->e)].ftag;
+ if (node->prev) {
+ /* BMESH_TOD */
+ (void)first;
+ //w += len_v3v3(node->v->co, first->e->v1->co) * 0.0001f;
+ //w += len_v3v3(node->v->co, first->e->v2->co) * 0.0001f;
+ }
+ }
+
+ w += 1.0f;
+ }
+
+ return w;
+}
+
+
+static void edge_free_path(PathBase *pathbase, EPath *path)
+{
+ EPathNode *node, *next;
+
+ for (node = path->nodes.first; node; node = next) {
+ next = node->next;
+ BLI_mempool_free(pathbase->nodepool, node);
+ }
+
+ BLI_mempool_free(pathbase->pathpool, path);
+}
+
+static EPath *edge_find_shortest_path(BMesh *bm, BMOperator *op, BMEdge *edge, EdgeData *edata,
+ VertData *vdata, PathBase *pathbase, int group)
+{
+ BMEdge *e;
+ GHash *gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "createops find shortest path");
+ BMVert *v1, *v2;
+ BMVert **verts = NULL;
+ BLI_array_staticdeclare(verts, 1024);
+ Heap *heap = BLI_heap_new();
+ EPath *path = NULL, *path2;
+ BMVert *startv;
+ BMVert *endv;
+ EPathNode *node;
+ int i, use_restrict = BMO_slot_int_get(op, "use_restrict");
+
+ startv = edata[BM_elem_index_get(edge)].ftag ? edge->v2 : edge->v1;
+ endv = edata[BM_elem_index_get(edge)].ftag ? edge->v1 : edge->v2;
+
+ path = edge_path_new(pathbase, startv, edge);
+ BLI_ghash_insert(gh, startv, NULL);
+ BLI_heap_insert(heap, path->weight, path);
+ path->group = group;
+
+ while (BLI_heap_size(heap)) {
+ VertData *vd;
+ EPathNode *last;
+ BMFace *f = NULL;
+
+ path = BLI_heap_popmin(heap);
+ last = path->nodes.last;
+ v1 = last->v;
+
+ if (v1 == endv) {
+ /* make sure this path loop doesn't already exist */
+ i = 0;
+ BLI_array_empty(verts);
+ for (i = 0, node = path->nodes.first; node; node = node->next, i++) {
+ BLI_array_growone(verts);
+ verts[i] = node->v;
+ }
+
+ if (BM_face_exists(bm, verts, i, &f)) {
+ if (!BMO_elem_flag_test(bm, f, FACE_IGNORE)) {
+ BLI_ghash_remove(gh, endv, NULL, NULL);
+ continue;
+ }
+ }
+ break;
+ }
+
+ vd = vdata + BM_elem_index_get(v1);
+ if (!vd->e)
+ continue;
+
+ v2 = NULL;
+ while (1) {
+ if (!last->cure) {
+ last->cure = e = vdata[BM_elem_index_get(last->v)].e;
+ }
+ else {
+ last->cure = e = rotsys_nextedge(last->cure, last->v, edata, vdata);
+ if (last->cure == vdata[BM_elem_index_get(last->v)].e) {
+ v2 = NULL;
+ break;
+ }
+ }
+
+ if (e == edge || !BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ v2 = BM_edge_other_vert(e, last->v);
+
+ if (BLI_ghash_haskey(gh, v2)) {
+ v2 = NULL;
+ continue;
+ }
+
+ if (use_restrict && BMO_slot_map_contains(bm, op, "restrict", e)) {
+ int group = BMO_slot_map_int_get(bm, op, "restrict", e);
+
+ if (!(group & path->group)) {
+ v2 = NULL;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (!v2) {
+ if (path) {
+ edge_free_path(pathbase, path);
+ path = NULL;
+ }
+ continue;
+ }
+
+ /* add path back into hea */
+ BLI_heap_insert(heap, path->weight, path);
+
+ /* put v2 in gh ma */
+ BLI_ghash_insert(gh, v2, NULL);
+
+ path2 = edge_copy_add_path(pathbase, path, v2, e);
+ path2->weight = edge_weight_path(path2, edata, vdata);
+
+ BLI_heap_insert(heap, path2->weight, path2);
+ }
+
+ if (path && ((EPathNode *)path->nodes.last)->v != endv) {
+ edge_free_path(pathbase, path);
+ path = NULL;
+ }
+
+ BLI_array_free(verts);
+ BLI_heap_free(heap, NULL);
+ BLI_ghash_free(gh, NULL, NULL);
+
+ return path;
+}
+
+static int count_edge_faces(BMesh *bm, BMEdge *e)
+{
+ int i = 0;
+ BMLoop *l = e->l;
+
+ if (!l) {
+ return 0;
+ }
+
+ do {
+ if (!BMO_elem_flag_test(bm, l->f, FACE_IGNORE)) {
+ i++;
+ }
+
+ l = l->radial_next;
+ } while (l != e->l);
+
+ return i;
+}
+
+void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter;
+ BMOIter siter;
+ BMFace *f;
+ BMEdge *e, *edge;
+ BMVert **verts = NULL;
+ BLI_array_declare(verts);
+ EPath *path;
+ EPathNode *node;
+ EdgeData *edata;
+ VertData *vdata;
+ BMEdge **edges = NULL;
+ PathBase *pathbase = edge_pathbase_new();
+ BLI_array_declare(edges);
+ int use_restrict = BMO_slot_int_get(op, "use_restrict");
+ int i, j, group = 0;
+ unsigned int winding[2]; /* accumulte winding directions for each edge which has a face */
+
+ if (!bm->totvert || !bm->totedge)
+ return;
+
+ edata = MEM_callocN(sizeof(EdgeData)*bm->totedge, "EdgeData");
+ vdata = MEM_callocN(sizeof(VertData)*bm->totvert, "VertData");
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+ BMO_slot_buffer_flag_enable(bm, op, "excludefaces", FACE_IGNORE, BM_FACE);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, f, ELE_ORIG);
+ }
+
+ i = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BM_elem_index_set(e, i); /* set_inline */
+
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ edata[i].tag = 2;
+ }
+
+ i++;
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ init_rotsys(bm, edata, vdata);
+
+ while (1) {
+ edge = NULL;
+ group = 0;
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ /* if restrict is on, only start on faces in the restrict map */
+ if (use_restrict && !BMO_slot_map_contains(bm, op, "restrict", e))
+ continue;
+
+ if (edata[BM_elem_index_get(e)].tag < 2) {
+ edge = e;
+
+ if (use_restrict) {
+ int i = 0, j = 0, gi = 0;
+
+ group = BMO_slot_map_int_get(bm, op, "restrict", e);
+
+ for (i = 0; i < 30; i++) {
+ if (group & (1 << i)) {
+ j++;
+ gi = i;
+
+ if (j - 1 == edata[BM_elem_index_get(e)].tag) {
+ break;
+ }
+ }
+ }
+
+ group = (1 << gi);
+ }
+
+ break;
+ }
+ }
+
+ if (!edge)
+ break;
+
+ edata[BM_elem_index_get(edge)].tag += 1;
+
+ path = edge_find_shortest_path(bm, op, edge, edata, vdata, pathbase, group);
+ if (!path)
+ continue;
+
+ winding[0] = winding[1] = 0;
+
+ BLI_array_empty(edges);
+ BLI_array_empty(verts);
+ i = 0;
+ for (node = path->nodes.first; node; node = node->next) {
+ if (!node->next)
+ continue;
+
+ e = BM_edge_exists(node->v, node->next->v);
+
+ /* this should never happe */
+ if (!e)
+ break;
+
+ /* check on the winding */
+ if (e->l) {
+ BMVert *test_v1, *test_v2;
+ /* we want to use the reverse winding to the existing order */
+ BM_edge_ordered_verts(edge, &test_v2, &test_v1);
+
+ /* edges vote on which winding wins out */
+ winding[(test_v1 == node->v)]++;
+ }
+
+ edata[BM_elem_index_get(e)].ftag++;
+ BLI_array_growone(edges);
+ edges[i++] = e;
+
+ BLI_array_append(verts, node->v);
+ }
+
+ BLI_array_growone(edges);
+ edges[i++] = edge;
+ edata[BM_elem_index_get(edge)].ftag++;
+
+ for (j = 0; j < i; j++) {
+ if (count_edge_faces(bm, edges[j]) >= 2) {
+ edge_free_path(pathbase, path);
+ break;
+ }
+ }
+
+ if (j != i) {
+ continue;
+ }
+
+ if (i) {
+ BMVert *v1, *v2;
+
+ /* to define the winding order must select first edge,
+ * otherwise we could leave this as-is */
+ edge = edges[0];
+
+ /* if these are even it doesnt really matter what to do,
+ * with consistent geometry one will be zero, the choice is clear */
+ if (winding[0] > winding[1]) {
+ v1 = verts[0];
+ v2 = verts[1];
+ }
+ else {
+ v1 = verts[1];
+ v2 = verts[0];
+ }
+
+ f = BM_face_create_ngon(bm, v1, v2, edges, i, TRUE);
+ if (f && !BMO_elem_flag_test(bm, f, ELE_ORIG)) {
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (use_restrict)
+ BMO_slot_map_int_insert(bm, op, "faceout_groupmap", f, path->group);
+ }
+
+ edge_free_path(pathbase, path);
+ }
+
+ BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
+
+ BLI_array_free(edges);
+ BLI_array_free(verts);
+ edge_pathbase_free(pathbase);
+ MEM_freeN(edata);
+ MEM_freeN(vdata);
+}
+
+static BMEdge *edge_next(BMesh *bm, BMEdge *e)
+{
+ BMIter iter;
+ BMEdge *e2;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
+ if ( (BMO_elem_flag_test(bm, e2, EDGE_MARK)) &&
+ (!BMO_elem_flag_test(bm, e2, EDGE_VIS)) &&
+ (e2 != e))
+ {
+ return e2;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e;
+ BMEdge **edges1 = NULL, **edges2 = NULL, **edges;
+ BLI_array_declare(edges1);
+ BLI_array_declare(edges2);
+ BLI_array_declare(edges);
+ int ok = 1;
+ int i, count;
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ /* validate that each edge has at most one other tagged edge in the
+ * disk cycle around each of it's vertices */
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ for (i = 0; i < 2; i++) {
+ count = BMO_vert_edge_flags_count(bm, i ? e->v2 : e->v1, EDGE_MARK);
+ if (count > 2) {
+ ok = 0;
+ break;
+ }
+ }
+
+ if (!ok) {
+ break;
+ }
+ }
+
+ /* we don't have valid edge layouts, retur */
+ if (!ok) {
+ return;
+ }
+
+ /* find connected loops within the input edge */
+ count = 0;
+ while (1) {
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_VIS)) {
+ if ( BMO_vert_edge_flags_count(bm, e->v1, EDGE_MARK) == 1 ||
+ BMO_vert_edge_flags_count(bm, e->v2, EDGE_MARK) == 1)
+ {
+ break;
+ }
+ }
+ }
+
+ if (!e) {
+ break;
+ }
+
+ if (!count) {
+ edges = edges1;
+ }
+ else if (count == 1) {
+ edges = edges2;
+ }
+ else {
+ break;
+ }
+
+ i = 0;
+ while (e) {
+ BMO_elem_flag_enable(bm, e, EDGE_VIS);
+ BLI_array_growone(edges);
+ edges[i] = e;
+
+ e = edge_next(bm, e);
+ i++;
+ }
+
+ if (!count) {
+ edges1 = edges;
+ BLI_array_set_length(edges1, BLI_array_count(edges));
+ }
+ else {
+ edges2 = edges;
+ BLI_array_set_length(edges2, BLI_array_count(edges));
+ }
+
+ BLI_array_empty(edges);
+ count++;
+ }
+
+ if (edges1 && BLI_array_count(edges1) > 2 &&
+ BM_edge_share_vert(edges1[0], edges1[BLI_array_count(edges1) - 1]))
+ {
+ if (edges2 && BLI_array_count(edges2) > 2 &&
+ BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
+ {
+ BLI_array_free(edges1);
+ BLI_array_free(edges2);
+ return;
+ }
+ else {
+ edges1 = edges2;
+ edges2 = NULL;
+ }
+ }
+
+ if (edges2 && BLI_array_count(edges2) > 2 &&
+ BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
+ {
+ edges2 = NULL;
+ }
+
+ /* two unconnected loops, connect the */
+ if (edges1 && edges2) {
+ BMVert *v1, *v2, *v3, *v4;
+
+ if (BLI_array_count(edges1) == 1) {
+ v1 = edges1[0]->v1;
+ v2 = edges1[0]->v2;
+ }
+ else {
+ if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
+ v1 = edges1[0]->v2;
+ else v1 = edges1[0]->v1;
+
+ i = BLI_array_count(edges1) - 1;
+ if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
+ v2 = edges1[i]->v2;
+ else v2 = edges1[i]->v1;
+ }
+
+ if (BLI_array_count(edges2) == 1) {
+ v3 = edges2[0]->v1;
+ v4 = edges2[0]->v2;
+ }
+ else {
+ if (BM_vert_in_edge(edges2[1], edges2[0]->v1))
+ v3 = edges2[0]->v2;
+ else v3 = edges2[0]->v1;
+
+ i = BLI_array_count(edges2) - 1;
+ if (BM_vert_in_edge(edges2[i - 1], edges2[i]->v1))
+ v4 = edges2[i]->v2;
+ else v4 = edges2[i]->v1;
+ }
+
+ /* avoid sqrt for comparison */
+ if (len_squared_v3v3(v1->co, v3->co) + len_squared_v3v3(v2->co, v4->co) >
+ len_squared_v3v3(v1->co, v4->co) + len_squared_v3v3(v2->co, v3->co))
+ {
+ BMVert *v;
+ v = v3;
+ v3 = v4;
+ v4 = v;
+ }
+
+ e = BM_edge_create(bm, v1, v3, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ e = BM_edge_create(bm, v2, v4, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ }
+ else if (edges1) {
+ BMVert *v1, *v2;
+
+ if (BLI_array_count(edges1) > 1) {
+ if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
+ v1 = edges1[0]->v2;
+ else v1 = edges1[0]->v1;
+
+ i = BLI_array_count(edges1) - 1;
+ if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
+ v2 = edges1[i]->v2;
+ else v2 = edges1[i]->v1;
+
+ e = BM_edge_create(bm, v1, v2, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", ELE_NEW, BM_EDGE);
+
+ BLI_array_free(edges1);
+ BLI_array_free(edges2);
+}
+
+/* this is essentially new fke */
+void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator op2;
+ BMOIter oiter;
+ BMIter iter;
+ BMHeader *h;
+ BMVert *v, *verts[4];
+ BMEdge *e;
+ BMFace *f;
+ int totv = 0, tote = 0, totf = 0, amount;
+
+ /* count number of each element type we were passe */
+ BMO_ITER(h, &oiter, bm, op, "geom", BM_VERT|BM_EDGE|BM_FACE) {
+ switch (h->htype) {
+ case BM_VERT: totv++; break;
+ case BM_EDGE: tote++; break;
+ case BM_FACE: totf++; break;
+ }
+
+ BMO_elem_flag_enable(bm, (BMElemF *)h, ELE_NEW);
+ }
+
+ /* --- Support for Special Case ---
+ * where there is a contiguous edge ring with one isolated vertex.
+ *
+ * This example shows 2 edges created from 3 verts
+ * with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
+ *
+ * note that this works for any sided shape.
+ *
+ * +--------+
+ * | .
+ * | .
+ * | .
+ * | .
+ * +........+ <-- starts out free standing.
+ *
+ */
+
+ /* Here we check for consistancy and create 2 edges */
+ if (totf == 0 && totv >= 4 && totv == tote + 2) {
+ /* find a free standing vertex and 2 endpoint verts */
+ BMVert *v_free = NULL, *v_a = NULL, *v_b = NULL;
+ int ok = TRUE;
+
+
+ BMO_ITER(v, &oiter, bm, op, "geom", BM_VERT) {
+ /* count how many flagged edges this vertex uses */
+ int tot_edges = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (BMO_elem_flag_test(bm, e, ELE_NEW)) {
+ tot_edges++;
+ if (tot_edges > 2) {
+ break;
+ }
+ }
+ }
+
+ if (tot_edges == 0) {
+ /* only accept 1 free vert */
+ if (v_free == NULL) v_free = v;
+ else ok = FALSE; /* only ever want one of these */
+ }
+ else if (tot_edges == 1) {
+ if (v_a == NULL) v_a = v;
+ else if (v_b == NULL) v_b = v;
+ else ok = FALSE; /* only ever want 2 of these */
+ }
+ else if (tot_edges == 2) {
+ /* do nothing, regular case */
+ }
+ else {
+ ok = FALSE; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
+ }
+
+ if (ok == FALSE) {
+ break;
+ }
+ }
+
+ if (ok == TRUE && v_free && v_a && v_b) {
+ e = BM_edge_create(bm, v_free, v_a, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+
+ e = BM_edge_create(bm, v_free, v_b, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ }
+ }
+ /* --- end special case support, continue as normal --- */
+
+
+ /* possible bug?, selecting 2 triangles and pressing F will make a quad rather then joining them,
+ * perhaps this should be looked into? - campbell */
+
+ /* call edgenet create */
+ /* call edgenet prepare op so additional face creation cases wor */
+ BMO_op_initf(bm, &op2, "edgenet_prepare edges=%fe", ELE_NEW);
+ BMO_op_exec(bm, &op2);
+ BMO_slot_buffer_flag_enable(bm, &op2, "edgeout", ELE_NEW, BM_EDGE);
+ BMO_op_finish(bm, &op2);
+
+ BMO_op_initf(bm, &op2, "edgenet_fill edges=%fe", ELE_NEW);
+ BMO_op_exec(bm, &op2);
+
+ /* return if edge net create did somethin */
+ if (BMO_slot_buf_count(bm, &op2, "faceout")) {
+ BMO_slot_copy(&op2, op, "faceout", "faceout");
+ BMO_op_finish(bm, &op2);
+ return;
+ }
+
+ BMO_op_finish(bm, &op2);
+
+ /* now call dissolve face */
+ BMO_op_initf(bm, &op2, "dissolvefaces faces=%ff", ELE_NEW);
+ BMO_op_exec(bm, &op2);
+
+ /* if we dissolved anything, then return */
+ if (BMO_slot_buf_count(bm, &op2, "regionout")) {
+ BMO_slot_copy(&op2, op, "regionout", "faceout");
+ BMO_op_finish(bm, &op2);
+ return;
+ }
+
+ BMO_op_finish(bm, &op2);
+
+ /* now, count how many verts we hav */
+ amount = 0;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, ELE_NEW)) {
+ verts[amount] = v;
+ amount++;
+
+ if (amount > 4) break;
+ }
+ }
+
+ if (amount == 2) {
+ /* create edg */
+ e = BM_edge_create(bm, verts[0], verts[1], NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_OUT);
+ }
+ else if (amount == 3) {
+ /* create triangl */
+ BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], NULL, NULL, TRUE);
+ }
+ else if (amount == 4) {
+ f = NULL;
+
+ /* the order of vertices can be anything, 6 cases to check */
+ if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], verts[3], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[3]->co, verts[1]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[3], verts[1], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[1]->co, verts[3]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[1], verts[3], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[3]->co, verts[2]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[3], verts[2], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[2]->co, verts[1]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[2], verts[1], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[1]->co, verts[2]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[1], verts[2], NULL, TRUE);
+ }
+ else {
+ printf("cannot find nice quad from concave set of vertices\n");
+ }
+
+ if (f) BMO_elem_flag_enable(bm, f, ELE_OUT);
+ }
+}
diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c
new file mode 100644
index 00000000000..05aead466d1
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_dissolve.c
@@ -0,0 +1,559 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#define FACE_MARK 1
+#define FACE_ORIG 2
+#define FACE_NEW 4
+#define EDGE_MARK 1
+
+#define VERT_MARK 1
+
+static int UNUSED_FUNCTION(check_hole_in_region)(BMesh *bm, BMFace *f)
+{
+ BMWalker regwalker;
+ BMIter liter2;
+ BMLoop *l2, *l3;
+ BMFace *f2;
+
+ /* checks if there are any unmarked boundary edges in the face regio */
+
+ BMW_init(&regwalker, bm, BMW_ISLAND,
+ BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
+ BMW_NIL_LAY);
+ f2 = BMW_begin(&regwalker, f);
+ for ( ; f2; f2 = BMW_step(&regwalker)) {
+ l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
+ for ( ; l2; l2 = BM_iter_step(&liter2)) {
+ l3 = bmesh_radial_nextloop(l2);
+ if ( BMO_elem_flag_test(bm, l3->f, FACE_MARK) !=
+ BMO_elem_flag_test(bm, l2->f, FACE_MARK))
+ {
+ if (!BMO_elem_flag_test(bm, l2->e, EDGE_MARK)) {
+ return FALSE;
+ }
+ }
+ }
+ }
+ BMW_end(&regwalker);
+
+ return TRUE;
+}
+
+void dissolvefaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter oiter;
+ BMFace *f, *f2 /* , *nf = NULL */;
+ BLI_array_declare(faces);
+ BLI_array_declare(regions);
+ BMFace ***regions = NULL;
+ BMFace **faces = NULL;
+ BMWalker regwalker;
+ int i;
+
+ int use_verts = BMO_slot_int_get(op, "use_verts");
+
+ if (use_verts) {
+ /* tag verts that start out with only 2 edges,
+ * don't remove these later */
+ BMIter viter;
+ BMVert *v;
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BM_vert_edge_count(v) == 2) {
+ BMO_elem_flag_disable(bm, v, VERT_MARK);
+ }
+ else {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ }
+ }
+ }
+
+ BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_MARK, BM_FACE);
+
+ /* collect region */
+ BMO_ITER(f, &oiter, bm, op, "faces", BM_FACE) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK)) continue;
+
+ BLI_array_empty(faces);
+ faces = NULL; /* forces different allocatio */
+
+ /* yay, walk */
+ BMW_init(&regwalker, bm, BMW_ISLAND,
+ BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
+ BMW_NIL_LAY);
+
+ f2 = BMW_begin(&regwalker, f);
+ for ( ; f2; f2 = BMW_step(&regwalker)) {
+ BLI_array_append(faces, f2);
+ }
+ BMW_end(&regwalker);
+
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ f2 = faces[i];
+ BMO_elem_flag_disable(bm, f2, FACE_MARK);
+ BMO_elem_flag_enable(bm, f2, FACE_ORIG);
+ }
+
+ if (BMO_error_occurred(bm)) {
+ BMO_error_clear(bm);
+ BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
+ goto cleanup;
+ }
+
+ BLI_array_append(faces, NULL);
+ BLI_array_append(regions, faces);
+ }
+
+ for (i = 0; i < BLI_array_count(regions); i++) {
+ int tot = 0;
+
+ faces = regions[i];
+ if (!faces[0]) {
+ BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
+ "Could not find boundary of dissolve region");
+ goto cleanup;
+ }
+
+ while (faces[tot])
+ tot++;
+
+ f = BM_faces_join(bm, faces, tot);
+ if (!f) {
+ BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
+ "Could not create merged face");
+ goto cleanup;
+ }
+
+ /* if making the new face failed (e.g. overlapping test)
+ * unmark the original faces for deletion */
+ BMO_elem_flag_disable(bm, f, FACE_ORIG);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+
+ }
+
+ BMO_op_callf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES);
+
+
+ if (use_verts) {
+ BMIter viter;
+ BMVert *v;
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ if (BM_vert_edge_count(v) == 2) {
+ BM_vert_collapse_edges(bm, v->e, v);
+ }
+ }
+ }
+ }
+
+ if (BMO_error_occurred(bm)) goto cleanup;
+
+ BMO_slot_from_flag(bm, op, "regionout", FACE_NEW, BM_FACE);
+
+cleanup:
+ /* free/cleanu */
+ for (i = 0; i < BLI_array_count(regions); i++) {
+ if (regions[i]) MEM_freeN(regions[i]);
+ }
+
+ BLI_array_free(regions);
+}
+
+/* almost identical to dissolve edge, except it cleans up vertice */
+void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op)
+{
+ /* BMOperator fop; */
+ BMOIter oiter;
+ BMIter iter;
+ BMVert *v, **verts = NULL;
+ BLI_array_declare(verts);
+ BMEdge *e;
+ /* BMFace *f; */
+ int i;
+
+ BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) {
+ if (BM_edge_face_count(e) == 2) {
+ BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
+ BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
+
+ BM_faces_join_pair(bm, e->l->f,
+ e->l->radial_next->f,
+ e);
+ }
+ }
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK) && BM_vert_edge_count(v) == 2) {
+ BLI_array_append(verts, v);
+ }
+ }
+
+ /* clean up extreneous 2-valence vertice */
+ for (i = 0; i < BLI_array_count(verts); i++) {
+ if (verts[i]->e) {
+ BM_vert_collapse_edges(bm, verts[i]->e, verts[i]);
+ }
+ }
+
+ BLI_array_free(verts);
+
+ //BMO_op_initf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK);
+ //BMO_op_exec(bm, &fop);
+
+ //BMO_slot_copy(op, &fop, "regionout", "regionout");
+
+ //BMO_op_finish(bm, &fop);
+}
+
+
+void dissolveedges_exec(BMesh *bm, BMOperator *op)
+{
+ /* might want to make this an option or mode - campbell */
+
+ /* BMOperator fop; */
+ BMOIter eiter;
+ BMEdge *e;
+
+ BMIter viter;
+ BMVert *v;
+
+ int use_verts = BMO_slot_int_get(op, "use_verts");
+
+ if (use_verts) {
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BM_vert_edge_count(v) == 2) {
+ BMO_elem_flag_disable(bm, v, VERT_MARK);
+ }
+ else {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ }
+ }
+ }
+
+ BMO_ITER(e, &eiter, bm, op, "edges", BM_EDGE) {
+ const int edge_face_count = BM_edge_face_count(e);
+ if (edge_face_count == 2) {
+
+ /* join faces */
+ BM_faces_join_pair(bm, e->l->f,
+ e->l->radial_next->f,
+ e);
+ }
+ }
+
+ if (use_verts) {
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ if (BM_vert_edge_count(v) == 2) {
+ BM_vert_collapse_edges(bm, v->e, v);
+ }
+ }
+ }
+ }
+}
+
+static int test_extra_verts(BMesh *bm, BMVert *v)
+{
+ BMIter iter, liter, iter2, iter3;
+ BMFace *f, *f2;
+ BMLoop *l;
+ BMEdge *e;
+ int found;
+
+ /* test faces around verts for verts that would be wronly killed
+ * by dissolve faces. */
+ f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&iter)) {
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&liter)) {
+ if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
+ /* if an edge around a vert is a boundary edge,
+ * then dissolve faces won't destroy it.
+ * also if it forms a boundary with one
+ * of the face region */
+ found = FALSE;
+ e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, l->v);
+ for ( ; e; e = BM_iter_step(&iter2)) {
+ if (BM_edge_face_count(e) == 1) {
+ found = TRUE;
+ }
+ f2 = BM_iter_new(&iter3, bm, BM_FACES_OF_EDGE, e);
+ for ( ; f2; f2 = BM_iter_step(&iter3)) {
+ if (!BMO_elem_flag_test(bm, f2, FACE_MARK)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found == TRUE) {
+ break;
+ }
+ }
+ if (found == FALSE) {
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+void dissolveverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMOpSlot *vinput;
+ BMIter iter, fiter;
+ BMVert *v;
+ BMFace *f;
+ /* int i; */
+
+ vinput = BMO_slot_get(op, "verts");
+ BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_MARK, BM_VERT);
+
+ for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ /* check if it's a two-valence ver */
+ if (BM_vert_edge_count(v) == 2) {
+
+ /* collapse the ver */
+ BM_vert_collapse_faces(bm, v->e, v, 1.0f, TRUE);
+ continue;
+ }
+
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&fiter)) {
+ BMO_elem_flag_enable(bm, f, FACE_ORIG);
+ BMO_elem_flag_enable(bm, f, FACE_MARK);
+ }
+
+ /* check if our additions to the input to face dissolve
+ * will destroy nonmarked vertices. */
+ if (!test_extra_verts(bm, v)) {
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&fiter)) {
+ if (BMO_elem_flag_test(bm, f, FACE_ORIG)) {
+ BMO_elem_flag_disable(bm, f, FACE_MARK);
+ BMO_elem_flag_disable(bm, f, FACE_ORIG);
+ }
+ }
+ }
+ else {
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&fiter)) {
+ BMO_elem_flag_disable(bm, f, FACE_ORIG);
+ }
+ }
+ }
+ }
+
+ BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_MARK);
+ if (BMO_error_occurred(bm)) {
+ const char *msg;
+
+ BMO_error_get(bm, &msg, NULL);
+ BMO_error_clear(bm);
+ BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg);
+ }
+
+ /* clean up any remainin */
+ for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ if (!BM_vert_dissolve(bm, v)) {
+ BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL);
+ return;
+ }
+ }
+ }
+
+}
+
+/* this code is for cleaning up two-edged faces, it shall become
+ * it's own function one day */
+#if 0
+void dummy_exec(BMesh *bm, BMOperator *op)
+{
+ {
+ /* clean up two-edged face */
+ /* basic idea is to keep joining 2-edged faces until their
+ * gone. this however relies on joining two 2-edged faces
+ * together to work, which doesn't */
+ found3 = 1;
+ while (found3) {
+ found3 = 0;
+ for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
+ if (!BM_face_validate(bm, f, stderr)) {
+ printf("error.\n");
+ }
+
+ if (f->len == 2) {
+ //this design relies on join faces working
+ //with two-edged faces properly.
+ //commenting this line disables the
+ //outermost loop.
+ //found3 = 1;
+ found2 = 0;
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ fe = l->e;
+ for ( ; l; l = BM_iter_step(&liter)) {
+ f2 = BM_iter_new(&fiter, bm,
+ BM_FACES_OF_EDGE, l->e);
+ for ( ; f2; f2 = BM_iter_step(&fiter)) {
+ if (f2 != f) {
+ BM_faces_join_pair(bm, f, f2, l->e);
+ found2 = 1;
+ break;
+ }
+ }
+ if (found2) break;
+ }
+
+ if (!found2) {
+ bmesh_kf(bm, f);
+ bmesh_ke(bm, fe);
+ }
+ } /* else if (f->len == 3) {
+ BMEdge *ed[3];
+ BMVert *vt[3];
+ BMLoop *lp[3];
+ int i = 0;
+
+ //check for duplicate edges
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&liter)) {
+ ed[i] = l->e;
+ lp[i] = l;
+ vt[i++] = l->v;
+ }
+ if (vt[0] == vt[1] || vt[0] == vt[2]) {
+ i += 1;
+ }
+ */
+ }
+ }
+ if (oldlen == len) break;
+ oldlen = len;
+ }
+}
+
+#endif
+
+/**/
+typedef struct DissolveElemWeight_t {
+ BMHeader *ele;
+ float weight;
+} DissolveElemWeight_t;
+
+static int dissolve_elem_cmp(const void *a1, const void *a2)
+{
+ const struct DissolveElemWeight_t *d1 = a1, *d2 = a2;
+
+ if (d1->weight > d2->weight) return 1;
+ else if (d1->weight < d2->weight) return -1;
+ return 0;
+}
+
+void dissolvelimit_exec(BMesh *bm, BMOperator *op)
+{
+ BMOpSlot *einput = BMO_slot_get(op, "edges");
+ BMOpSlot *vinput = BMO_slot_get(op, "verts");
+ const float angle_max = (float)M_PI / 2.0f;
+ const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit"));
+ DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) *
+ sizeof(DissolveElemWeight_t), __func__);
+ int i, tot_found;
+
+ /* --- first edges --- */
+
+ /* go through and split edge */
+ for (i = 0, tot_found = 0; i < einput->len; i++) {
+ BMEdge *e = ((BMEdge **)einput->data.p)[i];
+ const float angle = BM_edge_face_angle(bm, e);
+
+ if (angle < angle_limit) {
+ weight_elems[i].ele = (BMHeader *)e;
+ weight_elems[i].weight = angle;
+ tot_found++;
+ }
+ else {
+ weight_elems[i].ele = NULL;
+ weight_elems[i].weight = angle_max;
+ }
+ }
+
+ if (tot_found != 0) {
+ qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
+
+ for (i = 0; i < tot_found; i++) {
+ BMEdge *e = (BMEdge *)weight_elems[i].ele;
+ /* check twice because cumulative effect could disolve over angle limit */
+ if (BM_edge_face_angle(bm, e) < angle_limit) {
+ BMFace *nf = BM_faces_join_pair(bm, e->l->f,
+ e->l->radial_next->f,
+ e); /* join faces */
+
+ /* there may be some errors, we dont mind, just move on */
+ if (nf == NULL) {
+ BMO_error_clear(bm);
+ }
+ }
+ }
+ }
+
+ /* --- second verts --- */
+ for (i = 0, tot_found = 0; i < vinput->len; i++) {
+ BMVert *v = ((BMVert **)vinput->data.p)[i];
+ const float angle = BM_vert_edge_angle(bm, v);
+
+ if (angle < angle_limit) {
+ weight_elems[i].ele = (BMHeader *)v;
+ weight_elems[i].weight = angle;
+ tot_found++;
+ }
+ else {
+ weight_elems[i].ele = NULL;
+ weight_elems[i].weight = angle_max;
+ }
+ }
+
+ if (tot_found != 0) {
+ qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
+
+ for (i = 0; i < tot_found; i++) {
+ BMVert *v = (BMVert *)weight_elems[i].ele;
+ /* check twice because cumulative effect could disolve over angle limit */
+ if (BM_vert_edge_angle(bm, v) < angle_limit) {
+ BM_vert_collapse_edges(bm, v->e, v); /* join edges */
+ }
+ }
+ }
+
+ MEM_freeN(weight_elems);
+}
diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c
new file mode 100644
index 00000000000..2e1c91d920c
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_dupe.c
@@ -0,0 +1,512 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+
+#include "bmesh.h"
+
+/* local flag define */
+#define DUPE_INPUT 1 /* input from operato */
+#define DUPE_NEW 2
+#define DUPE_DONE 4
+#define DUPE_MAPPED 8
+
+/*
+ * COPY VERTEX
+ *
+ * Copy an existing vertex from one bmesh to another.
+ *
+ */
+static BMVert *copy_vertex(BMesh *source_mesh, BMVert *source_vertex, BMesh *target_mesh, GHash *vhash)
+{
+ BMVert *target_vertex = NULL;
+
+ /* Create a new verte */
+ target_vertex = BM_vert_create(target_mesh, source_vertex->co, NULL);
+
+ /* Insert new vertex into the vert has */
+ BLI_ghash_insert(vhash, source_vertex, target_vertex);
+
+ /* Copy attribute */
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_vertex, target_vertex);
+
+ /* Set internal op flag */
+ BMO_elem_flag_enable(target_mesh, target_vertex, DUPE_NEW);
+
+ return target_vertex;
+}
+
+/*
+ * COPY EDGE
+ *
+ * Copy an existing edge from one bmesh to another.
+ *
+ */
+static BMEdge *copy_edge(BMOperator *op, BMesh *source_mesh,
+ BMEdge *source_edge, BMesh *target_mesh,
+ GHash *vhash, GHash *ehash)
+{
+ BMEdge *target_edge = NULL;
+ BMVert *target_vert1, *target_vert2;
+ BMFace *face;
+ BMIter fiter;
+ int rlen;
+
+ /* see if any of the neighboring faces are
+ * not being duplicated. in that case,
+ * add it to the new/old map. */
+ rlen = 0;
+ for (face = BM_iter_new(&fiter, source_mesh, BM_FACES_OF_EDGE, source_edge);
+ face;
+ face = BM_iter_step(&fiter))
+ {
+ if (BMO_elem_flag_test(source_mesh, face, DUPE_INPUT)) {
+ rlen++;
+ }
+ }
+
+ /* Lookup v1 and v2 */
+ target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1);
+ target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2);
+
+ /* Create a new edg */
+ target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
+
+ /* add to new/old edge map if necassar */
+ if (rlen < 2) {
+ /* not sure what non-manifold cases of greater then three
+ * radial should do. */
+ BMO_slot_map_ptr_insert(source_mesh, op, "boundarymap",
+ source_edge, target_edge);
+ }
+
+ /* Insert new edge into the edge hash */
+ BLI_ghash_insert(ehash, source_edge, target_edge);
+
+ /* Copy attributes */
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_edge, target_edge);
+
+ /* Set internal op flags */
+ BMO_elem_flag_enable(target_mesh, target_edge, DUPE_NEW);
+
+ return target_edge;
+}
+
+/*
+ * COPY FACE
+ *
+ * Copy an existing face from one bmesh to another.
+ */
+
+static BMFace *copy_face(BMOperator *op, BMesh *source_mesh,
+ BMFace *source_face, BMesh *target_mesh,
+ BMVert **vtar, BMEdge **edar, GHash *vhash, GHash *ehash)
+{
+ /* BMVert *target_vert1, *target_vert2; */ /* UNUSED */
+ BMLoop *source_loop, *target_loop;
+ BMFace *target_face = NULL;
+ BMIter iter, iter2;
+ int i;
+
+ /* lookup the first and second vert */
+#if 0 /* UNUSED */
+ target_vert1 = BLI_ghash_lookup(vhash, BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face));
+ target_vert2 = BLI_ghash_lookup(vhash, BM_iter_step(&iter));
+#else
+ BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face);
+ BM_iter_step(&iter);
+#endif
+
+ /* lookup edge */
+ for (i = 0, source_loop = BM_iter_new(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face);
+ source_loop;
+ source_loop = BM_iter_step(&iter), i++)
+ {
+ vtar[i] = BLI_ghash_lookup(vhash, source_loop->v);
+ edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
+ }
+
+ /* create new fac */
+ target_face = BM_face_create(target_mesh, vtar, edar, source_face->len, FALSE);
+ BMO_slot_map_ptr_insert(source_mesh, op,
+ "facemap", source_face, target_face);
+ BMO_slot_map_ptr_insert(source_mesh, op,
+ "facemap", target_face, source_face);
+
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_face, target_face);
+
+ /* mark the face for outpu */
+ BMO_elem_flag_enable(target_mesh, target_face, DUPE_NEW);
+
+ /* copy per-loop custom dat */
+ BM_ITER(source_loop, &iter, source_mesh, BM_LOOPS_OF_FACE, source_face) {
+ BM_ITER(target_loop, &iter2, target_mesh, BM_LOOPS_OF_FACE, target_face) {
+ if (BLI_ghash_lookup(vhash, source_loop->v) == target_loop->v) {
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_loop, target_loop);
+ break;
+ }
+ }
+ }
+
+ return target_face;
+}
+
+/*
+ * COPY MESH
+ *
+ * Internal Copy function.
+ */
+static void copy_mesh(BMOperator *op, BMesh *source, BMesh *target)
+{
+
+ BMVert *v = NULL, *v2;
+ BMEdge *e = NULL;
+ BMFace *f = NULL;
+
+ BLI_array_declare(vtar);
+ BLI_array_declare(edar);
+ BMVert **vtar = NULL;
+ BMEdge **edar = NULL;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+
+ GHash *vhash;
+ GHash *ehash;
+
+ /* initialize pointer hashe */
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops v");
+ ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops e");
+
+ for (v = BM_iter_new(&verts, source, BM_VERTS_OF_MESH, source); v; v = BM_iter_step(&verts)) {
+ if ( BMO_elem_flag_test(source, v, DUPE_INPUT) &&
+ !BMO_elem_flag_test(source, v, DUPE_DONE))
+ {
+ BMIter iter;
+ int iso = 1;
+
+ v2 = copy_vertex(source, v, target, vhash);
+
+ BM_ITER(f, &iter, source, BM_FACES_OF_VERT, v) {
+ if (BMO_elem_flag_test(source, f, DUPE_INPUT)) {
+ iso = 0;
+ break;
+ }
+ }
+
+ if (iso) {
+ BM_ITER(e, &iter, source, BM_EDGES_OF_VERT, v) {
+ if (BMO_elem_flag_test(source, e, DUPE_INPUT)) {
+ iso = 0;
+ break;
+ }
+ }
+ }
+
+ if (iso) {
+ BMO_slot_map_ptr_insert(source, op, "isovertmap", v, v2);
+ }
+
+ BMO_elem_flag_enable(source, v, DUPE_DONE);
+ }
+ }
+
+ /* now we dupe all the edge */
+ for (e = BM_iter_new(&edges, source, BM_EDGES_OF_MESH, source); e; e = BM_iter_step(&edges)) {
+ if ( BMO_elem_flag_test(source, e, DUPE_INPUT) &&
+ !BMO_elem_flag_test(source, e, DUPE_DONE))
+ {
+ /* make sure that verts are copie */
+ if (!BMO_elem_flag_test(source, e->v1, DUPE_DONE)) {
+ copy_vertex(source, e->v1, target, vhash);
+ BMO_elem_flag_enable(source, e->v1, DUPE_DONE);
+ }
+ if (!BMO_elem_flag_test(source, e->v2, DUPE_DONE)) {
+ copy_vertex(source, e->v2, target, vhash);
+ BMO_elem_flag_enable(source, e->v2, DUPE_DONE);
+ }
+ /* now copy the actual edg */
+ copy_edge(op, source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, e, DUPE_DONE);
+ }
+ }
+
+ /* first we dupe all flagged faces and their elements from sourc */
+ for (f = BM_iter_new(&faces, source, BM_FACES_OF_MESH, source); f; f = BM_iter_step(&faces)) {
+ if (BMO_elem_flag_test(source, f, DUPE_INPUT)) {
+ /* vertex pas */
+ for (v = BM_iter_new(&verts, source, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) {
+ if (!BMO_elem_flag_test(source, v, DUPE_DONE)) {
+ copy_vertex(source, v, target, vhash);
+ BMO_elem_flag_enable(source, v, DUPE_DONE);
+ }
+ }
+
+ /* edge pas */
+ for (e = BM_iter_new(&edges, source, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) {
+ if (!BMO_elem_flag_test(source, e, DUPE_DONE)) {
+ copy_edge(op, source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, e, DUPE_DONE);
+ }
+ }
+
+ /* ensure arrays are the right size */
+ BLI_array_empty(vtar);
+ BLI_array_empty(edar);
+
+ BLI_array_growitems(vtar, f->len);
+ BLI_array_growitems(edar, f->len);
+
+ copy_face(op, source, f, target, vtar, edar, vhash, ehash);
+ BMO_elem_flag_enable(source, f, DUPE_DONE);
+ }
+ }
+
+ /* free pointer hashe */
+ BLI_ghash_free(vhash, NULL, NULL);
+ BLI_ghash_free(ehash, NULL, NULL);
+
+ BLI_array_free(vtar); /* free vert pointer array */
+ BLI_array_free(edar); /* free edge pointer array */
+}
+
+/*
+ * Duplicate Operator
+ *
+ * Duplicates verts, edges and faces of a mesh.
+ *
+ * INPUT SLOTS:
+ *
+ * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be duplicated
+ * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be duplicated
+ * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be duplicated
+ *
+ * OUTPUT SLOTS:
+ *
+ * BMOP_DUPE_VORIGINAL: Buffer containing pointers to the original mesh vertices
+ * BMOP_DUPE_EORIGINAL: Buffer containing pointers to the original mesh edges
+ * BMOP_DUPE_FORIGINAL: Buffer containing pointers to the original mesh faces
+ * BMOP_DUPE_VNEW: Buffer containing pointers to the new mesh vertices
+ * BMOP_DUPE_ENEW: Buffer containing pointers to the new mesh edges
+ * BMOP_DUPE_FNEW: Buffer containing pointers to the new mesh faces
+ *
+ */
+
+void dupeop_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator *dupeop = op;
+ BMesh *bm2 = BMO_slot_ptr_get(op, "dest");
+
+ if (!bm2)
+ bm2 = bm;
+
+ /* flag inpu */
+ BMO_slot_buffer_flag_enable(bm, dupeop, "geom", DUPE_INPUT, BM_ALL);
+
+ /* use the internal copy functio */
+ copy_mesh(dupeop, bm, bm2);
+
+ /* Outpu */
+ /* First copy the input buffers to output buffers - original dat */
+ BMO_slot_copy(dupeop, dupeop, "geom", "origout");
+
+ /* Now alloc the new output buffer */
+ BMO_slot_from_flag(bm, dupeop, "newout", DUPE_NEW, BM_ALL);
+}
+
+#if 0 /* UNUSED */
+/* executes the duplicate operation, feeding elements of
+ * type flag etypeflag and header flag flag to it. note,
+ * to get more useful information (such as the mapping from
+ * original to new elements) you should run the dupe op manually */
+void BMO_dupe_from_flag(BMesh *bm, int etypeflag, const char hflag)
+{
+ BMOperator dupeop;
+
+ BMO_op_init(bm, &dupeop, "dupe");
+ BMO_slot_from_hflag(bm, &dupeop, "geom", hflag, etypeflag);
+
+ BMO_op_exec(bm, &dupeop);
+ BMO_op_finish(bm, &dupeop);
+}
+#endif
+
+/*
+ * Split Operator
+ *
+ * Duplicates verts, edges and faces of a mesh but also deletes the originals.
+ *
+ * INPUT SLOTS:
+ *
+ * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be split
+ * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be split
+ * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be split
+ *
+ * OUTPUT SLOTS:
+ *
+ * BMOP_DUPE_VOUTPUT: Buffer containing pointers to the split mesh vertices
+ * BMOP_DUPE_EOUTPUT: Buffer containing pointers to the split mesh edges
+ * BMOP_DUPE_FOUTPUT: Buffer containing pointers to the split mesh faces
+ */
+
+#define SPLIT_INPUT 1
+void splitop_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator *splitop = op;
+ BMOperator dupeop;
+ BMOperator delop;
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f;
+ BMIter iter, iter2;
+ int found;
+
+ /* initialize our sub-operator */
+ BMO_op_init(bm, &dupeop, "dupe");
+ BMO_op_init(bm, &delop, "del");
+
+ BMO_slot_copy(splitop, &dupeop, "geom", "geom");
+ BMO_op_exec(bm, &dupeop);
+
+ BMO_slot_buffer_flag_enable(bm, splitop, "geom", SPLIT_INPUT, BM_ALL);
+
+ /* make sure to remove edges and verts we don't need */
+ for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); e; e = BM_iter_step(&iter)) {
+ found = 0;
+ f = BM_iter_new(&iter2, bm, BM_FACES_OF_EDGE, e);
+ for ( ; f; f = BM_iter_step(&iter2)) {
+ if (!BMO_elem_flag_test(bm, f, SPLIT_INPUT)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) BMO_elem_flag_enable(bm, e, SPLIT_INPUT);
+ }
+
+ for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
+ found = 0;
+ e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, v);
+ for ( ; e; e = BM_iter_step(&iter2)) {
+ if (!BMO_elem_flag_test(bm, e, SPLIT_INPUT)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) BMO_elem_flag_enable(bm, v, SPLIT_INPUT);
+
+ }
+
+ /* connect outputs of dupe to delete, exluding keep geometr */
+ BMO_slot_int_set(&delop, "context", DEL_FACES);
+ BMO_slot_from_flag(bm, &delop, "geom", SPLIT_INPUT, BM_ALL);
+
+ BMO_op_exec(bm, &delop);
+
+ /* now we make our outputs by copying the dupe output */
+ BMO_slot_copy(&dupeop, splitop, "newout", "geomout");
+ BMO_slot_copy(&dupeop, splitop, "boundarymap",
+ "boundarymap");
+ BMO_slot_copy(&dupeop, splitop, "isovertmap",
+ "isovertmap");
+
+ /* cleanu */
+ BMO_op_finish(bm, &delop);
+ BMO_op_finish(bm, &dupeop);
+}
+
+
+void delop_exec(BMesh *bm, BMOperator *op)
+{
+#define DEL_INPUT 1
+
+ BMOperator *delop = op;
+
+ /* Mark Buffer */
+ BMO_slot_buffer_flag_enable(bm, delop, "geom", DEL_INPUT, BM_ALL);
+
+ BMO_remove_tagged_context(bm, DEL_INPUT, BMO_slot_int_get(op, "context"));
+
+#undef DEL_INPUT
+}
+
+/*
+ * Spin Operator
+ *
+ * Extrude or duplicate geometry a number of times,
+ * rotating and possibly translating after each step
+ */
+
+void spinop_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator dupop, extop;
+ float cent[3], dvec[3];
+ float axis[3] = {0.0f, 0.0f, 1.0f};
+ float q[4];
+ float rmat[3][3];
+ float phi, si;
+ int steps, dupli, a, usedvec;
+
+ BMO_slot_vec_get(op, "cent", cent);
+ BMO_slot_vec_get(op, "axis", axis);
+ normalize_v3(axis);
+ BMO_slot_vec_get(op, "dvec", dvec);
+ usedvec = !is_zero_v3(dvec);
+ steps = BMO_slot_int_get(op, "steps");
+ phi = BMO_slot_float_get(op, "ang") * (float)M_PI / (360.0f * steps);
+ dupli = BMO_slot_int_get(op, "dupli");
+
+ si = (float)sin(phi);
+ q[0] = (float)cos(phi);
+ q[1] = axis[0]*si;
+ q[2] = axis[1]*si;
+ q[3] = axis[2]*si;
+ quat_to_mat3(rmat, q);
+
+ BMO_slot_copy(op, op, "geom", "lastout");
+ for (a = 0; a < steps; a++) {
+ if (dupli) {
+ BMO_op_initf(bm, &dupop, "dupe geom=%s", op, "lastout");
+ BMO_op_exec(bm, &dupop);
+ BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s",
+ cent, rmat, &dupop, "newout");
+ BMO_slot_copy(&dupop, op, "newout", "lastout");
+ BMO_op_finish(bm, &dupop);
+ }
+ else {
+ BMO_op_initf(bm, &extop, "extrudefaceregion edgefacein=%s",
+ op, "lastout");
+ BMO_op_exec(bm, &extop);
+ BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s",
+ cent, rmat, &extop, "geomout");
+ BMO_slot_copy(&extop, op, "geomout", "lastout");
+ BMO_op_finish(bm, &extop);
+ }
+
+ if (usedvec)
+ BMO_op_callf(bm, "translate vec=%v verts=%s", dvec, op, "lastout");
+ }
+}
diff --git a/source/blender/bmesh/operators/bmo_edgesplit.c b/source/blender/bmesh/operators/bmo_edgesplit.c
new file mode 100644
index 00000000000..7d719e1ddc9
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_edgesplit.c
@@ -0,0 +1,426 @@
+/*
+ * ***** 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
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+typedef struct EdgeTag {
+ BMVert *newv1, *newv2;
+ BMEdge *newe1, *newe2;
+ int tag;
+} EdgeTag;
+
+/* (EDGE_DEL == FACE_DEL) - this must be the case */
+#define EDGE_DEL 1
+#define EDGE_SEAM 2
+#define EDGE_MARK 4
+#define EDGE_RET1 8
+#define EDGE_RET2 16
+
+#define FACE_DEL 1
+#define FACE_NEW 2
+
+static BMFace *remake_face(BMesh *bm, EdgeTag *etags, BMFace *f, BMVert **verts, BMEdge **edges_tmp)
+{
+ BMIter liter1, liter2;
+ EdgeTag *et;
+ BMFace *f2;
+ BMLoop *l, *l2;
+ BMEdge *e;
+ BMVert *lastv1, *lastv2 /* , *v1, *v2 */ /* UNUSED */;
+ int i;
+
+ /* we do final edge last */
+ lastv1 = verts[f->len - 1];
+ lastv2 = verts[0];
+ /* v1 = verts[0]; */ /* UNUSED */
+ /* v2 = verts[1]; */ /* UNUSED */
+ for (i = 0; i < f->len - 1; i++) {
+ e = BM_edge_create(bm, verts[i], verts[i + 1], NULL, TRUE);
+ if (!e) {
+ return NULL;
+ }
+ edges_tmp[i] = e;
+ }
+
+ edges_tmp[i] = BM_edge_create(bm, lastv1, lastv2, NULL, TRUE);
+
+ f2 = BM_face_create(bm, verts, edges_tmp, f->len, FALSE);
+ if (!f2) {
+ return NULL;
+ }
+
+ BM_elem_attrs_copy(bm, bm, f, f2);
+
+ l = BM_iter_new(&liter1, bm, BM_LOOPS_OF_FACE, f);
+ l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
+ for ( ; l && l2; l = BM_iter_step(&liter1), l2 = BM_iter_step(&liter2)) {
+ BM_elem_attrs_copy(bm, bm, l, l2);
+ if (l->e != l2->e) {
+ /* set up data for figuring out the two sides of
+ * the split */
+
+ /* set edges index as dirty after running all */
+ BM_elem_index_set(l2->e, BM_elem_index_get(l->e)); /* set_dirty! */
+ et = &etags[BM_elem_index_get(l->e)];
+
+ if (!et->newe1) {
+ et->newe1 = l2->e;
+ }
+ else if (!et->newe2) {
+ et->newe2 = l2->e;
+ }
+ else {
+ /* Only two new edges should be created from each original edge
+ * for edge split operation */
+
+ //BLI_assert(et->newe1 == l2->e || et->newe2 == l2->e);
+ et->newe2 = l2->e;
+ }
+
+ if (BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
+ BMO_elem_flag_enable(bm, l2->e, EDGE_SEAM);
+ }
+
+ BM_elem_attrs_copy(bm, bm, l->e, l2->e);
+ }
+
+ BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
+ BMO_elem_flag_enable(bm, l2->e, EDGE_MARK);
+ }
+
+ return f2;
+}
+
+static void tag_out_edges(BMesh *bm, EdgeTag *etags, BMOperator *UNUSED(op))
+{
+ EdgeTag *et;
+ BMIter iter;
+ BMLoop *l, *startl;
+ BMEdge *e;
+ BMVert *v;
+ int i, ok;
+
+ ok = 0;
+ while (ok++ < 100000) {
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_SEAM))
+ continue;
+
+ et = &etags[BM_elem_index_get(e)];
+ if (!et->tag && e->l) {
+ break;
+ }
+ }
+
+ if (!e) {
+ break;
+ }
+
+ /* ok we found an edge, part of a region of splits we need
+ * to identify. now walk along it */
+ for (i = 0; i < 2; i++) {
+ l = e->l;
+
+ v = i ? l->next->v : l->v;
+
+ while (1) {
+ et = &etags[BM_elem_index_get(l->e)];
+ if (et->newe1 == l->e) {
+ if (et->newe1) {
+ BMO_elem_flag_enable(bm, et->newe1, EDGE_RET1);
+ BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM);
+ }
+ if (et->newe2) {
+ BMO_elem_flag_enable(bm, et->newe2, EDGE_RET2);
+ BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM);
+ }
+ }
+ else {
+ if (et->newe1) {
+ BMO_elem_flag_enable(bm, et->newe1, EDGE_RET2);
+ BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM);
+ }
+ if (et->newe2) {
+ BMO_elem_flag_enable(bm, et->newe2, EDGE_RET1);
+ BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM);
+ }
+ }
+
+ /* If the original edge was non-manifold edges, then it is
+ * possible l->e is not et->newe1 or et->newe2. So always clear
+ * the flag on l->e as well, to prevent infinite looping. */
+ BMO_elem_flag_disable(bm, l->e, EDGE_SEAM);
+
+ startl = l;
+ do {
+ l = BM_face_other_loop(l->e, l->f, v);
+ if (l == startl || BM_edge_face_count(l->e) != 2) {
+ break;
+ }
+ l = l->radial_next;
+ } while (l != startl && !BMO_elem_flag_test(bm, l->e, EDGE_SEAM));
+
+ if (l == startl || !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
+ break;
+ }
+
+ v = (l->v == v) ? l->next->v : l->v;
+ }
+ }
+ }
+}
+
+void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op)
+{
+ EdgeTag *etags, *et;
+ BMIter iter, liter;
+ BMOIter siter;
+ BMFace *f, *f2;
+ BMLoop *l, *nextl, *prevl, *l2, *l3;
+ BMEdge *e, *e2;
+ BMVert *v, *v2, **verts = NULL;
+ BLI_array_declare(verts);
+ BMEdge **edges_tmp = NULL;
+ BLI_array_declare(edges_tmp);
+ int i, j;
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_SEAM, BM_EDGE);
+
+ /* single marked edges unconnected to any other marked edges
+ * are illegal, go through and unmark them */
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ for (i = 0; i < 2; i++) {
+ BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
+ if (e != e2 && BMO_elem_flag_test(bm, e2, EDGE_SEAM)) {
+ break;
+ }
+ }
+ if (e2) {
+ break;
+ }
+ }
+
+ if (!e2) {
+ BMO_elem_flag_disable(bm, e, EDGE_SEAM);
+ }
+ }
+
+ etags = MEM_callocN(sizeof(EdgeTag)*bm->totedge, "EdgeTag");
+
+ BM_mesh_elem_index_ensure(bm, BM_EDGE);
+
+#ifdef ETV
+# undef ETV
+#endif
+#ifdef SETETV
+# undef SETETV
+#endif
+
+#define ETV(et, v, l) (l->e->v1 == v ? et->newv1 : et->newv2)
+#define SETETV(et, v, l, vs) l->e->v1 == v ? (et->newv1 = vs) : (et->newv2 = vs)
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+
+ if (BMO_elem_flag_test(bm, f, FACE_NEW)) {
+ continue;
+ }
+
+ BLI_array_empty(verts);
+ BLI_array_growitems(verts, f->len);
+ memset(verts, 0, sizeof(BMVert *) * f->len);
+
+ /* this is passed onto remake_face() so it doesnt need to allocate
+ * a new array on each call. */
+ BLI_array_empty(edges_tmp);
+ BLI_array_growitems(edges_tmp, f->len);
+
+ i = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (!BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
+ if (!verts[i]) {
+
+ et = &etags[BM_elem_index_get(l->e)];
+ if (ETV(et, l->v, l)) {
+ verts[i] = ETV(et, l->v, l);
+ }
+ else
+ {
+ verts[i] = l->v;
+ }
+ }
+ i++;
+ continue;
+ }
+
+ nextl = l->next;
+ prevl = l->prev;
+
+ for (j = 0; j < 2; j++) {
+ /* correct as long as i & j dont change during the loop */
+ const int fv_index = j ? (i + 1) % f->len : i; /* face vert index */
+ l2 = j ? nextl : prevl;
+ v = j ? l2->v : l->v;
+
+ if (BMO_elem_flag_test(bm, l2->e, EDGE_SEAM)) {
+ if (verts[fv_index] == NULL) {
+ /* make unique vert here for this face only */
+ v2 = BM_vert_create(bm, v->co, v);
+ verts[fv_index] = v2;
+ }
+ else {
+ v2 = verts[fv_index];
+ }
+ }
+ else {
+ /* generate unique vert for non-seam edge(s)
+ * around the manifold vert fan if necassary */
+
+ /* first check that we have two seam edges
+ * somewhere within this fa */
+ l3 = l2;
+ do {
+ if (BM_edge_face_count(l3->e) != 2) {
+ /* if we hit a boundary edge, tag
+ * l3 as null so we know to disconnect
+ * it */
+ if (BM_edge_face_count(l3->e) == 1) {
+ l3 = NULL;
+ }
+ break;
+ }
+
+ l3 = l3->radial_next;
+ l3 = BM_face_other_loop(l3->e, l3->f, v);
+ } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM));
+
+ if (l3 == NULL || (BMO_elem_flag_test(bm, l3->e, EDGE_SEAM) && l3->e != l->e)) {
+ et = &etags[BM_elem_index_get(l2->e)];
+ if (ETV(et, v, l2) == NULL) {
+ v2 = BM_vert_create(bm, v->co, v);
+
+ l3 = l2;
+ do {
+ SETETV(et, v, l3, v2);
+ if (BM_edge_face_count(l3->e) != 2) {
+ break;
+ }
+
+ l3 = l3->radial_next;
+ l3 = BM_face_other_loop(l3->e, l3->f, v);
+
+ et = &etags[BM_elem_index_get(l3->e)];
+ } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM));
+ }
+ else {
+ v2 = ETV(et, v, l2);
+ }
+
+ verts[fv_index] = v2;
+ }
+ else {
+ verts[fv_index] = v;
+ }
+ }
+ }
+
+ i++;
+ }
+
+ /* debugging code, quick way to find the face/vert combination
+ * which is failing assuming quads start planer - campbell */
+#if 0
+ if (f->len == 4) {
+ float no1[3];
+ float no2[3];
+ float angle_error;
+ printf(" ** found QUAD\n");
+ normal_tri_v3(no1, verts[0]->co, verts[1]->co, verts[2]->co);
+ normal_tri_v3(no2, verts[0]->co, verts[2]->co, verts[3]->co);
+ if ((angle_error = angle_v3v3(no1, no2)) > 0.05) {
+ printf(" ERROR %.4f\n", angle_error);
+ print_v3("0", verts[0]->co);
+ print_v3("1", verts[1]->co);
+ print_v3("2", verts[2]->co);
+ print_v3("3", verts[3]->co);
+
+ }
+ }
+ else {
+ printf(" ** fount %d len face\n", f->len);
+ }
+#endif
+
+ f2 = remake_face(bm, etags, f, verts, edges_tmp);
+ if (!f2) {
+ continue;
+ }
+
+ BMO_elem_flag_enable(bm, f, FACE_DEL);
+ BMO_elem_flag_enable(bm, f2, FACE_NEW);
+ }
+
+ /* remake_face() sets invalid indecies,
+ * likely these will be corrected on operator exit anyway */
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ /* cant call the operator because 'tag_out_edges'
+ * relies on original index values, from before editing geometry */
+
+#if 0
+ BMO_op_callf(bm, "del geom=%ff context=%i", FACE_DEL, DEL_ONLYFACES);
+#else
+ BMO_remove_tagged_context(bm, FACE_DEL, DEL_ONLYFACES);
+#endif
+
+ /* test EDGE_MARK'd edges if we need to delete them, EDGE_MARK
+ * is set in remake_face */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ if (!e->l) {
+ BMO_elem_flag_enable(bm, e, EDGE_DEL);
+ }
+ }
+ }
+
+#if 0
+ BMO_op_callf(bm, "del geom=%fe context=%i", EDGE_DEL, DEL_EDGES);
+#else
+ BMO_remove_tagged_context(bm, EDGE_DEL, DEL_EDGES);
+#endif
+
+ tag_out_edges(bm, etags, op);
+ BMO_slot_from_flag(bm, op, "edgeout1", EDGE_RET1, BM_EDGE);
+ BMO_slot_from_flag(bm, op, "edgeout2", EDGE_RET2, BM_EDGE);
+
+ BLI_array_free(verts);
+ BLI_array_free(edges_tmp);
+ if (etags) MEM_freeN(etags);
+}
+
+#undef ETV
+#undef SETETV
diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c
new file mode 100644
index 00000000000..b6a87e604ec
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_extrude.c
@@ -0,0 +1,591 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#define EXT_INPUT 1
+#define EXT_KEEP 2
+#define EXT_DEL 4
+
+#define VERT_MARK 1
+#define EDGE_MARK 1
+#define FACE_MARK 1
+#define VERT_NONMAN 2
+#define EDGE_NONMAN 2
+
+void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter liter, liter2;
+ BMFace *f, *f2, *f3;
+ BMLoop *l, *l2, *l3, *l4, *l_tmp;
+ BMEdge **edges = NULL, *e, *laste;
+ BMVert *v, *lastv, *firstv;
+ BLI_array_declare(edges);
+ int i;
+
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ BLI_array_empty(edges);
+ i = 0;
+ firstv = lastv = NULL;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BLI_array_growone(edges);
+
+ v = BM_vert_create(bm, l->v->co, l->v);
+
+ if (lastv) {
+ e = BM_edge_create(bm, lastv, v, l->e, FALSE);
+ edges[i++] = e;
+ }
+
+ lastv = v;
+ laste = l->e;
+ if (!firstv) firstv = v;
+ }
+
+ BLI_array_growone(edges);
+ e = BM_edge_create(bm, v, firstv, laste, FALSE);
+ edges[i++] = e;
+
+ BMO_elem_flag_enable(bm, f, EXT_DEL);
+
+ f2 = BM_face_create_ngon(bm, firstv, BM_edge_other_vert(edges[0], firstv), edges, f->len, FALSE);
+ if (!f2) {
+ BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Extrude failed; could not create face");
+ BLI_array_free(edges);
+ return;
+ }
+
+ BMO_elem_flag_enable(bm, f2, EXT_KEEP);
+ BM_elem_attrs_copy(bm, bm, f, f2);
+
+ l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_elem_attrs_copy(bm, bm, l, l2);
+
+ l3 = l->next;
+ l4 = l2->next;
+
+ f3 = BM_face_create_quad_tri(bm, l3->v, l4->v, l2->v, l->v, f, FALSE);
+
+ l_tmp = BM_FACE_FIRST_LOOP(f3);
+
+ BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next;
+ BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next;
+ BM_elem_attrs_copy(bm, bm, l, l_tmp); l_tmp = l_tmp->next;
+ BM_elem_attrs_copy(bm, bm, l, l_tmp);
+
+ l2 = BM_iter_step(&liter2);
+ }
+ }
+
+ BLI_array_free(edges);
+
+ BMO_op_callf(bm, "del geom=%ff context=%d", EXT_DEL, DEL_ONLYFACES);
+ BMO_slot_from_flag(bm, op, "faceout", EXT_KEEP, BM_FACE);
+}
+
+void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMOperator dupeop;
+ BMVert *v1, *v2, *v3, *v4;
+ BMEdge *e, *e2;
+ BMFace *f;
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ BMO_elem_flag_enable(bm, e, EXT_INPUT);
+ BMO_elem_flag_enable(bm, e->v1, EXT_INPUT);
+ BMO_elem_flag_enable(bm, e->v2, EXT_INPUT);
+ }
+
+ BMO_op_initf(bm, &dupeop, "dupe geom=%fve", EXT_INPUT);
+ BMO_op_exec(bm, &dupeop);
+
+ e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0);
+ for ( ; e; e = BMO_iter_step(&siter)) {
+ e2 = BMO_iter_map_value(&siter);
+ e2 = *(BMEdge **)e2;
+
+ if (e->l && e->v1 != e->l->v) {
+ v1 = e->v1;
+ v2 = e->v2;
+ v3 = e2->v2;
+ v4 = e2->v1;
+ }
+ else {
+ v1 = e2->v1;
+ v2 = e2->v2;
+ v3 = e->v2;
+ v4 = e->v1;
+ }
+ /* not sure what to do about example face, pass NULL for now */
+ f = BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
+
+ if (BMO_elem_flag_test(bm, e, EXT_INPUT))
+ e = e2;
+
+ BMO_elem_flag_enable(bm, f, EXT_KEEP);
+ BMO_elem_flag_enable(bm, e, EXT_KEEP);
+ BMO_elem_flag_enable(bm, e->v1, EXT_KEEP);
+ BMO_elem_flag_enable(bm, e->v2, EXT_KEEP);
+
+ }
+
+ BMO_op_finish(bm, &dupeop);
+
+ BMO_slot_from_flag(bm, op, "geomout", EXT_KEEP, BM_ALL);
+}
+
+void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMVert *v, *dupev;
+ BMEdge *e;
+
+ v = BMO_iter_new(&siter, bm, op, "verts", BM_VERT);
+ for ( ; v; v = BMO_iter_step(&siter)) {
+ dupev = BM_vert_create(bm, v->co, v);
+
+ e = BM_edge_create(bm, v, dupev, NULL, FALSE);
+
+ BMO_elem_flag_enable(bm, e, EXT_KEEP);
+ BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", EXT_KEEP, BM_VERT);
+ BMO_slot_from_flag(bm, op, "edgeout", EXT_KEEP, BM_EDGE);
+}
+
+void extrude_edge_context_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator dupeop, delop;
+ BMOIter siter;
+ BMIter iter, fiter, viter;
+ BMEdge *e, *newedge;
+ BMLoop *l, *l2;
+ BMVert *verts[4], *v, *v2;
+ BMFace *f;
+ int rlen, found, fwd, delorig = 0;
+
+ /* initialize our sub-operators */
+ BMO_op_init(bm, &dupeop, "dupe");
+
+ BMO_slot_buffer_flag_enable(bm, op, "edgefacein", EXT_INPUT, BM_EDGE|BM_FACE);
+
+ /* if one flagged face is bordered by an unflagged face, then we delete
+ * original geometry unless caller explicitly asked to keep it. */
+ if (!BMO_slot_int_get(op, "alwayskeeporig")) {
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) continue;
+
+ found = 0;
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_EDGE, e);
+ for (rlen = 0; f; f = BM_iter_step(&fiter), rlen++) {
+ if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
+ found = 1;
+ delorig = 1;
+ break;
+ }
+ }
+
+ if (!found && (rlen > 1)) BMO_elem_flag_enable(bm, e, EXT_DEL);
+ }
+ }
+
+ /* calculate verts to delet */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ found = 0;
+
+ BM_ITER(e, &viter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) {
+ found = 1;
+ break;
+ }
+ }
+
+ BM_ITER(f, &viter, bm, BM_FACES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ BMO_elem_flag_enable(bm, v, EXT_DEL);
+ }
+ }
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, f, EXT_INPUT))
+ BMO_elem_flag_enable(bm, f, EXT_DEL);
+ }
+
+ if (delorig) {
+ BMO_op_initf(bm, &delop, "del geom=%fvef context=%d",
+ EXT_DEL, DEL_ONLYTAGGED);
+ }
+
+ BMO_slot_copy(op, &dupeop, "edgefacein", "geom");
+ BMO_op_exec(bm, &dupeop);
+
+ if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT))
+ bm->act_face = BMO_slot_map_ptr_get(bm, &dupeop, "facemap", bm->act_face);
+
+ if (delorig) BMO_op_exec(bm, &delop);
+
+ /* if not delorig, reverse loops of original face */
+ if (!delorig) {
+ for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
+ if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
+ BM_face_normal_flip(bm, f);
+ }
+ }
+ }
+
+ BMO_slot_copy(&dupeop, op, "newout", "geomout");
+ e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0);
+ for ( ; e; e = BMO_iter_step(&siter)) {
+ if (BMO_slot_map_contains(bm, op, "exclude", e)) continue;
+
+ newedge = BMO_iter_map_value(&siter);
+ newedge = *(BMEdge **)newedge;
+ if (!newedge) continue;
+
+ /* orient loop to give same normal as a loop of newedge
+ * if it exists (will be an extruded face),
+ * else same normal as a loop of e, if it exists */
+ if (!newedge->l)
+ fwd = !e->l || !(e->l->v == e->v1);
+ else
+ fwd = (newedge->l->v == newedge->v1);
+
+
+ if (fwd) {
+ verts[0] = e->v1;
+ verts[1] = e->v2;
+ verts[2] = newedge->v2;
+ verts[3] = newedge->v1;
+ }
+ else {
+ verts[3] = e->v1;
+ verts[2] = e->v2;
+ verts[1] = newedge->v2;
+ verts[0] = newedge->v1;
+ }
+
+ /* not sure what to do about example face, pass NULL for now */
+ f = BM_face_create_quad_tri_v(bm, verts, 4, NULL, FALSE);
+
+ /* copy attribute */
+ l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&iter)) {
+ if (l->e != e && l->e != newedge) continue;
+ l2 = l->radial_next;
+
+ if (l2 == l) {
+ l2 = newedge->l;
+ BM_elem_attrs_copy(bm, bm, l2->f, l->f);
+
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ l2 = l2->next;
+ l = l->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ }
+ else {
+ BM_elem_attrs_copy(bm, bm, l2->f, l->f);
+
+ /* copy dat */
+ if (l2->v == l->v) {
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ l2 = l2->next;
+ l = l->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ }
+ else {
+ l2 = l2->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ l2 = l2->prev;
+ l = l->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ }
+ }
+ }
+ }
+
+ /* link isolated vert */
+ v = BMO_iter_new(&siter, bm, &dupeop, "isovertmap", 0);
+ for ( ; v; v = BMO_iter_step(&siter)) {
+ v2 = *((void **)BMO_iter_map_value(&siter));
+ BM_edge_create(bm, v, v2, v->e, TRUE);
+ }
+
+ /* cleanu */
+ if (delorig) BMO_op_finish(bm, &delop);
+ BMO_op_finish(bm, &dupeop);
+}
+
+/*
+ * Compute higher-quality vertex normals used by solidify.
+ * Only considers geometry in the marked solidify region.
+ * Note that this does not work so well for non-manifold
+ * regions.
+ */
+static void calc_solidify_normals(BMesh *bm)
+{
+ BMIter viter, eiter, fiter;
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f, *f1, *f2;
+ float edge_normal[3];
+ int i;
+
+ /* can't use BM_edge_face_count because we need to count only marked faces */
+ int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__);
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ }
+
+ BM_mesh_elem_index_ensure(bm, BM_EDGE);
+
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ continue;
+ }
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_FACE, f) {
+
+ /* And mark all edges and vertices on the
+ * marked faces */
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
+ BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
+ edge_face_count[BM_elem_index_get(e)]++;
+ }
+ }
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ i = edge_face_count[BM_elem_index_get(e)]++;
+
+ if (i == 0 || i > 2) {
+ /* Edge & vertices are non-manifold even when considering
+ * only marked faces */
+ BMO_elem_flag_enable(bm, e, EDGE_NONMAN);
+ BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN);
+ BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN);
+ }
+ }
+ MEM_freeN(edge_face_count);
+ edge_face_count = NULL; /* dont re-use */
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (!BM_vert_is_manifold(bm, v)) {
+ BMO_elem_flag_enable(bm, v, VERT_NONMAN);
+ continue;
+ }
+
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ zero_v3(v->no);
+ }
+ }
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
+
+ /* If the edge is not part of a the solidify region
+ * its normal should not be considered */
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ /* If the edge joins more than two marked faces high
+ * quality normal computation won't work */
+ if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) {
+ continue;
+ }
+
+ f1 = f2 = NULL;
+
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
+ if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ if (f1 == NULL) {
+ f1 = f;
+ }
+ else {
+ BLI_assert(f2 == NULL);
+ f2 = f;
+ }
+ }
+ }
+
+ BLI_assert(f1 != NULL);
+
+ if (f2 != NULL) {
+ const float angle = angle_normalized_v3v3(f1->no, f2->no);
+
+ if (angle > 0.0f) {
+ /* two faces using this edge, calculate the edge normal
+ * using the angle between the faces as a weighting */
+ add_v3_v3v3(edge_normal, f1->no, f2->no);
+ normalize_v3(edge_normal);
+ mul_v3_fl(edge_normal, angle);
+ }
+ else {
+ /* can't do anything useful here!
+ * Set the face index for a vert incase it gets a zero normal */
+ BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
+ BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
+ continue;
+ }
+ }
+ else {
+ /* only one face attached to that edge */
+ /* an edge without another attached- the weight on this is
+ * undefined, M_PI / 2 is 90d in radians and that seems good enough */
+ copy_v3_v3(edge_normal, f1->no);
+ mul_v3_fl(edge_normal, M_PI / 2);
+ }
+
+ add_v3_v3(e->v1->no, edge_normal);
+ add_v3_v3(e->v2->no, edge_normal);
+ }
+
+ /* normalize accumulated vertex normal */
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ continue;
+ }
+
+ if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) {
+ /* use standard normals for vertices connected to non-manifold edges */
+ BM_vert_normal_update(bm, v);
+ }
+ else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ /* exceptional case, totally flat. use the normal
+ * of any marked face around the vertex */
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_VERT, v) {
+ if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ break;
+ }
+ }
+ copy_v3_v3(v->no, f->no);
+ }
+ }
+}
+
+static void solidify_add_thickness(BMesh *bm, float dist)
+{
+ BMFace *f;
+ BMVert *v;
+ BMLoop *l;
+ BMIter iter, loopIter;
+ float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */
+ float *vert_accum = vert_angles + bm->totvert;
+ float angle;
+ int i, index;
+ float maxdist = dist * sqrtf(3.0f);
+
+ /* array for passing verts to angle_poly_v3 */
+ float **verts = NULL;
+ BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
+ /* array for receiving angles from angle_poly_v3 */
+ float *angles = NULL;
+ BLI_array_staticdeclare(angles, BM_NGON_STACK_SIZE);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ continue;
+ }
+
+ BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
+ BLI_array_append(verts, l->v->co);
+ BLI_array_growone(angles);
+ }
+
+ angle_poly_v3(angles, (const float **)verts, f->len);
+
+ i = 0;
+ BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
+ v = l->v;
+ index = BM_elem_index_get(v);
+ angle = angles[i];
+ vert_accum[index] += angle;
+ vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * angle;
+ i++;
+ }
+
+ BLI_array_empty(verts);
+ BLI_array_empty(angles);
+ }
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ index = BM_elem_index_get(v);
+ if (vert_accum[index]) { /* zero if unselected */
+ float vdist = MIN2(maxdist, dist * vert_angles[index] / vert_accum[index]);
+ madd_v3_v3fl(v->co, v->no, vdist);
+ }
+ }
+
+ MEM_freeN(vert_angles);
+}
+
+void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator extrudeop;
+ BMOperator reverseop;
+ float thickness;
+
+ thickness = BMO_slot_float_get(op, "thickness");
+
+ /* Flip original faces (so the shell is extruded inward) */
+ BMO_op_init(bm, &reverseop, "reversefaces");
+ BMO_slot_copy(op, &reverseop, "geom", "faces");
+ BMO_op_exec(bm, &reverseop);
+ BMO_op_finish(bm, &reverseop);
+
+ /* Extrude the region */
+ BMO_op_initf(bm, &extrudeop, "extrudefaceregion alwayskeeporig=%i", TRUE);
+ BMO_slot_copy(op, &extrudeop, "geom", "edgefacein");
+ BMO_op_exec(bm, &extrudeop);
+
+ /* Push the verts of the extruded faces inward to create thickness */
+ BMO_slot_buffer_flag_enable(bm, &extrudeop, "geomout", FACE_MARK, BM_FACE);
+ calc_solidify_normals(bm);
+ solidify_add_thickness(bm, thickness);
+
+ BMO_slot_copy(&extrudeop, op, "geomout", "geomout");
+
+ BMO_op_finish(bm, &extrudeop);
+}
diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c
new file mode 100644
index 00000000000..921c579447b
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_join_triangles.c
@@ -0,0 +1,373 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+/*
+ * JOIN_TRIANGLES.C
+ *
+ * utility bmesh operators, e.g. transform,
+ * translate, rotate, scale, etc.
+ *
+ */
+
+/* Bitflags for edges */
+#define T2QDELETE 1
+#define T2QCOMPLEX 2
+#define T2QJOIN 4
+
+/* assumes edges are validated before reaching this poin */
+static float measure_facepair(BMesh *UNUSED(bm), BMVert *v1, BMVert *v2,
+ BMVert *v3, BMVert *v4, float limit)
+{
+ /* gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would mak */
+ /* Note: this is more complicated than it needs to be and should be cleaned up.. */
+ float n1[3], n2[3], measure = 0.0f, angle1, angle2, diff;
+ float edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3];
+ float minarea, maxarea, areaA, areaB;
+
+ /* First Test: Normal differenc */
+ normal_tri_v3(n1, v1->co, v2->co, v3->co);
+ normal_tri_v3(n2, v1->co, v3->co, v4->co);
+
+ if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle1 = 0.0f;
+ else angle1 = angle_v3v3(n1, n2);
+
+ normal_tri_v3(n1, v2->co, v3->co, v4->co);
+ normal_tri_v3(n2, v4->co, v1->co, v2->co);
+
+ if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle2 = 0.0f;
+ else angle2 = angle_v3v3(n1, n2);
+
+ measure += (angle1 + angle2) * 0.5f;
+ if (measure > limit) {
+ return measure;
+ }
+
+ /* Second test: Colinearit */
+ sub_v3_v3v3(edgeVec1, v1->co, v2->co);
+ sub_v3_v3v3(edgeVec2, v2->co, v3->co);
+ sub_v3_v3v3(edgeVec3, v3->co, v4->co);
+ sub_v3_v3v3(edgeVec4, v4->co, v1->co);
+
+ /* a completely skinny face is 'pi' after halving */
+ diff = 0.25f * (fabsf(angle_v3v3(edgeVec1, edgeVec2) - (float)M_PI_2) +
+ fabsf(angle_v3v3(edgeVec2, edgeVec3) - (float)M_PI_2) +
+ fabsf(angle_v3v3(edgeVec3, edgeVec4) - (float)M_PI_2) +
+ fabsf(angle_v3v3(edgeVec4, edgeVec1) - (float)M_PI_2));
+
+ if (!diff) {
+ return 0.0;
+ }
+
+ measure += diff;
+ if (measure > limit) {
+ return measure;
+ }
+
+ /* Third test: Concavit */
+ areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co);
+ areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co);
+
+ if (areaA <= areaB) minarea = areaA;
+ else minarea = areaB;
+
+ if (areaA >= areaB) maxarea = areaA;
+ else maxarea = areaB;
+
+ if (!maxarea) measure += 1;
+ else measure += (1 - (minarea / maxarea));
+
+ return measure;
+}
+
+#define T2QUV_LIMIT 0.005f
+#define T2QCOL_LIMIT 3
+
+static int compareFaceAttribs(BMesh *bm, BMEdge *e, int douvs, int dovcols)
+{
+ MTexPoly *tp1, *tp2;
+ MLoopCol *lcol1, *lcol2, *lcol3, *lcol4;
+ MLoopUV *luv1, *luv2, *luv3, *luv4;
+ BMLoop *l1, *l2, *l3, *l4;
+ int mergeok_uvs = !douvs, mergeok_vcols = !dovcols;
+
+ l1 = e->l;
+ l3 = e->l->radial_next;
+
+ /* match up loops on each side of an edge corrusponding to each ver */
+ if (l1->v == l3->v) {
+ l2 = l1->next;
+ l4 = l2->next;
+ }
+ else {
+ l2 = l1->next;
+
+ l4 = l3;
+ l3 = l4->next;
+ }
+
+ lcol1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+ lcol2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+ lcol3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+ lcol4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+
+ luv1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+ luv2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+ luv3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+ luv4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+
+ tp1 = CustomData_bmesh_get(&bm->pdata, l1->f->head.data, CD_MTEXPOLY);
+ tp2 = CustomData_bmesh_get(&bm->pdata, l2->f->head.data, CD_MTEXPOLY);
+
+ if (!lcol1)
+ mergeok_vcols = 1;
+
+ if (!luv1)
+ mergeok_uvs = 1;
+
+ /* compare faceedges for each face attribute. Additional per face attributes can be added late */
+
+ /* do VCOL */
+ if (lcol1 && dovcols) {
+ char *cols[4] = {(char *)lcol1, (char *)lcol2, (char *)lcol3, (char *)lcol4};
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (cols[0][i] + T2QCOL_LIMIT < cols[2][i] - T2QCOL_LIMIT)
+ break;
+ if (cols[1][i] + T2QCOL_LIMIT < cols[3][i] - T2QCOL_LIMIT)
+ break;
+ }
+
+ if (i == 3)
+ mergeok_vcols = 1;
+ }
+
+ /* do UV */
+ if (luv1 && douvs) {
+ if (tp1->tpage != tp2->tpage); /* do nothin */
+ else {
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (luv1->uv[0] + T2QUV_LIMIT > luv3->uv[0] && luv1->uv[0] - T2QUV_LIMIT < luv3->uv[0] &&
+ luv1->uv[1] + T2QUV_LIMIT > luv3->uv[1] && luv1->uv[1] - T2QUV_LIMIT < luv3->uv[1])
+ {
+ if (luv2->uv[0] + T2QUV_LIMIT > luv4->uv[0] && luv2->uv[0] - T2QUV_LIMIT < luv4->uv[0] &&
+ luv2->uv[1] + T2QUV_LIMIT > luv4->uv[1] && luv2->uv[1] - T2QUV_LIMIT < luv4->uv[1])
+ {
+ mergeok_uvs = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (douvs == mergeok_uvs && dovcols == mergeok_vcols) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+typedef struct JoinEdge {
+ float weight;
+ BMEdge *e;
+} JoinEdge;
+
+#define EDGE_MARK 1
+#define EDGE_CHOSEN 2
+
+#define FACE_MARK 1
+#define FACE_INPUT 2
+
+static int fplcmp(const void *v1, const void *v2)
+{
+ const JoinEdge *e1 = (JoinEdge *)v1, *e2 = (JoinEdge *)v2;
+
+ if (e1->weight > e2->weight) return 1;
+ else if (e1->weight < e2->weight) return -1;
+
+ return 0;
+}
+
+void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter, liter;
+ BMOIter siter;
+ BMFace *f1, *f2;
+ BMLoop *l;
+ BMEdge *e;
+ BLI_array_declare(jedges);
+ JoinEdge *jedges = NULL;
+ int dosharp = BMO_slot_int_get(op, "compare_sharp"), douvs = BMO_slot_int_get(op, "compare_uvs");
+ int dovcols = BMO_slot_int_get(op, "compare_vcols"), domat = BMO_slot_int_get(op, "compare_materials");
+ float limit = BMO_slot_float_get(op, "limit");
+ int i, totedge;
+
+ /* flag all edges of all input face */
+ BMO_ITER(f1, &siter, bm, op, "faces", BM_FACE) {
+ BMO_elem_flag_enable(bm, f1, FACE_INPUT);
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f1) {
+ BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
+ }
+ }
+
+ /* unflag edges that are invalid; e.g. aren't surrounded by triangle */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
+ continue;
+
+ if (BM_edge_face_count(e) != 2) {
+ BMO_elem_flag_disable(bm, e, EDGE_MARK);
+ continue;
+ }
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ if (f1->len != 3 || f2->len != 3) {
+ BMO_elem_flag_disable(bm, e, EDGE_MARK);
+ continue;
+ }
+
+ if (!BMO_elem_flag_test(bm, f1, FACE_INPUT) || !BMO_elem_flag_test(bm, f2, FACE_INPUT)) {
+ BMO_elem_flag_disable(bm, e, EDGE_MARK);
+ continue;
+ }
+ }
+
+ i = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMVert *v1, *v2, *v3, *v4;
+ BMFace *f1, *f2;
+ float measure;
+
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
+ continue;
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ v1 = e->l->v;
+ v2 = e->l->prev->v;
+ v3 = e->l->next->v;
+ v4 = e->l->radial_next->prev->v;
+
+ if (dosharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH))
+ continue;
+
+ if ((douvs || dovcols) && compareFaceAttribs(bm, e, douvs, dovcols))
+ continue;
+
+ if (domat && f1->mat_nr != f2->mat_nr)
+ continue;
+
+ measure = measure_facepair(bm, v1, v2, v3, v4, limit);
+ if (measure < limit) {
+ BLI_array_growone(jedges);
+
+ jedges[i].e = e;
+ jedges[i].weight = measure;
+
+ i++;
+ }
+ }
+
+ if (!jedges)
+ return;
+
+ qsort(jedges, BLI_array_count(jedges), sizeof(JoinEdge), fplcmp);
+
+ totedge = BLI_array_count(jedges);
+ for (i = 0; i < totedge; i++) {
+ BMFace *f1, *f2;
+
+ e = jedges[i].e;
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ if (BMO_elem_flag_test(bm, f1, FACE_MARK) || BMO_elem_flag_test(bm, f2, FACE_MARK))
+ continue;
+
+ BMO_elem_flag_enable(bm, f1, FACE_MARK);
+ BMO_elem_flag_enable(bm, f2, FACE_MARK);
+ BMO_elem_flag_enable(bm, e, EDGE_CHOSEN);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN))
+ continue;
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ BM_faces_join_pair(bm, f1, f2, e);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ /* ok, this edge wasn't merged, check if it's
+ * in a 2-tri-pair island, and if so merg */
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ if (f1->len != 3 || f2->len != 3)
+ continue;
+
+ for (i = 0; i < 2; i++) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, i ? f2 : f1) {
+ if (l->e != e && BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
+ break;
+ }
+ }
+
+ /* if l isn't NULL, we broke out of the loo */
+ if (l) {
+ break;
+ }
+ }
+
+ /* if i isn't 2, we broke out of that loo */
+ if (i != 2) {
+ continue;
+ }
+
+ BM_faces_join_pair(bm, f1, f2, e);
+ }
+ }
+
+ BLI_array_free(jedges);
+}
diff --git a/source/blender/bmesh/operators/bmo_mesh_conv.c b/source/blender/bmesh/operators/bmo_mesh_conv.c
new file mode 100644
index 00000000000..4f6db9056e5
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_mesh_conv.c
@@ -0,0 +1,906 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_key_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_mesh.h"
+#include "BLI_listbase.h"
+#include "BKE_global.h"
+#include "BKE_key.h"
+#include "BKE_main.h"
+#include "BKE_customdata.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+/*
+ * MESH CONV.C
+ *
+ * This file contains functions
+ * for converting a Mesh
+ * into a Bmesh, and back again.
+ *
+ */
+
+void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op)
+{
+ Object *ob = BMO_slot_ptr_get(op, "object");
+ Mesh *me = BMO_slot_ptr_get(op, "mesh");
+ MVert *mvert;
+ BLI_array_declare(verts);
+ MEdge *medge;
+ MLoop *ml;
+ MPoly *mpoly;
+ KeyBlock *actkey, *block;
+ BMVert *v, **vt = NULL, **verts = NULL;
+ BMEdge *e, **fedges = NULL, **et = NULL;
+ BMFace *f;
+ BMLoop *l;
+ BLI_array_declare(fedges);
+ float (*keyco)[3] = NULL;
+ int *keyi;
+ int set_key = BMO_slot_int_get(op, "set_shapekey");
+ int totuv, i, j;
+
+ if (!me || !me->totvert) {
+ return; /* sanity check */
+ }
+
+ vt = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");
+
+ CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+
+ /* make sure uv layer names are consisten */
+ totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
+ for (i = 0; i < totuv; i++) {
+ int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i);
+ CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name);
+ }
+
+ if (!CustomData_has_layer(&bm->edata, CD_CREASE))
+ CustomData_add_layer(&bm->edata, CD_CREASE, CD_ASSIGN, NULL, 0);
+
+ if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT))
+ CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_ASSIGN, NULL, 0);
+
+ if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT))
+ CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_ASSIGN, NULL, 0);
+
+
+ if (me->key && ob->shapenr > me->key->totkey) {
+ ob->shapenr = me->key->totkey - 1;
+ }
+
+ actkey = ob_get_keyblock(ob);
+ if (actkey && actkey->totelem == me->totvert) {
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+
+ /* check if we need to generate unique ids for the shapekeys.
+ * this also exists in the file reading code, but is here for
+ * a sanity chec */
+ if (!me->key->uidgen) {
+ fprintf(stderr,
+ "%s had to generate shape key uid's in a situation we shouldn't need to! "
+ "(bmesh internal error)\n",
+ __func__);
+
+ me->key->uidgen = 1;
+ for (block = me->key->block.first; block; block = block->next) {
+ block->uid = me->key->uidgen++;
+ }
+ }
+
+ keyco = actkey->data;
+ bm->shapenr = ob->shapenr;
+ for (i = 0, block = me->key->block.first; block; block = block->next, i++) {
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
+ CD_ASSIGN, NULL, 0, block->name);
+
+ j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
+ bm->vdata.layers[j].uid = block->uid;
+ }
+ }
+ else if (actkey) {
+ printf("shapekey<->mesh mismatch!\n");
+ }
+
+ 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]);
+
+ for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
+ v = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL);
+ BM_elem_index_set(v, i); /* set_ok */
+ vt[i] = v;
+
+ /* transfer flag */
+ v->head.hflag = BM_vert_flag_from_mflag(mvert->flag);
+
+ /* this is necassary for selection counts to work properl */
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) BM_vert_select_set(bm, v, TRUE);
+
+ normal_short_to_float_v3(v->no, mvert->no);
+
+ BM_elem_float_data_set(&bm->vdata, v, CD_BWEIGHT, (float)mvert->bweight / 255.0f);
+
+ /* Copy Custom Dat */
+ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data);
+
+ /* set shapekey dat */
+ if (me->key) {
+ /* set shape key original inde */
+ keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi) {
+ *keyi = i;
+ }
+
+ for (block = me->key->block.first, j = 0; block; block = block->next, j++) {
+ float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);
+
+ if (co) {
+ copy_v3_v3(co, ((float *)block->data) + 3 * i);
+ }
+ }
+ }
+ }
+
+ bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */
+
+ if (!me->totedge) {
+ MEM_freeN(vt);
+ return;
+ }
+
+ et = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable");
+
+ medge = me->medge;
+ for (i = 0; i < me->totedge; i++, medge++) {
+ e = BM_edge_create(bm, vt[medge->v1], vt[medge->v2], NULL, FALSE);
+ BM_elem_index_set(e, i); /* set_ok */
+ et[i] = e;
+
+ /* transfer flags */
+ e->head.hflag = BM_edge_flag_from_mflag(medge->flag);
+
+ /* this is necassary for selection counts to work properly */
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) BM_elem_select_set(bm, e, TRUE);
+
+ /* Copy Custom Dat */
+ CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data);
+
+ BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (float)medge->crease / 255.0f);
+ BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (float)medge->bweight / 255.0f);
+ }
+
+ bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */
+
+ mpoly = me->mpoly;
+ for (i = 0; i < me->totpoly; i++, mpoly++) {
+ BMIter iter;
+
+ BLI_array_empty(fedges);
+ BLI_array_empty(verts);
+
+ BLI_array_growitems(fedges, mpoly->totloop);
+ BLI_array_growitems(verts, mpoly->totloop);
+
+ for (j = 0; j < mpoly->totloop; j++) {
+ ml = &me->mloop[mpoly->loopstart + j];
+ v = vt[ml->v];
+ e = et[ml->e];
+
+ fedges[j] = e;
+ verts[j] = v;
+ }
+
+ /* not sure what this block is supposed to do,
+ * but its unused. so commenting - campbell */
+#if 0
+ {
+ BMVert *v1, *v2;
+ v1 = vt[me->mloop[mpoly->loopstart].v];
+ v2 = vt[me->mloop[mpoly->loopstart + 1].v];
+
+ if (v1 == fedges[0]->v1) {
+ v2 = fedges[0]->v2;
+ }
+ else {
+ v1 = fedges[0]->v2;
+ v2 = fedges[0]->v1;
+ }
+ }
+#endif
+
+ f = BM_face_create(bm, verts, fedges, mpoly->totloop, FALSE);
+
+ if (!f) {
+ printf("%s: Warning! Bad face in mesh"
+ " \"%s\" at index %d!, skipping\n",
+ __func__, me->id.name + 2, i);
+ continue;
+ }
+
+ /* dont use 'i' since we may have skipped the face */
+ BM_elem_index_set(f, bm->totface - 1); /* set_ok */
+
+ /* transfer flag */
+ f->head.hflag = BM_face_flag_from_mflag(mpoly->flag);
+
+ /* this is necassary for selection counts to work properl */
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) BM_elem_select_set(bm, f, TRUE);
+
+ f->mat_nr = mpoly->mat_nr;
+ if (i == me->act_face) bm->act_face = f;
+
+ j = 0;
+ BM_ITER_INDEX(l, &iter, bm, BM_LOOPS_OF_FACE, f, j) {
+ /* Save index of correspsonding MLoop */
+ BM_elem_index_set(l, mpoly->loopstart + j); /* set_loop */
+ }
+
+ /* Copy Custom Dat */
+ CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data);
+ }
+
+ bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */
+
+ {
+ BMIter fiter;
+ BMIter liter;
+
+ /* Copy over loop CustomData. Doing this in a separate loop isn't necessary
+ * but is an optimization, to avoid copying a bunch of interpolated customdata
+ * for each BMLoop (from previous BMLoops using the same edge), always followed
+ * by freeing the interpolated data and overwriting it with data from the Mesh. */
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ int li = BM_elem_index_get(l);
+ CustomData_to_bmesh_block(&me->ldata, &bm->ldata, li, &l->head.data);
+ BM_elem_index_set(l, 0); /* set_loop */
+ }
+ }
+ }
+
+ if (me->mselect && me->totselect != 0) {
+ BMIter iter;
+ BMVert *vertex;
+ BMEdge *edge;
+ BMFace *face;
+ BMVert **vertex_array = MEM_callocN(sizeof(BMVert *) * bm->totvert,
+ "Selection Conversion Vertex Pointer Array");
+ BMEdge **edge_array = MEM_callocN(sizeof(BMEdge *) * bm->totedge,
+ "Selection Conversion Edge Pointer Array");
+ BMFace **face_array = MEM_callocN(sizeof(BMFace *) * bm->totface,
+ "Selection Conversion Face Pointer Array");
+
+ for (i = 0, vertex = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ vertex;
+ i++, vertex = BM_iter_step(&iter))
+ {
+ vertex_array[i] = vertex;
+ }
+
+ for (i = 0, edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL);
+ edge;
+ i++, edge = BM_iter_step(&iter))
+ {
+ edge_array[i] = edge;
+ }
+
+ for (i = 0, face = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL);
+ face;
+ i++, face = BM_iter_step(&iter))
+ {
+ face_array[i] = face;
+ }
+
+ if (me->mselect) {
+ for (i = 0; i < me->totselect; i++) {
+ if (me->mselect[i].type == ME_VSEL) {
+ BM_select_history_store(bm, vertex_array[me->mselect[i].index]);
+ }
+ else if (me->mselect[i].type == ME_ESEL) {
+ BM_select_history_store(bm, edge_array[me->mselect[i].index]);
+ }
+ else if (me->mselect[i].type == ME_FSEL) {
+ BM_select_history_store(bm, face_array[me->mselect[i].index]);
+ }
+ }
+ }
+ else {
+ me->totselect = 0;
+ }
+
+ MEM_freeN(vertex_array);
+ MEM_freeN(edge_array);
+ MEM_freeN(face_array);
+ }
+ else {
+ me->totselect = 0;
+ if (me->mselect) {
+ MEM_freeN(me->mselect);
+ me->mselect = NULL;
+ }
+ }
+
+ BLI_array_free(fedges);
+ BLI_array_free(verts);
+
+ MEM_freeN(vt);
+ MEM_freeN(et);
+}
+
+void object_load_bmesh_exec(BMesh *bm, BMOperator *op)
+{
+ Object *ob = BMO_slot_ptr_get(op, "object");
+ /* Scene *scene = BMO_slot_ptr_get(op, "scene"); */
+ Mesh *me = ob->data;
+
+ BMO_op_callf(bm, "bmesh_to_mesh mesh=%p object=%p notesselation=%i", me, ob, TRUE);
+}
+
+
+static BMVert **bmesh_to_mesh_vertex_map(BMesh *bm, int ototvert)
+{
+ BMVert **vertMap = NULL;
+ BMVert *eve;
+ int index;
+ int i = 0;
+ BMIter iter;
+
+ /* caller needs to ensure this */
+ BLI_assert(ototvert > 0);
+
+ vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap");
+ if (CustomData_has_layer(&bm->vdata, CD_SHAPE_KEYINDEX)) {
+ int *keyi;
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi) {
+ if (((index = *keyi) != ORIGINDEX_NONE) && (index < ototvert)) {
+ vertMap[index] = eve;
+ }
+ }
+ else {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ }
+ i++;
+ }
+ }
+ else {
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ else {
+ break;
+ }
+ i++;
+ }
+ }
+
+ return vertMap;
+}
+
+BM_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
+{
+ /* this is a cheap way to set the edge draw, its not precise and will
+ * pick the first 2 faces an edge uses */
+
+
+ if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
+ (e->l && (e->l != e->l->radial_next)) &&
+ (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.998f))
+ {
+ med->flag &= ~ME_EDGEDRAW;
+ }
+}
+
+
+void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op)
+{
+ Mesh *me = BMO_slot_ptr_get(op, "mesh");
+ /* Object *ob = BMO_slot_ptr_get(op, "object"); */
+ MLoop *mloop;
+ MPoly *mpoly;
+ MVert *mvert, *oldverts;
+ MEdge *med, *medge;
+ BMVert *v, *eve;
+ BMEdge *e;
+ BMLoop *l;
+ BMFace *f;
+ BMIter iter, liter;
+ int i, j, *keyi, ototvert, totloop;
+ int dotess = !BMO_slot_int_get(op, "notesselation");
+
+ ototvert = me->totvert;
+
+ /* new Vertex block */
+ if (bm->totvert == 0) mvert = NULL;
+ else mvert = MEM_callocN(bm->totvert * sizeof(MVert), "loadeditbMesh vert");
+
+ /* new Edge block */
+ if (bm->totedge == 0) medge = NULL;
+ else medge = MEM_callocN(bm->totedge * sizeof(MEdge), "loadeditbMesh edge");
+
+ /* build ngon dat */
+ /* new Ngon Face block */
+ if (bm->totface == 0) mpoly = NULL;
+ else mpoly = MEM_callocN(bm->totface * sizeof(MPoly), "loadeditbMesh poly");
+
+ /* find number of loops to allocat */
+ totloop = 0;
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ totloop += f->len;
+ }
+
+ if (totloop == 0) mloop = NULL;
+ else mloop = MEM_callocN(totloop * sizeof(MLoop), "loadeditbMesh loop");
+
+ /* lets save the old verts just in case we are actually working on
+ * a key ... we now do processing of the keys at the end */
+ oldverts = me->mvert;
+
+ /* don't free this yet */
+ CustomData_set_layer(&me->vdata, CD_MVERT, NULL);
+
+ /* free custom data */
+ CustomData_free(&me->vdata, me->totvert);
+ CustomData_free(&me->edata, me->totedge);
+ CustomData_free(&me->fdata, me->totface);
+ CustomData_free(&me->ldata, me->totloop);
+ CustomData_free(&me->pdata, me->totpoly);
+
+ /* add new custom data */
+ me->totvert = bm->totvert;
+ me->totedge = bm->totedge;
+ me->totloop = totloop;
+ me->totpoly = bm->totface;
+ /* will be overwritten with a valid value if 'dotess' is set, otherwise we
+ * end up with 'me->totface' and me->mface == NULL which can crash [#28625]
+ */
+ me->totface = 0;
+
+ CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert);
+ CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge);
+ CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop);
+ CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly);
+
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
+
+ /* this is called again, 'dotess' arg is used there */
+ mesh_update_customdata_pointers(me, 0);
+
+ i = 0;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ float *bweight = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BWEIGHT);
+
+ mvert->bweight = bweight ? (char)((*bweight) * 255) : 0;
+
+ copy_v3_v3(mvert->co, v->co);
+ normal_float_to_short_v3(mvert->no, v->no);
+
+ mvert->flag = BM_vert_flag_to_mflag(v);
+
+ BM_elem_index_set(v, i); /* set_inline */
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i);
+
+ i++;
+ mvert++;
+
+ BM_CHECK_ELEMENT(bm, v);
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+ med = medge;
+ i = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ float *crease = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
+ float *bweight = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BWEIGHT);
+
+ med->v1 = BM_elem_index_get(e->v1);
+ med->v2 = BM_elem_index_get(e->v2);
+ med->crease = crease ? (char)((*crease) * 255) : 0;
+ med->bweight = bweight ? (char)((*bweight) * 255) : 0;
+
+ med->flag = BM_edge_flag_to_mflag(e);
+
+ BM_elem_index_set(e, i); /* set_inline */
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i);
+
+ bmesh_quick_edgedraw_flag(med, e);
+
+ i++;
+ med++;
+ BM_CHECK_ELEMENT(bm, e);
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ i = 0;
+ j = 0;
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ mpoly->loopstart = j;
+ mpoly->totloop = f->len;
+ mpoly->mat_nr = f->mat_nr;
+ mpoly->flag = BM_face_flag_to_mflag(f);
+
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&liter), j++, mloop++) {
+ mloop->e = BM_elem_index_get(l->e);
+ mloop->v = BM_elem_index_get(l->v);
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, j);
+ BM_CHECK_ELEMENT(bm, l);
+ BM_CHECK_ELEMENT(bm, l->e);
+ BM_CHECK_ELEMENT(bm, l->v);
+ }
+
+ if (f == bm->act_face) me->act_face = i;
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i);
+
+ i++;
+ mpoly++;
+ BM_CHECK_ELEMENT(bm, f);
+ }
+
+ /* patch hook indices and vertex parents */
+ if (ototvert > 0) {
+ Object *ob;
+ ModifierData *md;
+ BMVert **vertMap = NULL;
+ int i, j;
+
+ for (ob = G.main->object.first; ob; ob = ob->id.next) {
+ if (ob->parent == bm->ob && ELEM(ob->partype, PARVERT1, PARVERT3)) {
+
+ if (vertMap == NULL) {
+ vertMap = bmesh_to_mesh_vertex_map(bm, ototvert);
+ }
+
+ if (ob->par1 < ototvert) {
+ eve = vertMap[ob->par1];
+ if (eve) ob->par1 = BM_elem_index_get(eve);
+ }
+ if (ob->par2 < ototvert) {
+ eve = vertMap[ob->par2];
+ if (eve) ob->par2 = BM_elem_index_get(eve);
+ }
+ if (ob->par3 < ototvert) {
+ eve = vertMap[ob->par3];
+ if (eve) ob->par3 = BM_elem_index_get(eve);
+ }
+
+ }
+ if (ob->data == me) {
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Hook) {
+ HookModifierData *hmd = (HookModifierData *) md;
+
+ if (vertMap == NULL) {
+ vertMap = bmesh_to_mesh_vertex_map(bm, ototvert);
+ }
+
+ for (i = j = 0; i < hmd->totindex; i++) {
+ if (hmd->indexar[i] < ototvert) {
+ eve = vertMap[hmd->indexar[i]];
+
+ if (eve) {
+ hmd->indexar[j++] = BM_elem_index_get(eve);
+ }
+ }
+ else j++;
+ }
+
+ hmd->totindex = j;
+ }
+ }
+ }
+ }
+
+ if (vertMap) MEM_freeN(vertMap);
+ }
+
+ if (dotess) {
+ BKE_mesh_tessface_calc(me);
+ }
+
+ mesh_update_customdata_pointers(me, dotess);
+
+ {
+ BMEditSelection *selected;
+ me->totselect = BLI_countlist(&(bm->selected));
+
+ if (me->mselect) MEM_freeN(me->mselect);
+
+ me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+
+
+ for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ if (selected->htype == BM_VERT) {
+ me->mselect[i].type = ME_VSEL;
+
+ }
+ else if (selected->htype == BM_EDGE) {
+ me->mselect[i].type = ME_ESEL;
+
+ }
+ else if (selected->htype == BM_FACE) {
+ me->mselect[i].type = ME_FSEL;
+ }
+
+ me->mselect[i].index = BM_elem_index_get(selected->data);
+ }
+ }
+
+ /* see comment below, this logic is in twice */
+
+ if (me->key) {
+ KeyBlock *currkey;
+ KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+
+ float (*ofs)[3] = NULL;
+
+ /* go through and find any shapekey customdata layers
+ * that might not have corrusponding KeyBlocks, and add them if
+ * necassary */
+ j = 0;
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY)
+ continue;
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == bm->vdata.layers[i].uid)
+ break;
+ }
+
+ if (!currkey) {
+ currkey = MEM_callocN(sizeof(KeyBlock), "KeyBlock mesh_conv.c");
+ currkey->type = KEY_LINEAR;
+ currkey->slidermin = 0.0f;
+ currkey->slidermax = 1.0f;
+
+ BLI_addtail(&me->key->block, currkey);
+ me->key->totkey++;
+ }
+
+ j++;
+ }
+
+
+ /* editing the base key should update others */
+ if (me->key->type == KEY_RELATIVE && oldverts) {
+ int act_is_basis = 0;
+ /* find if this key is a basis for any others */
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (bm->shapenr - 1 == currkey->relative) {
+ act_is_basis = 1;
+ break;
+ }
+ }
+
+ if (act_is_basis) { /* active key is a base */
+ float (*fp)[3] = actkey->data;
+ int *keyi;
+ i = 0;
+ ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ mvert = me->mvert;
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi && *keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]);
+ }
+ i++;
+ mvert++;
+ }
+ }
+ }
+
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ j = 0;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY)
+ continue;
+
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
+ float *fp, *co;
+ float (*ofs_pt)[3] = ofs;
+
+ if (currkey->data)
+ MEM_freeN(currkey->data);
+ currkey->data = fp = MEM_mallocN(sizeof(float) * 3 * bm->totvert, "shape key data");
+ currkey->totelem = bm->totvert;
+
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ co = (currkey == actkey) ?
+ eve->co :
+ CustomData_bmesh_get_n(&bm->vdata, eve->head.data, CD_SHAPEKEY, j);
+
+ copy_v3_v3(fp, co);
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, *ofs_pt++);
+ }
+
+ fp += 3;
+ }
+ break;
+ }
+
+ j++;
+ }
+
+ /* if we didn't find a shapekey, tag the block to be reconstructed
+ * via the old method below */
+ if (j == CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)) {
+ currkey->flag |= KEYBLOCK_MISSING;
+ }
+ }
+
+ if (ofs) MEM_freeN(ofs);
+ }
+
+ /* XXX, code below is from trunk and a duplicate functionality
+ * to the block above.
+ * We should use one or the other, having both means we have to maintain
+ * both and keep them working the same way which is a hassle - campbell */
+
+ /* old method of reconstructing keys via vertice's original key indices,
+ * currently used if the new method above fails (which is theoretically
+ * possible in certain cases of undo) */
+ if (me->key) {
+ float *fp, *newkey, *oldkey;
+ KeyBlock *currkey;
+ KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+
+ float (*ofs)[3] = NULL;
+
+ /* editing the base key should update others */
+ if (me->key->type == KEY_RELATIVE && oldverts) {
+ int act_is_basis = 0;
+ /* find if this key is a basis for any others */
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (bm->shapenr - 1 == currkey->relative) {
+ act_is_basis = 1;
+ break;
+ }
+ }
+
+ if (act_is_basis) { /* active key is a base */
+ float (*fp)[3] = actkey->data;
+ int *keyi;
+ i = 0;
+ ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ mvert = me->mvert;
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi && *keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]);
+ }
+ i++;
+ mvert++;
+ }
+ }
+ }
+
+ /* Lets reorder the key data so that things line up roughly
+ * with the way things were before editmode */
+ currkey = me->key->block.first;
+ while (currkey) {
+ int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
+
+ if (!(currkey->flag & KEYBLOCK_MISSING)) {
+ currkey = currkey->next;
+ continue;
+ }
+
+ printf("warning: had to hackishly reconstruct shape key \"%s\","
+ " it may not be correct anymore.\n", currkey->name);
+
+ currkey->flag &= ~KEYBLOCK_MISSING;
+
+ fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
+ oldkey = currkey->data;
+
+ eve = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+
+ i = 0;
+ mvert = me->mvert;
+ while (eve) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (!keyi) {
+ break;
+ }
+ if (*keyi >= 0 && *keyi < currkey->totelem) { // valid old vertex
+ if (currkey == actkey) {
+ if (actkey == me->key->refkey) {
+ copy_v3_v3(fp, mvert->co);
+ }
+ else {
+ copy_v3_v3(fp, mvert->co);
+ if (oldverts) {
+ copy_v3_v3(mvert->co, oldverts[*keyi].co);
+ }
+ }
+ }
+ else {
+ if (oldkey) {
+ copy_v3_v3(fp, oldkey + 3 * *keyi);
+ }
+ }
+ }
+ else {
+ copy_v3_v3(fp, mvert->co);
+ }
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, ofs[i]);
+ }
+
+ fp+= 3;
+ ++i;
+ ++mvert;
+ eve = BM_iter_step(&iter);
+ }
+ currkey->totelem = bm->totvert;
+ if (currkey->data) MEM_freeN(currkey->data);
+ currkey->data = newkey;
+
+ currkey = currkey->next;
+ }
+
+ if (ofs) MEM_freeN(ofs);
+ }
+
+ if (oldverts) MEM_freeN(oldverts);
+}
diff --git a/source/blender/bmesh/operators/bmo_mirror.c b/source/blender/bmesh/operators/bmo_mirror.c
new file mode 100644
index 00000000000..82e77fc9a96
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_mirror.c
@@ -0,0 +1,126 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+#include "bmesh_operators_private.h" /* own include */
+
+#define ELE_NEW 1
+
+void bmesh_mirror_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator dupeop, weldop;
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v, *v2, **vmap = NULL;
+ BLI_array_declare(vmap);
+ BMEdge /* *e, */ **emap = NULL;
+ BLI_array_declare(emap);
+ float mtx[4][4];
+ float imtx[4][4];
+ float scale[3] = {1.0f, 1.0f, 1.0f};
+ float dist = BMO_slot_float_get(op, "mergedist");
+ int i, ototvert, ototedge, axis = BMO_slot_int_get(op, "axis");
+ int mirroru = BMO_slot_int_get(op, "mirror_u");
+ int mirrorv = BMO_slot_int_get(op, "mirror_v");
+
+ ototvert = bm->totvert;
+ ototedge = bm->totedge;
+
+ BMO_slot_mat4_get(op, "mat", mtx);
+ invert_m4_m4(imtx, mtx);
+
+ BMO_op_initf(bm, &dupeop, "dupe geom=%s", op, "geom");
+ BMO_op_exec(bm, &dupeop);
+
+ BMO_slot_buffer_flag_enable(bm, &dupeop, "newout", ELE_NEW, BM_ALL);
+
+ /* create old -> new mappin */
+ i = 0;
+ v2 = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ BMO_ITER(v, &siter, bm, &dupeop, "newout", BM_VERT) {
+ BLI_array_growone(vmap);
+ vmap[i] = v;
+
+ /* BMESH_TODO, double check this is being used, calling following operators will overwrite anyway - campbell */
+ BM_elem_index_set(v2, i); /* set_dirty! */
+ v2 = BM_iter_step(&iter);
+
+ i++;
+ }
+ bm->elem_index_dirty |= BM_VERT;
+
+ /* feed old data to transform bmo */
+ scale[axis] = -1.0f;
+ BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, mtx);
+ BMO_op_callf(bm, "scale verts=%fv vec=%v", ELE_NEW, scale);
+ BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, imtx);
+
+ BMO_op_init(bm, &weldop, "weldverts");
+
+ v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ for (i = 0; i < ototvert; i++) {
+ if (ABS(v->co[axis]) <= dist) {
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", vmap[i], v);
+ }
+ v = BM_iter_step(&iter);
+ }
+
+ if (mirroru || mirrorv) {
+ BMFace *f;
+ BMLoop *l;
+ MLoopUV *luv;
+ int totlayer;
+ BMIter liter;
+
+ BMO_ITER(f, &siter, bm, &dupeop, "newout", BM_FACE) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ totlayer = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
+ for (i = 0; i < totlayer; i++) {
+ luv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i);
+ if (mirroru)
+ luv->uv[0] = 1.0f - luv->uv[0];
+ if (mirrorv)
+ luv->uv[1] = 1.0f - luv->uv[1];
+ }
+ }
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+
+ BMO_op_finish(bm, &weldop);
+ BMO_op_finish(bm, &dupeop);
+
+ BMO_slot_from_flag(bm, op, "newout", ELE_NEW, BM_ALL);
+
+ BLI_array_free(vmap);
+ BLI_array_free(emap);
+}
diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c
new file mode 100644
index 00000000000..f7a1a8f4d06
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -0,0 +1,734 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "ED_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+
+
+/* ************************ primitives ******************* */
+
+static float icovert[12][3] = {
+ {0.0f,0.0f,-200.0f},
+ {144.72f, -105.144f,-89.443f},
+ {-55.277f, -170.128,-89.443f},
+ {-178.885f,0.0f,-89.443f},
+ {-55.277f,170.128f,-89.443f},
+ {144.72f,105.144f,-89.443f},
+ {55.277f,-170.128f,89.443f},
+ {-144.72f,-105.144f,89.443f},
+ {-144.72f,105.144f,89.443f},
+ {55.277f,170.128f,89.443f},
+ {178.885f,0.0f,89.443f},
+ {0.0f,0.0f,200.0f}
+};
+
+static short icoface[20][3] = {
+ {0,1,2},
+ {1,0,5},
+ {0,2,3},
+ {0,3,4},
+ {0,4,5},
+ {1,5,10},
+ {2,1,6},
+ {3,2,7},
+ {4,3,8},
+ {5,4,9},
+ {1,10,6},
+ {2,6,7},
+ {3,7,8},
+ {4,8,9},
+ {5,9,10},
+ {6,10,11},
+ {7,6,11},
+ {8,7,11},
+ {9,8,11},
+ {10,9,11}
+};
+
+// HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker
+// this hack is only used so that scons & mingw + split-sources hack works
+// ------------------------------- start copied code
+/* these are not the monkeys you are looking for */
+static int monkeyo = 4;
+static int monkeynv = 271;
+static int monkeynf = 250;
+static signed char monkeyv[271][3] = {
+ {-71,21,98},{-63,12,88},{-57,7,74},{-82,-3,79},{-82,4,92},
+ {-82,17,100},{-92,21,102},{-101,12,95},{-107,7,83},
+ {-117,31,84},{-109,31,95},{-96,31,102},{-92,42,102},
+ {-101,50,95},{-107,56,83},{-82,66,79},{-82,58,92},
+ {-82,46,100},{-71,42,98},{-63,50,88},{-57,56,74},
+ {-47,31,72},{-55,31,86},{-67,31,97},{-66,31,99},
+ {-70,43,100},{-82,48,103},{-93,43,105},{-98,31,105},
+ {-93,20,105},{-82,31,106},{-82,15,103},{-70,20,100},
+ {-127,55,95},{-127,45,105},{-127,-87,94},{-127,-41,100},
+ {-127,-24,102},{-127,-99,92},{-127,52,77},{-127,73,73},
+ {-127,115,-70},{-127,72,-109},{-127,9,-106},{-127,-49,-45},
+ {-101,-24,72},{-87,-56,73},{-82,-89,73},{-80,-114,68},
+ {-85,-121,67},{-104,-124,71},{-127,-126,74},{-71,-18,68},
+ {-46,-5,69},{-21,19,57},{-17,55,76},{-36,62,80},
+ {-64,77,88},{-86,97,94},{-107,92,97},{-119,63,96},
+ {-106,53,99},{-111,39,98},{-101,12,95},{-79,2,90},
+ {-64,8,86},{-47,24,83},{-45,38,83},{-50,48,85},
+ {-72,56,92},{-95,60,97},{-127,-98,94},{-113,-92,94},
+ {-112,-107,91},{-119,-113,89},{-127,-114,88},{-127,-25,96},
+ {-127,-18,95},{-114,-19,95},{-111,-29,96},{-116,-37,95},
+ {-76,-6,86},{-48,7,80},{-34,26,77},{-32,48,84},
+ {-39,53,93},{-71,70,102},{-87,82,107},{-101,79,109},
+ {-114,55,108},{-111,-13,104},{-100,-57,91},{-95,-90,88},
+ {-93,-105,85},{-97,-117,81},{-106,-119,81},{-127,-121,82},
+ {-127,6,93},{-127,27,98},{-85,61,95},{-106,18,96},
+ {-110,27,97},{-112,-88,94},{-117,-57,96},{-127,-57,96},
+ {-127,-42,95},{-115,-35,100},{-110,-29,102},{-113,-17,100},
+ {-122,-16,100},{-127,-26,106},{-121,-19,104},{-115,-20,104},
+ {-113,-29,106},{-117,-32,103},{-127,-37,103},{-94,-40,71},
+ {-106,-31,91},{-104,-40,91},{-97,-32,71},{-127,-112,88},
+ {-121,-111,88},{-115,-105,91},{-115,-95,93},{-127,-100,84},
+ {-115,-96,85},{-115,-104,82},{-121,-109,81},{-127,-110,81},
+ {-105,28,100},{-103,20,99},{-84,55,97},{-92,54,99},
+ {-73,51,99},{-55,45,89},{-52,37,88},{-53,25,87},
+ {-66,13,92},{-79,8,95},{-98,14,100},{-104,38,100},
+ {-100,48,100},{-97,46,97},{-102,38,97},{-96,16,97},
+ {-79,11,93},{-68,15,90},{-57,27,86},{-56,36,86},
+ {-59,43,87},{-74,50,96},{-91,51,98},{-84,52,96},
+ {-101,22,96},{-102,29,96},{-113,59,78},{-102,85,79},
+ {-84,88,76},{-65,71,71},{-40,58,63},{-25,52,59},
+ {-28,21,48},{-50,0,53},{-71,-12,60},{-127,115,37},
+ {-127,126,-10},{-127,-25,-86},{-127,-59,24},{-127,-125,59},
+ {-127,-103,44},{-127,-73,41},{-127,-62,36},{-18,30,7},
+ {-17,41,-6},{-28,34,-56},{-68,56,-90},{-33,-6,9},
+ {-51,-16,-21},{-45,-1,-55},{-84,7,-85},{-97,-45,52},
+ {-104,-53,33},{-90,-91,49},{-95,-64,50},{-85,-117,51},
+ {-109,-97,47},{-111,-69,46},{-106,-121,56},{-99,-36,55},
+ {-100,-29,60},{-101,-22,64},{-100,-50,21},{-89,-40,-34},
+ {-83,-19,-69},{-69,111,-49},{-69,119,-9},{-69,109,30},
+ {-68,67,55},{-34,52,43},{-46,58,36},{-45,90,7},
+ {-25,72,16},{-25,79,-15},{-45,96,-25},{-45,87,-57},
+ {-25,69,-46},{-48,42,-75},{-65,3,-70},{-22,42,-26},
+ {-75,-22,19},{-72,-25,-27},{-13,52,-30},{-28,-18,-16},
+ {6,-13,-42},{37,7,-55},{46,41,-54},{31,65,-54},
+ {4,61,-40},{3,53,-37},{25,56,-50},{35,37,-52},
+ {28,10,-52},{5,-5,-39},{-21,-9,-17},{-9,46,-28},
+ {-6,39,-37},{-14,-3,-27},{6,0,-47},{25,12,-57},
+ {31,32,-57},{23,46,-56},{4,44,-46},{-19,37,-27},
+ {-20,22,-35},{-30,12,-35},{-22,11,-35},{-19,2,-35},
+ {-23,-2,-35},{-34,0,-9},{-35,-3,-22},{-35,5,-24},
+ {-25,26,-27},{-13,31,-34},{-13,30,-41},{-23,-2,-41},
+ {-18,2,-41},{-21,10,-41},{-29,12,-41},{-19,22,-41},
+ {6,42,-53},{25,44,-62},{34,31,-63},{28,11,-62},
+ {7,0,-54},{-14,-2,-34},{-5,37,-44},{-13,14,-42},
+ {-7,8,-43},{1,16,-47},{-4,22,-45},{3,30,-48},
+ {8,24,-49},{15,27,-50},{12,35,-50},{4,56,-62},
+ {33,60,-70},{48,38,-64},{41,7,-68},{6,-11,-63},
+ {-26,-16,-42},{-17,49,-49},
+};
+
+static signed char monkeyf[250][4] = {
+ {27,4,5,26}, {25,4,5,24}, {3,6,5,4}, {1,6,5,2}, {5,6,7,4},
+ {3,6,7,2}, {5,8,7,6}, {3,8,7,4}, {7,8,9,6},
+ {5,8,9,4}, {7,10,9,8}, {5,10,9,6}, {9,10,11,8},
+ {7,10,11,6}, {9,12,11,10}, {7,12,11,8}, {11,6,13,12},
+ {5,4,13,12}, {3,-2,13,12}, {-3,-4,13,12}, {-5,-10,13,12},
+ {-11,-12,14,12}, {-13,-18,14,13}, {-19,4,5,13}, {10,12,4,4},
+ {10,11,9,9}, {8,7,9,9}, {7,5,6,6}, {6,3,4,4},
+ {5,1,2,2}, {4,-1,0,0}, {3,-3,-2,-2}, {22,67,68,23},
+ {20,65,66,21}, {18,63,64,19}, {16,61,62,17}, {14,59,60,15},
+ {12,19,48,57}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47},
+ {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47},
+ {18,19,48,47}, {18,-9,-8,47}, {18,27,45,46}, {26,55,43,44},
+ {24,41,42,54}, {22,39,40,23}, {20,37,38,21}, {18,35,36,19},
+ {16,33,34,17}, {14,31,32,15}, {12,39,30,13}, {11,48,45,38},
+ {8,36,-19,9}, {8,-20,44,47}, {42,45,46,43}, {18,19,40,39},
+ {16,17,38,37}, {14,15,36,35}, {32,44,43,33}, {12,33,32,42},
+ {19,44,43,42}, {40,41,42,-27}, {8,9,39,-28}, {15,43,42,16},
+ {13,43,42,14}, {11,43,42,12}, {9,-30,42,10}, {37,12,38,-32},
+ {-33,37,45,46}, {-33,40,41,39}, {38,40,41,37}, {36,40,41,35},
+ {34,40,41,33}, {36,39,38,37}, {35,40,39,38}, {1,2,14,21},
+ {1,2,40,13}, {1,2,40,39}, {1,24,12,39}, {-34,36,38,11},
+ {35,38,36,37}, {-37,8,35,37}, {-11,-12,-45,40}, {-11,-12,39,38},
+ {-11,-12,37,36}, {-11,-12,35,34}, {33,34,40,41}, {33,34,38,39},
+ {33,34,36,37}, {33,-52,34,35}, {33,37,36,34}, {33,35,34,34},
+ {8,7,37,36}, {-32,7,35,46}, {-34,-33,45,46}, {4,-33,43,34},
+ {-34,-33,41,42}, {-34,-33,39,40}, {-34,-33,37,38}, {-34,-33,35,36},
+ {-34,-33,33,34}, {-34,-33,31,32}, {-34,-4,28,30}, {-5,-34,28,27},
+ {-35,-44,36,27}, {26,35,36,45}, {24,25,44,45}, {25,23,44,42},
+ {25,24,41,40}, {25,24,39,38}, {25,24,37,36}, {25,24,35,34},
+ {25,24,33,32}, {25,24,31,30}, {15,24,29,38}, {25,24,27,26},
+ {23,12,37,26}, {11,12,35,36}, {-86,-59,36,-80}, {-60,-61,36,35},
+ {-62,-63,36,35}, {-64,-65,36,35}, {-66,-67,36,35}, {-68,-69,36,35},
+ {-70,-71,36,35}, {-72,-73,36,35}, {-74,-75,36,35}, {42,43,53,58},
+ {40,41,57,56}, {38,39,55,57}, {-81,-80,37,56}, {-83,-82,55,52},
+ {-85,-84,51,49}, {-87,-86,48,49}, {47,50,51,48}, {46,48,51,49},
+ {43,46,49,44}, {-92,-91,45,42}, {-23,49,50,-20}, {-94,40,48,-24},
+ {-96,-22,48,49}, {-97,48,21,-90}, {-100,36,50,23}, {22,49,48,-100},
+ {-101,47,46,22}, {21,45,35,25}, {33,34,44,41}, {13,14,28,24},
+ {-107,26,30,-106}, {14,46,45,15}, {14,44,43,-110}, {-111,42,23,-110},
+ {6,7,45,46}, {45,44,47,46}, {45,46,47,48}, {47,46,49,48},
+ {17,49,47,48}, {17,36,46,48}, {35,36,44,45}, {35,36,40,43},
+ {35,36,38,39}, {-4,-3,37,35}, {-123,34,33,1}, {-9,-8,-7,-6},
+ {-10,-7,32,-125}, {-127,-11,-126,-126}, {-7,-6,5,31}, {4,5,33,30},
+ {4,39,33,32}, {4,35,32,38}, {20,21,39,38}, {4,37,38,5},
+ {-11,-10,36,3}, {-11,15,14,35}, {13,16,34,34}, {-13,14,13,13},
+ {-3,1,30,29}, {-3,28,29,1}, {-2,31,28,-1}, {12,13,27,30},
+ {-2,26,12,12}, {35,29,42,36}, {34,35,36,33}, {32,35,36,31},
+ {30,35,36,29}, {28,35,36,27}, {26,35,36,25}, {34,39,38,35},
+ {32,39,38,33}, {30,39,38,31}, {28,39,38,29}, {26,39,38,27},
+ {25,31,32,38}, {-18,-17,45,44}, {-18,17,28,44}, {-24,-20,42,-23},
+ {11,35,27,14}, {25,28,39,41}, {37,41,40,38}, {34,40,36,35},
+ {32,40,39,33}, {30,39,31,40}, {21,29,39,22}, {-31,37,28,4},
+ {-32,33,35,36}, {32,33,34,34}, {18,35,36,48}, {34,25,40,35},
+ {24,25,38,39}, {24,25,36,37}, {24,25,34,35}, {24,25,32,33},
+ {24,13,41,31}, {17,11,41,35}, {15,16,34,35}, {13,14,34,35},
+ {11,12,34,35}, {9,10,34,35}, {7,8,34,35}, {26,25,37,36},
+ {35,36,37,38}, {37,36,39,38}, {37,38,39,40}, {25,31,36,39},
+ {18,34,35,30}, {17,22,30,33}, {19,29,21,20}, {16,26,29,17},
+ {24,29,28,25}, {22,31,28,23}, {20,31,30,21}, {18,31,30,19},
+ {16,30,17,17}, {-21,-22,35,34}, {-21,-22,33,32}, {-21,-22,31,30},
+ {-21,-22,29,28}, {-21,-22,27,26}, {-28,-22,25,31}, {24,28,29,30},
+ {23,24,26,27}, {23,24,25,25}, {-69,-35,-32,27}, {-70,26,25,-66},
+ {-68,-67,24,-33},
+};
+
+#define VERT_MARK 1
+
+#define EDGE_ORIG 1
+#define EDGE_MARK 2
+
+#define FACE_MARK 1
+#define FACE_NEW 2
+
+void bmesh_create_grid_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator bmop, prevop;
+ BMVert *eve, *preveve;
+ BMEdge *e;
+ float vec[3], mat[4][4], phi, phid, dia = BMO_slot_float_get(op, "size");
+ int a, tot = BMO_slot_int_get(op, "xsegments"), seg = BMO_slot_int_get(op, "ysegments");
+
+ if (tot < 2) tot = 2;
+ if (seg < 2) seg = 2;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ /* one segment first: the X axis */
+ phi = 1.0f;
+ phid = 2.0f / ((float)tot - 1);
+ for (a = 0; a < tot; a++) {
+ vec[0] = dia * phi;
+ vec[1] = -dia;
+ vec[2] = 0.0f;
+ mul_m4_v3(mat, vec);
+
+ eve = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, eve, VERT_MARK);
+
+ if (a) {
+ e = BM_edge_create(bm, preveve, eve, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, EDGE_ORIG);
+ }
+
+ preveve = eve;
+ phi -= phid;
+ }
+
+ /* extrude and translate */
+ vec[0] = vec[2] = 0.0f;
+ vec[1] = dia * phid;
+ mul_mat3_m4_v3(mat, vec);
+
+ for (a = 0; a < seg - 1; a++) {
+ if (a) {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout");
+ BMO_op_exec(bm, &bmop);
+ BMO_op_finish(bm, &prevop);
+
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ }
+ else {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG);
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ }
+
+ BMO_op_callf(bm, "translate vec=%v verts=%s", vec, &bmop, "geomout");
+ prevop = bmop;
+ }
+
+ if (a)
+ BMO_op_finish(bm, &bmop);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator bmop, prevop;
+ BMVert *eve, *preveve;
+ BMEdge *e;
+ BMIter iter;
+ float vec[3], mat[4][4], cmat[3][3], phi, q[4];
+ float phid, dia = BMO_slot_float_get(op, "diameter");
+ int a, seg = BMO_slot_int_get(op, "segments"), tot = BMO_slot_int_get(op, "revolutions");
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ phid = 2.0f * (float)M_PI / tot;
+ phi = 0.25f * (float)M_PI;
+
+ /* one segment first */
+ phi = 0;
+ phid /= 2;
+ for (a = 0; a <= tot; a++) {
+ /* Going in this direction, then edge extruding, makes normals face outward */
+ vec[0] = -dia * sinf(phi);
+ vec[1] = 0.0;
+ vec[2] = dia * cosf(phi);
+ eve = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, eve, VERT_MARK);
+
+ if (a != 0) {
+ e = BM_edge_create(bm, preveve, eve, NULL, FALSE);
+ BMO_elem_flag_enable(bm, e, EDGE_ORIG);
+ }
+
+ phi+= phid;
+ preveve = eve;
+ }
+
+ /* extrude and rotate; negative phi to make normals face outward */
+ phi = -M_PI / seg;
+ q[0] = cosf(phi);
+ q[3] = sinf(phi);
+ q[1] = q[2] = 0.0f;
+ quat_to_mat3(cmat, q);
+
+ for (a = 0; a < seg; a++) {
+ if (a) {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout");
+ BMO_op_exec(bm, &bmop);
+ BMO_op_finish(bm, &prevop);
+ }
+ else {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG);
+ BMO_op_exec(bm, &bmop);
+ }
+
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", vec, cmat, &bmop, "geomout");
+
+ prevop = bmop;
+ }
+
+ if (a)
+ BMO_op_finish(bm, &bmop);
+
+ {
+ float len, len2, vec2[3];
+
+ len= 2*dia*sinf(phid / 2.0f);
+
+ /* length of one segment in shortest parallen */
+ vec[0]= dia*sinf(phid);
+ vec[1]= 0.0;
+ vec[2]= dia*cosf(phid);
+
+ mul_v3_m3v3(vec2, cmat, vec);
+ len2= len_v3v3(vec, vec2);
+
+ /* use shortest segment length divided by 3 as merge threshold */
+ BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, MIN2(len, len2) / 3.0f);
+ }
+
+ /* and now do imat */
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, eve, VERT_MARK)) {
+ mul_m4_v3(mat, eve->co);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *eva[12];
+ BMVert *v;
+ BMIter liter;
+ BMIter viter;
+ BMLoop *l;
+ float vec[3], mat[4][4] /* , phi, phid */;
+ float dia = BMO_slot_float_get(op, "diameter");
+ int a, subdiv = BMO_slot_int_get(op, "subdivisions");
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ /* phid = 2.0f * (float)M_PI / subdiv; */ /* UNUSED */
+ /* phi = 0.25f * (float)M_PI; */ /* UNUSED */
+
+ dia /= 200.0f;
+ for (a = 0; a < 12; a++) {
+ vec[0] = dia * icovert[a][0];
+ vec[1] = dia * icovert[a][1];
+ vec[2] = dia * icovert[a][2];
+ eva[a] = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, eva[a], VERT_MARK);
+ }
+
+ for (a = 0; a < 20; a++) {
+ BMFace *eftemp;
+ BMVert *v1, *v2, *v3;
+
+ v1 = eva[icoface[a][0]];
+ v2 = eva[icoface[a][1]];
+ v3 = eva[icoface[a][2]];
+
+ eftemp = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, NULL, FALSE);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, eftemp) {
+ BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
+ }
+
+ BMO_elem_flag_enable(bm, eftemp, FACE_MARK);
+ }
+
+ dia *= 200.0f;
+
+ for (a = 1; a < subdiv; a++) {
+ BMOperator bmop;
+
+ BMO_op_initf(bm, &bmop,
+ "esubd edges=%fe smooth=%f numcuts=%i gridfill=%i beauty=%i",
+ EDGE_MARK, dia, 1, 1, B_SPHERE);
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", EDGE_MARK, BM_EDGE);
+ BMO_op_finish(bm, &bmop);
+ }
+
+ /* must transform after becayse of sphere subdivision */
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ mul_m4_v3(mat, v->co);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *eve;
+ BMVert **tv = MEM_mallocN(sizeof(*tv)*monkeynv * 2, "tv");
+ float mat[4][4];
+ int i;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ for (i = 0; i < monkeynv; i++) {
+ float v[3];
+
+ v[0] = (monkeyv[i][0] + 127) / 128.0, v[1] = monkeyv[i][1] / 128.0, v[2] = monkeyv[i][2] / 128.0;
+
+ tv[i] = BM_vert_create(bm, v, NULL);
+ BMO_elem_flag_enable(bm, tv[i], VERT_MARK);
+
+ tv[monkeynv + i] = (fabsf(v[0] = -v[0]) < 0.001f) ?
+ tv[i] :
+ (eve = BM_vert_create(bm, v, NULL), mul_m4_v3(mat, eve->co), eve);
+
+ BMO_elem_flag_enable(bm, tv[monkeynv + i], VERT_MARK);
+
+ mul_m4_v3(mat, tv[i]->co);
+ }
+
+ for (i = 0; i < monkeynf; i++) {
+ BM_face_create_quad_tri(bm,
+ tv[monkeyf[i][0] + i - monkeyo],
+ tv[monkeyf[i][1] + i - monkeyo],
+ tv[monkeyf[i][2] + i - monkeyo],
+ (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeyf[i][3] + i - monkeyo] : NULL,
+ NULL, FALSE);
+
+ BM_face_create_quad_tri(bm,
+ tv[monkeynv + monkeyf[i][2] + i - monkeyo],
+ tv[monkeynv + monkeyf[i][1] + i - monkeyo],
+ tv[monkeynv + monkeyf[i][0] + i - monkeyo],
+ (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeynv + monkeyf[i][3] + i - monkeyo]: NULL,
+ NULL, FALSE);
+ }
+
+ MEM_freeN(tv);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+
+void bmesh_create_circle_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL;
+ float vec[3], mat[4][4], phi, phid;
+ float dia = BMO_slot_float_get(op, "diameter");
+ int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments");
+ int cap_tris = BMO_slot_int_get(op, "cap_tris");
+ int a;
+
+ if (!segs)
+ return;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ phid = 2.0f * (float)M_PI / segs;
+ phi = .25f * (float)M_PI;
+
+ if (cap_ends) {
+ vec[0] = vec[1] = 0.0f;
+ vec[2] = 0.0;
+ mul_m4_v3(mat, vec);
+
+ cent1 = BM_vert_create(bm, vec, NULL);
+ }
+
+ for (a = 0; a < segs; a++, phi += phid) {
+ /* Going this way ends up with normal(s) upward */
+ vec[0] = -dia * sinf(phi);
+ vec[1] = dia * cosf(phi);
+ vec[2] = 0.0f;
+ mul_m4_v3(mat, vec);
+ v1 = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, v1, VERT_MARK);
+
+ if (lastv1)
+ BM_edge_create(bm, v1, lastv1, NULL, FALSE);
+
+ if (a && cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (!firstv1)
+ firstv1 = v1;
+
+ lastv1 = v1;
+ }
+
+ if (!a)
+ return;
+
+ BM_edge_create(bm, lastv1, firstv1, NULL, FALSE);
+
+ if (cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (!cap_tris) {
+ BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW);
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_cone_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2;
+ float vec[3], mat[4][4], phi, phid;
+ float dia1 = BMO_slot_float_get(op, "diameter1");
+ float dia2 = BMO_slot_float_get(op, "diameter2");
+ float depth = BMO_slot_float_get(op, "depth");
+ int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments");
+ int cap_tris = BMO_slot_int_get(op, "cap_tris");
+ int a;
+
+ if (!segs)
+ return;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ phid = 2.0f * (float)M_PI / segs;
+ phi = 0.25f * (float)M_PI;
+
+ depth *= 0.5f;
+ if (cap_ends) {
+ vec[0] = vec[1] = 0.0f;
+ vec[2] = -depth;
+ mul_m4_v3(mat, vec);
+
+ cent1 = BM_vert_create(bm, vec, NULL);
+
+ vec[0] = vec[1] = 0.0f;
+ vec[2] = depth;
+ mul_m4_v3(mat, vec);
+
+ cent2 = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, cent1, VERT_MARK);
+ BMO_elem_flag_enable(bm, cent2, VERT_MARK);
+ }
+
+ for (a = 0; a < segs; a++, phi += phid) {
+ vec[0] = dia1 * sinf(phi);
+ vec[1] = dia1 * cosf(phi);
+ vec[2] = -depth;
+ mul_m4_v3(mat, vec);
+ v1 = BM_vert_create(bm, vec, NULL);
+
+ vec[0] = dia2 * sinf(phi);
+ vec[1] = dia2 * cosf(phi);
+ vec[2] = depth;
+ mul_m4_v3(mat, vec);
+ v2 = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, v1, VERT_MARK);
+ BMO_elem_flag_enable(bm, v2, VERT_MARK);
+
+ if (a) {
+ if (cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+ BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, FALSE);
+ }
+ else {
+ firstv1 = v1;
+ firstv2 = v2;
+ }
+
+ lastv1 = v1;
+ lastv2 = v2;
+ }
+
+ if (!a)
+ return;
+
+ if (cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (!cap_tris) {
+ BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW);
+ }
+
+ BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, FALSE);
+
+ BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, 0.000001);
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_cube_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *v1, *v2, *v3, *v4, *v5, *v6, *v7, *v8;
+ float vec[3], mat[4][4], off = BMO_slot_float_get(op, "size") / 2.0f;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ if (!off) off = 0.5f;
+
+ vec[0] = -off;
+ vec[1] = -off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v1 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v1, VERT_MARK);
+
+ vec[0] = -off;
+ vec[1] = off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v2 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v2, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v3 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v3, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = -off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v4 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v4, VERT_MARK);
+
+ vec[0] = -off;
+ vec[1] = -off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v5 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v5, VERT_MARK);
+
+ vec[0] = -off;
+ vec[1] = off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v6 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v6, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v7 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v7, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = -off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v8 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v8, VERT_MARK);
+
+ /* the four sides */
+ BM_face_create_quad_tri(bm, v5, v6, v2, v1, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v6, v7, v3, v2, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v7, v8, v4, v3, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v8, v5, v1, v4, NULL, FALSE);
+
+ /* top/bottom */
+ BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v8, v7, v6, v5, NULL, FALSE);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c
new file mode 100644
index 00000000000..2eb1cf7db3e
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_removedoubles.c
@@ -0,0 +1,590 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op)
+{
+ BMIter liter;
+ BMLoop *l;
+ BMVert *v2, *doub;
+ int split = FALSE;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", l->v);
+ /* ok: if v2 is NULL (e.g. not in the map) then it's
+ * a target vert, otherwise it's a doubl */
+ if ((v2 && BM_vert_in_face(f, v2)) &&
+ (v2 != l->prev->v) &&
+ (v2 != l->next->v))
+ {
+ doub = l->v;
+ split = TRUE;
+ break;
+ }
+ }
+
+ if (split && doub != v2) {
+ BMLoop *nl;
+ BMFace *f2 = BM_face_split(bm, f, doub, v2, &nl, NULL);
+
+ remdoubles_splitface(f, bm, op);
+ remdoubles_splitface(f2, bm, op);
+ }
+}
+
+#define ELE_DEL 1
+#define EDGE_COL 2
+#define FACE_MARK 2
+
+#if 0
+int remdoubles_face_overlaps(BMesh *bm, BMVert **varr,
+ int len, BMFace *exclude,
+ 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;
+}
+#endif
+
+void bmesh_weldverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter, liter;
+ BMVert *v, *v2;
+ BMEdge *e, *e2, **edges = NULL;
+ BLI_array_declare(edges);
+ BMLoop *l, *l2, **loops = NULL;
+ BLI_array_declare(loops);
+ BMFace *f, *f2;
+ int a, b;
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if ((v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v))) {
+ BMO_elem_flag_enable(bm, v, ELE_DEL);
+
+ /* merge the vertex flags, else we get randomly selected/unselected verts */
+ BM_elem_flag_merge(v, v2);
+ }
+ }
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ remdoubles_splitface(f, bm, op);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e->v1, ELE_DEL) || BMO_elem_flag_test(bm, e->v2, ELE_DEL)) {
+ v = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v1);
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v2);
+
+ if (!v) v = e->v1;
+ if (!v2) v2 = e->v2;
+
+ if (v == v2)
+ BMO_elem_flag_enable(bm, e, EDGE_COL);
+ else if (!BM_edge_exists(v, v2))
+ BM_edge_create(bm, v, v2, e, TRUE);
+
+ BMO_elem_flag_enable(bm, e, ELE_DEL);
+ }
+ }
+
+ /* BMESH_TODO, stop abusing face index here */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_elem_index_set(f, 0); /* set_dirty! */
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (BMO_elem_flag_test(bm, l->v, ELE_DEL)) {
+ BMO_elem_flag_enable(bm, f, FACE_MARK|ELE_DEL);
+ }
+ if (BMO_elem_flag_test(bm, l->e, EDGE_COL)) {
+ BM_elem_index_set(f, BM_elem_index_get(f) + 1); /* set_dirty! */
+ }
+ }
+ }
+ bm->elem_index_dirty |= BM_FACE;
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK))
+ continue;
+
+ if (f->len - BM_elem_index_get(f) < 3) {
+ BMO_elem_flag_enable(bm, f, ELE_DEL);
+ continue;
+ }
+
+ BLI_array_empty(edges);
+ BLI_array_empty(loops);
+ a = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ v = l->v;
+ v2 = l->next->v;
+ if (BMO_elem_flag_test(bm, v, ELE_DEL)) {
+ v = BMO_slot_map_ptr_get(bm, op, "targetmap", v);
+ }
+ if (BMO_elem_flag_test(bm, v2, ELE_DEL)) {
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2);
+ }
+
+ e2 = v != v2 ? BM_edge_exists(v, v2) : NULL;
+ if (e2) {
+ for (b = 0; b < a; b++) {
+ if (edges[b] == e2) {
+ break;
+ }
+ }
+ if (b != a) {
+ continue;
+ }
+
+ BLI_array_growone(edges);
+ BLI_array_growone(loops);
+
+ edges[a] = e2;
+ loops[a] = l;
+
+ a++;
+ }
+ }
+
+ if (BLI_array_count(loops) < 3)
+ continue;
+ v = loops[0]->v;
+ v2 = loops[1]->v;
+
+ if (BMO_elem_flag_test(bm, v, ELE_DEL)) {
+ v = BMO_slot_map_ptr_get(bm, op, "targetmap", v);
+ }
+ if (BMO_elem_flag_test(bm, v2, ELE_DEL)) {
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2);
+ }
+
+ f2 = BM_face_create_ngon(bm, v, v2, edges, a, TRUE);
+ if (f2 && (f2 != f)) {
+ BM_elem_attrs_copy(bm, bm, f, f2);
+
+ a = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f2) {
+ l2 = loops[a];
+ BM_elem_attrs_copy(bm, bm, l2, l);
+
+ a++;
+ }
+ }
+ }
+
+ BMO_op_callf(bm, "del geom=%fvef context=%i", ELE_DEL, DEL_ONLYTAGGED);
+
+ BLI_array_free(edges);
+ BLI_array_free(loops);
+}
+
+static int vergaverco(const void *e1, const void *e2)
+{
+ const BMVert *v1 = *(void **)e1, *v2 = *(void **)e2;
+ float x1 = v1->co[0] + v1->co[1] + v1->co[2];
+ float x2 = v2->co[0] + v2->co[1] + v2->co[2];
+
+ if (x1 > x2) return 1;
+ else if (x1 < x2) return -1;
+ else return 0;
+}
+
+#define VERT_TESTED 1
+#define VERT_DOUBLE 2
+#define VERT_TARGET 4
+#define VERT_KEEP 8
+#define VERT_MARK 16
+#define VERT_IN 32
+
+#define EDGE_MARK 1
+
+void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v, *snapv;
+ BMLoop *l, *firstl = NULL;
+ float fac;
+ int i, tot;
+
+ snapv = BMO_iter_new(&siter, bm, op, "snapv", BM_VERT);
+ tot = BM_vert_face_count(snapv);
+
+ if (!tot)
+ return;
+
+ fac = 1.0f / tot;
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, snapv) {
+ if (!firstl) {
+ firstl = l;
+ }
+
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ int type = bm->ldata.layers[i].type;
+ void *e1, *e2;
+
+ e1 = CustomData_bmesh_get_layer_n(&bm->ldata, firstl->head.data, i);
+ e2 = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
+
+ CustomData_data_multiply(type, e2, fac);
+
+ if (l != firstl)
+ CustomData_data_add(type, e1, e2);
+ }
+ }
+ }
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
+ if (l == firstl) {
+ continue;
+ }
+
+ CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, firstl->head.data, &l->head.data);
+ }
+ }
+}
+
+void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v;
+ BMLoop *l /* , *firstl = NULL */;
+ CDBlockBytes min, max;
+ void *block;
+ int i, type;
+
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (!CustomData_layer_has_math(&bm->ldata, i))
+ continue;
+
+ type = bm->ldata.layers[i].type;
+ CustomData_data_initminmax(type, &min, &max);
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
+ block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
+ CustomData_data_dominmax(type, block, &min, &max);
+ }
+ }
+
+ CustomData_data_multiply(type, &min, 0.5f);
+ CustomData_data_multiply(type, &max, 0.5f);
+ CustomData_data_add(type, &min, &max);
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
+ block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
+ CustomData_data_copy_value(type, &min, block);
+ }
+ }
+ }
+}
+
+void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator weldop;
+ BMOIter siter;
+ BMVert *v, *snapv = NULL;
+ float vec[3];
+
+ BMO_slot_vec_get(op, "mergeco", vec);
+
+ //BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges");
+ BMO_op_init(bm, &weldop, "weldverts");
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ if (!snapv) {
+ snapv = v;
+ copy_v3_v3(snapv->co, vec);
+ }
+ else {
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", v, snapv);
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+ BMO_op_finish(bm, &weldop);
+}
+
+void bmesh_collapse_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator weldop;
+ BMWalker walker;
+ BMIter iter;
+ BMEdge *e, **edges = NULL;
+ BLI_array_declare(edges);
+ float min[3], max[3];
+ int i, tot;
+
+ BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges");
+ BMO_op_init(bm, &weldop, "weldverts");
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ BMW_init(&walker, bm, BMW_SHELL,
+ BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP,
+ BMW_NIL_LAY);
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
+ continue;
+
+ e = BMW_begin(&walker, e->v1);
+ BLI_array_empty(edges);
+
+ INIT_MINMAX(min, max);
+ for (tot = 0; e; tot++, e = BMW_step(&walker)) {
+ BLI_array_growone(edges);
+ edges[tot] = e;
+
+ DO_MINMAX(e->v1->co, min, max);
+ DO_MINMAX(e->v2->co, min, max);
+ }
+
+ add_v3_v3v3(min, min, max);
+ mul_v3_fl(min, 0.5f);
+
+ /* snap edges to a point. for initial testing purposes anyway */
+ for (i = 0; i < tot; i++) {
+ copy_v3_v3(edges[i]->v1->co, min);
+ copy_v3_v3(edges[i]->v2->co, min);
+
+ if (edges[i]->v1 != edges[0]->v1)
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v1, edges[0]->v1);
+ if (edges[i]->v2 != edges[0]->v1)
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v2, edges[0]->v1);
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+ BMO_op_finish(bm, &weldop);
+
+ BMW_end(&walker);
+ BLI_array_free(edges);
+}
+
+/* uv collapse functio */
+static void bmesh_collapsecon_do_layer(BMesh *bm, BMOperator *op, int layer)
+{
+ BMIter iter, liter;
+ BMFace *f;
+ BMLoop *l, *l2;
+ BMWalker walker;
+ void **blocks = NULL;
+ BLI_array_declare(blocks);
+ CDBlockBytes min, max;
+ int i, tot, type = bm->ldata.layers[layer].type;
+
+ /* clear all short flags */
+ BMO_mesh_flag_disable_all(bm, op, BM_ALL, (1 << 16) - 1);
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND,
+ BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP,
+ layer);
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
+ /* wal */
+ BLI_array_empty(blocks);
+ tot = 0;
+ l2 = BMW_begin(&walker, l);
+
+ CustomData_data_initminmax(type, &min, &max);
+ for (tot = 0; l2; tot++, l2 = BMW_step(&walker)) {
+ BLI_array_growone(blocks);
+ blocks[tot] = CustomData_bmesh_get_layer_n(&bm->ldata, l2->head.data, layer);
+ CustomData_data_dominmax(type, blocks[tot], &min, &max);
+ }
+
+ if (tot) {
+ CustomData_data_multiply(type, &min, 0.5f);
+ CustomData_data_multiply(type, &max, 0.5f);
+ CustomData_data_add(type, &min, &max);
+
+ /* snap CD (uv, vcol) points to their centroi */
+ for (i = 0; i < tot; i++) {
+ CustomData_data_copy_value(type, &min, blocks[i]);
+ }
+ }
+ }
+ }
+ }
+
+ BMW_end(&walker);
+ BLI_array_free(blocks);
+}
+
+void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op)
+{
+ int i;
+
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i))
+ bmesh_collapsecon_do_layer(bm, op, i);
+ }
+}
+
+void bmesh_finddoubles_common(BMesh *bm, BMOperator *op, BMOperator *optarget, const char *targetmapname)
+{
+ BMOIter oiter;
+ BMVert *v, *v2;
+ BMVert **verts = NULL;
+ BLI_array_declare(verts);
+ float dist, dist3;
+ int i, j, len, keepvert = 0;
+
+ dist = BMO_slot_float_get(op, "dist");
+ dist3 = dist * 3.0f;
+
+ i = 0;
+ BMO_ITER(v, &oiter, bm, op, "verts", BM_VERT) {
+ BLI_array_growone(verts);
+ verts[i++] = v;
+ }
+
+ /* Test whether keepverts arg exists and is non-empty */
+ if (BMO_slot_exists(op, "keepverts")) {
+ keepvert = BMO_iter_new(&oiter, bm, op, "keepverts", BM_VERT) != NULL;
+ }
+
+ /* sort by vertex coordinates added togethe */
+ qsort(verts, BLI_array_count(verts), sizeof(void *), vergaverco);
+
+ /* Flag keepverts */
+ if (keepvert) {
+ BMO_slot_buffer_flag_enable(bm, op, "keepverts", VERT_KEEP, BM_VERT);
+ }
+
+ len = BLI_array_count(verts);
+ for (i = 0; i < len; i++) {
+ v = verts[i];
+ if (BMO_elem_flag_test(bm, v, VERT_DOUBLE)) continue;
+
+ for (j = i + 1; j < len; j++) {
+ v2 = verts[j];
+
+ /* Compare sort values of the verts using 3x tolerance (allowing for the tolerance
+ * on each of the three axes). This avoids the more expensive length comparison
+ * for most vertex pairs. */
+ if ((v2->co[0]+v2->co[1]+v2->co[2])-(v->co[0]+v->co[1]+v->co[2]) > dist3)
+ break;
+
+ if (keepvert) {
+ if (BMO_elem_flag_test(bm, v2, VERT_KEEP) == BMO_elem_flag_test(bm, v, VERT_KEEP))
+ continue;
+ }
+
+ if (compare_len_v3v3(v->co, v2->co, dist)) {
+
+ /* If one vert is marked as keep, make sure it will be the target */
+ if (BMO_elem_flag_test(bm, v2, VERT_KEEP)) {
+ SWAP(BMVert *, v, v2);
+ }
+
+ BMO_elem_flag_enable(bm, v2, VERT_DOUBLE);
+ BMO_elem_flag_enable(bm, v, VERT_TARGET);
+
+ BMO_slot_map_ptr_insert(bm, optarget, targetmapname, v2, v);
+ }
+ }
+ }
+
+ BLI_array_free(verts);
+}
+
+void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator weldop;
+
+ BMO_op_init(bm, &weldop, "weldverts");
+ bmesh_finddoubles_common(bm, op, &weldop, "targetmap");
+ BMO_op_exec(bm, &weldop);
+ BMO_op_finish(bm, &weldop);
+}
+
+
+void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op)
+{
+ bmesh_finddoubles_common(bm, op, op, "targetmapout");
+}
+
+void bmesh_automerge_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator findop, weldop;
+ BMIter viter;
+ BMVert *v;
+
+ /* The "verts" input sent to this op is the set of verts that
+ * can be merged away into any other verts. Mark all other verts
+ * as VERT_KEEP. */
+ BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_IN, BM_VERT);
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, v, VERT_IN)) {
+ BMO_elem_flag_enable(bm, v, VERT_KEEP);
+ }
+ }
+
+ /* Search for doubles among all vertices, but only merge non-VERT_KEEP
+ * vertices into VERT_KEEP vertices. */
+ BMO_op_initf(bm, &findop, "finddoubles verts=%av keepverts=%fv", VERT_KEEP);
+ BMO_slot_copy(op, &findop, "dist", "dist");
+ BMO_op_exec(bm, &findop);
+
+ /* weld the vertices */
+ BMO_op_init(bm, &weldop, "weldverts");
+ BMO_slot_copy(&findop, &weldop, "targetmapout", "targetmap");
+ BMO_op_exec(bm, &weldop);
+
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
+}
diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c
new file mode 100644
index 00000000000..310762e0e37
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_subdivide.c
@@ -0,0 +1,1104 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_array.h"
+#include "BLI_noise.h"
+
+#include "BKE_customdata.h"
+
+#include "DNA_object_types.h"
+
+#include "ED_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#include "bmo_subdivide.h" /* own include */
+
+/* flags for all elements share a common bitfield space */
+#define SUBD_SPLIT 1
+
+#define EDGE_PERCENT 2
+
+/* I don't think new faces are flagged, currently, but
+ * better safe than sorry. */
+#define FACE_CUSTOMFILL 4
+#define ELE_INNER 8
+#define ELE_SPLIT 16
+
+/*
+ * NOTE: beauty has been renamed to flag!
+ */
+
+/* generic subdivision rules:
+ *
+ * - two selected edges in a face should make a link
+ * between them.
+ *
+ * - one edge should do, what? make pretty topology, or just
+ * split the edge only?
+ */
+
+/* connects face with smallest len, which I think should always be correct for
+ * edge subdivision */
+static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_nf)
+{
+ BMIter iter, iter2;
+ BMVert *v;
+ BMLoop *nl;
+ BMFace *face, *curf = NULL;
+
+ /* this isn't the best thing in the world. it doesn't handle cases where there's
+ * multiple faces yet. that might require a convexity test to figure out which
+ * face is "best," and who knows what for non-manifold conditions. */
+ 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) {
+ if (!curf || face->len < curf->len) curf = face;
+ }
+ }
+ }
+
+ if (curf) {
+ face = BM_face_split(bm, curf, v1, v2, &nl, NULL);
+
+ if (r_nf) *r_nf = face;
+ return nl ? nl->e : NULL;
+ }
+
+ return NULL;
+}
+/* calculates offset for co, based on fractal, sphere or smooth settings */
+static void alter_co(BMesh *bm, BMVert *v, BMEdge *UNUSED(origed), const subdparams *params, float perc,
+ BMVert *vsta, BMVert *vend)
+{
+ float tvec[3], prev_co[3], fac;
+ float *co = NULL;
+ int i, totlayer = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY);
+
+ BM_vert_normal_update_all(bm, v);
+
+ co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, params->origkey);
+ copy_v3_v3(prev_co, co);
+
+ if (params->beauty & B_SMOOTH) {
+ /* we calculate an offset vector vec1[], to be added to *co */
+ float len, nor[3], nor1[3], nor2[3], smooth = params->smooth;
+
+ sub_v3_v3v3(nor, vsta->co, vend->co);
+ len = 0.5f * normalize_v3(nor);
+
+ copy_v3_v3(nor1, vsta->no);
+ copy_v3_v3(nor2, vend->no);
+
+ /* cosine angle */
+ fac= dot_v3v3(nor, nor1);
+ mul_v3_v3fl(tvec, nor1, fac);
+
+ /* cosine angle */
+ fac = -dot_v3v3(nor, nor2);
+ madd_v3_v3fl(tvec, nor2, fac);
+
+ /* falloff for multi subdivide */
+ smooth *= sqrtf(fabsf(1.0f - 2.0f * fabsf(0.5f-perc)));
+
+ mul_v3_fl(tvec, smooth * len);
+
+ add_v3_v3(co, tvec);
+ }
+ else if (params->beauty & B_SPHERE) { /* subdivide sphere */
+ normalize_v3(co);
+ mul_v3_fl(co, params->smooth);
+ }
+
+ if (params->beauty & B_FRACTAL) {
+ float len = len_v3v3(vsta->co, vend->co);
+ float vec2[3] = {0.0f, 0.0f, 0.0f}, co2[3];
+
+ fac = params->fractal * len;
+
+ add_v3_v3(vec2, vsta->no);
+ add_v3_v3(vec2, vend->no);
+ mul_v3_fl(vec2, 0.5f);
+
+ add_v3_v3v3(co2, v->co, params->off);
+ tvec[0] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
+ tvec[1] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
+ tvec[2] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
+
+ mul_v3_v3(vec2, tvec);
+
+ /* add displacemen */
+ add_v3_v3v3(co, co, vec2);
+ }
+
+ /* apply the new difference to the rest of the shape keys,
+ * note that this doent take rotations into account, we _could_ support
+ * this by getting the normals and coords for each shape key and
+ * re-calculate the smooth value for each but this is quite involved.
+ * for now its ok to simply apply the difference IMHO - campbell */
+ sub_v3_v3v3(tvec, prev_co, co);
+
+ for (i = 0; i < totlayer; i++) {
+ if (params->origkey != i) {
+ co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, i);
+ sub_v3_v3(co, tvec);
+ }
+ }
+
+}
+
+/* assumes in the edge is the correct interpolated vertices already */
+/* percent defines the interpolation, rad and flag are for special options */
+/* results in new vertex with correct coordinate, vertex normal and weight group info */
+static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge,
+ const subdparams *params, float percent,
+ float percent2,
+ BMEdge **out, BMVert *vsta, BMVert *vend)
+{
+ BMVert *ev;
+
+ ev = BM_edge_split(bm, edge->v1, edge, out, percent);
+
+ BMO_elem_flag_enable(bm, ev, ELE_INNER);
+
+ /* offset for smooth or sphere or fractal */
+ alter_co(bm, ev, oedge, params, percent2, vsta, vend);
+
+#if 0 //BMESH_TODO
+ /* clip if needed by mirror modifier */
+ if (edge->v1->f2) {
+ if (edge->v1->f2 & edge->v2->f2 & 1) {
+ co[0] = 0.0f;
+ }
+ if (edge->v1->f2 & edge->v2->f2 & 2) {
+ co[1] = 0.0f;
+ }
+ if (edge->v1->f2 & edge->v2->f2 & 4) {
+ co[2] = 0.0f;
+ }
+ }
+#endif
+
+ return ev;
+}
+
+static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, BMEdge *oedge,
+ int curpoint, int totpoint, const subdparams *params,
+ BMEdge **newe, BMVert *vsta, BMVert *vend)
+{
+ BMVert *ev;
+ float percent, percent2 = 0.0f;
+
+ if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1)
+ percent = BMO_slot_map_float_get(bm, params->op, "edgepercents", edge);
+ else {
+ percent = 1.0f / (float)(totpoint + 1-curpoint);
+ percent2 = (float)(curpoint + 1) / (float)(totpoint + 1);
+
+ }
+
+ ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent,
+ percent2, newe, vsta, vend);
+ return ev;
+}
+
+static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const subdparams *params,
+ BMVert *vsta, BMVert *vend)
+{
+ BMEdge *eed = edge, *newe, temp = *edge;
+ BMVert *v, ov1 = *edge->v1, ov2 = *edge->v2, *v1 = edge->v1, *v2 = edge->v2;
+ int i, numcuts = params->numcuts;
+
+ temp.v1 = &ov1;
+ temp.v2 = &ov2;
+
+ for (i = 0; i < numcuts; i++) {
+ v = subdivideedgenum(bm, eed, &temp, i, params->numcuts, params, &newe, vsta, vend);
+
+ BMO_elem_flag_enable(bm, v, SUBD_SPLIT);
+ BMO_elem_flag_enable(bm, eed, SUBD_SPLIT);
+ BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
+
+ BMO_elem_flag_enable(bm, v, ELE_SPLIT);
+ BMO_elem_flag_enable(bm, eed, ELE_SPLIT);
+ BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
+
+ BM_CHECK_ELEMENT(bm, v);
+ if (v->e) BM_CHECK_ELEMENT(bm, v->e);
+ if (v->e && v->e->l) BM_CHECK_ELEMENT(bm, v->e->l->f);
+ }
+
+ alter_co(bm, v1, &temp, params, 0, &ov1, &ov2);
+ alter_co(bm, v2, &temp, params, 1.0, &ov1, &ov2);
+}
+
+/* note: the patterns are rotated as necassary to
+ * match the input geometry. they're based on the
+ * pre-split state of the face */
+
+/*
+ * v3---------v2
+ * | |
+ * | |
+ * | |
+ * | |
+ * v4---v0---v1
+ */
+static void quad_1edge_split(BMesh *bm, BMFace *UNUSED(face),
+ BMVert **verts, const subdparams *params)
+{
+ BMFace *nf;
+ int i, add, numcuts = params->numcuts;
+
+ /* if it's odd, the middle face is a quad, otherwise it's a triangl */
+ if ((numcuts % 2) == 0) {
+ add = 2;
+ for (i = 0; i < numcuts; i++) {
+ if (i == numcuts / 2) {
+ add -= 1;
+ }
+ connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
+ }
+ }
+ else {
+ add = 2;
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
+ if (i == numcuts/2) {
+ add -= 1;
+ connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
+ }
+ }
+
+ }
+}
+
+static SubDPattern quad_1edge = {
+ {1, 0, 0, 0},
+ quad_1edge_split,
+ 4,
+};
+
+
+/*
+ * v6--------v5
+ * | |
+ * | |v4s
+ * | |v3s
+ * | s s |
+ * v7-v0--v1-v2
+ */
+static void quad_2edge_split_path(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ int i, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
+ }
+ connect_smallest_face(bm, verts[numcuts * 2 + 3], verts[numcuts * 2 + 1], &nf);
+}
+
+static SubDPattern quad_2edge_path = {
+ {1, 1, 0, 0},
+ quad_2edge_split_path,
+ 4,
+};
+
+/*
+ * v6--------v5
+ * | |
+ * | |v4s
+ * | |v3s
+ * | s s |
+ * v7-v0--v1-v2
+ */
+static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ BMVert *v, *lastv;
+ BMEdge *e, *ne, olde;
+ int i, numcuts = params->numcuts;
+
+ lastv = verts[numcuts];
+
+ for (i = numcuts - 1; i >= 0; i--) {
+ e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
+
+ olde = *e;
+ v = bm_subdivide_edge_addvert(bm, e, &olde, params, 0.5f, 0.5f, &ne, e->v1, e->v2);
+
+ if (i != numcuts - 1) {
+ connect_smallest_face(bm, lastv, v, &nf);
+ }
+
+ lastv = v;
+ }
+
+ connect_smallest_face(bm, lastv, verts[numcuts * 2 + 2], &nf);
+}
+
+static SubDPattern quad_2edge_innervert = {
+ {1, 1, 0, 0},
+ quad_2edge_split_innervert,
+ 4,
+};
+
+/*
+ * v6--------v5
+ * | |
+ * | |v4s
+ * | |v3s
+ * | s s |
+ * v7-v0--v1-v2
+ *
+ */
+static void quad_2edge_split_fan(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ /* BMVert *v; */ /* UNUSED */
+ /* BMVert *lastv = verts[2]; */ /* UNUSED */
+ /* BMEdge *e, *ne; */ /* UNUSED */
+ int i, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts * 2 + 2], &nf);
+ connect_smallest_face(bm, verts[numcuts + (numcuts - i)], verts[numcuts * 2 + 2], &nf);
+ }
+}
+
+static SubDPattern quad_2edge_fan = {
+ {1, 1, 0, 0},
+ quad_2edge_split_fan,
+ 4,
+};
+
+/*
+ * s s
+ * v8--v7--v6-v5
+ * | |
+ * | v4 s
+ * | |
+ * | v3 s
+ * | s s |
+ * v9-v0--v1-v2
+ */
+static void quad_3edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ int i, add = 0, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ if (i == numcuts / 2) {
+ if (numcuts % 2 != 0) {
+ connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
+ }
+ add = numcuts * 2 + 2;
+ }
+ connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
+ }
+
+ for (i = 0; i < numcuts / 2 + 1; i++) {
+ connect_smallest_face(bm, verts[i], verts[(numcuts - i) + numcuts * 2 + 1], &nf);
+ }
+}
+
+static SubDPattern quad_3edge = {
+ {1, 1, 1, 0},
+ quad_3edge_split,
+ 4,
+};
+
+/*
+ * v8--v7-v6--v5
+ * | s |
+ * |v9 s s|v4
+ * first line | | last line
+ * |v10s s s|v3
+ * v11-v0--v1-v2
+ *
+ * it goes from bottom up
+ */
+static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ BMVert *v, *v1, *v2;
+ BMEdge *e, *ne, temp;
+ BMVert **lines;
+ int numcuts = params->numcuts;
+ int i, j, a, b, s = numcuts + 2 /* , totv = numcuts * 4 + 4 */;
+
+ lines = MEM_callocN(sizeof(BMVert *)*(numcuts + 2)*(numcuts + 2), "q_4edge_split");
+ /* build a 2-dimensional array of verts,
+ * containing every vert (and all new ones)
+ * in the face */
+
+ /* first line */
+ for (i = 0; i < numcuts + 2; i++) {
+ lines[i] = verts[numcuts * 3 + 2 + (numcuts - i + 1)];
+ }
+
+ /* last line */
+ for (i = 0; i < numcuts + 2; i++) {
+ lines[(s - 1) * s + i] = verts[numcuts + i];
+ }
+
+ /* first and last members of middle lines */
+ for (i = 0; i < numcuts; i++) {
+ a = i;
+ b = numcuts + 1 + numcuts + 1 + (numcuts - i - 1);
+
+ e = connect_smallest_face(bm, verts[a], verts[b], &nf);
+ if (!e)
+ continue;
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+
+
+ v1 = lines[(i + 1)*s] = verts[a];
+ v2 = lines[(i + 1)*s + s - 1] = verts[b];
+
+ temp = *e;
+ for (a = 0; a < numcuts; a++) {
+ v = subdivideedgenum(bm, e, &temp, a, numcuts, params, &ne,
+ v1, v2);
+ if (!v)
+ bmesh_error();
+
+ BMO_elem_flag_enable(bm, ne, ELE_INNER);
+ lines[(i + 1) * s + a + 1] = v;
+ }
+ }
+
+ for (i = 1; i < numcuts + 2; i++) {
+ for (j = 1; j < numcuts + 1; j++) {
+ a = i * s + j;
+ b = (i - 1) * s + j;
+ e = connect_smallest_face(bm, lines[a], lines[b], &nf);
+ if (!e)
+ continue;
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+ }
+ }
+
+ MEM_freeN(lines);
+}
+
+/*
+ * v3
+ * / \
+ * / \
+ * / \
+ * / \
+ * / \
+ * v4--v0--v1--v2
+ * s s
+ */
+static void tri_1edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ int i, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts + 1], &nf);
+ }
+}
+
+static SubDPattern tri_1edge = {
+ {1, 0, 0},
+ tri_1edge_split,
+ 3,
+};
+
+/* v5
+ * / \
+ * s v6/---\ v4 s
+ * / \ / \
+ * sv7/---v---\ v3 s
+ * / \/ \/ \
+ * v8--v0--v1--v2
+ * s s
+ */
+static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ BMEdge *e, *ne, temp;
+ BMVert ***lines, *v, ov1, ov2;
+ void *stackarr[1];
+ int i, j, a, b, numcuts = params->numcuts;
+
+ /* number of verts in each lin */
+ lines = MEM_callocN(sizeof(void *)*(numcuts + 2), "triangle vert table");
+
+ lines[0] = (BMVert **) stackarr;
+ lines[0][0] = verts[numcuts * 2 + 1];
+
+ lines[numcuts + 1] = MEM_callocN(sizeof(void *) * (numcuts + 2), "triangle vert table 2");
+ for (i = 0; i < numcuts; i++) {
+ lines[numcuts + 1][i + 1] = verts[i];
+ }
+ lines[numcuts + 1][0] = verts[numcuts * 3 + 2];
+ lines[numcuts + 1][numcuts + 1] = verts[numcuts];
+
+ for (i = 0; i < numcuts; i++) {
+ lines[i + 1] = MEM_callocN(sizeof(void *)*(2 + i), "triangle vert table row");
+ a = numcuts * 2 + 2 + i;
+ b = numcuts + numcuts - i;
+ e = connect_smallest_face(bm, verts[a], verts[b], &nf);
+ if (!e) goto cleanup;
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+
+ lines[i + 1][0] = verts[a];
+ lines[i + 1][i + 1] = verts[b];
+
+ temp = *e;
+ ov1 = *verts[a];
+ ov2 = *verts[b];
+ temp.v1 = &ov1;
+ temp.v2 = &ov2;
+ for (j = 0; j < i; j++) {
+ v = subdivideedgenum(bm, e, &temp, j, i, params, &ne,
+ verts[a], verts[b]);
+ lines[i + 1][j + 1] = v;
+
+ BMO_elem_flag_enable(bm, ne, ELE_INNER);
+ }
+ }
+
+ /*
+ * v5
+ * / \
+ * s v6/---\ v4 s
+ * / \ / \
+ * sv7/---v---\ v3 s
+ * / \/ \/ \
+ * v8--v0--v1--v2
+ * s s
+ */
+ for (i = 1; i < numcuts + 1; i++) {
+ for (j = 0; j < i; j++) {
+ e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &nf);
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+
+ e = connect_smallest_face(bm, lines[i][j + 1], lines[i + 1][j + 1], &nf);
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+ }
+ }
+
+cleanup:
+ for (i = 1; i < numcuts + 2; i++) {
+ if (lines[i]) MEM_freeN(lines[i]);
+ }
+
+ MEM_freeN(lines);
+}
+
+static SubDPattern tri_3edge = {
+ {1, 1, 1},
+ tri_3edge_subdivide,
+ 3,
+};
+
+
+static SubDPattern quad_4edge = {
+ {1, 1, 1, 1},
+ quad_4edge_subdivide,
+ 4,
+};
+
+static SubDPattern *patterns[] = {
+ NULL, //quad single edge pattern is inserted here
+ NULL, //quad corner vert pattern is inserted here
+ NULL, //tri single edge pattern is inserted here
+ NULL,
+ &quad_3edge,
+ NULL,
+};
+
+#define PLEN (sizeof(patterns) / sizeof(void *))
+
+typedef struct subd_facedata {
+ BMVert *start; SubDPattern *pat;
+ int totedgesel; //only used if pat was NULL, e.g. no pattern was found
+ BMFace *face;
+} subd_facedata;
+
+void esubdivide_exec(BMesh *bmesh, BMOperator *op)
+{
+ BMOpSlot *einput;
+ SubDPattern *pat;
+ subdparams params;
+ subd_facedata *facedata = NULL;
+ BMIter viter, fiter, liter;
+ BMVert *v, **verts = NULL;
+ BMEdge *edge, **edges = NULL;
+ BMLoop *nl, *l, **splits = NULL, **loops = NULL;
+ BMFace *face;
+ BLI_array_declare(splits);
+ BLI_array_declare(loops);
+ BLI_array_declare(facedata);
+ BLI_array_declare(edges);
+ BLI_array_declare(verts);
+ float smooth, fractal;
+ int beauty, cornertype, singleedge, gridfill;
+ int skey, seed, i, j, matched, a, b, numcuts, totesel;
+
+ BMO_slot_buffer_flag_enable(bmesh, op, "edges", SUBD_SPLIT, BM_EDGE);
+
+ numcuts = BMO_slot_int_get(op, "numcuts");
+ seed = BMO_slot_int_get(op, "seed");
+ smooth = BMO_slot_float_get(op, "smooth");
+ fractal = BMO_slot_float_get(op, "fractal");
+ beauty = BMO_slot_int_get(op, "beauty");
+ cornertype = BMO_slot_int_get(op, "quadcornertype");
+ singleedge = BMO_slot_int_get(op, "singleedge");
+ gridfill = BMO_slot_int_get(op, "gridfill");
+
+ BLI_srandom(seed);
+
+ patterns[1] = NULL;
+ //straight cut is patterns[1] == NULL
+ switch (cornertype) {
+ case SUBD_PATH:
+ patterns[1] = &quad_2edge_path;
+ break;
+ case SUBD_INNERVERT:
+ patterns[1] = &quad_2edge_innervert;
+ break;
+ case SUBD_FAN:
+ patterns[1] = &quad_2edge_fan;
+ break;
+ }
+
+ if (singleedge) {
+ patterns[0] = &quad_1edge;
+ patterns[2] = &tri_1edge;
+ }
+ else {
+ patterns[0] = NULL;
+ patterns[2] = NULL;
+ }
+
+ if (gridfill) {
+ patterns[3] = &quad_4edge;
+ patterns[5] = &tri_3edge;
+ }
+ else {
+ patterns[3] = NULL;
+ patterns[5] = NULL;
+ }
+
+ /* add a temporary shapekey layer to store displacements on current geometr */
+ BM_data_layer_add(bmesh, &bmesh->vdata, CD_SHAPEKEY);
+ skey = CustomData_number_of_layers(&bmesh->vdata, CD_SHAPEKEY) - 1;
+
+ BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
+ float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
+ copy_v3_v3(co, v->co);
+ }
+
+ /* first go through and tag edge */
+ BMO_slot_from_flag(bmesh, op, "edges",
+ SUBD_SPLIT, BM_EDGE);
+
+ params.numcuts = numcuts;
+ params.op = op;
+ params.smooth = smooth;
+ params.seed = seed;
+ params.fractal = fractal;
+ params.beauty = beauty;
+ params.origkey = skey;
+ params.off[0] = (float)BLI_drand() * 200.0f;
+ params.off[1] = (float)BLI_drand() * 200.0f;
+ params.off[2] = (float)BLI_drand() * 200.0f;
+
+ BMO_slot_map_to_flag(bmesh, op, "custompatterns",
+ FACE_CUSTOMFILL);
+
+ BMO_slot_map_to_flag(bmesh, op, "edgepercents",
+ EDGE_PERCENT);
+
+ for (face = BM_iter_new(&fiter, bmesh, BM_FACES_OF_MESH, NULL);
+ face;
+ face = BM_iter_step(&fiter))
+ {
+ BMEdge *e1 = NULL, *e2 = NULL;
+ float vec1[3], vec2[3];
+
+ /* figure out which pattern to us */
+
+ BLI_array_empty(edges);
+ BLI_array_empty(verts);
+ matched = 0;
+
+ i = 0;
+ totesel = 0;
+ for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
+ BLI_array_growone(edges);
+ BLI_array_growone(verts);
+ edges[i] = nl->e;
+ verts[i] = nl->v;
+
+ if (BMO_elem_flag_test(bmesh, edges[i], SUBD_SPLIT)) {
+ if (!e1) e1 = edges[i];
+ else e2 = edges[i];
+
+ totesel++;
+ }
+
+ i++;
+ }
+
+ /* make sure the two edges have a valid angle to each othe */
+ if (totesel == 2 && BM_edge_share_vert(e1, e2)) {
+ float angle;
+
+ sub_v3_v3v3(vec1, e1->v2->co, e1->v1->co);
+ sub_v3_v3v3(vec2, e2->v2->co, e2->v1->co);
+ normalize_v3(vec1);
+ normalize_v3(vec2);
+
+ angle = dot_v3v3(vec1, vec2);
+ angle = fabsf(angle);
+ if (fabsf(angle - 1.0f) < 0.01f) {
+ totesel = 0;
+ }
+ }
+
+ if (BMO_elem_flag_test(bmesh, face, FACE_CUSTOMFILL)) {
+ pat = BMO_slot_map_data_get(bmesh, op,
+ "custompatterns", face);
+ for (i = 0; i < pat->len; i++) {
+ matched = 1;
+ for (j = 0; j < pat->len; j++) {
+ a = (j + i) % pat->len;
+ if ((!!BMO_elem_flag_test(bmesh, edges[a], SUBD_SPLIT)) != (!!pat->seledges[j])) {
+ matched = 0;
+ break;
+ }
+ }
+ if (matched) {
+ BLI_array_growone(facedata);
+ b = BLI_array_count(facedata) - 1;
+ facedata[b].pat = pat;
+ facedata[b].start = verts[i];
+ facedata[b].face = face;
+ facedata[b].totedgesel = totesel;
+ BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
+ break;
+ }
+ }
+
+ /* obvously don't test for other patterns matchin */
+ continue;
+ }
+
+ for (i = 0; i < PLEN; i++) {
+ pat = patterns[i];
+ if (!pat) continue;
+
+ if (pat->len == face->len) {
+ for (a = 0; a < pat->len; a++) {
+ matched = 1;
+ for (b = 0; b < pat->len; b++) {
+ j = (b + a) % pat->len;
+ if ((!!BMO_elem_flag_test(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) {
+ matched = 0;
+ break;
+ }
+ }
+ if (matched) {
+ break;
+ }
+ }
+ if (matched) {
+ BLI_array_growone(facedata);
+ j = BLI_array_count(facedata) - 1;
+
+ BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
+
+ facedata[j].pat = pat;
+ facedata[j].start = verts[a];
+ facedata[j].face = face;
+ facedata[j].totedgesel = totesel;
+ break;
+ }
+ }
+
+ }
+
+ if (!matched && totesel) {
+ BLI_array_growone(facedata);
+ j = BLI_array_count(facedata) - 1;
+
+ BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
+ facedata[j].totedgesel = totesel;
+ facedata[j].face = face;
+ }
+ }
+
+ einput = BMO_slot_get(op, "edges");
+
+ /* go through and split edge */
+ for (i = 0; i < einput->len; i++) {
+ edge = ((BMEdge **)einput->data.p)[i];
+ bm_subdivide_multicut(bmesh, edge, &params, edge->v1, edge->v2);
+ }
+
+ i = 0;
+ for (i = 0; i < BLI_array_count(facedata); i++) {
+ face = facedata[i].face;
+
+ /* figure out which pattern to us */
+ BLI_array_empty(verts);
+
+ pat = facedata[i].pat;
+
+ if (!pat && facedata[i].totedgesel == 2) {
+ int vlen;
+
+ /* ok, no pattern. we still may be able to do something */
+ BLI_array_empty(loops);
+ BLI_array_empty(splits);
+
+ /* for case of two edges, connecting them shouldn't be too har */
+ BM_ITER(l, &liter, bmesh, BM_LOOPS_OF_FACE, face) {
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = l;
+ }
+
+ vlen = BLI_array_count(loops);
+
+ /* find the boundary of one of the split edge */
+ for (a = 1; a < vlen; a++) {
+ if (!BMO_elem_flag_test(bmesh, loops[a - 1]->v, ELE_INNER) &&
+ BMO_elem_flag_test(bmesh, loops[a]->v, ELE_INNER))
+ {
+ break;
+ }
+ }
+
+ if (BMO_elem_flag_test(bmesh, loops[(a + numcuts + 1) % vlen]->v, ELE_INNER)) {
+ b = (a + numcuts + 1) % vlen;
+ }
+ else {
+ /* find the boundary of the other edge. */
+ for (j = 0; j < vlen; j++) {
+ b = (j + a + numcuts + 1) % vlen;
+ if (!BMO_elem_flag_test(bmesh, loops[b == 0 ? vlen - 1 : b - 1]->v, ELE_INNER) &&
+ BMO_elem_flag_test(bmesh, loops[b]->v, ELE_INNER))
+ {
+ break;
+ }
+ }
+ }
+
+ b += numcuts - 1;
+
+ for (j = 0; j < numcuts; j++) {
+ BLI_array_growone(splits);
+ splits[BLI_array_count(splits) - 1] = loops[a];
+
+ BLI_array_growone(splits);
+ splits[BLI_array_count(splits) - 1] = loops[b];
+
+ b = (b - 1) % vlen;
+ a = (a + 1) % vlen;
+ }
+
+ //BM_face_legal_splits(bmesh, face, splits, BLI_array_count(splits)/2);
+
+ for (j = 0; j < BLI_array_count(splits) / 2; j++) {
+ if (splits[j * 2]) {
+ /* BMFace *nf = */ /* UNUSED */
+ BM_face_split(bmesh, face, splits[j * 2]->v, splits[j * 2 + 1]->v, &nl, NULL);
+ }
+ }
+
+ continue;
+ }
+ else if (!pat) {
+ continue;
+ }
+
+ j = a = 0;
+ for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face);
+ nl;
+ nl = BM_iter_step(&liter))
+ {
+ if (nl->v == facedata[i].start) {
+ a = j + 1;
+ break;
+ }
+ j++;
+ }
+
+ for (j = 0; j < face->len; j++) {
+ BLI_array_growone(verts);
+ }
+
+ j = 0;
+ for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
+ b = (j - a + face->len) % face->len;
+ verts[b] = nl->v;
+ j += 1;
+ }
+
+ BM_CHECK_ELEMENT(bmesh, face);
+ pat->connectexec(bmesh, face, verts, &params);
+ }
+
+ /* copy original-geometry displacements to current coordinate */
+ BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
+ float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
+ copy_v3_v3(v->co, co);
+ }
+
+ BM_data_layer_free_n(bmesh, &bmesh->vdata, CD_SHAPEKEY, skey);
+
+ if (facedata) BLI_array_free(facedata);
+ if (edges) BLI_array_free(edges);
+ if (verts) BLI_array_free(verts);
+ BLI_array_free(splits);
+ BLI_array_free(loops);
+
+ BMO_slot_from_flag(bmesh, op, "outinner",
+ ELE_INNER, BM_ALL);
+ BMO_slot_from_flag(bmesh, op, "outsplit",
+ ELE_SPLIT, BM_ALL);
+
+ BMO_slot_from_flag(bmesh, op, "geomout",
+ ELE_INNER|ELE_SPLIT|SUBD_SPLIT, BM_ALL);
+}
+
+/* editmesh-emulating functio */
+void BM_mesh_esubdivideflag(Object *UNUSED(obedit), BMesh *bm, int flag, float smooth,
+ float fractal, int beauty, int numcuts,
+ int seltype, int cornertype, int singleedge,
+ int gridfill, int seed)
+{
+ BMOperator op;
+
+ BMO_op_initf(bm, &op, "esubd edges=%he smooth=%f fractal=%f "
+ "beauty=%d numcuts=%d quadcornertype=%d singleedge=%d "
+ "gridfill=%d seed=%d",
+ flag, smooth, fractal, beauty, numcuts,
+ cornertype, singleedge, gridfill, seed);
+
+ BMO_op_exec(bm, &op);
+
+ if (seltype == SUBDIV_SELECT_INNER) {
+ BMOIter iter;
+ BMHeader *ele;
+ // int i;
+
+ ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
+ for ( ; ele; ele = BMO_iter_step(&iter)) {
+ BM_elem_select_set(bm, ele, TRUE);
+ }
+ }
+ else if (seltype == SUBDIV_SELECT_LOOPCUT) {
+ BMOIter iter;
+ BMHeader *ele;
+ // int i;
+
+ /* deselect input */
+ BM_mesh_elem_flag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT);
+
+ ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
+ for ( ; ele; ele = BMO_iter_step(&iter)) {
+ BM_elem_select_set(bm, ele, TRUE);
+
+ if (ele->htype == BM_VERT) {
+ BMEdge *e;
+ BMIter eiter;
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, ele) {
+ if (!BM_elem_flag_test(e, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(e->v2, BM_ELEM_SELECT))
+ {
+ BM_elem_select_set(bm, e, TRUE);
+ bm->totedgesel += 1;
+ }
+ else if (BM_elem_flag_test(e, BM_ELEM_SELECT) &&
+ (!BM_elem_flag_test(e->v1, BM_ELEM_SELECT) ||
+ !BM_elem_flag_test(e->v2, BM_ELEM_SELECT)))
+ {
+ BM_elem_select_set(bm, e, FALSE);
+ bm->totedgesel -= 1;
+ }
+ }
+ }
+ }
+ }
+
+ BMO_op_finish(bm, &op);
+}
+
+void esplit_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e;
+ subdparams params;
+ int skey;
+
+ params.numcuts = BMO_slot_get(op, "numcuts")->data.i;
+ params.op = op;
+
+ BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY);
+ skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1;
+
+ params.origkey = skey;
+
+ /* go through and split edge */
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ bm_subdivide_multicut(bm, e, &params, e->v1, e->v2);
+ }
+
+ BMO_slot_from_flag(bm, op, "outsplit", ELE_SPLIT, BM_ALL);
+
+ BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey);
+}
diff --git a/source/blender/bmesh/operators/bmo_subdivide.h b/source/blender/bmesh/operators/bmo_subdivide.h
new file mode 100644
index 00000000000..dd5198bdc30
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_subdivide.h
@@ -0,0 +1,66 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMO_SUBDIVIDE_H__
+#define __BMO_SUBDIVIDE_H__
+
+/** \file blender/bmesh/operators/bmo_subdivide.h
+ * \ingroup bmesh
+ */
+
+typedef struct subdparams {
+ int numcuts;
+ float smooth;
+ float fractal;
+ int beauty;
+ int seed;
+ int origkey; /* shapekey holding displaced vertex coordinates for current geometry */
+ BMOperator *op;
+ float off[3];
+} subdparams;
+
+typedef void (*subd_pattern_fill_fp)(BMesh *bm, BMFace *face, BMVert **verts,
+ const subdparams *params);
+
+/*
+ * note: this is a pattern-based edge subdivider.
+ * it tries to match a pattern to edge selections on faces,
+ * then executes functions to cut them.
+ */
+typedef struct SubDPattern {
+ int seledges[20]; /* selected edges mask, for splitting */
+
+ /* verts starts at the first new vert cut, not the first vert in the face */
+ subd_pattern_fill_fp connectexec;
+ int len; /* total number of verts, before any subdivision */
+} SubDPattern;
+
+/* generic subdivision rules:
+ *
+ * - two selected edges in a face should make a link
+ * between them.
+ *
+ * - one edge should do, what? make pretty topology, or just
+ * split the edge only?
+ */
+
+#endif /* __BMO_SUBDIVIDE_H__ */
diff --git a/source/blender/bmesh/operators/bmo_triangulate.c b/source/blender/bmesh/operators/bmo_triangulate.c
new file mode 100644
index 00000000000..90efe0b6e44
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_triangulate.c
@@ -0,0 +1,219 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_scanfill.h"
+#include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_editVert.h"
+#include "BLI_smallhash.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#define EDGE_NEW 1
+#define FACE_NEW 1
+
+#define ELE_NEW 1
+#define FACE_MARK 2
+#define EDGE_MARK 4
+
+void triangulate_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMFace *face, **newfaces = NULL;
+ BLI_array_declare(newfaces);
+ float (*projectverts)[3] = NULL;
+ BLI_array_declare(projectverts);
+ int i, lastlen = 0 /* , count = 0 */;
+
+ face = BMO_iter_new(&siter, bm, op, "faces", BM_FACE);
+ for ( ; face; face = BMO_iter_step(&siter)) {
+ if (lastlen < face->len) {
+ BLI_array_empty(projectverts);
+ BLI_array_empty(newfaces);
+ for (lastlen = 0; lastlen < face->len; lastlen++) {
+ BLI_array_growone(projectverts);
+ BLI_array_growone(projectverts);
+ BLI_array_growone(projectverts);
+ BLI_array_growone(newfaces);
+ }
+ }
+
+ BM_face_triangulate(bm, face, projectverts, EDGE_NEW, FACE_NEW, newfaces);
+
+ BMO_slot_map_ptr_insert(bm, op, "facemap", face, face);
+ for (i = 0; newfaces[i]; i++) {
+ BMO_slot_map_ptr_insert(bm, op, "facemap",
+ newfaces[i], face);
+
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", EDGE_NEW, BM_EDGE);
+ BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
+
+ BLI_array_free(projectverts);
+ BLI_array_free(newfaces);
+}
+
+void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMFace *f;
+ BMEdge *e;
+ int stop = 0;
+
+ BMO_slot_buffer_flag_enable(bm, op, "constrain_edges", EDGE_MARK, BM_EDGE);
+
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ if (f->len == 3)
+ BMO_elem_flag_enable(bm, f, FACE_MARK);
+ }
+
+ while (!stop) {
+ stop = 1;
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMVert *v1, *v2, *v3, *v4;
+
+ if (BM_edge_face_count(e) != 2 || BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ if (!BMO_elem_flag_test(bm, e->l->f, FACE_MARK) ||
+ !BMO_elem_flag_test(bm, e->l->radial_next->f, FACE_MARK))
+ {
+ continue;
+ }
+
+ v1 = e->l->prev->v;
+ v2 = e->l->v;
+ v3 = e->l->radial_next->prev->v;
+ v4 = e->l->next->v;
+
+ if (is_quad_convex_v3(v1->co, v2->co, v3->co, v4->co)) {
+ float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2;
+ /* testing rule:
+ * the area divided by the total edge lengths
+ */
+ len1 = len_v3v3(v1->co, v2->co);
+ len2 = len_v3v3(v2->co, v3->co);
+ len3 = len_v3v3(v3->co, v4->co);
+ len4 = len_v3v3(v4->co, v1->co);
+ len5 = len_v3v3(v1->co, v3->co);
+ len6 = len_v3v3(v2->co, v4->co);
+
+ opp1 = area_tri_v3(v1->co, v2->co, v3->co);
+ opp2 = area_tri_v3(v1->co, v3->co, v4->co);
+
+ fac1 = opp1 / (len1 + len2 + len5) + opp2 / (len3 + len4 + len5);
+
+ opp1 = area_tri_v3(v2->co, v3->co, v4->co);
+ opp2 = area_tri_v3(v2->co, v4->co, v1->co);
+
+ fac2 = opp1 / (len2 + len3 + len6) + opp2 / (len4 + len1 + len6);
+
+ if (fac1 > fac2) {
+ e = BM_edge_rotate(bm, e, 0);
+ if (e) {
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+
+ BMO_elem_flag_enable(bm, e->l->f, FACE_MARK|ELE_NEW);
+ BMO_elem_flag_enable(bm, e->l->radial_next->f, FACE_MARK|ELE_NEW);
+ stop = 0;
+ }
+ }
+ }
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE);
+}
+
+void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e;
+ BMOperator bmop;
+ EditEdge *eed;
+ EditVert *eve, *v1, *v2;
+ EditFace *efa;
+ SmallHash hash;
+
+ BLI_smallhash_init(&hash);
+
+ BLI_begin_edgefill();
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+
+ if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v1)) {
+ eve = BLI_addfillvert(e->v1->co);
+ eve->tmp.p = e->v1;
+ BLI_smallhash_insert(&hash, (uintptr_t)e->v1, eve);
+ }
+
+ if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v2)) {
+ eve = BLI_addfillvert(e->v2->co);
+ eve->tmp.p = e->v2;
+ BLI_smallhash_insert(&hash, (uintptr_t)e->v2, eve);
+ }
+
+ v1 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v1);
+ v2 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v2);
+ eed = BLI_addfilledge(v1, v2);
+ eed->tmp.p = e;
+ }
+
+ BLI_edgefill(0);
+
+ for (efa = fillfacebase.first; efa; efa = efa->next) {
+ BMFace *f = BM_face_create_quad_tri(bm,
+ efa->v1->tmp.p, efa->v2->tmp.p, efa->v3->tmp.p, NULL,
+ NULL, TRUE);
+ BMLoop *l;
+ BMIter liter;
+
+ BMO_elem_flag_enable(bm, f, ELE_NEW);
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (!BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
+ BMO_elem_flag_enable(bm, l->e, ELE_NEW);
+ }
+ }
+ }
+
+ BLI_end_edgefill();
+ BLI_smallhash_release(&hash);
+
+ /* clean up fill */
+ BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", ELE_NEW, EDGE_MARK);
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", ELE_NEW, BM_FACE|BM_EDGE);
+ BMO_op_finish(bm, &bmop);
+
+ BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE);
+}
diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c
new file mode 100644
index 00000000000..e0187476a6a
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_utils.c
@@ -0,0 +1,1297 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_heap.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+/*
+ * UTILS.C
+ *
+ * utility bmesh operators, e.g. transform,
+ * translate, rotate, scale, etc.
+ *
+ */
+
+void bmesh_makevert_exec(BMesh *bm, BMOperator *op)
+{
+ float vec[3];
+
+ BMO_slot_vec_get(op, "co", vec);
+
+ BMO_elem_flag_enable(bm, BM_vert_create(bm, vec, NULL), 1);
+ BMO_slot_from_flag(bm, op, "newvertout", 1, BM_VERT);
+}
+
+void bmesh_transform_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter iter;
+ BMVert *v;
+ float mat[4][4];
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ BMO_ITER(v, &iter, bm, op, "verts", BM_VERT) {
+ mul_m4_v3(mat, v->co);
+ }
+}
+
+void bmesh_translate_exec(BMesh *bm, BMOperator *op)
+{
+ float mat[4][4], vec[3];
+
+ BMO_slot_vec_get(op, "vec", vec);
+
+ unit_m4(mat);
+ copy_v3_v3(mat[3], vec);
+
+ BMO_op_callf(bm, "transform mat=%m4 verts=%s", mat, op, "verts");
+}
+
+void bmesh_scale_exec(BMesh *bm, BMOperator *op)
+{
+ float mat[3][3], vec[3];
+
+ BMO_slot_vec_get(op, "vec", vec);
+
+ unit_m3(mat);
+ mat[0][0] = vec[0];
+ mat[1][1] = vec[1];
+ mat[2][2] = vec[2];
+
+ BMO_op_callf(bm, "transform mat=%m3 verts=%s", mat, op, "verts");
+}
+
+void bmesh_rotate_exec(BMesh *bm, BMOperator *op)
+{
+ float vec[3];
+
+ BMO_slot_vec_get(op, "cent", vec);
+
+ /* there has to be a proper matrix way to do this, but
+ * this is how editmesh did it and I'm too tired to think
+ * through the math right now. */
+ mul_v3_fl(vec, -1.0f);
+ BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
+
+ BMO_op_callf(bm, "transform mat=%s verts=%s", op, "mat", op, "verts");
+
+ mul_v3_fl(vec, -1.0f);
+ BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
+}
+
+void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMFace *f;
+
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ BM_face_normal_flip(bm, f);
+ }
+}
+
+void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e, *e2;
+ int ccw = BMO_slot_int_get(op, "ccw");
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ if (!(e2 = BM_edge_rotate(bm, e, ccw))) {
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge");
+ return;
+ }
+
+ BMO_elem_flag_enable(bm, e2, 1);
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", 1, BM_EDGE);
+}
+
+#define SEL_FLAG 1
+#define SEL_ORIG 2
+
+static void bmesh_regionextend_extend(BMesh *bm, BMOperator *op, int usefaces)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMIter eiter;
+ BMOIter siter;
+
+ if (!usefaces) {
+ BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
+ break;
+ }
+
+ if (e) {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ BMO_elem_flag_enable(bm, e, SEL_FLAG);
+ BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG);
+ }
+ }
+ }
+ }
+ else {
+ BMIter liter, fiter;
+ BMFace *f, *f2;
+ BMLoop *l;
+
+ BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
+ if (!BMO_elem_flag_test(bm, f2, SEL_ORIG))
+ BMO_elem_flag_enable(bm, f2, SEL_FLAG);
+ }
+ }
+ }
+ }
+}
+
+static void bmesh_regionextend_constrict(BMesh *bm, BMOperator *op, int usefaces)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMIter eiter;
+ BMOIter siter;
+
+ if (!usefaces) {
+ BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
+ break;
+ }
+
+ if (e) {
+ BMO_elem_flag_enable(bm, v, SEL_FLAG);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ BMO_elem_flag_enable(bm, e, SEL_FLAG);
+ }
+ }
+ }
+ }
+ else {
+ BMIter liter, fiter;
+ BMFace *f, *f2;
+ BMLoop *l;
+
+ BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
+ if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) {
+ BMO_elem_flag_enable(bm, f, SEL_FLAG);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void bmesh_regionextend_exec(BMesh *bm, BMOperator *op)
+{
+ int usefaces = BMO_slot_int_get(op, "usefaces");
+ int constrict = BMO_slot_int_get(op, "constrict");
+
+ BMO_slot_buffer_flag_enable(bm, op, "geom", SEL_ORIG, BM_ALL);
+
+ if (constrict)
+ bmesh_regionextend_constrict(bm, op, usefaces);
+ else
+ bmesh_regionextend_extend(bm, op, usefaces);
+
+ BMO_slot_from_flag(bm, op, "geomout", SEL_FLAG, BM_ALL);
+}
+
+/********* righthand faces implementation ****** */
+
+#define FACE_VIS 1
+#define FACE_FLAG 2
+#define FACE_MARK 4
+#define FACE_FLIP 8
+
+/* NOTE: these are the original righthandfaces comment in editmesh_mods.c,
+ * copied here for reference. */
+
+/* based at a select-connected to witness loose objects */
+
+/* count per edge the amount of faces
+ * find the ultimate left, front, upper face (not manhattan dist!!)
+ * also evaluate both triangle cases in quad, since these can be non-flat
+ *
+ * put normal to the outside, and set the first direction flags in edges
+ *
+ * then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces
+ * this is in fact the 'select connected'
+ *
+ * in case (selected) faces were not done: start over with 'find the ultimate ...' */
+
+/* NOTE: this function uses recursion, which is a little unusual for a bmop
+ * function, but acceptable I think. */
+
+/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */
+
+void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter liter, liter2;
+ BMOIter siter;
+ BMFace *f, *startf, **fstack = NULL;
+ BLI_array_declare(fstack);
+ BMLoop *l, *l2;
+ float maxx, cent[3];
+ int i, maxi, flagflip = BMO_slot_int_get(op, "doflip");
+
+ startf = NULL;
+ maxx = -1.0e10;
+
+ BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_FLAG, BM_FACE);
+
+ /* find a starting face */
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+
+ /* clear dirty flag */
+ BM_elem_flag_disable(f, BM_ELEM_TAG);
+
+ if (BMO_elem_flag_test(bm, f, FACE_VIS))
+ continue;
+
+ if (!startf) startf = f;
+
+ BM_face_center_bounds_calc(bm, f, cent);
+
+ cent[0] = cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2];
+ if (cent[0] > maxx) {
+ maxx = cent[0];
+ startf = f;
+ }
+ }
+
+ if (!startf) return;
+
+ BM_face_center_bounds_calc(bm, startf, cent);
+
+ /* make sure the starting face has the correct winding */
+ if (dot_v3v3(cent, startf->no) < 0.0f) {
+ BM_face_normal_flip(bm, startf);
+ BMO_elem_flag_toggle(bm, startf, FACE_FLIP);
+
+ if (flagflip)
+ BM_elem_flag_toggle(startf, BM_ELEM_TAG);
+ }
+
+ /* now that we've found our starting face, make all connected faces
+ * have the same winding. this is done recursively, using a manual
+ * stack (if we use simple function recursion, we'd end up overloading
+ * the stack on large meshes). */
+
+ BLI_array_growone(fstack);
+ fstack[0] = startf;
+ BMO_elem_flag_enable(bm, startf, FACE_VIS);
+
+ i = 0;
+ maxi = 1;
+ while (i >= 0) {
+ f = fstack[i];
+ i--;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_LOOP, l) {
+ if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l)
+ continue;
+
+ if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) {
+ BMO_elem_flag_enable(bm, l2->f, FACE_VIS);
+ i++;
+
+ if (l2->v == l->v) {
+ BM_face_normal_flip(bm, l2->f);
+
+ BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP);
+ if (flagflip)
+ BM_elem_flag_toggle(l2->f, BM_ELEM_TAG);
+ }
+ else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
+ if (flagflip) {
+ BM_elem_flag_disable(l->f, BM_ELEM_TAG);
+ BM_elem_flag_disable(l2->f, BM_ELEM_TAG);
+ }
+ }
+
+ if (i == maxi) {
+ BLI_array_growone(fstack);
+ maxi++;
+ }
+
+ fstack[i] = l2->f;
+ }
+ }
+ }
+ }
+
+ BLI_array_free(fstack);
+
+ /* check if we have faces yet to do. if so, recurse */
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ if (!BMO_elem_flag_test(bm, f, FACE_VIS)) {
+ bmesh_righthandfaces_exec(bm, op);
+ break;
+ }
+ }
+}
+
+void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v;
+ BMEdge *e;
+ BLI_array_declare(cos);
+ float (*cos)[3] = NULL;
+ float *co, *co2, clipdist = BMO_slot_float_get(op, "clipdist");
+ int i, j, clipx, clipy, clipz;
+
+ clipx = BMO_slot_int_get(op, "mirror_clip_x");
+ clipy = BMO_slot_int_get(op, "mirror_clip_y");
+ clipz = BMO_slot_int_get(op, "mirror_clip_z");
+
+ i = 0;
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BLI_array_growone(cos);
+ co = cos[i];
+
+ j = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
+ co2 = BM_edge_other_vert(e, v)->co;
+ add_v3_v3v3(co, co, co2);
+ j += 1;
+ }
+
+ if (!j) {
+ copy_v3_v3(co, v->co);
+ i++;
+ continue;
+ }
+
+ mul_v3_fl(co, 1.0f / (float)j);
+ mid_v3_v3v3(co, co, v->co);
+
+ if (clipx && fabsf(v->co[0]) <= clipdist)
+ co[0] = 0.0f;
+ if (clipy && fabsf(v->co[1]) <= clipdist)
+ co[1] = 0.0f;
+ if (clipz && fabsf(v->co[2]) <= clipdist)
+ co[2] = 0.0f;
+
+ i++;
+ }
+
+ i = 0;
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ copy_v3_v3(v->co, cos[i]);
+ i++;
+ }
+
+ BLI_array_free(cos);
+}
+
+/*
+ * compute the perimeter of an ngon
+ *
+ * NOTE: This should probably go to bmesh_polygon.c
+ */
+static float ngon_perimeter(BMesh *bm, BMFace *f)
+{
+ BMIter liter;
+ BMLoop *l;
+ int num_verts = 0;
+ float v[3], sv[3];
+ float perimeter = 0.0f;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (num_verts == 0) {
+ copy_v3_v3(v, l->v->co);
+ copy_v3_v3(sv, l->v->co);
+ }
+ else {
+ perimeter += len_v3v3(v, l->v->co);
+ copy_v3_v3(v, l->v->co);
+ }
+ num_verts++;
+ }
+
+ perimeter += len_v3v3(v, sv);
+
+ return perimeter;
+}
+
+/*
+ * compute the fake surface of an ngon
+ * This is done by decomposing the ngon into triangles who share the centroid of the ngon
+ * while this method is far from being exact, it should garantee an invariance.
+ *
+ * NOTE: This should probably go to bmesh_polygon.c
+ */
+static float ngon_fake_area(BMesh *bm, BMFace *f)
+{
+ BMIter liter;
+ BMLoop *l;
+ int num_verts = 0;
+ float v[3], sv[3], c[3];
+ float area = 0.0f;
+
+ BM_face_center_mean_calc(bm, f, c);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (num_verts == 0) {
+ copy_v3_v3(v, l->v->co);
+ copy_v3_v3(sv, l->v->co);
+ num_verts++;
+ }
+ else {
+ area += area_tri_v3(v, c, l->v->co);
+ copy_v3_v3(v, l->v->co);
+ num_verts++;
+ }
+ }
+
+ area += area_tri_v3(v, c, sv);
+
+ return area;
+}
+
+/*
+ * extra face data (computed data)
+ */
+typedef struct tmp_face_ext {
+ BMFace *f; /* the face */
+ float c[3]; /* center */
+ union {
+ float area; /* area */
+ float perim; /* perimeter */
+ float d; /* 4th component of plane (the first three being the normal) */
+ struct Image *t; /* image pointer */
+ };
+} tmp_face_ext;
+
+/*
+ * Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h
+ * We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces
+ */
+void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter fm_iter;
+ BMFace *fs, *fm;
+ BMOIter fs_iter;
+ int num_sels = 0, num_total = 0, i = 0, idx = 0;
+ float angle = 0.0f;
+ tmp_face_ext *f_ext = NULL;
+ int *indices = NULL;
+ float t_no[3]; /* temporary normal */
+ int type = BMO_slot_int_get(op, "type");
+ float thresh = BMO_slot_float_get(op, "thresh");
+
+ num_total = BM_mesh_elem_count(bm, BM_FACE);
+
+ /*
+ ** The first thing to do is to iterate through all the the selected items and mark them since
+ ** they will be in the selection anyway.
+ ** This will increase performance, (especially when the number of originaly selected faces is high)
+ ** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces,
+ ** and n is the total number of faces
+ */
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (!BMO_elem_flag_test(bm, fs, FACE_MARK)) { /* is this really needed ? */
+ BMO_elem_flag_enable(bm, fs, FACE_MARK);
+ num_sels++;
+ }
+ }
+
+ /* allocate memory for the selected faces indices and for all temporary faces */
+ indices = (int *)MEM_callocN(sizeof(int) * num_sels, "face indices util.c");
+ f_ext = (tmp_face_ext *)MEM_callocN(sizeof(tmp_face_ext) * num_total, "f_ext util.c");
+
+ /* loop through all the faces and fill the faces/indices structure */
+ BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) {
+ f_ext[i].f = fm;
+ if (BMO_elem_flag_test(bm, fm, FACE_MARK)) {
+ indices[idx] = i;
+ idx++;
+ }
+ i++;
+ }
+
+ /*
+ ** Save us some computation burden: In case of perimeter/area/coplanar selection we compute
+ ** only once.
+ */
+ if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) {
+ for (i = 0; i < num_total; i++) {
+ switch (type) {
+ case SIMFACE_PERIMETER:
+ /* set the perimeter */
+ f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f);
+ break;
+
+ case SIMFACE_COPLANAR:
+ /* compute the center of the polygon */
+ BM_face_center_mean_calc(bm, f_ext[i].f, f_ext[i].c);
+
+ /* normalize the polygon normal */
+ copy_v3_v3(t_no, f_ext[i].f->no);
+ normalize_v3(t_no);
+
+ /* compute the plane distance */
+ f_ext[i].d = dot_v3v3(t_no, f_ext[i].c);
+ break;
+
+ case SIMFACE_AREA:
+ f_ext[i].area = ngon_fake_area(bm, f_ext[i].f);
+ break;
+
+ case SIMFACE_IMAGE:
+ f_ext[i].t = NULL;
+ if (CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY)) {
+ MTexPoly *mtpoly = CustomData_bmesh_get(&bm->pdata, f_ext[i].f->head.data, CD_MTEXPOLY);
+ f_ext[i].t = mtpoly->tpage;
+ }
+ break;
+ }
+ }
+ }
+
+ /* now select the rest (if any) */
+ for (i = 0; i < num_total; i++) {
+ fm = f_ext[i].f;
+ if (!BMO_elem_flag_test(bm, fm, FACE_MARK) && !BM_elem_flag_test(fm, BM_ELEM_HIDDEN)) {
+ int cont = 1;
+ for (idx = 0; idx < num_sels && cont == 1; idx++) {
+ fs = f_ext[indices[idx]].f;
+ switch (type) {
+ case SIMFACE_MATERIAL:
+ if (fm->mat_nr == fs->mat_nr) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_IMAGE:
+ if (f_ext[i].t == f_ext[indices[idx]].t) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_NORMAL:
+ angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* if the angle between the normals -> 0 */
+ if (angle / 180.0f <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_COPLANAR:
+ angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* angle -> 0 */
+ if (angle / 180.0f <= thresh) { /* and dot product difference -> 0 */
+ if (fabsf(f_ext[i].d - f_ext[indices[idx]].d) <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ }
+ break;
+
+ case SIMFACE_AREA:
+ if (fabsf(f_ext[i].area - f_ext[indices[idx]].area) <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_PERIMETER:
+ if (fabsf(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(f_ext);
+ MEM_freeN(indices);
+
+ /* transfer all marked faces to the output slot */
+ BMO_slot_from_flag(bm, op, "faceout", FACE_MARK, BM_FACE);
+}
+
+/******************************************************************************
+** Similar Edges
+**************************************************************************** */
+#define EDGE_MARK 1
+
+/*
+ * compute the angle of an edge (i.e. the angle between two faces)
+ */
+static float edge_angle(BMesh *bm, BMEdge *e)
+{
+ BMIter fiter;
+ BMFace *f, *f_prev = NULL;
+
+ /* first edge faces, dont account for 3+ */
+
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
+ if (f_prev == NULL) {
+ f_prev = f;
+ }
+ else {
+ return angle_v3v3(f_prev->no, f->no);
+ }
+ }
+
+ return 0.0f;
+}
+/*
+ * extra edge information
+ */
+typedef struct tmp_edge_ext {
+ BMEdge *e;
+ union {
+ float dir[3];
+ float angle; /* angle between the face */
+ };
+
+ union {
+ float length; /* edge length */
+ int faces; /* faces count */
+ };
+} tmp_edge_ext;
+
+/*
+ * select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
+ * choices are length, direction, face, ...
+ */
+void bmesh_similaredges_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter es_iter; /* selected edges iterator */
+ BMIter e_iter; /* mesh edges iterator */
+ BMEdge *es; /* selected edge */
+ BMEdge *e; /* mesh edge */
+ int idx = 0, i = 0 /* , f = 0 */;
+ int *indices = NULL;
+ tmp_edge_ext *e_ext = NULL;
+ // float *angles = NULL;
+ float angle;
+
+ int num_sels = 0, num_total = 0;
+ int type = BMO_slot_int_get(op, "type");
+ float thresh = BMO_slot_float_get(op, "thresh");
+
+ num_total = BM_mesh_elem_count(bm, BM_EDGE);
+
+ /* iterate through all selected edges and mark them */
+ BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) {
+ BMO_elem_flag_enable(bm, es, EDGE_MARK);
+ num_sels++;
+ }
+
+ /* allocate memory for the selected edges indices and for all temporary edges */
+ indices = (int *)MEM_callocN(sizeof(int) * num_sels, "indices util.c");
+ e_ext = (tmp_edge_ext *)MEM_callocN(sizeof(tmp_edge_ext) * num_total, "e_ext util.c");
+
+ /* loop through all the edges and fill the edges/indices structure */
+ BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) {
+ e_ext[i].e = e;
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ indices[idx] = i;
+ idx++;
+ }
+ i++;
+ }
+
+ /* save us some computation time by doing heavy computation once */
+ if (type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || type == SIMEDGE_FACE_ANGLE) {
+ for (i = 0; i < num_total; i++) {
+ switch (type) {
+ case SIMEDGE_LENGTH: /* compute the length of the edge */
+ e_ext[i].length = len_v3v3(e_ext[i].e->v1->co, e_ext[i].e->v2->co);
+ break;
+
+ case SIMEDGE_DIR: /* compute the direction */
+ sub_v3_v3v3(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co);
+ break;
+
+ case SIMEDGE_FACE: /* count the faces around the edge */
+ e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
+ break;
+
+ case SIMEDGE_FACE_ANGLE:
+ e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
+ if (e_ext[i].faces == 2)
+ e_ext[i].angle = edge_angle(bm, e_ext[i].e);
+ break;
+ }
+ }
+ }
+
+ /* select the edges if any */
+ for (i = 0; i < num_total; i++) {
+ e = e_ext[i].e;
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ int cont = 1;
+ for (idx = 0; idx < num_sels && cont == 1; idx++) {
+ es = e_ext[indices[idx]].e;
+ switch (type) {
+ case SIMEDGE_LENGTH:
+ if (fabsf(e_ext[i].length - e_ext[indices[idx]].length) <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_DIR:
+ /* compute the angle between the two edges */
+ angle = RAD2DEGF(angle_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir));
+
+ if (angle > 90.0f) /* use the smallest angle between the edges */
+ angle = fabsf(angle - 180.0f);
+
+ if (angle / 90.0f <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_FACE:
+ if (e_ext[i].faces == e_ext[indices[idx]].faces) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_FACE_ANGLE:
+ if (e_ext[i].faces == 2) {
+ if (e_ext[indices[idx]].faces == 2) {
+ if (fabsf(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ }
+ }
+ else {
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_CREASE:
+ if (CustomData_has_layer(&bm->edata, CD_CREASE)) {
+ float *c1, *c2;
+
+ c1 = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
+ c2 = CustomData_bmesh_get(&bm->edata, es->head.data, CD_CREASE);
+
+ if (c1 && c2 && fabsf(*c1 - *c2) <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ }
+ break;
+
+ case SIMEDGE_SEAM:
+ if (BM_elem_flag_test(e, BM_ELEM_SEAM) == BM_elem_flag_test(es, BM_ELEM_SEAM)) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_SHARP:
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == BM_elem_flag_test(es, BM_ELEM_SMOOTH)) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(e_ext);
+ MEM_freeN(indices);
+
+ /* transfer all marked edges to the output slot */
+ BMO_slot_from_flag(bm, op, "edgeout", EDGE_MARK, BM_EDGE);
+}
+
+/******************************************************************************
+** Similar Vertices
+**************************************************************************** */
+#define VERT_MARK 1
+
+typedef struct tmp_vert_ext {
+ BMVert *v;
+ union {
+ int num_faces; /* adjacent faces */
+ MDeformVert *dvert; /* deform vertex */
+ };
+} tmp_vert_ext;
+
+/*
+ * select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
+ * choices are normal, face, vertex group...
+ */
+void bmesh_similarverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter vs_iter; /* selected verts iterator */
+ BMIter v_iter; /* mesh verts iterator */
+ BMVert *vs; /* selected vertex */
+ BMVert *v; /* mesh vertex */
+ tmp_vert_ext *v_ext = NULL;
+ int *indices = NULL;
+ int num_total = 0, num_sels = 0, i = 0, idx = 0;
+ int type = BMO_slot_int_get(op, "type");
+ float thresh = BMO_slot_float_get(op, "thresh");
+
+ num_total = BM_mesh_elem_count(bm, BM_VERT);
+
+ /* iterate through all selected edges and mark them */
+ BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) {
+ BMO_elem_flag_enable(bm, vs, VERT_MARK);
+ num_sels++;
+ }
+
+ /* allocate memory for the selected vertices indices and for all temporary vertices */
+ indices = (int *)MEM_mallocN(sizeof(int) * num_sels, "vertex indices");
+ v_ext = (tmp_vert_ext *)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra");
+
+ /* loop through all the vertices and fill the vertices/indices structure */
+ BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
+ v_ext[i].v = v;
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ indices[idx] = i;
+ idx++;
+ }
+
+ switch (type) {
+ case SIMVERT_FACE:
+ /* calling BM_vert_face_count every time is time consumming, so call it only once per vertex */
+ v_ext[i].num_faces = BM_vert_face_count(v);
+ break;
+
+ case SIMVERT_VGROUP:
+ if (CustomData_has_layer(&(bm->vdata), CD_MDEFORMVERT)) {
+ v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT);
+ }
+ else {
+ v_ext[i].dvert = NULL;
+ }
+ break;
+ }
+
+ i++;
+ }
+
+ /* select the vertices if any */
+ for (i = 0; i < num_total; i++) {
+ v = v_ext[i].v;
+ if (!BMO_elem_flag_test(bm, v, VERT_MARK) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+ int cont = 1;
+ for (idx = 0; idx < num_sels && cont == 1; idx++) {
+ vs = v_ext[indices[idx]].v;
+ switch (type) {
+ case SIMVERT_NORMAL:
+ /* compare the angle between the normals */
+ if (RAD2DEGF(angle_v3v3(v->no, vs->no)) / 180.0f <= thresh) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ cont = 0;
+ }
+ break;
+ case SIMVERT_FACE:
+ /* number of adjacent faces */
+ if (v_ext[i].num_faces == v_ext[indices[idx]].num_faces) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMVERT_VGROUP:
+ if (v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL) {
+ int v1, v2;
+ for (v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++) {
+ for (v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++) {
+ if (v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ cont = 0;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(indices);
+ MEM_freeN(v_ext);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+/******************************************************************************
+** Cycle UVs for a face
+**************************************************************************** */
+
+void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ // int n;
+
+ int dir = BMO_slot_int_get(op, "dir");
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
+ if (dir == DIRECTION_CW) { /* same loops direction */
+ BMLoop *lf; /* current face loops */
+ MLoopUV *f_luv; /* first face loop uv */
+ float p_uv[2]; /* previous uvs */
+ float t_uv[2]; /* tmp uvs */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop uv is the previous loop uv */
+ MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+ if (n == 0) {
+ f_luv = luv;
+ copy_v2_v2(p_uv, luv->uv);
+ }
+ else {
+ copy_v2_v2(t_uv, luv->uv);
+ copy_v2_v2(luv->uv, p_uv);
+ copy_v2_v2(p_uv, t_uv);
+ }
+ n++;
+ }
+
+ copy_v2_v2(f_luv->uv, p_uv);
+ }
+ else if (dir == DIRECTION_CCW) { /* counter loop direction */
+ BMLoop *lf; /* current face loops */
+ MLoopUV *p_luv; /* previous loop uv */
+ MLoopUV *luv;
+ float t_uv[2]; /* current uvs */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* previous loop uv is the current loop uv */
+ luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+ if (n == 0) {
+ p_luv = luv;
+ copy_v2_v2(t_uv, luv->uv);
+ }
+ else {
+ copy_v2_v2(p_luv->uv, luv->uv);
+ p_luv = luv;
+ }
+ n++;
+ }
+
+ copy_v2_v2(luv->uv, t_uv);
+ }
+ }
+ }
+
+}
+
+/******************************************************************************
+** Reverse UVs for a face
+**************************************************************************** */
+
+void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ BLI_array_declare(uvs);
+ float (*uvs)[2] = NULL;
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
+ BMLoop *lf; /* current face loops */
+ int i = 0;
+
+ BLI_array_empty(uvs);
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+
+ /* current loop uv is the previous loop uv */
+ BLI_array_growone(uvs);
+ uvs[i][0] = luv->uv[0];
+ uvs[i][1] = luv->uv[1];
+ i++;
+ }
+
+ /* now that we have the uvs in the array, reverse! */
+ i = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop uv is the previous loop uv */
+ MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+ luv->uv[0] = uvs[(fs->len - i - 1)][0];
+ luv->uv[1] = uvs[(fs->len - i - 1)][1];
+ i++;
+ }
+ }
+ }
+
+ BLI_array_free(uvs);
+}
+
+/******************************************************************************
+** Cycle colors for a face
+**************************************************************************** */
+
+void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ // int n;
+
+ int dir = BMO_slot_int_get(op, "dir");
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
+ if (dir == DIRECTION_CW) { /* same loops direction */
+ BMLoop *lf; /* current face loops */
+ MLoopCol *f_lcol; /* first face loop color */
+ MLoopCol p_col; /* previous color */
+ MLoopCol t_col; /* tmp color */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop color is the previous loop color */
+ MLoopCol *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+ if (n == 0) {
+ f_lcol = luv;
+ p_col = *luv;
+ }
+ else {
+ t_col = *luv;
+ *luv = p_col;
+ p_col = t_col;
+ }
+ n++;
+ }
+
+ *f_lcol = p_col;
+ }
+ else if (dir == DIRECTION_CCW) { /* counter loop direction */
+ BMLoop *lf; /* current face loops */
+ MLoopCol *p_lcol; /* previous loop color */
+ MLoopCol *lcol;
+ MLoopCol t_col; /* current color */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* previous loop color is the current loop color */
+ lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+ if (n == 0) {
+ p_lcol = lcol;
+ t_col = *lcol;
+ }
+ else {
+ *p_lcol = *lcol;
+ p_lcol = lcol;
+ }
+ n++;
+ }
+
+ *lcol = t_col;
+ }
+ }
+ }
+}
+
+/******************************************************************************
+** Reverse colors for a face
+**************************************************************************** */
+
+void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ BLI_array_declare(cols);
+ MLoopCol *cols = NULL;
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
+ BMLoop *lf; /* current face loops */
+ int i = 0;
+
+ BLI_array_empty(cols);
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+
+ /* current loop uv is the previous loop color */
+ BLI_array_growone(cols);
+ cols[i] = *lcol;
+ i++;
+ }
+
+ /* now that we have the uvs in the array, reverse! */
+ i = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop uv is the previous loop color */
+ MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+ *lcol = cols[(fs->len - i - 1)];
+ i++;
+ }
+ }
+ }
+
+ BLI_array_free(cols);
+}
+
+
+/******************************************************************************
+** shortest vertex path select
+**************************************************************************** */
+
+typedef struct element_node {
+ BMVert *v; /* vertex */
+ BMVert *parent; /* node parent id */
+ float weight; /* node weight */
+ HeapNode *hn; /* heap node */
+} element_node;
+
+void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter vs_iter /* , vs2_iter */; /* selected verts iterator */
+ BMIter v_iter; /* mesh verts iterator */
+ BMVert *vs, *sv, *ev; /* starting vertex, ending vertex */
+ BMVert *v; /* mesh vertex */
+ Heap *h = NULL;
+
+ element_node *vert_list = NULL;
+
+ int num_total = 0 /*, num_sels = 0 */, i = 0;
+ int type = BMO_slot_int_get(op, "type");
+
+ BMO_ITER(vs, &vs_iter, bm, op, "startv", BM_VERT) {
+ sv = vs;
+ }
+ BMO_ITER(vs, &vs_iter, bm, op, "endv", BM_VERT) {
+ ev = vs;
+ }
+
+ num_total = BM_mesh_elem_count(bm, BM_VERT);
+
+ /* allocate memory for the nodes */
+ vert_list = (element_node *)MEM_mallocN(sizeof(element_node) * num_total, "vertex nodes");
+
+ /* iterate through all the mesh vertices */
+ /* loop through all the vertices and fill the vertices/indices structure */
+ i = 0;
+ BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
+ vert_list[i].v = v;
+ vert_list[i].parent = NULL;
+ vert_list[i].weight = FLT_MAX;
+ BM_elem_index_set(v, i); /* set_inline */
+ i++;
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+ /*
+ ** we now have everything we need, start Dijkstra path finding algorithm
+ */
+
+ /* set the distance/weight of the start vertex to 0 */
+ vert_list[BM_elem_index_get(sv)].weight = 0.0f;
+
+ h = BLI_heap_new();
+
+ for (i = 0; i < num_total; i++) {
+ vert_list[i].hn = BLI_heap_insert(h, vert_list[i].weight, vert_list[i].v);
+ }
+
+ while (!BLI_heap_empty(h)) {
+ BMEdge *e;
+ BMIter e_i;
+ float v_weight;
+
+ /* take the vertex with the lowest weight out of the heap */
+ BMVert *v = (BMVert *)BLI_heap_popmin(h);
+
+ if (vert_list[BM_elem_index_get(v)].weight == FLT_MAX) /* this means that there is no path */
+ break;
+
+ v_weight = vert_list[BM_elem_index_get(v)].weight;
+
+ BM_ITER(e, &e_i, bm, BM_EDGES_OF_VERT, v) {
+ BMVert *u;
+ float e_weight = v_weight;
+
+ if (type == VPATH_SELECT_EDGE_LENGTH)
+ e_weight += len_v3v3(e->v1->co, e->v2->co);
+ else e_weight += 1.0f;
+
+ u = (e->v1 == v) ? e->v2 : e->v1;
+
+ if (e_weight < vert_list[BM_elem_index_get(u)].weight) { /* is this path shorter ? */
+ /* add it if so */
+ vert_list[BM_elem_index_get(u)].parent = v;
+ vert_list[BM_elem_index_get(u)].weight = e_weight;
+
+ /* we should do a heap update node function!!! :-/ */
+ BLI_heap_remove(h, vert_list[BM_elem_index_get(u)].hn);
+ BLI_heap_insert(h, e_weight, u);
+ }
+ }
+ }
+
+ /* now we trace the path (if it exists) */
+ v = ev;
+
+ while (vert_list[BM_elem_index_get(v)].parent != NULL) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ v = vert_list[BM_elem_index_get(v)].parent;
+ }
+
+ BLI_heap_free(h, NULL);
+ MEM_freeN(vert_list);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
diff --git a/source/blender/bmesh/tools/BME_bevel.c b/source/blender/bmesh/tools/BME_bevel.c
new file mode 100644
index 00000000000..b20b7316eb4
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_bevel.c
@@ -0,0 +1,1011 @@
+/*
+ * ***** 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 and Levi Schooley.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_listBase.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BKE_utildefines.h"
+#include "BKE_tessmesh.h"
+#include "BKE_bmesh.h"
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+/* ------- Bevel code starts here -------- */
+
+BME_TransData_Head *BME_init_transdata(int bufsize)
+{
+ BME_TransData_Head *td;
+
+ td = MEM_callocN(sizeof(BME_TransData_Head), "BM transdata header");
+ td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "BME_init_transdata gh");
+ td->ma = BLI_memarena_new(bufsize, "BME_TransData arena");
+ BLI_memarena_use_calloc(td->ma);
+
+ return td;
+}
+
+void BME_free_transdata(BME_TransData_Head *td)
+{
+ BLI_ghash_free(td->gh, NULL, NULL);
+ BLI_memarena_free(td->ma);
+ MEM_freeN(td);
+}
+
+BME_TransData *BME_assign_transdata(
+ BME_TransData_Head *td, BMesh *bm, BMVert *v,
+ float *co, float *org, float *vec, float *loc,
+ float factor, float weight, float maxfactor, float *max)
+{
+ BME_TransData *vtd;
+ int is_new = 0;
+
+ if (v == NULL) {
+ return NULL;
+ }
+
+ if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) {
+ vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd));
+ BLI_ghash_insert(td->gh, v, vtd);
+ td->len++;
+ is_new = 1;
+ }
+
+ vtd->bm = bm;
+ vtd->v = v;
+
+ if (co != NULL) {
+ copy_v3_v3(vtd->co, co);
+ }
+
+ if (org == NULL && is_new) {
+ copy_v3_v3(vtd->org, v->co); /* default */
+ }
+ else if (org != NULL) {
+ copy_v3_v3(vtd->org, org);
+ }
+
+ if (vec != NULL) {
+ copy_v3_v3(vtd->vec, vec);
+ normalize_v3(vtd->vec);
+ }
+
+ vtd->loc = loc;
+
+ vtd->factor = factor;
+ vtd->weight = weight;
+ vtd->maxfactor = maxfactor;
+ vtd->max = max;
+
+ return vtd;
+}
+
+BME_TransData *BME_get_transdata(BME_TransData_Head *td, BMVert *v)
+{
+ BME_TransData *vtd;
+ vtd = BLI_ghash_lookup(td->gh, v);
+ return vtd;
+}
+
+/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */
+float *BME_new_transdata_float(BME_TransData_Head *td)
+{
+ return BLI_memarena_alloc(td->ma, sizeof(float));
+}
+
+/* BM_disk_dissolve is a real mess, and crashes bevel if called instead of this.
+ * The drawback, though, is that this code doesn't merge customdata. */
+static int BME_Bevel_Dissolve_Disk(BMesh *bm, BMVert *v)
+{
+ BMIter iter;
+ BMEdge *e, *elast;
+ BMLoop *l1, *l2;
+
+ if (!BM_vert_is_manifold(bm, v)) {
+ return 0;
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (BM_edge_face_count(e) != 2) {
+ return 0;
+ }
+ }
+
+ if (BM_vert_edge_count(v) > 2) {
+ while (BM_vert_edge_count(v) > 2) {
+ e = v->e;
+ l1 = e->l;
+ l2 = l1->radial_next;
+ bmesh_jfke(bm, l1->f, l2->f, e);
+ }
+
+ e = v->e;
+ elast = bmesh_disk_nextedge(e, v);
+ bmesh_jekv(bm, e, v);
+
+ l1 = elast->l;
+ l2 = l1->radial_next;
+ bmesh_jfke(bm, l1->f, l2->f, elast);
+ }
+
+ return 1;
+}
+
+static int BME_bevel_is_split_vert(BMesh *bm, BMLoop *l)
+{
+ /* look for verts that have already been added to the edge when
+ * beveling other polys; this can be determined by testing the
+ * vert and the edges around it for originality
+ */
+ if ( !BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
+ BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG) &&
+ BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/* get a vector, vec, that points from v1->co to wherever makes sense to
+ * the bevel operation as a whole based on the relationship between v1 and v2
+ * (won't necessarily be a vec from v1->co to v2->co, though it probably will be);
+ * the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */
+static int BME_bevel_get_vec(float *vec, BMVert *v1, BMVert *v2, BME_TransData_Head *td)
+{
+ BME_TransData *vtd1, *vtd2;
+
+ vtd1 = BME_get_transdata(td, v1);
+ vtd2 = BME_get_transdata(td, v2);
+ if (!vtd1 || !vtd2) {
+ //printf("BME_bevel_get_vec() got called without proper BME_TransData\n");
+ return -1;
+ }
+
+ /* compare the transform origins to see if we can use the vert co's;
+ * if they belong to different origins, then we will use the origins to determine
+ * the vector */
+ if (compare_v3v3(vtd1->org, vtd2->org, 0.000001f)) {
+ sub_v3_v3v3(vec, v2->co, v1->co);
+ if (len_v3(vec) < 0.000001f) {
+ zero_v3(vec);
+ }
+ return 0;
+ }
+ else {
+ sub_v3_v3v3(vec, vtd2->org, vtd1->org);
+ if (len_v3(vec) < 0.000001f) {
+ zero_v3(vec);
+ }
+ return 1;
+ }
+}
+
+/* "Projects" a vector perpendicular to vec2 against vec1, such that
+ * the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2.
+ * note: the direction, is_forward, is used in conjunction with up_vec to determine
+ * whether this is a convex or concave corner. If it is a concave corner, it will
+ * be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards).
+ * vec1 is the vector to project onto (expected to be normalized)
+ * vec2 is the direction of projection (pointing away from vec1)
+ * up_vec is used for orientation (expected to be normalized)
+ * returns the length of the projected vector that lies along vec1 */
+static float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *UNUSED(td))
+{
+ float factor, vec3[3], tmp[3], c1, c2;
+
+ cross_v3_v3v3(tmp, vec1, vec2);
+ normalize_v3(tmp);
+ factor = dot_v3v3(up_vec, tmp);
+ if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) {
+ cross_v3_v3v3(vec3, vec2, tmp); /* hmm, maybe up_vec should be used instead of tmp */
+ }
+ else {
+ cross_v3_v3v3(vec3, tmp, vec2); /* hmm, maybe up_vec should be used instead of tmp */
+ }
+ normalize_v3(vec3);
+ c1 = dot_v3v3(vec3, vec1);
+ c2 = dot_v3v3(vec1, vec1);
+ if (fabsf(c1) < 0.000001f || fabsf(c2) < 0.000001f) {
+ factor = 0.0f;
+ }
+ else {
+ factor = c2 / c1;
+ }
+
+ return factor;
+}
+
+/* BME_bevel_split_edge() is the main math work-house; its responsibilities are:
+ * using the vert and the loop passed, get or make the split vert, set its coordinates
+ * and transform properties, and set the max limits.
+ * Finally, return the split vert. */
+static BMVert *BME_bevel_split_edge(BMesh *bm, BMVert *v, BMVert *v1, BMLoop *l, float *up_vec, float value, BME_TransData_Head *td)
+{
+ BME_TransData *vtd, *vtd1, *vtd2;
+ BMVert *sv, *v2, *v3, *ov;
+ BMLoop *lv1, *lv2;
+ BMEdge *ne, *e1, *e2;
+ float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3];
+ int is_edge, forward, is_split_vert;
+
+ if (l == NULL) {
+ /* what you call operator overloading in C :)
+ * I wanted to use the same function for both wire edges and poly loops
+ * so... here we walk around edges to find the needed verts */
+ forward = 1;
+ is_split_vert = 0;
+ if (v->e == NULL) {
+ //printf("We can't split a loose vert's edge!\n");
+ return NULL;
+ }
+ e1 = v->e; /* we just use the first two edges */
+ e2 = bmesh_disk_nextedge(v->e, v);
+ if (e1 == e2) {
+ //printf("You need at least two edges to use BME_bevel_split_edge()\n");
+ return NULL;
+ }
+ v2 = BM_edge_other_vert(e1, v);
+ v3 = BM_edge_other_vert(e2, v);
+ if (v1 != v2 && v1 != v3) {
+ //printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n");
+ return NULL;
+ }
+ if (v1 == v2) {
+ v2 = v3;
+ }
+ else {
+ e1 = e2;
+ }
+ ov = BM_edge_other_vert(e1, v);
+ sv = BM_edge_split(bm, v, e1, &ne, 0);
+ //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
+ //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
+ //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
+ BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
+ BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
+ BME_bevel_get_vec(vec1, v1, v, td);
+ BME_bevel_get_vec(vec2, v2, v, td);
+ cross_v3_v3v3(t_up_vec, vec1, vec2);
+ normalize_v3(t_up_vec);
+ up_vec = t_up_vec;
+ }
+ else {
+ /* establish loop direction */
+ if (l->v == v) {
+ forward = 1;
+ lv1 = l->next;
+ lv2 = l->prev;
+ v1 = l->next->v;
+ v2 = l->prev->v;
+ }
+ else if (l->next->v == v) {
+ forward = 0;
+ lv1 = l;
+ lv2 = l->next->next;
+ v1 = l->v;
+ v2 = l->next->next->v;
+ }
+ else {
+ //printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n");
+ return NULL;
+ }
+
+ if (BME_bevel_is_split_vert(bm, lv1)) {
+ is_split_vert = 1;
+ sv = v1;
+ v1 = forward ? l->next->next->v : l->prev->v;
+ }
+ else {
+ is_split_vert = 0;
+ ov = BM_edge_other_vert(l->e, v);
+ sv = BM_edge_split(bm, v, l->e, &ne, 0);
+ //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
+ //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
+ //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
+ BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
+ BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
+ }
+
+ if (BME_bevel_is_split_vert(bm, lv2)) {
+ v2 = forward ? lv2->prev->v : lv2->next->v;
+ }
+ }
+
+ is_edge = BME_bevel_get_vec(vec1, v, v1, td); /* get the vector we will be projecting onto */
+ BME_bevel_get_vec(vec2, v, v2, td); /* get the vector we will be projecting parallel to */
+ len = len_v3(vec1);
+ normalize_v3(vec1);
+
+ vtd = BME_get_transdata(td, sv);
+ vtd1 = BME_get_transdata(td, v);
+ vtd2 = BME_get_transdata(td, v1);
+
+ if (vtd1->loc == NULL) {
+ /* this is a vert with data only for calculating initial weights */
+ if (vtd1->weight < 0) {
+ vtd1->weight = 0;
+ }
+ scale = vtd1->weight / vtd1->factor;
+ if (!vtd1->max) {
+ vtd1->max = BME_new_transdata_float(td);
+ *vtd1->max = -1;
+ }
+ }
+ else {
+ scale = vtd1->weight;
+ }
+ vtd->max = vtd1->max;
+
+ if (is_edge && vtd1->loc != NULL) {
+ maxfactor = vtd1->maxfactor;
+ }
+ else {
+ maxfactor = scale * BME_bevel_project_vec(vec1, vec2, up_vec, forward, td);
+ if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) {
+ maxfactor = vtd->maxfactor;
+ }
+ }
+
+ dis = BMO_elem_flag_test(bm, v1, BME_BEVEL_ORIG) ? len / 3 : len / 2;
+ if (is_edge || dis > maxfactor * value) {
+ dis = maxfactor * value;
+ }
+ madd_v3_v3v3fl(sv->co, v->co, vec1, dis);
+ sub_v3_v3v3(vec1, sv->co, vtd1->org);
+ dis = len_v3(vec1);
+ normalize_v3(vec1);
+ BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max);
+
+ return sv;
+}
+
+#if 0 /* UNUSED */
+static float BME_bevel_set_max(BMVert *v1, BMVert *v2, float value, BME_TransData_Head *td)
+{
+ BME_TransData *vtd1, *vtd2;
+ float max, fac1, fac2, vec1[3], vec2[3], vec3[3];
+
+ BME_bevel_get_vec(vec1, v1, v2, td);
+ vtd1 = BME_get_transdata(td, v1);
+ vtd2 = BME_get_transdata(td, v2);
+
+ if (vtd1->loc == NULL) {
+ fac1 = 0;
+ }
+ else {
+ copy_v3_v3(vec2, vtd1->vec);
+ mul_v3_fl(vec2, vtd1->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec2, vec1);
+ fac1 = len_v3(vec2) / value;
+ }
+ else {
+ fac1 = 0;
+ }
+ }
+
+ if (vtd2->loc == NULL) {
+ fac2 = 0;
+ }
+ else {
+ copy_v3_v3(vec3, vtd2->vec);
+ mul_v3_fl(vec3, vtd2->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec3, vec1);
+ fac2 = len_v3(vec2) / value;
+ }
+ else {
+ fac2 = 0;
+ }
+ }
+
+ if (fac1 || fac2) {
+ max = len_v3(vec1) / (fac1 + fac2);
+ if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
+ *vtd1->max = max;
+ }
+ if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
+ *vtd2->max = max;
+ }
+ }
+ else {
+ max = -1;
+ }
+
+ return max;
+}
+#endif
+
+#if 0 /* UNUSED */
+static BMVert *BME_bevel_wire(BMesh *bm, BMVert *v, float value, int res, int UNUSED(options), BME_TransData_Head *td)
+{
+ BMVert *ov1, *ov2, *v1, *v2;
+
+ ov1 = BM_edge_other_vert(v->e, v);
+ ov2 = BM_edge_other_vert(bmesh_disk_nextedge(v->e, v), v);
+
+ /* split the edges */
+ v1 = BME_bevel_split_edge(bm, v, ov1, NULL, NULL, value, td);
+ BMO_elem_flag_enable(bm, v1, BME_BEVEL_NONMAN);
+ v2 = BME_bevel_split_edge(bm, v, ov2, NULL, NULL, value, td);
+ BMO_elem_flag_enable(bm, v2, BME_BEVEL_NONMAN);
+
+ if (value > 0.5) {
+ BME_bevel_set_max(v1, ov1, value, td);
+ BME_bevel_set_max(v2, ov2, value, td);
+ }
+
+ /* remove the original vert */
+ if (res) {
+ /* bmesh_jekv; */
+
+ //void BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){
+ //hrm, why is there a fac here? it just removes a vert
+ BM_vert_collapse_edges(bm, v->e, v);
+ }
+
+ return v1;
+}
+#endif
+
+static BMLoop *BME_bevel_edge(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
+{
+ BMVert *v1, *v2, *kv;
+ BMLoop *kl = NULL, *nl;
+ BMEdge *e, *ke, *se;
+ BMFace *f, *jf;
+
+ f = l->f;
+ e = l->e;
+
+ /* sanity check */
+ if ( !BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) &&
+ (BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_BEVEL)))
+ {
+ return l;
+ }
+
+ /* checks and operations for prev edge */
+ /* first, check to see if this edge was inset previously */
+ if ( !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG) &&
+ !BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN))
+ {
+ kl = l->prev->radial_next;
+ kl = (kl->v == l->v) ? kl->prev : kl->next;
+ kv = l->v;
+ }
+ else {
+ kv = NULL;
+ }
+ /* get/make the first vert to be used in SFME */
+ if (BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN)) {
+ v1 = l->v;
+ }
+ else { /* we'll need to split the previous edge */
+ v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
+ }
+ /* if we need to clean up geometry... */
+ if (kv) {
+ se = l->next->e;
+ jf = NULL;
+ if (kl->v == kv) {
+ BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ else {
+ BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ /* find saved loop pointer */
+ l = se->l;
+ while (l->f != jf) {
+ l = bmesh_radial_nextloop(l);
+ BLI_assert(l != se->l);
+ }
+ l = l->prev;
+ }
+
+ /* checks and operations for the next edge */
+ /* first, check to see if this edge was inset previously */
+ if ( !BMO_elem_flag_test(bm, l->next->e, BME_BEVEL_ORIG) &&
+ !BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN))
+ {
+ kl = l->next->radial_next;
+ kl = (kl->v == l->next->v) ? kl->prev : kl->next;
+ kv = l->next->v;
+ }
+ else {
+ kv = NULL;
+ }
+ /* get/make the second vert to be used in SFME */
+ if (BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN)) {
+ v2 = l->next->v;
+ }
+ else { /* we'll need to split the next edge */
+ v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
+ }
+ /* if we need to clean up geometry... */
+ if (kv) {
+ se = l->e;
+ jf = NULL;
+ if (kl->v == kv) {
+ BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ else {
+ BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ /* find saved loop pointer */
+ l = se->l;
+ while (l->f != jf) {
+ l = bmesh_radial_nextloop(l);
+ BLI_assert(l != se->l);
+ }
+ }
+
+ if (!BMO_elem_flag_test(bm, v1, BME_BEVEL_NONMAN) || !BMO_elem_flag_test(bm, v2, BME_BEVEL_NONMAN)) {
+ BM_face_split(bm, f, v2, v1, &l, e);
+ BMO_elem_flag_enable(bm, l->e, BME_BEVEL_BEVEL);
+ l = l->radial_next;
+ }
+
+ if (l->f != f){
+ //printf("Whoops! You got something out of order in BME_bevel_edge()!\n");
+ }
+
+ return l;
+}
+
+static BMLoop *BME_bevel_vert(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
+{
+ BMVert *v1, *v2;
+ BMFace *f;
+
+ /* get/make the first vert to be used in SFME */
+ /* may need to split the previous edge */
+ v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
+
+ /* get/make the second vert to be used in SFME */
+ /* may need to split this edge (so move l) */
+ l = l->prev;
+ v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
+ l = l->next->next;
+
+ /* "cut off" this corner */
+ f = BM_face_split(bm, l->f, v2, v1, NULL, l->e);
+
+ return l;
+}
+
+/*
+ * BME_bevel_poly
+ *
+ * Polygon inset tool:
+ *
+ * Insets a polygon/face based on the flagss of its vertices
+ * and edges. Used by the bevel tool only, for now.
+ * The parameter "value" is the distance to inset (should be negative).
+ * The parameter "options" is not currently used.
+ *
+ * Returns -
+ * A BMFace pointer to the resulting inner face.
+ */
+static BMFace *BME_bevel_poly(BMesh *bm, BMFace *f, float value, int options, BME_TransData_Head *td)
+{
+ BMLoop *l/*, *o */;
+ BME_TransData *vtd1, *vtd2;
+ float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1;
+ int len, i;
+ BMIter iter;
+
+ zero_v3(up_vec);
+
+ /* find a good normal for this face (there's better ways, I'm sure) */
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
+ BME_bevel_get_vec(vec1, l->v, l->next->v, td);
+ BME_bevel_get_vec(vec2, l->prev->v, l->v, td);
+ cross_v3_v3v3(vec3, vec2, vec1);
+ add_v3_v3(up_vec, vec3);
+ }
+ normalize_v3(up_vec);
+
+ /* Can't use a BM_LOOPS_OF_FACE iterator here, because the loops are being modified
+ * and so the end condition will never hi */
+ for (l = BM_FACE_FIRST_LOOP(f)->prev, i = 0, len = f->len; i < len; i++, l = l->next) {
+ if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
+ max = 1.0f;
+ l = BME_bevel_edge(bm, l, value, options, up_vec, td);
+ }
+ else if ( BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) &&
+ BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
+ !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_BEVEL))
+ {
+ max = 1.0f;
+ l = BME_bevel_vert(bm, l, value, options, up_vec, td);
+ }
+ }
+
+ f = l->f;
+
+ /* max pass */
+ if (value > 0.5f && max > 0) {
+ max = -1;
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
+ if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
+ BME_bevel_get_vec(vec1, l->v, l->next->v, td);
+ vtd1 = BME_get_transdata(td, l->v);
+ vtd2 = BME_get_transdata(td, l->next->v);
+ if (vtd1->loc == NULL) {
+ fac1 = 0;
+ }
+ else {
+ copy_v3_v3(vec2, vtd1->vec);
+ mul_v3_fl(vec2, vtd1->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec2, vec1);
+ fac1 = len_v3(vec2) / value;
+ }
+ else {
+ fac1 = 0;
+ }
+ }
+ if (vtd2->loc == NULL) {
+ fac2 = 0;
+ }
+ else {
+ copy_v3_v3(vec3, vtd2->vec);
+ mul_v3_fl(vec3, vtd2->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec3, vec1);
+ fac2 = len_v3(vec2) / value;
+ }
+ else {
+ fac2 = 0;
+ }
+ }
+ if (fac1 || fac2) {
+ max = len_v3(vec1)/(fac1 + fac2);
+ if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
+ *vtd1->max = max;
+ }
+ if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
+ *vtd2->max = max;
+ }
+ }
+ }
+ }
+ }
+
+ /* return l->f; */
+ return NULL;
+}
+
+static void BME_bevel_add_vweight(BME_TransData_Head *td, BMesh *bm, BMVert *v, float weight, float factor, int options)
+{
+ BME_TransData *vtd;
+
+ if (BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) return;
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
+ if ((vtd = BME_get_transdata(td, v))) {
+ if (options & BME_BEVEL_EMIN) {
+ vtd->factor = 1.0;
+ if (vtd->weight < 0 || weight < vtd->weight) {
+ vtd->weight = weight;
+ }
+ }
+ else if (options & BME_BEVEL_EMAX) {
+ vtd->factor = 1.0;
+ if (weight > vtd->weight) {
+ vtd->weight = weight;
+ }
+ }
+ else if (vtd->weight < 0) {
+ vtd->factor = factor;
+ vtd->weight = weight;
+ }
+ else {
+ vtd->factor += factor; /* increment number of edges with weights (will be averaged) */
+ vtd->weight += weight; /* accumulate all the weights */
+ }
+ }
+ else {
+ /* we'll use vtd->loc == NULL to mark that this vert is not moving */
+ vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL);
+ }
+}
+
+static void bevel_init_verts(BMesh *bm, int options, BME_TransData_Head *td)
+{
+ BMVert *v;
+ BMIter iter;
+ float weight;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ weight = 0.0;
+ if (!BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) {
+ /* modifiers should not use selection */
+ if(options & BME_BEVEL_SELECT){
+ if(BM_elem_flag_test(v, BM_ELEM_SELECT)) weight = 1.0;
+ }
+ /* bevel weight NYI */
+ else if(options & BME_BEVEL_WEIGHT)
+ weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT);
+ else
+ weight = 1.0;
+ if(weight > 0.0){
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
+ BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, weight, -1, NULL);
+ }
+ }
+ }
+}
+
+static void bevel_init_edges(BMesh *bm, int options, BME_TransData_Head *td)
+{
+ BMEdge *e;
+ int count;
+ float weight;
+ BMIter iter;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ weight = 0.0;
+ if (!BMO_elem_flag_test(bm, e, BME_BEVEL_NONMAN)) {
+ if(options & BME_BEVEL_SELECT){
+ if(BM_elem_flag_test(e, BM_ELEM_SELECT)) weight = 1.0;
+ }
+ else if(options & BME_BEVEL_WEIGHT) {
+ weight = BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT);
+ }
+ else {
+ weight = 1.0;
+ }
+ if(weight > 0.0){
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_BEVEL);
+ BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options);
+ BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options);
+ }
+ }
+ }
+
+ /* clean up edges with 2 faces that share more than one edg */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL)) {
+ count = BM_face_share_edges(e->l->f, e->l->radial_next->f);
+ if(count > 1) BMO_elem_flag_disable(bm, e, BME_BEVEL_BEVEL);
+ }
+ }
+}
+
+static BMesh *BME_bevel_initialize(BMesh *bm, int options, int UNUSED(defgrp_index), float UNUSED(angle), BME_TransData_Head *td)
+{
+ BMVert *v/*, *v2 */;
+ BMEdge *e/*, *curedg */;
+ BMFace *f;
+ BMIter iter;
+ int /* wire, */ len;
+
+ /* tag non-manifold geometr */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
+ if(v->e){
+ BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL);
+ if (!BM_vert_is_manifold(bm, v))
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
+ /* test wire ver */
+ len = BM_vert_edge_count(v);
+ if (len == 2 && BM_vert_is_wire(bm, v))
+ BMO_elem_flag_disable(bm, v, BME_BEVEL_NONMAN);
+ }
+ else
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
+ if (!BM_edge_is_manifold(bm, e)) {
+ BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_NONMAN);
+ BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_NONMAN);
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
+ }
+ if(BMO_elem_flag_test(bm, e->v1, BME_BEVEL_NONMAN) || BMO_elem_flag_test(bm, e->v2, BME_BEVEL_NONMAN)) BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
+ }
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL)
+ BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
+ if(options & BME_BEVEL_VERT) bevel_init_verts(bm, options, td);
+ else bevel_init_edges(bm, options, td);
+ return bm;
+
+}
+
+#if 0
+
+static BMesh *BME_bevel_reinitialize(BMesh *bm)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f;
+ BMIter iter;
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
+ }
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
+ }
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
+ }
+ return bm;
+
+}
+
+#endif
+
+/**
+ * BME_bevel_mesh
+ *
+ * Mesh beveling tool:
+ *
+ * Bevels an entire mesh. It currently uses the flags of
+ * its vertices and edges to track topological changes.
+ * The parameter "value" is the distance to inset (should be negative).
+ * The parameter "options" is not currently used.
+ *
+ * Returns -
+ * A BMesh pointer to the BM passed as a parameter.
+ */
+
+static BMesh *BME_bevel_mesh(BMesh *bm, float value, int UNUSED(res), int options, int UNUSED(defgrp_index), BME_TransData_Head *td)
+{
+ BMVert *v;
+ BMEdge *e, *curedge;
+ BMLoop *l, *l2;
+ BMFace *f;
+ BMIter iter;
+
+ /* unsigned int i, len; */
+
+ /* bevel poly */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, f, BME_BEVEL_ORIG)) {
+ BME_bevel_poly(bm, f, value, options, td);
+ }
+ }
+
+ /* get rid of beveled edge */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, e, BME_BEVEL_ORIG)) {
+ BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
+ }
+ }
+
+ /* link up corners and cli */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, v, BME_BEVEL_ORIG) && BMO_elem_flag_test(bm, v, BME_BEVEL_BEVEL)) {
+ curedge = v->e;
+ do{
+ l = curedge->l;
+ l2 = l->radial_next;
+ if(l->v != v) l = l->next;
+ if(l2->v != v) l2 = l2->next;
+ if(l->f->len > 3)
+ BM_face_split(bm, l->f, l->next->v, l->prev->v, &l, l->e); /* clip this corner off */
+ if(l2->f->len > 3)
+ BM_face_split(bm, l2->f, l2->next->v, l2->prev->v, &l, l2->e); /* clip this corner off */
+ curedge = bmesh_disk_nextedge(curedge, v);
+ } while(curedge != v->e);
+ BME_Bevel_Dissolve_Disk(bm, v);
+ }
+ }
+
+ /* Debug print, remov */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if(f->len == 2){
+ printf("warning");
+ }
+ }
+
+ return bm;
+}
+
+BMesh *BME_bevel(BMEditMesh *em, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd)
+{
+ BMesh *bm = em->bm;
+ BMVert *v;
+ BMIter iter;
+
+ BME_TransData_Head *td;
+ BME_TransData *vtd;
+ int i;
+ double fac = 1, d;
+
+ td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE);
+ /* recursion math courtesy of Martin Poirier (theeth) */
+ for (i = 0; i < res - 1; i++) {
+ if (i == 0) fac += 1.0f / 3.0f; else fac += 1.0f / (3 * i * 2.0f);
+ }
+ d = 1.0f / fac;
+
+ for (i = 0; i < res || (res == 0 && i == 0); i++) {
+ BMO_push(bm, NULL);
+ BME_bevel_initialize(bm, options, defgrp_index, angle, td);
+ //if (i != 0) BME_bevel_reinitialize(bm);
+ bmesh_begin_edit(bm, 0);
+ BME_bevel_mesh(bm, (float)d, res, options, defgrp_index, td);
+ bmesh_end_edit(bm, 0);
+ d /= (i == 0) ? 3.0 : 2.0;
+ BMO_pop(bm);
+ }
+
+ BMEdit_RecalcTesselation(em);
+
+ /* interactive preview? */
+ if (rtd) {
+ *rtd = td;
+ return bm;
+ }
+
+ /* otherwise apply transforms */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if ( (vtd = BME_get_transdata(td, v)) ) {
+ if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) {
+ d = *vtd->max;
+ }
+ else {
+ d = value;
+ }
+ madd_v3_v3v3fl(v->co, vtd->org, vtd->vec, vtd->factor * d);
+ }
+ }
+
+ BME_free_transdata(td);
+ return bm;
+}
diff --git a/source/blender/bmesh/tools/BME_dupe_ops.c b/source/blender/bmesh/tools/BME_dupe_ops.c
new file mode 100644
index 00000000000..fb357390ecc
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_dupe_ops.c
@@ -0,0 +1,322 @@
+#if 0
+
+/*
+ * BME_DUPLICATE.C
+ *
+ * This file contains functions for duplicating, copying, and splitting
+ * elements from a bmesh.
+ *
+ */
+
+/*
+ * COPY VERTEX
+ *
+ * Copy an existing vertex from one bmesh to another.
+ *
+*/
+
+static BMVert *copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash)
+{
+ BMVert *target_vertex = NULL;
+
+ /*create a new vertex*/
+ target_vertex = BM_vert_create(target, source_vertex->co, NULL);
+
+ /*insert new vertex into the vert hash*/
+ BLI_ghash_insert(vhash, source_vertex, target_vertex);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data);
+
+ /*Copy Markings*/
+ if(BM_Is_Selected((BMHeader*)source_vertex)) BM_vert_select_set(target_mesh, target_vertex, TRUE);
+ if(BM_Is_Hidden((BMHeader*)source_vertex)) BM_Mark_Hidden((BMHeader*)target_vertex, 1);
+
+ BMO_elem_flag_enable(target_mesh, (BMHeader*)target_vertex, DUPE_NEW);
+
+ return target_vertex;
+}
+
+/*
+ * COPY EDGE
+ *
+ * Copy an existing edge from one bmesh to another.
+ *
+*/
+
+static BMEdge *copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash)
+{
+ BMEdge *target_edge = NULL;
+ BMVert *target_vert1, *target_vert2;
+
+ /*lookup v1 and v2*/
+ target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1);
+ target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2);
+
+ /*create a new edge*/
+ target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
+
+ /*insert new edge into the edge hash*/
+ BLI_ghash_insert(ehash, source_edge, target_edge);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data);
+
+ /*copy flags*/
+ if(BM_Is_Selected((BMHeader*) source_edge)) BM_edge_select_set(target_mesh, target_edge, TRUE);
+ if(BM_Is_Hidden((BMHeader*) source_edge)) BM_Mark_Hidden(target_mesh, target_edge, 1);
+ if(BM_Is_Sharp((BMHeader*) source_edge)) BM_Mark_Sharp(target_edge, 1);
+ if(BM_Is_Seam((BMHeader*) source_edge)) BM_Mark_Seam(target_edge, 1);
+ if(BM_Is_Fgon((BMHeader*) source_edge)) BM_Mark_Fgon(target_edge, 1);
+
+ BMO_elem_flag_enable(target_mesh, (BMHeader*)target_edge, DUPE_NEW);
+
+ return target_edge;
+}
+
+/*
+ * COPY FACE
+ *
+ * Copy an existing face from one bmesh to another.
+ *
+*/
+
+static BMFace *copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash)
+{
+ BMEdge *target_edge;
+ BMVert *target_vert1, *target_vert2;
+ BMLoop *source_loop, *target_loop;
+ BMFace *target_face = NULL;
+ int i;
+
+ /*lookup the first and second verts*/
+ target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v);
+ target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
+
+ /*lookup edges*/
+ i = 0;
+ source_loop = source_face->lbase;
+ do{
+ edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
+ i++;
+ source_loop = source_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ /*create new face*/
+ target_face = BM_face_create_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0);
+
+ /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/
+ CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data);
+
+ /*copy flags*/
+ if(BM_Is_Selected((BMHeader*)source_face)) BM_Select_face(target, target_face, TRUE);
+ if(BM_Is_Hidden((BMHeader*)source_face)) BM_Mark_Hidden((BMHeader*)target_face, 1);
+
+ /*mark the face for output*/
+ BMO_elem_flag_enable(target_mesh, (BMHeader*)target_face, DUPE_NEW);
+
+ /*copy per-loop custom data*/
+ source_loop = source_face->lbase;
+ target_loop = target_face->lbase;
+ do{
+ CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data);
+ source_loop = source_loop->next;
+ target_loop = target_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ return target_face;
+}
+
+/*
+ * COPY MESH
+ *
+ * Internal Copy function.
+*/
+
+/*local flag defines*/
+
+#define DUPE_INPUT 1 /*input from operator*/
+#define DUPE_NEW 2
+#define DUPE_DONE 3
+
+static void copy_mesh(BMMesh *source, BMMesh *target)
+{
+
+ BMVert *v = NULL;
+ BMEdge *e = NULL, **edar = NULL;
+ BMLoop *l = NULL;
+ BMFace *f = NULL;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+ BMIter loops;
+
+ GHash *vhash;
+ GHash *ehash;
+
+ int maxlength = 0, flag;
+
+ /*initialize pointer hashes*/
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+ ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp);
+
+ /*initialize edge pointer array*/
+ for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f = BM_iter_step(&faces)){
+ if(f->len > maxlength) maxlength = f->len;
+ }
+ edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array");
+
+
+ /*first we dupe all flagged faces and their elements from source*/
+ for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f= BM_iter_step(&faces)){
+ if(BMO_elem_flag_test(source, (BMHeader*)f, DUPE_INPUT)){
+ /*vertex pass*/
+ for(v = BM_iter_new(&verts, source, BM_VERT_OF_FACE, f, 0, NULL); v; v = BM_iter_step(&verts)){
+ if(!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE)){
+ copy_vertex(source,v, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
+ }
+ }
+
+ /*edge pass*/
+ for(e = BM_iter_new(&edges, source, BM_EDGE_OF_FACE, f, 0, NULL); e; e = BMeshIter_step(&edges)){
+ if(!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE)){
+ copy_edge(source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
+ }
+ }
+ copy_face(source, f, target, edar, vhash, ehash);
+ BMO_elem_flag_enable(source, (BMHeader*)f, DUPE_DONE);
+ }
+ }
+
+ /*now we dupe all the edges*/
+ for(e = BM_iter_new(&edges, source, BM_EDGES, source, 0, NULL); e; e = BM_iter_step(&edges)){
+ if(BMO_elem_flag_test(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE))){
+ /*make sure that verts are copied*/
+ if(!BMO_elem_flag_test(source, (BMHeader*)e->v1, DUPE_DONE){
+ copy_vertex(source, e->v1, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)e->v1, DUPE_DONE);
+ }
+ if(!BMO_elem_flag_test(source, (BMHeader*)e->v2, DUPE_DONE){
+ copy_vertex(source, e->v2, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)e->v2, DUPE_DONE);
+ }
+ /*now copy the actual edge*/
+ copy_edge(source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
+ }
+ }
+
+ /*finally dupe all loose vertices*/
+ for(v = BM_iter_new(&verts, source, BM_VERTS, source, 0, NULL); v; v = BM_iter_step(&verts)){
+ if(BMO_elem_flag_test(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE))){
+ copy_vertex(source, v, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
+ }
+ }
+
+ /*free pointer hashes*/
+ BLI_ghash_free(vhash, NULL, NULL);
+ BLI_ghash_free(ehash, NULL, NULL);
+
+ /*free edge pointer array*/
+ if(edar)
+ MEM_freeN(edar);
+}
+/*
+BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4])
+{
+ BMMesh *target = NULL;
+ target = bmesh_make_mesh(allocsize);
+
+
+ CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+
+
+ CustomData_bmesh_init_pool(&target->vdata, allocsize[0]);
+ CustomData_bmesh_init_pool(&target->edata, allocsize[1]);
+ CustomData_bmesh_init_pool(&target->ldata, allocsize[2]);
+ CustomData_bmesh_init_pool(&target->pdata, allocsize[3]);
+
+ bmesh_begin_edit(bm);
+ bmesh_begin_edit(target);
+
+ bmesh_copy_mesh(bm, target, 0);
+
+ bmesh_end_edit(bm);
+ bmesh_end_edit(target);
+
+ return target;
+
+}
+*/
+
+void dupeop_exec(BMMesh *bm, BMOperator *op)
+{
+ BMOperator *dupeop = op;
+ BMOpSlot *vinput, *einput, *finput, *vnew, *enew, *fnew;
+ int i;
+
+ vinput = BMO_Get_Slot(dupeop, BMOP_DUPE_VINPUT);
+ einput = BMO_Get_Slot(dupeop, BMOP_DUPE_EINPUT);
+ finput = BMO_Get_Slot(dupeop, BMOP_DUPE_FINPUT);
+
+ /*go through vinput, einput, and finput and flag elements with private flags*/
+ BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_VINPUT, DUPE_INPUT);
+ BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_EINPUT, DUPE_INPUT);
+ BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_FINPUT, DUPE_INPUT);
+
+ /*use the internal copy function*/
+ copy_mesh(bm, bm);
+
+ /*Output*/
+ /*First copy the input buffers to output buffers - original data*/
+ BMO_Copy_Opslot_Buffer_Alloc(dupeop, vinput, BMO_Get_Slot(dupeop, BMOP_DUPE_VORIGINAL));
+ BMO_Copy_Opslot_Buffer_Alloc(dupeop, einput, BMO_Get_Slot(dupeop, BMOP_DUPE_EORIGINAL));
+ BMO_Copy_Opslot_Buffer_Alloc(dupeop, finput, BMO_Get_Slot(dupeop, BMOP_DUPE_FORIGINAL));
+
+ /*Now alloc the new output buffers*/
+ BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_VNEW, DUPE_NEW, BMESH_VERT);
+ BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_ENEW, DUPE_NEW, BMESH_EDGE);
+ BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_FNEW, DUPE_NEW, BMESH_FACE);
+}
+
+void splitop_exec(BMMesh *bm, BMOperator *op)
+{
+ BMOperator *splitop = op;
+ BMOperator dupeop;
+ BMOperator delop;
+
+ /*initialize our sub-operators*/
+ BMO_op_init(&dupeop, BMOP_DUPE);
+ BMO_op_init(&delop, BMOP_DEL);
+
+ BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_VINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_VINPUT));
+ BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_EINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_EINPUT));
+ BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_FINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_FINPUT));
+
+ BMO_op_exec(&dupeop);
+
+ /*connect outputs of dupe to delete*/
+ BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_VORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_VINPUT));
+ BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_EORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_EINPUT));
+ BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_FORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_FINPUT));
+
+ BMO_op_exec(&delop);
+
+ /*now we make our outputs by copying the dupe outputs*/
+ BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_VNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_VOUTPUT));
+ BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_ENEW), BMO_Get_Slot(splitop, BMOP_SPLIT_EOUTPUT));
+ BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_FNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_FOUTPUT));
+
+ /*cleanup*/
+ BMO_op_finish(&dupeop);
+ BMO_op_finish(&delop);
+}
+
+#endif
diff --git a/source/blender/bmesh/tools/BME_duplicate.c b/source/blender/bmesh/tools/BME_duplicate.c
new file mode 100644
index 00000000000..11151de9ed7
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_duplicate.c
@@ -0,0 +1,310 @@
+#if 0
+
+/*
+ * BME_DUPLICATE.C
+ *
+ * This file contains functions for duplicating, copying, and splitting
+ * elements from a bmesh.
+ *
+ */
+
+/*
+ * BMESH COPY VERTEX
+ *
+ * Copy an existing vertex from one bmesh to another.
+ *
+*/
+
+static BMVert *bmesh_copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash)
+{
+ BMVert *target_vertex = NULL;
+
+ /*create a new vertex*/
+ target_vertex = bmesh_make_vert(target, source_vertex->co, NULL);
+
+ /*insert new vertex into the vert hash*/
+ BLI_ghash_insert(vhash, source_vertex, target_vertex);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data);
+
+ /*copy flags*/
+ if(bmesh_test_flag(source_vertex, BMESH_SELECT)) bmesh_set_flag(target_vertex, BMESH_SELECT);
+ if(bmesh_test_flag(source_vertex, BMESH_HIDDEN)) bmesh_set_flag(target_vertex, BMESH_HIDDEN);
+
+ return target_vertex;
+}
+
+/*
+ * BMESH COPY EDGE
+ *
+ * Copy an existing edge from one bmesh to another.
+ *
+*/
+
+static BMEdge *bmesh_copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash)
+{
+ BMEdge *target_edge = NULL;
+ BMVert *target_vert1, *target_vert2;
+
+ /*lookup v1 and v2*/
+ target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1);
+ target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2);
+
+ /*create a new edge*/
+ target_edge = bmesh_make_edge(target_mesh, target_vert1, target_vert2, NULL, 0);
+
+ /*insert new edge into the edge hash*/
+ BLI_ghash_insert(ehash, source_edge, target_edge);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data);
+
+ /*copy flags*/
+ if(bmesh_test_flag(source_edge, BMESH_SELECT)) bmesh_set_flag(target_edge, BMESH_SELECT);
+ if(bmesh_test_flag(source_edge, BMESH_HIDDEN)) bmesh_set_flag(target_edge, BMESH_SELECT);
+ if(bmesh_test_flag(source_edge, BMESH_SHARP)) bmesh_set_flag(target_edge, BMESH_SHARP);
+ if(bmesh_test_flag(source_edge, BMESH_SEAM)) bmesh_set_flag(target_edge, BMESH_SEAM);
+ if(bmesh_test_flag(source_edge, BMESH_FGON)) bmesh_set_flag(target_edge, BMESH_FGON);
+
+ return target_edge;
+}
+
+/*
+ * BMESH COPY FACE
+ *
+ * Copy an existing face from one bmesh to another.
+ *
+*/
+
+static BMFace *bmesh_copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash)
+{
+ BMEdge *target_edge;
+ BMVert *target_vert1, *target_vert2;
+ BMLoop *source_loop, *target_loop;
+ BMFace *target_face = NULL;
+ int i;
+
+
+ /*lookup the first and second verts*/
+ target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v);
+ target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
+
+ /*lookup edges*/
+ i = 0;
+ source_loop = source_face->lbase;
+ do{
+ edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
+ i++;
+ source_loop = source_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ /*create new face*/
+ target_face = bmesh_make_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0);
+
+ /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/
+ CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data);
+
+ /*copy flags*/
+ if(bmesh_test_flag(source_face, BMESH_SELECT)) bmesh_set_flag(target_face, BMESH_SELECT);
+ if(bmesh_test_flag(source_face, BMESH_HIDDEN)) bmesh_set_flag(target_face, BMESH_HIDDEN);
+
+ /*mark the face as dirty for normal and tesselation calcs*/
+ bmesh_set_flag(target_face, BMESH_DIRTY);
+
+ /*copy per-loop custom data*/
+ source_loop = source_face->lbase;
+ target_loop = target_face->lbase;
+ do{
+ CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data);
+ source_loop = source_loop->next;
+ target_loop = target_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ return target_face;
+}
+
+/*
+ * BMESH COPY MESH
+ *
+ * Internal Copy function. copies flagged elements from
+ * source to target, which may in fact be the same mesh.
+ * Note that if __flag is 0, all elements will be copied.
+ *
+*/
+
+static void bmesh_copy_mesh(BMMesh *source, BMMesh *target, int __flag)
+{
+
+ BMVert *v;
+ BMEdge *e, **edar;
+ BMLoop *l;
+ BMFace *f;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+ BMIter loops;
+
+ GHash *vhash;
+ GHash *ehash;
+
+ int maxlength = 0, flag;
+
+ /*initialize pointer hashes*/
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+ ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp);
+
+ /*initialize edge pointer array*/
+ for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)){
+ if(f->len > maxlength) maxlength = f->len;
+ }
+ edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array");
+
+ /*begin modelling loop for target*/
+ bmesh_begin_edit(target);
+
+ /*we make special exception for __flag == 0... we copy all*/
+ if(!__flag){
+ flag = BMESH_DUPE;
+ for(v = BMeshIter_init(verts, BM_VERTS, source, 0); v; v = BMeshIter_step(verts)) bmesh_set_flag(v, BMESH_DUPE);
+ for(e = BMeshIter_init(verts, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)) bmesh_set_flag(e, BMESH_DUPE);
+ for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)) bmesh_set_flag(f, BMESH_DUPE);
+ } else{
+ flag = __flag;
+ }
+
+ /*first we dupe all flagged faces and their elements from source*/
+ for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f= BMeshIter_step(faces)){
+ if(bmesh_test_flag(f, flag)){
+ /*vertex pass*/
+ for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){
+ if(!bmesh_test_flag(l->v, BMESH_DUPED)){
+ bmesh_copy_vertex(source,l->v, target, vhash);
+ bmesh_set_flag(l->v, BMESH_DUPED);
+ }
+ }
+
+ /*edge pass*/
+ for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){
+ if(!bmesh_test_flag(l->e, BMESH_DUPED)){
+ bmesh_copy_edge(source, l->e, target, vhash, ehash);
+ bmesh_set_flag(l->e, BMESH_DUPED);
+ }
+ }
+ bmesh_copy_face(source, f, target, edar, vhash, ehash);
+ bmesh_set_flag(f, BMESH_DUPED);
+ }
+ }
+
+ /*now we dupe all the edges*/
+ for(e = BMeshIter_init(edges, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)){
+ if(bmesh_test_flag(e, flag) && (!bmesh_test_flag(e, BMESH_DUPED))){
+ /*make sure that verts are copied*/
+ if(!bmesh_test_flag(e->v1, BMESH_DUPED)){
+ bmesh_copy_vertex(source, e->v1, target, vhash);
+ bmesh_set_flag(e->v1, BMESH_DUPED);
+ }
+ if(!bmesh_test_flag(e->v2, BMESH_DUPED)){
+ bmesh_copy_vertex(source, e->v2, target, vhash);
+ bmesh_set_flag(e->v2, BMESH_DUPED);
+ }
+ /*now copy the actual edge*/
+ bmesh_copy_edge(source, e, target, vhash, ehash);
+ bmesh_set_flag(e, BMESH_DUPED);
+ }
+ }
+
+ /*finally dupe all loose vertices*/
+ for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){
+ if(bmesh_test_flag(v, flag) && (!bmesh_test_flag(v, BMESH_DUPED))){
+ bmesh_copy_vertex(source, v, target, vhash);
+ bmesh_set_flag(v, BMESH_DUPED);
+ }
+ }
+
+ /*finish*/
+ bmesh_end_edit(target, BMESH_CALC_NORM | BMESH_CALC_TESS);
+
+ /*free pointer hashes*/
+ BLI_ghash_free(vhash, NULL, NULL);
+ BLI_ghash_free(ehash, NULL, NULL);
+
+ /*free edge pointer array*/
+ MEM_freeN(edar);
+}
+
+/*
+ * BMESH MAKE MESH FROM MESH
+ *
+ * Creates a new mesh by duplicating an existing one.
+ *
+*/
+
+BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4])
+{
+ BMMesh *target = NULL;
+ target = bmesh_make_mesh(allocsize);
+
+ /*copy custom data layout*/
+ CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+
+ /*initialize memory pools*/
+ CustomData_bmesh_init_pool(&target->vdata, allocsize[0]);
+ CustomData_bmesh_init_pool(&target->edata, allocsize[1]);
+ CustomData_bmesh_init_pool(&target->ldata, allocsize[2]);
+ CustomData_bmesh_init_pool(&target->pdata, allocsize[3]);
+
+ bmesh_begin_edit(bm);
+ bmesh_begin_edit(target);
+
+ bmesh_copy_mesh(bm, target, 0); /* copy all elements */
+
+ bmesh_end_edit(bm);
+ bmesh_end_edit(target);
+
+ return target;
+
+}
+
+/*
+ * BMESH SPLIT MESH
+ *
+ * Copies flagged elements then deletes them.
+ *
+*/
+
+void bmesh_split_mesh(BMMesh *bm, int flag)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+
+ bmesh_begin_edit(bm);
+ bmesh_copy_mesh(bm, bm, flag);
+
+ /*mark verts for deletion*/
+ for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){
+ if(bmesh_test_flag(v, flag)) bmesh_delete_vert(bm, v);
+ }
+ /*mark edges for deletion*/
+ for(e = BMeshIter_init(edges, BM_EDGES, bm, 0); e; e = BMeshIter_step(edges)){
+ if(bmesh_test_flag(e, flag)) bmesh_delete_edge(bm, e);
+
+ }
+ /*mark faces for deletion*/
+ for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f; f= BMeshIter_step(faces)){
+ if(bmesh_tes t_flag(f, flag)) bmesh_delete_face(bm, f);
+
+ }
+ bmesh_end_edit(bm);
+}
+
+#endif
diff --git a/source/blender/bmesh/tools/BME_weld.c b/source/blender/bmesh/tools/BME_weld.c
new file mode 100644
index 00000000000..fe1a340d94a
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_weld.c
@@ -0,0 +1,341 @@
+#if
+
+/*
+ * BME_WELD.C
+ *
+ * This file contains functions for welding
+ * elements in a mesh togather (remove doubles,
+ * collapse, ect).
+ *
+ * TODO:
+ * -Rewrite this to fit into the new API
+ * -Seperate out find doubles code and put it in
+ * BME_queries.c
+ *
+*/
+
+
+/********* qsort routines *********/
+
+
+typedef struct xvertsort {
+ float x;
+ BMVert *v1;
+} xvertsort;
+
+static int vergxco(const void *v1, const void *v2)
+{
+ const xvertsort *x1=v1, *x2=v2;
+
+ if( x1->x > x2->x ) return 1;
+ else if( x1->x < x2->x) return -1;
+ return 0;
+}
+
+struct facesort {
+ unsigned long x;
+ struct BMFace *f;
+};
+
+
+static int vergface(const void *v1, const void *v2)
+{
+ const struct facesort *x1=v1, *x2=v2;
+
+ if( x1->x > x2->x ) return 1;
+ else if( x1->x < x2->x) return -1;
+ return 0;
+}
+
+
+
+/*break this into two functions.... 'find doubles' and 'remove doubles'?*/
+
+static void BME_remove_doubles__splitface(BME_Mesh *bm,BMFace *f,GHash *vhash)
+{
+ BMVert *doub=NULL, *target=NULL;
+ BME_Loop *l;
+ BMFace *f2=NULL;
+ int split=0;
+
+ l=f->loopbase;
+ do{
+ if(l->v->tflag1 == 2){
+ target = BLI_ghash_lookup(vhash,l->v);
+ if((BME_vert_in_face(target,f)) && (target != l->next->v) && (target != l->prev->v)){
+ doub = l->v;
+ split = 1;
+ break;
+ }
+ }
+
+ l= l->next;
+ }while(l!= f->loopbase);
+
+ if(split){
+ f2 = BME_SFME(bm,f,doub,target,NULL);
+ BME_remove_doubles__splitface(bm,f,vhash);
+ BME_remove_doubles__splitface(bm,f2,vhash);
+ }
+}
+
+int BME_remove_doubles(BME_Mesh *bm, float limit)
+{
+
+ /* all verts with (flag & 'flag') are being evaluated */
+ BMVert *v, *v2, *target;
+ BMEdge *e, **edar, *ne;
+ BME_Loop *l;
+ BMFace *f, *nf;
+ xvertsort *sortblock, *sb, *sb1;
+ struct GHash *vhash;
+ struct facesort *fsortblock, *vsb, *vsb1;
+ int a, b, test, amount=0, found;
+ float dist;
+
+ /*Build a hash table of doubles to thier target vert/edge.*/
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+ /*count amount of selected vertices*/
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(BME_SELECTED(v))amount++;
+ }
+
+ /*qsort vertices based upon average of coordinate. We test this way first.*/
+ sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub");
+
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(BME_SELECTED(v)){
+ sb->x = v->co[0]+v->co[1]+v->co[2];
+ sb->v1 = v;
+ sb++;
+ }
+ }
+ qsort(sortblock, amount, sizeof(xvertsort), vergxco);
+
+ /* test for doubles */
+ sb= sortblock;
+ for(a=0; a<amount; a++) {
+ v= sb->v1;
+ if(!(v->tflag1)) { //have we tested yet?
+ sb1= sb+1;
+ for(b=a+1; b<amount; b++) {
+ /* first test: simple distance. Simple way to discard*/
+ dist= sb1->x - sb->x;
+ if(dist > limit) break;
+
+ /* second test: have we already done this vertex?
+ (eh this should be swapped, simple equality test should be cheaper than math above... small savings
+ though) */
+ v2= sb1->v1;
+ if(!(v2->tflag1)) {
+ dist= fabsf(v2->co[0]-v->co[0]);
+ if(dist<=limit) {
+ dist= fabsf(v2->co[1]-v->co[1]);
+ if(dist<=limit) {
+ dist= fabsf(v2->co[2]-v->co[2]);
+ if(dist<=limit) {
+ /*v2 is a double of v. We want to throw out v1 and relink everything to v*/
+ BLI_ghash_insert(vhash,v2, v);
+ v->tflag1 = 1; //mark this vertex as a target
+ v->tflag2++; //increase user count for this vert.
+ v2->tflag1 = 2; //mark this vertex as a double.
+ BME_VISIT(v2); //mark for delete
+ }
+ }
+ }
+ }
+ sb1++;
+ }
+ }
+ sb++;
+ }
+ MEM_freeN(sortblock);
+
+
+ /*todo... figure out what this is for...
+ for(eve = em->verts.first; eve; eve=eve->next)
+ if((eve->f & flag) && (eve->f & 128))
+ EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f);
+ */
+
+ /*We cannot collapse a vertex onto another vertex if they share a face and are not connected via a collapsable edge.
+ so to deal with this we simply find these offending vertices and split the faces. Note that this is not optimal, but works.
+ */
+
+
+ for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){
+ if(!(BME_NEWELEM(f))){
+ BME_remove_doubles__splitface(bm,f,vhash);
+ }
+ }
+
+ for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){
+ /*If either vertices of this edge are a double, we must mark it for removal and we create a new one.*/
+ if(e->v1->tflag1 == 2 || e->v2->tflag1 == 2){
+ v = v2 = NULL;
+ /*For each vertex in the edge, test to find out what it should equal now.*/
+ if(e->v1->tflag1 == 2) v= BLI_ghash_lookup(vhash,e->v1);
+ else v = e->v1;
+ if(e->v2->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,e->v2);
+ else v2 = e->v2;
+
+ /*small optimization, test to see if the edge needs to be rebuilt at all*/
+ if((e->v1 != v) || (e->v2 != v2)){ /*will this always be true of collapsed edges?*/
+ if(v == v2) e->tflag1 = 2; /*mark as a collapsed edge*/
+ else if(!BME_disk_existedge(v,v2)) ne = BME_ME(bm,v,v2);
+ BME_VISIT(e); /*mark for delete*/
+ }
+ }
+ }
+
+
+ /* need to remove double edges as well. To do this we decide on one edge to keep,
+ * and if its inserted into hash then we need to remove all other
+ * edges incident upon and relink.*/
+ /*
+ * REBUILD FACES
+ *
+ * Loop through double faces and if they have vertices that have been flagged, they need to be rebuilt.
+ * We do this by looking up the face rebuild faces.
+ * loop through original face, for each loop, if the edge it is attached to is marked for delete and has no
+ * other edge in the hash edge, then we know to skip that loop on face recreation. Simple.
+ */
+
+ /*1st loop through, just marking elements*/
+ for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ //insert bit here about double edges, mark with a flag (e->tflag2) so that we can nuke it later.
+ l = f->loopbase;
+ do{
+ if(l->v->tflag1 == 2) f->tflag1 = 1; //double, mark for rebuild
+ if(l->e->tflag1 != 2) f->tflag2++; //count number of edges in the new face.
+ l=l->next;
+ }while(l!=f->loopbase);
+ }
+
+ /*now go through and create new faces*/
+ for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){
+ if(f->tflag1 && f->tflag2 < 3) BME_VISIT(f); //mark for delete
+ else if (f->tflag1 == 1){ /*is the face marked for rebuild*/
+ edar = MEM_callocN(sizeof(BMEdge *)*f->tflag2,"Remove doubles face creation array.");
+ a=0;
+ l = f->loopbase;
+ do{
+ v = l->v;
+ v2 = l->next->v;
+ if(l->v->tflag1 == 2) v = BLI_ghash_lookup(vhash,l->v);
+ if(l->next->v->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,l->next->v);
+ ne = BME_disk_existedge(v,v2); //use BME_disk_next_edgeflag here or something to find the edge that is marked as 'target'.
+ //add in call here to edge doubles hash array... then bobs your uncle.
+ if(ne){
+ edar[a] = ne;
+ a++;
+ }
+ l=l->next;
+ }while(l!=f->loopbase);
+
+ if(BME_vert_in_edge(edar[1],edar[0]->v2)){
+ v = edar[0]->v1;
+ v2 = edar[0]->v2;
+ }
+ else{
+ v = edar[0]->v2;
+ v2 = edar[0]->v1;
+ }
+
+ nf = BME_MF(bm,v,v2,edar,f->tflag2);
+
+ /*copy per loop data here*/
+ if(nf){
+ BME_VISIT(f); //mark for delete
+ }
+ MEM_freeN(edar);
+ }
+ }
+
+ /*count amount of removed vert doubles*/
+ a = 0;
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(v->tflag1 == 2) a++;
+ }
+
+ /*free memory and return amount removed*/
+ remove_tagged_polys(bm);
+ remove_tagged_edges(bm);
+ remove_tagged_verts(bm);
+ BLI_ghash_free(vhash,NULL, NULL);
+ BME_selectmode_flush(bm);
+ return a;
+}
+
+static void BME_MeshWalk__collapsefunc(void *userData, BMEdge *applyedge)
+{
+ int index;
+ GHash *collected = userData;
+ index = BLI_ghash_size(collected);
+ if(!BLI_ghash_lookup(collected,applyedge->v1)){
+ BLI_ghash_insert(collected,index,applyedge->v1);
+ index++;
+ }
+ if(!BLI_ghash_lookup(collected,applyedge->v2)){
+ BLI_ghash_insert(collected,index,applyedge->v2);
+ }
+}
+
+void BME_collapse_edges(BME_Mesh *bm)
+{
+
+ BMVert *v, *cvert;
+ GHash *collected;
+ float min[3], max[3], cent[3];
+ int size, i=0, j, num=0;
+
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(!(BME_ISVISITED(v)) && v->edge){
+ /*initiate hash table*/
+ collected = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+ /*do the walking.*/
+ BME_MeshWalk(bm,v,BME_MeshWalk__collapsefunc,collected,BME_RESTRICTSELECT);
+ /*now loop through the hash table twice, once to calculate bounding box, second time to do the actual collapse*/
+ size = BLI_ghash_size(collected);
+ /*initial values*/
+ copy_v3_v3(min,v->co);
+ copy_v3_v3(max,v->co);
+ cent[0] = cent[1] = cent[2]=0;
+ for(i=0; i<size; i++){
+ cvert = BLI_ghash_lookup(collected,i);
+ cent[0] = cent[0] + cvert->co[0];
+ cent[1] = cent[1] + cvert->co[1];
+ cent[2] = cent[2] + cvert->co[2];
+ }
+
+ cent[0] = cent[0] / size;
+ cent[1] = cent[1] / size;
+ cent[2] = cent[2] / size;
+
+ for(i=0; i<size; i++){
+ cvert = BLI_ghash_lookup(collected,i);
+ copy_v3_v3(cvert->co,cent);
+ num++;
+ }
+ /*free the hash table*/
+ BLI_ghash_free(collected,NULL, NULL);
+ }
+ }
+ /*if any collapsed, call remove doubles*/
+ if(num){
+ //need to change selection mode here, OR do something else? Or does tool change selection mode?
+ //selectgrep
+ //first clear flags
+ BMEdge *e;
+ BMFace *f;
+ BME_clear_flag_all(bm,BME_VISITED);
+ for(v=BME_first(bm,BME_VERT); v; v=BME_next(bm,BME_VERT,v)) v->tflag1 = v->tflag2 = 0;
+ for(e=BME_first(bm,BME_EDGE); e; e=BME_next(bm,BME_EDGE,e)) e->tflag1 = e->tflag2 = 0;
+ for(f=BME_first(bm,BME_POLY); f; f=BME_next(bm,BME_POLY,f)) f->tflag1 = f->tflag2 = 0;
+ /*now call remove doubles*/
+ BME_remove_doubles(bm,0.0000001);
+ }
+ BME_selectmode_flush(bm);
+}
+
+#endif