Merged changes in the trunk up to revision 44266 (including BMesh).
authorTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>
Tue, 21 Feb 2012 01:40:04 +0000 (01:40 +0000)
committerTamito Kajiyama <rd6t-kjym@asahi-net.or.jp>
Tue, 21 Feb 2012 01:40:04 +0000 (01:40 +0000)
Freestyle edge/face marks were ported to BMesh.

Conflicts resolved:
source/blender/editors/mesh/editface.c
source/blender/editors/space_view3d/drawobject.c
source/blender/makesdna/DNA_meshdata_types.h
source/blender/blenkernel/intern/editderivedmesh.c

42 files changed:
1  2 
release/scripts/startup/bl_ui/space_view3d.py
source/blender/CMakeLists.txt
source/blender/SConscript
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/SConscript
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/subsurf_ccg.c
source/blender/blenlib/CMakeLists.txt
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/bmesh/bmesh.h
source/blender/bmesh/bmesh_operators.h
source/blender/bmesh/intern/bmesh_construct.c
source/blender/bmesh/operators/bmo_utils.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/mesh/bmesh_select.c
source/blender/editors/mesh/bmesh_tools.c
source/blender/editors/mesh/mesh_intern.h
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/render/CMakeLists.txt
source/blender/editors/render/SConscript
source/blender/editors/render/render_shading.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform_orientations.c
source/blender/makesdna/DNA_mesh_types.h
source/blender/makesdna/DNA_meshdata_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/SConscript
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/rna_mesh.c
source/blender/makesrna/intern/rna_scene.c
source/blender/render/SConscript
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/shadeoutput.c
source/blender/windowmanager/SConscript
source/blender/windowmanager/intern/wm_files.c
source/blenderplayer/bad_level_call_stubs/stubs.c
source/creator/CMakeLists.txt
source/creator/creator.c

index 0c9c4ff8fd27e4255c6c967d868943a91c3ac248,be5aa91db3cb17b20c7d9f49f78c54d8a874e6b6..1144cbf188d05431b0e7f76a6c6aff39ef7b928c
@@@ -1657,11 -1661,6 +1661,11 @@@ class VIEW3D_MT_edit_mesh_edges(Menu)
  
          layout.separator()
  
-         layout.operator("mesh.mark_freestyle_edge")
++        layout.operator("mesh.mark_freestyle_edge").clear = False
 +        layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
 +
 +        layout.separator()
 +
          layout.operator("mesh.edge_rotate", text="Rotate Edge CW").direction = 'CW'
          layout.operator("mesh.edge_rotate", text="Rotate Edge CCW").direction = 'CCW'
  
@@@ -1699,16 -1702,6 +1707,11 @@@ class VIEW3D_MT_edit_mesh_faces(Menu)
  
          layout.separator()
  
-         layout.operator("mesh.fgon_make")
-         layout.operator("mesh.fgon_clear")
-         layout.separator()
-         layout.operator("mesh.mark_freestyle_face")
++        layout.operator("mesh.mark_freestyle_face").clear = False
 +        layout.operator("mesh.mark_freestyle_face", text="Clear Freestyle Face").clear = True
 +
 +        layout.separator()
 +
          layout.operator("mesh.quads_convert_to_tris")
          layout.operator("mesh.tris_convert_to_quads")
          layout.operator("mesh.edge_flip")
Simple merge
Simple merge
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,ae85c40e27017c14b575852534515d4ae53ad94d..ee41948589f07574511267fc116481f9991609d8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,380 +1,382 @@@
+ /*
+  * ***** 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. */
++#define BM_ELEM_FREESTYLE     (1 << 5) /* used for faces and edges */
++
+ /* 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__ */
index 0000000000000000000000000000000000000000,de92f57bc23d2973c32c958e91e5e6bae44ae321..0a84246091b58b38b26451f9e9a40ca036061a16
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,105 +1,107 @@@
 -      SIMFACE_COPLANAR
+ /*
+  * ***** 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,
 -      SIMEDGE_SHARP
++      SIMFACE_COPLANAR,
++      SIMFACE_FREESTYLE
+ };
+ /* similar edge selection slot values */
+ enum {
+       SIMEDGE_LENGTH = 101,
+       SIMEDGE_DIR,
+       SIMEDGE_FACE,
+       SIMEDGE_FACE_ANGLE,
+       SIMEDGE_CREASE,
+       SIMEDGE_SEAM,
++      SIMEDGE_SHARP,
++      SIMEDGE_FREESTYLE
+ };
+ /* 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__ */
index 0000000000000000000000000000000000000000,ac37041b073075aea6c60b180f32dbf055e68efc..ca16e568bb6cc5240d58d82a7e5059b59982bda9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,773 +1,777 @@@
 -               ((meflag & ME_HIDE)       ? BM_ELEM_HIDDEN : 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.
+  *
+  * ***** 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)
++               ((meflag & ME_HIDE)       ? BM_ELEM_HIDDEN : 0) |
++               ((meflag & ME_FREESTYLE_EDGE) ? BM_ELEM_FREESTYLE : 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) |
 -               ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE     : 0)
++               ((meflag & ME_HIDE)      ? BM_ELEM_HIDDEN : 0) |
++               ((meflag & ME_FREESTYLE_FACE) ? BM_ELEM_FREESTYLE : 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) |
++               ((hflag & BM_ELEM_FREESTYLE) ? ME_FREESTYLE_EDGE : 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) |
++               ((hflag & BM_ELEM_FREESTYLE) ? ME_FREESTYLE_FACE : 0)
+                );
+ }
index 0000000000000000000000000000000000000000,e0187476a6a97d028f67249d6d9068f5b8ebae4f..f4dd89e1848a59d5b9e9e0a1ba460957c0dbddc2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1297 +1,1311 @@@
+ /*
+  * ***** 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;
++
++                                      case SIMFACE_FREESTYLE:
++                                              if (BM_elem_flag_test(fm, BM_ELEM_FREESTYLE) == BM_elem_flag_test(fs, BM_ELEM_FREESTYLE)) {
++                                                      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;
++
++                                      case SIMEDGE_FREESTYLE:
++                                              if (BM_elem_flag_test(e, BM_ELEM_FREESTYLE) == BM_elem_flag_test(es, BM_ELEM_FREESTYLE)) {
++                                                      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);
+ }
index 0000000000000000000000000000000000000000,38daf3c0d2227e72d02e358968c40b50d3f73a17..89bccf8f00e881d7a59185ed2f373df15e1acc70
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2756 +1,2767 @@@
 -                      for (a = SIMFACE_MATERIAL; a <= SIMFACE_COPLANAR; a++) {
+ /*
+  * ***** 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): none yet.
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ #include "MEM_guardedalloc.h"
+ #include "BLI_blenlib.h"
+ #include "BLI_math.h"
+ #include "BLI_rand.h"
+ #include "BLI_array.h"
+ #include "BLI_smallhash.h"
+ #include "BLI_heap.h"
+ #include "BKE_context.h"
+ #include "BKE_displist.h"
+ #include "BKE_depsgraph.h"
+ #include "BKE_report.h"
+ #include "BKE_paint.h"
+ #include "BKE_tessmesh.h"
+ #include "IMB_imbuf_types.h"
+ #include "IMB_imbuf.h"
+ #include "WM_api.h"
+ #include "WM_types.h"
+ #include "RNA_access.h"
+ #include "RNA_define.h"
+ #include "ED_mesh.h"
+ #include "ED_screen.h"
+ #include "ED_view3d.h"
+ #include "BIF_gl.h"
+ #include "DNA_scene_types.h"
+ #include "DNA_object_types.h"
+ #include "DNA_mesh_types.h"
+ #include "mesh_intern.h"
+ /* ****************************** MIRROR **************** */
+ void EDBM_select_mirrored(Object *UNUSED(obedit), BMEditMesh *em, int extend)
+ {
+       BMVert *v1, *v2;
+       BMIter iter;
+       BM_ITER(v1, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+               if (!BM_elem_flag_test(v1, BM_ELEM_SELECT) || BM_elem_flag_test(v1, BM_ELEM_HIDDEN)) {
+                       BM_elem_flag_disable(v1, BM_ELEM_TAG);
+               }
+               else {
+                       BM_elem_flag_enable(v1, BM_ELEM_TAG);
+               }
+       }
+       EDBM_CacheMirrorVerts(em, TRUE);
+       if (!extend)
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       BM_ITER(v1, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+               if (!BM_elem_flag_test(v1, BM_ELEM_TAG) || BM_elem_flag_test(v1, BM_ELEM_HIDDEN))
+                       continue;
+               v2 = EDBM_GetMirrorVert(em, v1);
+               if (v2 && !BM_elem_flag_test(v2, BM_ELEM_HIDDEN)) {
+                       BM_elem_select_set(em->bm, v2, TRUE);
+               }
+       }
+       EDBM_EndMirrorCache(em);
+ }
+ void EDBM_automerge(Scene *scene, Object *obedit, int update)
+ {
+       BMEditMesh *em;
+       
+       if ((scene->toolsettings->automerge) &&
+           (obedit && obedit->type == OB_MESH))
+       {
+               em = ((Mesh *)obedit->data)->edit_btmesh;
+               if (!em)
+                       return;
+               BMO_op_callf(em->bm, "automerge verts=%hv dist=%f", BM_ELEM_SELECT, scene->toolsettings->doublimit);
+               if (update) {
+                       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+               }
+       }
+ }
+ /* ****************************** SELECTION ROUTINES **************** */
+ unsigned int bm_solidoffs = 0, bm_wireoffs = 0, bm_vertoffs = 0;      /* set in drawobject.c ... for colorindices */
+ /* facilities for border select and circle select */
+ static char *selbuf = NULL;
+ /* opengl doesn't support concave... */
+ static void draw_triangulated(int mcords[][2], short tot)
+ {
+       ListBase lb = {NULL, NULL};
+       DispList *dl;
+       float *fp;
+       int a;
+       
+       /* make displist */
+       dl = MEM_callocN(sizeof(DispList), "poly disp");
+       dl->type = DL_POLY;
+       dl->parts = 1;
+       dl->nr = tot;
+       dl->verts = fp = MEM_callocN(tot * 3 * sizeof(float), "poly verts");
+       BLI_addtail(&lb, dl);
+       
+       for (a = 0; a < tot; a++, fp += 3) {
+               fp[0] = (float)mcords[a][0];
+               fp[1] = (float)mcords[a][1];
+       }
+       
+       /* do the fill */
+       filldisplist(&lb, &lb, 0);
+       /* do the draw */
+       dl = lb.first;  /* filldisplist adds in head of list */
+       if (dl->type == DL_INDEX3) {
+               int *index;
+               
+               a = dl->parts;
+               fp = dl->verts;
+               index = dl->index;
+               glBegin(GL_TRIANGLES);
+               while (a--) {
+                       glVertex3fv(fp + 3 * index[0]);
+                       glVertex3fv(fp + 3 * index[1]);
+                       glVertex3fv(fp + 3 * index[2]);
+                       index += 3;
+               }
+               glEnd();
+       }
+       
+       freedisplist(&lb);
+ }
+ /* reads rect, and builds selection array for quick lookup */
+ /* returns if all is OK */
+ int EDBM_init_backbuf_border(ViewContext *vc, short xmin, short ymin, short xmax, short ymax)
+ {
+       struct ImBuf *buf;
+       unsigned int *dr;
+       int a;
+       
+       if (vc->obedit == NULL || vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) {
+               return 0;
+       }
+       
+       buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
+       if (buf == NULL) return 0;
+       if (bm_vertoffs == 0) return 0;
+       dr = buf->rect;
+       
+       /* build selection lookup */
+       selbuf = MEM_callocN(bm_vertoffs + 1, "selbuf");
+       
+       a = (xmax - xmin + 1) * (ymax-ymin + 1);
+       while (a--) {
+               if (*dr > 0 && *dr <= bm_vertoffs)
+                       selbuf[*dr] = 1;
+               dr++;
+       }
+       IMB_freeImBuf(buf);
+       return 1;
+ }
+ int EDBM_check_backbuf(unsigned int index)
+ {
+       if (selbuf == NULL) return 1;
+       if (index > 0 && index <= bm_vertoffs)
+               return selbuf[index];
+       return 0;
+ }
+ void EDBM_free_backbuf(void)
+ {
+       if (selbuf) MEM_freeN(selbuf);
+       selbuf = NULL;
+ }
+ /* mcords is a polygon mask
+    - grab backbuffer,
+    - draw with black in backbuffer, 
+    - grab again and compare
+    returns 'OK' 
+ */
+ int EDBM_mask_init_backbuf_border(ViewContext *vc, int mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax)
+ {
+       unsigned int *dr, *drm;
+       struct ImBuf *buf, *bufmask;
+       int a;
+       
+       /* method in use for face selecting too */
+       if (vc->obedit == NULL) {
+               if (paint_facesel_test(vc->obact));
+               else if (paint_vertsel_test(vc->obact));
+               else return 0;
+       }
+       else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) {
+               return 0;
+       }
+       buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
+       if (buf == NULL) return 0;
+       if (bm_vertoffs == 0) return 0;
+       dr = buf->rect;
+       /* draw the mask */
+       glDisable(GL_DEPTH_TEST);
+       
+       glColor3ub(0, 0, 0);
+       
+       /* yah, opengl doesn't do concave... tsk! */
+       ED_region_pixelspace(vc->ar);
+       draw_triangulated(mcords, tot);
+       
+       glBegin(GL_LINE_LOOP);  /* for zero sized masks, lines */
+       for (a = 0; a < tot; a++) {
+               glVertex2iv(mcords[a]);
+       }
+       glEnd();
+       
+       glFinish();     /* to be sure readpixels sees mask */
+       
+       /* grab mask */
+       bufmask = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
+       drm = bufmask->rect;
+       if (bufmask == NULL) {
+               return 0; /* only when mem alloc fails, go crash somewhere else! */
+       }
+       
+       /* build selection lookup */
+       selbuf = MEM_callocN(bm_vertoffs + 1, "selbuf");
+       
+       a = (xmax - xmin + 1) * (ymax - ymin + 1);
+       while (a--) {
+               if (*dr > 0 && *dr <= bm_vertoffs && *drm == 0) selbuf[*dr] = 1;
+               dr++; drm++;
+       }
+       IMB_freeImBuf(buf);
+       IMB_freeImBuf(bufmask);
+       return 1;
+ }
+ /* circle shaped sample area */
+ int EDBM_init_backbuf_circle(ViewContext *vc, short xs, short ys, short rads)
+ {
+       struct ImBuf *buf;
+       unsigned int *dr;
+       short xmin, ymin, xmax, ymax, xc, yc;
+       int radsq;
+       
+       /* method in use for face selecting too */
+       if (vc->obedit == NULL) {
+               if (paint_facesel_test(vc->obact));
+               else if (paint_vertsel_test(vc->obact));
+               else return 0;
+       }
+       else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) return 0;
+       
+       xmin = xs - rads; xmax = xs + rads;
+       ymin = ys - rads; ymax = ys + rads;
+       buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
+       if (bm_vertoffs == 0) return 0;
+       if (buf == NULL) return 0;
+       dr = buf->rect;
+       
+       /* build selection lookup */
+       selbuf = MEM_callocN(bm_vertoffs + 1, "selbuf");
+       radsq = rads * rads;
+       for (yc = -rads; yc <= rads; yc++) {
+               for (xc = -rads; xc <= rads; xc++, dr++) {
+                       if (xc * xc + yc * yc < radsq) {
+                               if (*dr > 0 && *dr <= bm_vertoffs) selbuf[*dr] = 1;
+                       }
+               }
+       }
+       IMB_freeImBuf(buf);
+       return 1;
+       
+ }
+ static void findnearestvert__doClosest(void *userData, BMVert *eve, int x, int y, int index)
+ {
+       struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; BMVert *closest; } *data = userData;
+       if (data->pass == 0) {
+               if (index <= data->lastIndex)
+                       return;
+       }
+       else {
+               if (index > data->lastIndex)
+                       return;
+       }
+       if (data->dist > 3) {
+               int temp = abs(data->mval[0] - x) + abs(data->mval[1]- y);
+               if (BM_elem_flag_test(eve, BM_ELEM_SELECT) == data->select) {
+                       if (data->strict == 1)
+                               return;
+                       else
+                               temp += 5;
+               }
+               if (temp < data->dist) {
+                       data->dist = temp;
+                       data->closest = eve;
+                       data->closestIndex = index;
+               }
+       }
+ }
+ static unsigned int findnearestvert__backbufIndextest(void *handle, unsigned int index)
+ {
+       BMEditMesh *em = (BMEditMesh *)handle;
+       BMVert *eve = BM_vert_at_index(em->bm, index - 1);
+       if (eve && BM_elem_flag_test(eve, BM_ELEM_SELECT)) return 0;
+       return 1;
+ }
+ /**
+  * findnearestvert
+  * 
+  * dist (in/out): minimal distance to the nearest and at the end, actual distance
+  * sel: selection bias
+  *            if SELECT, selected vertice are given a 5 pixel bias to make them farter than unselect verts
+  *            if 0, unselected vertice are given the bias
+  * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased 
+  */
+ BMVert *EDBM_findnearestvert(ViewContext *vc, int *dist, short sel, short strict)
+ {
+       if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
+               int distance;
+               unsigned int index;
+               BMVert *eve;
+               
+               if (strict) {
+                       index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance,
+                                                          strict, vc->em, findnearestvert__backbufIndextest);
+               }
+               else {
+                       index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance,
+                                                          0, NULL, NULL);
+               }
+               
+               eve = BM_vert_at_index(vc->em->bm, index - 1);
+               
+               if (eve && distance < *dist) {
+                       *dist = distance;
+                       return eve;
+               }
+               else {
+                       return NULL;
+               }
+                       
+       }
+       else {
+               struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; BMVert *closest; } data;
+               static int lastSelectedIndex = 0;
+               static BMVert *lastSelected = NULL;
+               
+               if (lastSelected && BM_vert_at_index(vc->em->bm, lastSelectedIndex) != lastSelected) {
+                       lastSelectedIndex = 0;
+                       lastSelected = NULL;
+               }
+               data.lastIndex = lastSelectedIndex;
+               data.mval[0] = vc->mval[0];
+               data.mval[1] = vc->mval[1];
+               data.select = sel;
+               data.dist = *dist;
+               data.strict = strict;
+               data.closest = NULL;
+               data.closestIndex = 0;
+               data.pass = 0;
+               ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+               mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, V3D_CLIP_TEST_RV3D_CLIPPING);
+               if (data.dist > 3) {
+                       data.pass = 1;
+                       mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, V3D_CLIP_TEST_RV3D_CLIPPING);
+               }
+               *dist = data.dist;
+               lastSelected = data.closest;
+               lastSelectedIndex = data.closestIndex;
+               return data.closest;
+       }
+ }
+ /* returns labda for closest distance v1 to line-piece v2 - v3 */
+ float labda_PdistVL2Dfl(const float v1[3], const float v2[3], const float v3[3])
+ {
+       float rc[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.0f)
+               return 0.0f;
+       
+       return (rc[0] * (v1[0] - v2[0]) + rc[1] * (v1[1] - v2[1]) ) / len;
+ }
+ /* note; uses v3d, so needs active 3d window */
+ static void findnearestedge__doClosest(void *userData, BMEdge *eed, int x0, int y0, int x1, int y1, int UNUSED(index))
+ {
+       struct { ViewContext vc; float mval[2]; int dist; BMEdge *closest; } *data = userData;
+       float v1[2], v2[2];
+       int distance;
+               
+       v1[0] = x0;
+       v1[1] = y0;
+       v2[0] = x1;
+       v2[1] = y1;
+               
+       distance = dist_to_line_segment_v2(data->mval, v1, v2);
+               
+       if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+               distance += 5;
+       }
+       if (distance < data->dist) {
+               if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
+                       float labda = labda_PdistVL2Dfl(data->mval, v1, v2);
+                       float vec[3];
+                       vec[0] = eed->v1->co[0] + labda * (eed->v2->co[0] - eed->v1->co[0]);
+                       vec[1] = eed->v1->co[1] + labda * (eed->v2->co[1] - eed->v1->co[1]);
+                       vec[2] = eed->v1->co[2] + labda * (eed->v2->co[2] - eed->v1->co[2]);
+                       mul_m4_v3(data->vc.obedit->obmat, vec);
+                       if (ED_view3d_test_clipping(data->vc.rv3d, vec, 1) == 0) {
+                               data->dist = distance;
+                               data->closest = eed;
+                       }
+               }
+               else {
+                       data->dist = distance;
+                       data->closest = eed;
+               }
+       }
+ }
+ BMEdge *EDBM_findnearestedge(ViewContext *vc, int *dist)
+ {
+       if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
+               int distance;
+               unsigned int index;
+               BMEdge *eed;
+               
+               view3d_validate_backbuf(vc);
+               
+               index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_solidoffs, bm_wireoffs, &distance,0, NULL, NULL);
+               eed = BM_edge_at_index(vc->em->bm, index - 1);
+               
+               if (eed && distance < *dist) {
+                       *dist = distance;
+                       return eed;
+               }
+               else {
+                       return NULL;
+               }
+       }
+       else {
+               struct { ViewContext vc; float mval[2]; int dist; BMEdge *closest; } data;
+               data.vc = *vc;
+               data.mval[0] = vc->mval[0];
+               data.mval[1] = vc->mval[1];
+               data.dist = *dist;
+               data.closest = NULL;
+               ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+               mesh_foreachScreenEdge(vc, findnearestedge__doClosest, &data, 2);
+               *dist = data.dist;
+               return data.closest;
+       }
+ }
+ static void findnearestface__getDistance(void *userData, BMFace *efa, int x, int y, int UNUSED(index))
+ {
+       struct { short mval[2]; int dist; BMFace *toFace; } *data = userData;
+       if (efa == data->toFace) {
+               int temp = abs(data->mval[0] - x) + abs(data->mval[1] - y);
+               if (temp < data->dist)
+                       data->dist = temp;
+       }
+ }
+ static void findnearestface__doClosest(void *userData, BMFace *efa, int x, int y, int index)
+ {
+       struct { short mval[2], pass; int dist, lastIndex, closestIndex; BMFace *closest; } *data = userData;
+       if (data->pass == 0) {
+               if (index <= data->lastIndex)
+                       return;
+       }
+       else {
+               if (index > data->lastIndex)
+                       return;
+       }
+       if (data->dist > 3) {
+               int temp = abs(data->mval[0] - x) + abs(data->mval[1] - y);
+               if (temp < data->dist) {
+                       data->dist = temp;
+                       data->closest = efa;
+                       data->closestIndex = index;
+               }
+       }
+ }
+ BMFace *EDBM_findnearestface(ViewContext *vc, int *dist)
+ {
+       if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
+               unsigned int index;
+               BMFace *efa;
+               view3d_validate_backbuf(vc);
+               index = view3d_sample_backbuf(vc, vc->mval[0], vc->mval[1]);
+               efa = BM_face_at_index(vc->em->bm, index - 1);
+               
+               if (efa) {
+                       struct { short mval[2]; int dist; BMFace *toFace; } data;
+                       data.mval[0] = vc->mval[0];
+                       data.mval[1] = vc->mval[1];
+                       data.dist = 0x7FFF;             /* largest short */
+                       data.toFace = efa;
+                       mesh_foreachScreenFace(vc, findnearestface__getDistance, &data);
+                       if (vc->em->selectmode == SCE_SELECT_FACE || data.dist < *dist) {       /* only faces, no dist check */
+                               *dist = data.dist;
+                               return efa;
+                       }
+               }
+               
+               return NULL;
+       }
+       else {
+               struct { short mval[2], pass; int dist, lastIndex, closestIndex; BMFace *closest; } data;
+               static int lastSelectedIndex = 0;
+               static BMFace *lastSelected = NULL;
+               if (lastSelected && BM_face_at_index(vc->em->bm, lastSelectedIndex) != lastSelected) {
+                       lastSelectedIndex = 0;
+                       lastSelected = NULL;
+               }
+               data.lastIndex = lastSelectedIndex;
+               data.mval[0] = vc->mval[0];
+               data.mval[1] = vc->mval[1];
+               data.dist = *dist;
+               data.closest = NULL;
+               data.closestIndex = 0;
+               ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+               data.pass = 0;
+               mesh_foreachScreenFace(vc, findnearestface__doClosest, &data);
+               if (data.dist > 3) {
+                       data.pass = 1;
+                       ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
+                       mesh_foreachScreenFace(vc, findnearestface__doClosest, &data);
+               }
+               *dist = data.dist;
+               lastSelected = data.closest;
+               lastSelectedIndex = data.closestIndex;
+               return data.closest;
+       }
+ }
+ /* best distance based on screen coords. 
+    use em->selectmode to define how to use 
+    selected vertices and edges get disadvantage
+    return 1 if found one
+ */
+ static int unified_findnearest(ViewContext *vc, BMVert **eve, BMEdge **eed, BMFace **efa) 
+ {
+       BMEditMesh *em = vc->em;
+       int dist = 75;
+       
+       *eve = NULL;
+       *eed = NULL;
+       *efa = NULL;
+       
+       /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */
+       view3d_validate_backbuf(vc);
+       
+       if (em->selectmode & SCE_SELECT_VERTEX)
+               *eve = EDBM_findnearestvert(vc, &dist, BM_ELEM_SELECT, 0);
+       if (em->selectmode & SCE_SELECT_FACE)
+               *efa = EDBM_findnearestface(vc, &dist);
+       dist-= 20;      /* since edges select lines, we give dots advantage of 20 pix */
+       if (em->selectmode & SCE_SELECT_EDGE)
+               *eed = EDBM_findnearestedge(vc, &dist);
+       /* return only one of 3 pointers, for frontbuffer redraws */
+       if (*eed) {
+               *efa = NULL; *eve = NULL;
+       }
+       else if (*efa) {
+               *eve = NULL;
+       }
+       
+       return (*eve || *eed || *efa);
+ }
+ /* ****************  SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */
+ static EnumPropertyItem prop_similar_types[] = {
+       {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""},
+       {SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""},
+       {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""},
+       {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""},
+       {SIMEDGE_DIR, "DIR", 0, "Direction", ""},
+       {SIMEDGE_FACE, "FACE", 0, "Amount of Faces Around an Edge", ""},
+       {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""},
+       {SIMEDGE_CREASE, "CREASE", 0, "Crease", ""},
+       {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""},
+       {SIMEDGE_SHARP, "SHARP", 0, "Sharpness", ""},
++      {SIMEDGE_FREESTYLE, "FREESTYLE_EDGE", 0, "Freestyle Edge Marks", ""},
+       {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""},
+       {SIMFACE_IMAGE, "IMAGE", 0, "Image", ""},
+       {SIMFACE_AREA, "AREA", 0, "Area", ""},
+       {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""},
+       {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""},
+       {SIMFACE_COPLANAR, "COPLANAR", 0, "Co-planar", ""},
++      {SIMFACE_FREESTYLE, "FREESTYLE_FACE", 0, "Freestyle Face Marks", ""},
+       {0, NULL, 0, NULL, NULL}
+ };
+ /* selects new faces/edges/verts based on the existing selection */
+ static int similar_face_select_exec(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* get the type from RNA */
+       int type = RNA_enum_get(op->ptr, "type");
+       float thresh = CTX_data_tool_settings(C)->select_thresh;
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "similarfaces faces=%hf type=%d thresh=%f", BM_ELEM_SELECT, type, thresh);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* clear the existing selection */
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       /* select the output */
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "faceout", BM_ELEM_SELECT, BM_ALL);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       /* dependencies graph and notification stuff */
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }     
+ /* ***************************************************** */
+ /* EDGE GROUP */
+ /* wrap the above function but do selection flushing edge to face */
+ static int similar_edge_select_exec(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* get the type from RNA */
+       int type = RNA_enum_get(op->ptr, "type");
+       float thresh = CTX_data_tool_settings(C)->select_thresh;
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "similaredges edges=%he type=%d thresh=%f", BM_ELEM_SELECT, type, thresh);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* clear the existing selection */
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       /* select the output */
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "edgeout", BM_ELEM_SELECT, BM_ALL);
+       EDBM_selectmode_flush(em);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       /* dependencies graph and notification stuff */
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }
+ /* ********************************* */
+ /*
+ VERT GROUP
+  mode 1: same normal
+  mode 2: same number of face users
+  mode 3: same vertex groups
+ */
+ static int similar_vert_select_exec(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* get the type from RNA */
+       int type = RNA_enum_get(op->ptr, "type");
+       float thresh = CTX_data_tool_settings(C)->select_thresh;
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "similarverts verts=%hv type=%d thresh=%f", BM_ELEM_SELECT, type, thresh);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* clear the existing selection */
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       /* select the output */
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "vertout", BM_ELEM_SELECT, BM_ALL);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       EDBM_selectmode_flush(em);
+       /* dependencies graph and notification stuff */
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }
+ static int select_similar_exec(bContext *C, wmOperator *op)
+ {
+       int type = RNA_enum_get(op->ptr, "type");
+       if (type < 100)
+               return similar_vert_select_exec(C, op);
+       else if (type < 200)
+               return similar_edge_select_exec(C, op);
+       else
+               return similar_face_select_exec(C, op);
+ }
+ static EnumPropertyItem *select_similar_type_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+                                                    int *free)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       if (obedit && obedit->type == OB_MESH) {
+               EnumPropertyItem *item = NULL;
+               int a, totitem = 0;
+               BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+               if (em->selectmode & SCE_SELECT_VERTEX) {
+                       for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) {
+                               RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
+                       }
+               }
+               else if (em->selectmode & SCE_SELECT_EDGE) {
+                       for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) {
+                               RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
+                       }
+               }
+               else if (em->selectmode & SCE_SELECT_FACE) {
++                      for (a = SIMFACE_MATERIAL; a <= SIMFACE_FREESTYLE; a++) {
+                               RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
+                       }
+               }
+               RNA_enum_item_end(&item, &totitem);
+               *free = 1;
+               return item;
+       }
+       
+       return NULL;
+ }
+ void MESH_OT_select_similar(wmOperatorType *ot)
+ {
+       PropertyRNA *prop;
+       /* identifiers */
+       ot->name = "Select Similar";
+       ot->idname = "MESH_OT_select_similar";
+       
+       /* api callbacks */
+       ot->invoke = WM_menu_invoke;
+       ot->exec = select_similar_exec;
+       ot->poll = ED_operator_editmesh;
+       ot->description = "Select similar vertices, edges or faces by property types";
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* properties */
+       prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
+       RNA_def_enum_funcs(prop, select_similar_type_itemf);
+ }
+ /* ***************************************************** */
+ /* ****************  LOOP SELECTS *************** */
+ static void walker_select(BMEditMesh *em, int walkercode, void *start, int select)
+ {
+       BMesh *bm = em->bm;
+       BMHeader *h;
+       BMWalker walker;
+       BMW_init(&walker, bm, walkercode,
+                BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
+                BMW_NIL_LAY);
+       h = BMW_begin(&walker, start);
+       for ( ; h; h = BMW_step(&walker)) {
+               if (!select) {
+                       BM_select_history_remove(bm, h);
+               }
+               BM_elem_select_set(bm, h, select);
+       }
+       BMW_end(&walker);
+ }
+ static int loop_multiselect(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMEdge *eed;
+       BMEdge **edarray;
+       int edindex;
+       int looptype = RNA_boolean_get(op->ptr, "ring");
+       
+       BMIter iter;
+       int totedgesel = 0;
+       for (eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
+           eed; eed = BM_iter_step(&iter)) {
+               if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+                       totedgesel++;
+               }
+       }
+       
+       edarray = MEM_mallocN(sizeof(BMEdge *)*totedgesel,"edge array");
+       edindex = 0;
+       
+       for (eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
+            eed;
+            eed = BM_iter_step(&iter))
+       {
+               if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+                       edarray[edindex] = eed;
+                       edindex++;
+               }
+       }
+       
+       if (looptype) {
+               for (edindex = 0; edindex < totedgesel; edindex += 1) {
+                       eed = edarray[edindex];
+                       walker_select(em, BMW_EDGERING, eed, 1);
+               }
+               EDBM_selectmode_flush(em);
+       }
+       else{
+               for (edindex = 0; edindex < totedgesel; edindex += 1) {
+                       eed = edarray[edindex];
+                       walker_select(em, BMW_LOOP, eed, 1);
+               }
+               EDBM_selectmode_flush(em);
+       }
+       MEM_freeN(edarray);
+ //    if (EM_texFaceCheck())
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_loop_multi_select(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Multi Select Loops";
+       ot->idname = "MESH_OT_loop_multi_select";
+       
+       /* api callbacks */
+       ot->exec = loop_multiselect;
+       ot->poll = ED_operator_editmesh;
+       ot->description = "Select a loop of connected edges by connection type";
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* properties */
+       RNA_def_boolean(ot->srna, "ring", 0, "Ring", "");
+ }
+               
+ /* ***************** MAIN MOUSE SELECTION ************** */
+ /* ***************** loop select (non modal) ************** */
+ static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
+ {
+       ViewContext vc;
+       BMEditMesh *em;
+       BMEdge *eed;
+       int select = TRUE;
+       int dist = 50;
+       
+       em_setup_viewcontext(C, &vc);
+       vc.mval[0] = mval[0];
+       vc.mval[1] = mval[1];
+       em = vc.em;
+       
+       /* no afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad */
+       view3d_validate_backbuf(&vc);
+       eed = EDBM_findnearestedge(&vc, &dist);
+       if (eed) {
+               if (extend == 0) {
+                       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+               }
+       
+               if (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0) {
+                       select = TRUE;
+               }
+               else if (extend) {
+                       select = FALSE;
+               }
+               if (em->selectmode & SCE_SELECT_FACE) {
+                       walker_select(em, BMW_FACELOOP, eed, select);
+               }
+               else if (em->selectmode & SCE_SELECT_EDGE) {
+                       if (ring)
+                               walker_select(em, BMW_EDGERING, eed, select);
+                       else
+                               walker_select(em, BMW_LOOP, eed, select);
+               }
+               else if (em->selectmode & SCE_SELECT_VERTEX) {
+                       if (ring)
+                               walker_select(em, BMW_EDGERING, eed, select);
+                       else
+                               walker_select(em, BMW_LOOP, eed, select);
+               }
+               EDBM_selectmode_flush(em);
+ //                    if (EM_texFaceCheck())
+               
+               /* sets as active, useful for other tools */
+               if (select) {
+                       if (em->selectmode & SCE_SELECT_VERTEX) {
+                               /* TODO: would be nice if the edge vertex chosen here
+                                * was the one closer to the selection pointer, instead
+                                * of arbitrarily selecting the first one */
+                               EDBM_store_selection(em, eed->v1);
+                       }
+                       else if (em->selectmode & SCE_SELECT_EDGE) {
+                               EDBM_store_selection(em, eed);
+                       }
+                       /* TODO: would be nice if the nearest face that
+                        * belongs to the selected edge could be set to
+                        * active here in face select mode */
+               }
+               WM_event_add_notifier(C, NC_GEOM|ND_SELECT, vc.obedit);
+       }
+ }
+ static int mesh_select_loop_invoke(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       
+       view3d_operator_needs_opengl(C);
+       
+       mouse_mesh_loop(C, event->mval, RNA_boolean_get(op->ptr, "extend"),
+                                       RNA_boolean_get(op->ptr, "ring"));
+       
+       /* cannot do tweaks for as long this keymap is after transform map */
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_loop_select(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Loop Select";
+       ot->idname = "MESH_OT_loop_select";
+       ot->description = "Select a loop";
+       
+       /* api callbacks */
+       ot->invoke = mesh_select_loop_invoke;
+       ot->poll = ED_operator_editmesh_region_view3d;
+       ot->description = "Select a loop of connected edges";
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* properties */
+       RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection");
+       RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring");
+ }
+ void MESH_OT_edgering_select (wmOperatorType *ot)
+ {
+       /* description */
+       ot->name = "Edge Ring Select";
+       ot->idname = "MESH_OT_edgering_select";
+       ot->description = "Select an edge ring";
+       
+       /* callbacks */
+       ot->invoke = mesh_select_loop_invoke;
+       ot->poll = ED_operator_editmesh_region_view3d;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+       RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring");
+ }
+ /* ******************* edgetag_shortest_path and helpers ****************** */
+ static float edgetag_cut_cost(BMEditMesh *UNUSED(em), BMEdge *e1, BMEdge *e2, BMVert *v)
+ {
+       BMVert *v1 = (e1->v1 == v) ? e1->v2 : e1->v1;
+       BMVert *v2 = (e2->v1 == v) ? e2->v2 : e2->v1;
+       float cost, d1[3], d2[3];
+       /* The cost is based on the simple sum of the length of the two edgees... */
+       sub_v3_v3v3(d1, v->co, v1->co);
+       sub_v3_v3v3(d2, v2->co, v->co);
+       cost = len_v3(d1);
+       cost += len_v3(d2);
+       /*.but is biased to give higher values to sharp turns, so that it will take
+        * paths with fewer "turns" when selecting between equal-weighted paths between
+        * the two edges */
+       cost = cost + 0.5f * cost * (2.0f - sqrt(fabs(dot_v3v3(d1, d2))));
+       return cost;
+ }
+ static void edgetag_add_adjacent(BMEditMesh *em, SmallHash *visithash, Heap *heap, int mednum, int vertnum, 
+                                                                int *nedges, int *edges, int *prevedge, float *cost)
+ {
+       BMEdge *e1 = EDBM_get_edge_for_index(em, mednum);
+       BMVert *v = EDBM_get_vert_for_index(em, vertnum);
+       int startadj, endadj = nedges[vertnum + 1];
+       for (startadj = nedges[vertnum]; startadj < endadj; startadj++) {
+               int adjnum = edges[startadj];
+               BMEdge *e2 = EDBM_get_edge_for_index(em, adjnum);
+               float newcost;
+               float cutcost;
+               if (BLI_smallhash_haskey(visithash, (uintptr_t)e2))
+                       continue;
+               cutcost = edgetag_cut_cost(em, e1, e2, v);
+               newcost = cost[mednum] + cutcost;
+               if (cost[adjnum] > newcost) {
+                       cost[adjnum] = newcost;
+                       prevedge[adjnum] = mednum;
+                       BLI_heap_insert(heap, newcost, SET_INT_IN_POINTER(adjnum));
+               }
+       }
+ }
+ static void edgetag_context_set(BMEditMesh *em, Scene *scene, BMEdge *e, int val)
+ {
+       
+       switch (scene->toolsettings->edge_mode) {
+       case EDGE_MODE_SELECT:
+               BM_elem_select_set(em->bm, e, val);
+               break;
+       case EDGE_MODE_TAG_SEAM:
+               if (val)                {BM_elem_flag_enable(e, BM_ELEM_SEAM);}
+               else                    {BM_elem_flag_disable(e, BM_ELEM_SEAM);}
+               break;
+       case EDGE_MODE_TAG_SHARP:
+               if (val)                {BM_elem_flag_enable(e, BM_ELEM_SEAM);}
+               else                    {BM_elem_flag_disable(e, BM_ELEM_SEAM);}
+               break;
++      case EDGE_MODE_TAG_FREESTYLE:
++              if (val)                {BM_elem_flag_enable(e, BM_ELEM_FREESTYLE);}
++              else                    {BM_elem_flag_disable(e, BM_ELEM_FREESTYLE);}
++              break;
+       case EDGE_MODE_TAG_CREASE:
+        {
+               float *crease = CustomData_bmesh_get(&em->bm->edata, e->head.data, CD_CREASE);
+               
+               if (val)                {*crease = 1.0f;}
+               else                    {*crease = 0.0f;}
+               break;
+        }
+       case EDGE_MODE_TAG_BEVEL:
+        {
+               float *bweight = CustomData_bmesh_get(&em->bm->edata, e->head.data, CD_BWEIGHT);
+               if (val)                {*bweight = 1.0f;}
+               else                    {*bweight = 0.0f;}
+               break;
+        }
+       }
+ }
+ static int edgetag_context_check(Scene *scene, BMEditMesh *em, BMEdge *e)
+ {
+       switch (scene->toolsettings->edge_mode) {
+       case EDGE_MODE_SELECT:
+               return BM_elem_flag_test(e, BM_ELEM_SELECT) ? 1 : 0;
+       case EDGE_MODE_TAG_SEAM:
+               return BM_elem_flag_test(e, BM_ELEM_SEAM);
+       case EDGE_MODE_TAG_SHARP:
+               return !BM_elem_flag_test(e, BM_ELEM_SMOOTH);
++      case EDGE_MODE_TAG_FREESTYLE:
++              return !BM_elem_flag_test(e, BM_ELEM_FREESTYLE);
+       case EDGE_MODE_TAG_CREASE:      
+               return BM_elem_float_data_get(&em->bm->edata, e, CD_CREASE) ? 1 : 0;
+       case EDGE_MODE_TAG_BEVEL:
+               return BM_elem_float_data_get(&em->bm->edata, e, CD_BWEIGHT) ? 1 : 0;
+       }
+       return 0;
+ }
+ static int edgetag_shortest_path(Scene *scene, BMEditMesh *em, BMEdge *source, BMEdge *target)
+ {
+       BMEdge *e;
+       BMIter iter;
+       Heap *heap;
+       SmallHash visithash;
+       float *cost;
+       int i, totvert = 0, totedge = 0, *nedges, *edges, *prevedge, mednum = -1, nedgeswap = 0;
+       int targetnum;
+       BLI_smallhash_init(&visithash);
+       /* note, would pass BM_EDGE except we are looping over all edges anyway */
+       BM_mesh_elem_index_ensure(em->bm, BM_VERT /* | BM_EDGE */);
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               e->oflags[0].f = 0; /* XXX, whats this for, BMESH_TODO, double check if this is needed */
+               if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+                       BLI_smallhash_insert(&visithash, (uintptr_t)e, NULL);
+               }
+               BM_elem_index_set(e, totedge); /* set_inline */
+               totedge++;
+       }
+       em->bm->elem_index_dirty &= ~BM_EDGE;
+       /* alloc */
+       totvert = em->bm->totvert;
+       nedges = MEM_callocN(sizeof(*nedges) * totvert + 1, "SeamPathNEdges");
+       edges = MEM_mallocN(sizeof(*edges) * totedge * 2, "SeamPathEdges");
+       prevedge = MEM_mallocN(sizeof(*prevedge) * totedge, "SeamPathPrevious");
+       cost = MEM_mallocN(sizeof(*cost) * totedge, "SeamPathCost");
+       /* count edges, compute adjacent edges offsets and fill adjacent */
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               nedges[BM_elem_index_get(e->v1) + 1]++;
+               nedges[BM_elem_index_get(e->v2) + 1]++;
+       }
+       for (i = 1; i < totvert; i++) {
+               int newswap = nedges[i + 1];
+               nedges[i + 1] = nedgeswap + nedges[i];
+               nedgeswap = newswap;
+       }
+       nedges[0] = nedges[1] = 0;
+       i = 0;
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               edges[nedges[BM_elem_index_get(e->v1) + 1]++] = i;
+               edges[nedges[BM_elem_index_get(e->v2) + 1]++] = i;
+               cost[i] = 1e20f;
+               prevedge[i] = -1;
+               i++;
+       }
+       /*
+        * Arrays are now filled as follows:
+        *
+        *      nedges[n] = sum of the # of edges incident to all vertices numbered 0 thru n - 1
+        *      edges[edges[n]..edges[n - 1]] = the indices of of the edges incident to vertex n
+        *
+        * As the search continues, prevedge[n] will be the previous edge on the shortest
+        * path found so far to edge n. The visitedhash will of course contain entries
+        * for edges that have been visited, cost[n] will contain the length of the shortest
+        * path to edge n found so far, Finally, heap is a priority heap which is built on the
+        * the same data as the cost arry, but inverted: it is a worklist of edges prioritized
+        * by the shortest path found so far to the edge.
+       */
+ #if 0 /* UNUSED */ /* this block does nothing, not sure why its here? - campbell */
+       for (i = 0; i < totvert; i++) {
+               int start = nedges[i], end = nedges[i + 1], cur;
+               for (cur = start; cur < end; cur++) {
+                       BMEdge *e = EDBM_get_edge_for_index(em, edges[cur]);
+               }
+       }
+ #endif
+       /* regular dijkstra shortest path, but over edges instead of vertices */
+       heap = BLI_heap_new();
+       BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(BM_elem_index_get(source)));
+       cost[BM_elem_index_get(source)] = 0.0f;
+       EDBM_init_index_arrays(em, 1, 1, 0);
+       targetnum = BM_elem_index_get(target);
+       while (!BLI_heap_empty(heap)) {
+               mednum = GET_INT_FROM_POINTER(BLI_heap_popmin(heap));
+               e = EDBM_get_edge_for_index(em, mednum);
+               if (mednum == targetnum)
+                       break;
+               if (BLI_smallhash_haskey(&visithash, (uintptr_t)e))
+                       continue;
+               BLI_smallhash_insert(&visithash, (uintptr_t)e, NULL);
+               edgetag_add_adjacent(em, &visithash, heap, mednum, BM_elem_index_get(e->v1), nedges, edges, prevedge, cost);
+               edgetag_add_adjacent(em, &visithash, heap, mednum, BM_elem_index_get(e->v2), nedges, edges, prevedge, cost);
+       }
+       
+       if (mednum == targetnum) {
+               short allseams = 1;
+               /* Check whether the path is already completely tagged.
+                * if it is, the tags will be cleared instead of set. */
+               mednum = targetnum;
+               do {
+                       e = EDBM_get_edge_for_index(em, mednum);
+                       if (!edgetag_context_check(scene, em, e)) {
+                               allseams = 0;
+                               break;
+                       }
+                       mednum = prevedge[mednum];
+               } while (mednum != BM_elem_index_get(source));
+               /* Follow path back and source and add or remove tags */
+               mednum = targetnum;
+               do {
+                       e = EDBM_get_edge_for_index(em, mednum);
+                       if (allseams)
+                               edgetag_context_set(em, scene, e, 0);
+                       else
+                               edgetag_context_set(em, scene, e, 1);
+                       mednum = prevedge[mednum];
+               } while (mednum != -1);
+       }
+       EDBM_free_index_arrays(em);
+       MEM_freeN(nedges);
+       MEM_freeN(edges);
+       MEM_freeN(prevedge);
+       MEM_freeN(cost);
+       BLI_heap_free(heap, NULL);
+       BLI_smallhash_release(&visithash);
+       return 1;
+ }
+ /* ******************* mesh shortest path select, uses prev-selected edge ****************** */
+ /* since you want to create paths with multiple selects, it doesn't have extend option */
+ static void mouse_mesh_shortest_path(bContext *C, int mval[2])
+ {
+       Object *ob = CTX_data_edit_object(C);
+       ViewContext vc;
+       BMEditMesh *em;
+       BMEdge *e;
+       int dist = 50;
+       
+       em_setup_viewcontext(C, &vc);
+       vc.mval[0] = mval[0];
+       vc.mval[1] = mval[1];
+       em = vc.em;
+       
+       e = EDBM_findnearestedge(&vc, &dist);
+       if (e) {
+               Mesh *me = vc.obedit->data;
+               int path = 0;
+               
+               if (em->bm->selected.last) {
+                       BMEditSelection *ese = em->bm->selected.last;
+                       
+                       if (ese && ese->htype == BM_EDGE) {
+                               BMEdge *e_act;
+                               e_act = (BMEdge *)ese->data;
+                               if (e_act != e) {
+                                       if (edgetag_shortest_path(vc.scene, em, e_act, e)) {
+                                               EDBM_remove_selection(em, e_act);
+                                               path = 1;
+                                       }
+                               }
+                       }
+               }
+               if (path == 0) {
+                       int act = (edgetag_context_check(vc.scene, em, e) == 0);
+                       edgetag_context_set(em, vc.scene, e, act); /* switch the edge option */
+               }
+               
+               EDBM_selectmode_flush(em);
+               /* even if this is selected it may not be in the selection list */
+               if (edgetag_context_check(vc.scene, em, e) == 0)
+                       EDBM_remove_selection(em, e);
+               else
+                       EDBM_store_selection(em, e);
+       
+               /* force drawmode for mesh */
+               switch (CTX_data_tool_settings(C)->edge_mode) {
+                       
+                       case EDGE_MODE_TAG_SEAM:
+                               me->drawflag |= ME_DRAWSEAMS;
+                               break;
+                       case EDGE_MODE_TAG_SHARP:
+                               me->drawflag |= ME_DRAWSHARP;
+                               break;
+                       case EDGE_MODE_TAG_CREASE:      
+                               me->drawflag |= ME_DRAWCREASES;
+                               break;
+                       case EDGE_MODE_TAG_BEVEL:
+                               me->drawflag |= ME_DRAWBWEIGHTS;
+                               break;
++                      case EDGE_MODE_TAG_FREESTYLE:
++                              me->drawflag |= ME_DRAW_FREESTYLE_EDGE;
++                              break;
+               }
+               
+               DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+               WM_event_add_notifier(C, NC_GEOM|ND_SELECT, ob->data);
+       }
+ }
+ static int mesh_shortest_path_select_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
+ {
+       
+       view3d_operator_needs_opengl(C);
+       mouse_mesh_shortest_path(C, event->mval);
+       
+       return OPERATOR_FINISHED;
+ }
+       
+ void MESH_OT_select_shortest_path(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Shortest Path Select";
+       ot->idname = "MESH_OT_select_shortest_path";
+       
+       /* api callbacks */
+       ot->invoke = mesh_shortest_path_select_invoke;
+       ot->poll = ED_operator_editmesh;
+       ot->description = "Select shortest path between two selections";
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* properties */
+       RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
+ }
+ /* ************************************************** */
+ /* here actual select happens */
+ /* gets called via generic mouse select operator */
+ int mouse_mesh(bContext *C, const int mval[2], short extend)
+ {
+       ViewContext vc;
+       BMVert *eve = NULL;
+       BMEdge *eed = NULL;
+       BMFace *efa = NULL;
+       
+       /* setup view context for argument to callbacks */
+       em_setup_viewcontext(C, &vc);
+       vc.mval[0] = mval[0];
+       vc.mval[1] = mval[1];
+       
+       if (unified_findnearest(&vc, &eve, &eed, &efa)) {
+               
+               if (extend == 0) EDBM_flag_disable_all(vc.em, BM_ELEM_SELECT);
+               
+               if (efa) {
+                       /* set the last selected face */
+                       BM_active_face_set(vc.em->bm, efa);
+                       
+                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+                               EDBM_store_selection(vc.em, efa);
+                               BM_elem_select_set(vc.em->bm, efa, TRUE);
+                       }
+                       else if (extend) {
+                               EDBM_remove_selection(vc.em, efa);
+                               BM_elem_select_set(vc.em->bm, efa, FALSE);
+                       }
+               }
+               else if (eed) {
+                       if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+                               EDBM_store_selection(vc.em, eed);
+                               BM_elem_select_set(vc.em->bm, eed, TRUE);
+                       }
+                       else if (extend) {
+                               EDBM_remove_selection(vc.em, eed);
+                               BM_elem_select_set(vc.em->bm, eed, FALSE);
+                       }
+               }
+               else if (eve) {
+                       if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+                               EDBM_store_selection(vc.em, eve);
+                               BM_elem_select_set(vc.em->bm, eve, TRUE);
+                       }
+                       else if (extend) {
+                               EDBM_remove_selection(vc.em, eve);
+                               BM_elem_select_set(vc.em->bm, eve, FALSE);
+                       }
+               }
+               
+               EDBM_selectmode_flush(vc.em);
+                 
+ //            if (EM_texFaceCheck()) {
+               if (efa && efa->mat_nr != vc.obedit->actcol - 1) {
+                       vc.obedit->actcol = efa->mat_nr + 1;
+                       vc.em->mat_nr = efa->mat_nr;
+ //                    BIF_preview_changed(ID_MA);
+               }
+               WM_event_add_notifier(C, NC_GEOM|ND_SELECT, vc.obedit);
+               return 1;
+       }
+       return 0;
+ }
+ static void EDBM_strip_selections(BMEditMesh *em)
+ {
+       BMEditSelection *ese, *nextese;
+       if (!(em->selectmode & SCE_SELECT_VERTEX)) {
+               ese = em->bm->selected.first;
+               while (ese) {
+                       nextese = ese->next;
+                       if (ese->htype == BM_VERT) BLI_freelinkN(&(em->bm->selected), ese);
+                       ese = nextese;
+               }
+       }
+       if (!(em->selectmode & SCE_SELECT_EDGE)) {
+               ese = em->bm->selected.first;
+               while (ese) {
+                       nextese = ese->next;
+                       if (ese->htype == BM_EDGE) BLI_freelinkN(&(em->bm->selected), ese);
+                       ese = nextese;
+               }
+       }
+       if (!(em->selectmode & SCE_SELECT_FACE)) {
+               ese = em->bm->selected.first;
+               while (ese) {
+                       nextese = ese->next;
+                       if (ese->htype == BM_FACE) BLI_freelinkN(&(em->bm->selected), ese);
+                       ese = nextese;
+               }
+       }
+ }
+ /* when switching select mode, makes sure selection is consistant for editing */
+ /* also for paranoia checks to make sure edge or face mode works */
+ void EDBM_selectmode_set(BMEditMesh *em)
+ {
+       BMVert *eve;
+       BMEdge *eed;
+       BMFace *efa;
+       BMIter iter;
+       
+       em->bm->selectmode = em->selectmode;
+       EDBM_strip_selections(em); /* strip BMEditSelections from em->selected that are not relevant to new mode */
+       
+       if (em->selectmode & SCE_SELECT_VERTEX) {
+               EDBM_select_flush(em);
+       }
+       else if (em->selectmode & SCE_SELECT_EDGE) {
+               /* deselect vertices, and select again based on edge select */
+               eve = BM_iter_new(&iter, em->bm, BM_VERTS_OF_MESH, NULL);
+               for ( ; eve; eve = BM_iter_step(&iter)) BM_elem_select_set(em->bm, eve, FALSE);
+               
+               eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
+               for ( ; eed; eed = BM_iter_step(&iter)) {
+                       if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+                               BM_elem_select_set(em->bm, eed, TRUE);
+                       }
+               }
+               
+               /* selects faces based on edge status */
+               EDBM_selectmode_flush(em);
+       }
+       else if (em->selectmode & SCE_SELECT_FACE) {
+               /* deselect eges, and select again based on face select */
+               eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
+               for ( ; eed; eed = BM_iter_step(&iter)) BM_elem_select_set(em->bm, eed, FALSE);
+               
+               efa = BM_iter_new(&iter, em->bm, BM_FACES_OF_MESH, NULL);
+               for ( ; efa; efa = BM_iter_step(&iter)) {
+                       if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+                               BM_elem_select_set(em->bm, efa, TRUE);
+                       }
+               }
+       }
+ }
+ void EDBM_convertsel(BMEditMesh *em, short oldmode, short selectmode)
+ {
+       BMEdge *eed;
+       BMFace *efa;
+       BMIter iter;
+       /* have to find out what the selectionmode was previously */
+       if (oldmode == SCE_SELECT_VERTEX) {
+               if (selectmode == SCE_SELECT_EDGE) {
+                       /* select all edges associated with every selected vertex */
+                       eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
+                       for ( ; eed; eed = BM_iter_step(&iter)) {
+                               if ( (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) ||
+                                     BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)))
+                               {
+                                       BM_elem_select_set(em->bm, eed, TRUE);
+                               }
+                       }
+               }               
+               else if (selectmode == SCE_SELECT_FACE) {
+                       BMIter liter;
+                       BMLoop *l;
+                       /* select all faces associated with every selected vertex */
+                       efa = BM_iter_new(&iter, em->bm, BM_FACES_OF_MESH, NULL);
+                       for ( ; efa; efa = BM_iter_step(&iter)) {
+                               l = BM_iter_new(&liter, em->bm, BM_LOOPS_OF_FACE, efa);
+                               for (; l; l = BM_iter_step(&liter)) {
+                                       if (BM_elem_flag_test(l->v, BM_ELEM_SELECT)) {
+                                               BM_elem_select_set(em->bm, efa, TRUE);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       if (oldmode == SCE_SELECT_EDGE) {
+               if (selectmode == SCE_SELECT_FACE) {
+                       BMIter liter;
+                       BMLoop *l;
+                       /* select all faces associated with every selected vertex */
+                       efa = BM_iter_new(&iter, em->bm, BM_FACES_OF_MESH, NULL);
+                       for ( ; efa; efa = BM_iter_step(&iter)) {
+                               l = BM_iter_new(&liter, em->bm, BM_LOOPS_OF_FACE, efa);
+                               for (; l; l = BM_iter_step(&liter)) {
+                                       if (BM_elem_flag_test(l->v, BM_ELEM_SELECT)) {
+                                               BM_elem_select_set(em->bm, efa, TRUE);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+ }
+ void EDBM_deselect_by_material(struct BMEditMesh *em, const short index, const short select)
+ {
+       BMIter iter;
+       BMFace *efa;
+       BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
+                       continue;
+               if (efa->mat_nr == index) {
+                       BM_elem_select_set(em->bm, efa, select);
+               }
+       }
+ }
+ void EDBM_select_swap(BMEditMesh *em) /* exported for UV */
+ {
+       BMIter iter;
+       BMVert *eve;
+       BMEdge *eed;
+       BMFace *efa;
+       
+       if (em->bm->selectmode & SCE_SELECT_VERTEX) {
+               BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN))
+                               continue;
+                       BM_elem_select_set(em->bm, eve, !BM_elem_flag_test(eve, BM_ELEM_SELECT));
+               }
+       }
+       else if (em->selectmode & SCE_SELECT_EDGE) {
+               BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
+                               continue;
+                       BM_elem_select_set(em->bm, eed, !BM_elem_flag_test(eed, BM_ELEM_SELECT));
+               }
+       }
+       else {
+               BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
+                               continue;
+                       BM_elem_select_set(em->bm, efa, !BM_elem_flag_test(efa, BM_ELEM_SELECT));
+               }
+       }
+ //    if (EM_texFaceCheck())
+ }
+ int EDBM_select_interior_faces(struct BMEditMesh *em)
+ {
+       BMesh *bm = em->bm;
+       BMIter iter;
+       BMIter eiter;
+       BMFace *efa;
+       BMEdge *eed;
+       int ok;
+       int change = FALSE;
+       BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
+                       continue;
+               ok = TRUE;
+               BM_ITER(eed, &eiter, bm, BM_EDGES_OF_FACE, efa) {
+                       if (BM_edge_face_count(eed) < 3) {
+                               ok = FALSE;
+                               break;
+                       }
+               }
+               if (ok) {
+                       BM_elem_select_set(bm, efa, TRUE);
+                       change = TRUE;
+               }
+       }
+       return change;
+ }
+ static void linked_limit_default(bContext *C, wmOperator *op)
+ {
+       if (!RNA_struct_property_is_set(op->ptr, "limit")) {
+               Object *obedit = CTX_data_edit_object(C);
+               BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+               if (em->selectmode == SCE_SELECT_FACE)
+                       RNA_boolean_set(op->ptr, "limit", TRUE);
+               else
+                       RNA_boolean_set(op->ptr, "limit", FALSE);
+       }
+ }
+ static int select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       ViewContext vc;
+       BMesh *bm;
+       BMWalker walker;
+       BMEditMesh *em;
+       BMVert *eve;
+       BMEdge *e, *eed;
+       BMFace *efa;
+       int sel = !RNA_boolean_get(op->ptr, "deselect");
+       int limit;
+       linked_limit_default(C, op);
+       limit = RNA_boolean_get(op->ptr, "limit");
+       /* unified_finednearest needs ogl */
+       view3d_operator_needs_opengl(C);
+       
+       /* setup view context for argument to callbacks */
+       em_setup_viewcontext(C, &vc);
+       em = vc.em;
+       if (em->bm->totedge == 0)
+               return OPERATOR_CANCELLED;
+       
+       bm = em->bm;
+       vc.mval[0] = event->mval[0];
+       vc.mval[1] = event->mval[1];
+       
+       /* return warning! */
+       
+       if (unified_findnearest(&vc, &eve, &eed, &efa) == 0) {
+               WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       
+               return OPERATOR_CANCELLED;
+       }
+       
+       if (em->selectmode == SCE_SELECT_FACE) {
+               BMIter iter;
+               if (efa == NULL)
+                       return OPERATOR_CANCELLED;
+               if (limit) {
+                       BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+                               if (!BM_elem_flag_test(e, BM_ELEM_SEAM)) BMO_elem_flag_enable(bm, e, BM_ELEM_SELECT);
+                               else                           BMO_elem_flag_disable(bm, e, BM_ELEM_SELECT); /* is this needed ? */
+                       }
+               }
+               /* walk */
+               BMW_init(&walker, bm, BMW_ISLAND,
+                        BMW_MASK_NOP, limit ? BM_ELEM_SELECT : BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
+                        BMW_NIL_LAY);
+               e = BMW_begin(&walker, efa);
+               for (; efa; efa = BMW_step(&walker)) {
+                       BM_elem_select_set(bm, efa, sel);
+               }
+               BMW_end(&walker);
+       }
+       else {
+               if (efa) {
+                       eed = BM_FACE_FIRST_LOOP(efa)->e;
+               }
+               else if (!eed) {
+                       if (!eve || !eve->e)
+                               return OPERATOR_CANCELLED;
+                       eed = eve->e;
+               }
+               BMW_init(&walker, bm, BMW_SHELL,
+                        BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
+                        BMW_NIL_LAY);
+               e = BMW_begin(&walker, eed->v1);
+               for ( ; e; e = BMW_step(&walker)) {
+                       BM_elem_select_set(bm, e, sel);
+               }
+               BMW_end(&walker);
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_linked_pick(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Linked";
+       ot->idname = "MESH_OT_select_linked_pick";
+       
+       /* api callbacks */
+       ot->invoke = select_linked_pick_invoke;
+       ot->poll = ED_operator_editmesh;
+       ot->description = "select/deselect all vertices linked to the edge under the mouse cursor";
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+       RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", "");
+ }
+ static int select_linked_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMesh *bm = em->bm;
+       BMIter iter;
+       BMVert *v;
+       BMEdge *e;
+       BMWalker walker;
+       int limit;
+       linked_limit_default(C, op);
+       limit = RNA_boolean_get(op->ptr, "limit");
+       if (em->selectmode == SCE_SELECT_FACE) {
+               BMFace *efa;
+               BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+                               BM_elem_flag_enable(efa, BM_ELEM_TAG);
+                       }
+                       else {
+                               BM_elem_flag_disable(efa, BM_ELEM_TAG);
+                       }
+               }
+               if (limit) {
+                       BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+                               if (!BM_elem_flag_test(e, BM_ELEM_SEAM)) BMO_elem_flag_enable(bm, e, BM_ELEM_SELECT);
+                               else                           BMO_elem_flag_disable(bm, e, BM_ELEM_SELECT); /* is this needed ? */
+                       }
+               }
+               BMW_init(&walker, bm, BMW_ISLAND,
+                        BMW_MASK_NOP, limit ? BM_ELEM_SELECT : BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
+                        BMW_NIL_LAY);
+               BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+                               e = BMW_begin(&walker, efa);
+                               for (; efa; efa = BMW_step(&walker)) {
+                                       BM_elem_select_set(bm, efa, TRUE);
+                               }
+                       }
+               }
+               BMW_end(&walker);
+       }
+       else  {
+               BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(v, BM_ELEM_SELECT) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+                               BM_elem_flag_enable(v, BM_ELEM_TAG);
+                       }
+                       else {
+                               BM_elem_flag_disable(v, BM_ELEM_TAG);
+                       }
+               }
+               BMW_init(&walker, em->bm, BMW_SHELL,
+                        BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
+                        BMW_NIL_LAY);
+               BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+                               e = BMW_begin(&walker, v);
+                               for (; e; e = BMW_step(&walker)) {
+                                       BM_elem_select_set(em->bm, e->v1, TRUE);
+                                       BM_elem_select_set(em->bm, e->v2, TRUE);
+                               }
+                       }
+               }
+               BMW_end(&walker);
+       }
+       EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_linked(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Linked All";
+       ot->idname = "MESH_OT_select_linked";
+       
+       /* api callbacks */
+       ot->exec = select_linked_exec;
+       ot->poll = ED_operator_editmesh;
+       ot->description = "Select all vertices linked to the active mesh";
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", "");
+ }
+ /* ******************** **************** */
+ static int select_more(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
+       EDBM_select_more(em);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_more(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select More";
+       ot->idname = "MESH_OT_select_more";
+       ot->description = "Select more vertices, edges or faces connected to initial selection";
+       /* api callbacks */
+       ot->exec = select_more;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int select_less(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
+       EDBM_select_less(em);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_less(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Less";
+       ot->idname = "MESH_OT_select_less";
+       ot->description = "Deselect vertices, edges or faces at the boundary of each selection region";
+       /* api callbacks */
+       ot->exec = select_less;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ /* Walk all reachable elements of the same type as h_act in breadth-first
+    order, starting from h_act. Deselects elements if the depth when they
+    are reached is not a multiple of "nth". */
+ static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h_act)
+ {
+       BMHeader *h;
+       BMesh *bm = em->bm;
+       BMWalker walker;
+       BMIter iter;
+       int walktype = 0, itertype = 0, flushtype = 0;
+       short mask_vert = 0, mask_edge = 0, mask_loop = 0, mask_face = 0;
+       /* No active element from which to start - nothing to do */
+       if (h_act == NULL) {
+               return;
+       }
+       /* Determine which type of iter, walker, and select flush to use
+        * based on type of the elements being deselected */
+       switch (h_act->htype) {
+       case BM_VERT:
+               itertype = BM_VERTS_OF_MESH;
+               walktype = BMW_CONNECTED_VERTEX;
+               flushtype = SCE_SELECT_VERTEX;
+               mask_vert = BM_ELEM_SELECT;
+               break;
+       case BM_EDGE:
+               itertype = BM_EDGES_OF_MESH;
+               walktype = BMW_SHELL;
+               flushtype = SCE_SELECT_EDGE;
+               mask_edge = BM_ELEM_SELECT;
+               break;
+       case BM_FACE:
+               itertype = BM_FACES_OF_MESH;
+               walktype = BMW_ISLAND;
+               flushtype = SCE_SELECT_FACE;
+               mask_face = BM_ELEM_SELECT;
+               break;
+       }
+       /* Walker restrictions uses BMO flags, not header flags,
+        * so transfer BM_ELEM_SELECT from HFlags onto a BMO flag layer. */
+       BMO_push(bm, NULL);
+       BM_ITER(h, &iter, bm, itertype, NULL) {
+               if (BM_elem_flag_test(h, BM_ELEM_SELECT)) {
+                       BMO_elem_flag_enable(bm, (BMElemF *)h, BM_ELEM_SELECT);
+               }
+       }
+       /* Walk over selected elements starting at active */
+       BMW_init(&walker, bm, walktype,
+                mask_vert, mask_edge, mask_loop, mask_face,
+                BMW_NIL_LAY);
+       BLI_assert(walker.order == BMW_BREADTH_FIRST);
+       for (h = BMW_begin(&walker, h_act); h != NULL; h = BMW_step(&walker)) {
+               /* Deselect elements that aren't at "nth" depth from active */
+               if ((offset + BMW_current_depth(&walker)) % nth) {
+                       BM_elem_select_set(bm, h, FALSE);
+               }
+       }
+       BMW_end(&walker);
+       BMO_pop(bm);
+       /* Flush selection up */
+       EDBM_selectmode_flush_ex(em, flushtype);
+ }
+ static void deselect_nth_active(BMEditMesh *em, BMVert **v_p, BMEdge **e_p, BMFace **f_p)
+ {
+       BMVert *v;
+       BMEdge *e;
+       BMFace *f;
+       BMIter iter;
+       BMEditSelection *ese;
+       *v_p = NULL;
+       *e_p = NULL;
+       *f_p = NULL;
+       EDBM_selectmode_flush(em);
+       ese = (BMEditSelection *)em->bm->selected.last;
+       if (ese) {
+               switch(ese->htype) {
+               case BM_VERT:
+                       *v_p = (BMVert *)ese->data;
+                       return;
+               case BM_EDGE:
+                       *e_p = (BMEdge *)ese->data;
+                       return;
+               case BM_FACE:
+                       *f_p = (BMFace *)ese->data;
+                       return;
+               }
+       }
+       if (em->selectmode & SCE_SELECT_VERTEX) {
+               BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+                               *v_p = v;
+                               return;
+                       }
+               }
+       }
+       else if (em->selectmode & SCE_SELECT_EDGE) {
+               BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+                               *e_p = e;
+                               return;
+                       }
+               }
+       }
+       else if (em->selectmode & SCE_SELECT_FACE) {
+               f = BM_active_face_get(em->bm, TRUE);
+               if (f) {
+                       *f_p = f;
+                       return;
+               }
+       }
+ }
+ static int EM_deselect_nth(BMEditMesh *em, int nth, int offset)
+ {
+       BMVert *v;
+       BMEdge *e;
+       BMFace *f;
+       deselect_nth_active(em, &v, &e, &f);
+       if (v) {
+               walker_deselect_nth(em, nth, offset, &v->head);
+               return 1;
+       }
+       else if (e) {
+               walker_deselect_nth(em, nth, offset, &e->head);
+               return 1;
+       }
+       else if (f) {
+               walker_deselect_nth(em, nth, offset, &f->head);
+               return 1;
+       }
+       return 0;
+ }
+ static int mesh_select_nth_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       int nth = RNA_int_get(op->ptr, "nth");
+       int offset = RNA_int_get(op->ptr, "offset");
+       offset = MIN2(nth, offset);
+       if (EM_deselect_nth(em, nth, offset) == 0) {
+               BKE_report(op->reports, RPT_ERROR, "Mesh has no active vert/edge/face");
+               return OPERATOR_CANCELLED;
+       }
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_nth(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Nth";
+       ot->description = "";
+       ot->idname = "MESH_OT_select_nth";
+       /* api callbacks */
+       ot->exec = mesh_select_nth_exec;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_int(ot->srna, "nth", 2, 2, 100, "Nth Selection", "", 1, INT_MAX);
+       RNA_def_int(ot->srna, "offset", 0, 0, 100, "Offset", "", 0, INT_MAX);
+ }
+ void em_setup_viewcontext(bContext *C, ViewContext *vc)
+ {
+       view3d_set_viewcontext(C, vc);
+       
+       if (vc->obedit) {
+               Mesh *me = vc->obedit->data;
+               vc->em = me->edit_btmesh;
+       }
+ }
+ /* poll call for mesh operators requiring a view3d context */
+ int EM_view3d_poll(bContext *C)
+ {
+       if (ED_operator_editmesh(C) && ED_operator_view3d_active(C))
+               return 1;
+       return 0;
+ }
+ static int select_sharp_edges_exec(bContext *C, wmOperator *op)
+ {
+       /* Find edges that have exactly two neighboring faces,
+       * check the angle between those faces, and if angle is
+       * small enough, select the edge
+       */
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMIter iter;
+       BMEdge *e;
+       BMLoop *l1, *l2;
+       float sharp = RNA_float_get(op->ptr, "sharpness"), angle;
+       sharp = DEG2RADF(sharp);
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) || !e->l)
+                       continue;
+               l1 = e->l;
+               l2 = l1->radial_next;
+               if (l1 == l2)
+                       continue;
+               /* edge has exactly two neighboring faces, check angle */
+               angle = angle_normalized_v3v3(l1->f->no, l2->f->no);
+               if (fabsf(angle) > sharp) {
+                       BM_elem_select_set(em->bm, e, TRUE);
+               }
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_edges_select_sharp(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Sharp Edges";
+       ot->description = "Marked selected edges as sharp";
+       ot->idname = "MESH_OT_edges_select_sharp";
+       
+       /* api callbacks */
+       ot->exec = select_sharp_edges_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_float(ot->srna, "sharpness", 1.0f, 0.01f, FLT_MAX, "sharpness", "", 1.0f, 180.0f);
+ }
+ static int select_linked_flat_faces_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMIter iter, liter, liter2;
+       BMFace *f, **stack = NULL;
+       BLI_array_declare(stack);
+       BMLoop *l, *l2;
+       float sharp = RNA_float_get(op->ptr, "sharpness");
+       int i;
+       sharp = (sharp * M_PI) / 180.0;
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               BM_elem_flag_disable(f, BM_ELEM_TAG);
+       }
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) || !BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_TAG))
+                       continue;
+               BLI_array_empty(stack);
+               i = 1;
+               BLI_array_growone(stack);
+               stack[i - 1] = f;
+               while (i) {
+                       f = stack[i - 1];
+                       i--;
+                       BM_elem_select_set(em->bm, f, TRUE);
+                       BM_elem_flag_enable(f, BM_ELEM_TAG);
+                       BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
+                               BM_ITER(l2, &liter2, em->bm, BM_LOOPS_OF_LOOP, l) {
+                                       float angle;
+                                       if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l2->f, BM_ELEM_HIDDEN))
+                                               continue;
+                                       /* edge has exactly two neighboring faces, check angle */
+                                       angle = angle_normalized_v3v3(f->no, l2->f->no);
+                                       /* invalidate: edge too sharp */
+                                       if (angle < sharp) {
+                                               BLI_array_growone(stack);
+                                               stack[i] = l2->f;
+                                               i++;
+                                       }
+                               }
+                       }
+               }
+       }
+       BLI_array_free(stack);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_faces_select_linked_flat(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Linked Flat Faces";
+       ot->description = "Select linked faces by angle";
+       ot->idname = "MESH_OT_faces_select_linked_flat";
+       
+       /* api callbacks */
+       ot->exec = select_linked_flat_faces_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_float(ot->srna, "sharpness", 1.0f, 0.01f, FLT_MAX, "sharpness", "", 1.0f, 180.0f);
+ }
+ static int select_non_manifold_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMVert *v;
+       BMEdge *e;
+       BMIter iter;
+       /* Selects isolated verts, and edges that do not have 2 neighboring
+        * faces
+        */
+       
+       if (em->selectmode == SCE_SELECT_FACE) {
+               BKE_report(op->reports, RPT_ERROR, "Doesn't work in face selection mode");
+               return OPERATOR_CANCELLED;
+       }
+       
+       BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+               if (!BM_elem_flag_test(em->bm, BM_ELEM_HIDDEN) && !BM_vert_is_manifold(em->bm, v)) {
+                       BM_elem_select_set(em->bm, v, TRUE);
+               }
+       }
+       
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               if (!BM_elem_flag_test(em->bm, BM_ELEM_HIDDEN) && BM_edge_face_count(e) != 2) {
+                       BM_elem_select_set(em->bm, e, TRUE);
+               }
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_non_manifold(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Non Manifold";
+       ot->description = "Select all non-manifold vertices or edges";
+       ot->idname = "MESH_OT_select_non_manifold";
+       
+       /* api callbacks */
+       ot->exec = select_non_manifold_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int mesh_select_random_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMVert *eve;
+       BMEdge *eed;
+       BMFace *efa;
+       BMIter iter;
+       float randfac =  RNA_float_get(op->ptr, "percent")/100.0f;
+       BLI_srand(BLI_rand()); /* random seed */
+       
+       if (!RNA_boolean_get(op->ptr, "extend"))
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       if (em->selectmode & SCE_SELECT_VERTEX) {
+               BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN) && BLI_frand() < randfac) {
+                               BM_elem_select_set(em->bm, eve, TRUE);
+                       }
+               }
+               EDBM_selectmode_flush(em);
+       }
+       else if (em->selectmode & SCE_SELECT_EDGE) {
+               BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && BLI_frand() < randfac) {
+                               BM_elem_select_set(em->bm, eed, TRUE);
+                       }
+               }
+               EDBM_selectmode_flush(em);
+       }
+       else {
+               BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && BLI_frand() < randfac) {
+                               BM_elem_select_set(em->bm, efa, TRUE);
+                       }
+               }
+               EDBM_selectmode_flush(em);
+       }
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_random(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Random";
+       ot->description = "Randomly select vertices";
+       ot->idname = "MESH_OT_select_random";
+       /* api callbacks */
+       ot->exec = mesh_select_random_exec;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f,
+                                "Percent", "Percentage of elements to select randomly", 0.f, 100.0f);
+       RNA_def_boolean(ot->srna, "extend", 0,
+                       "Extend Selection", "Extend selection instead of deselecting everything first");
+ }
+ static int select_next_loop(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
+       BMFace *f;
+       BMVert *v;
+       BMIter iter;
+       
+       BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+               BM_elem_flag_disable(v, BM_ELEM_TAG);
+       }
+       
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               BMLoop *l;
+               BMIter liter;
+               
+               BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
+                       if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) && !BM_elem_flag_test(l->v, BM_ELEM_HIDDEN)) {
+                               BM_elem_flag_enable(l->next->v, BM_ELEM_TAG);
+                               BM_elem_select_set(em->bm, l->v, FALSE);
+                       }
+               }
+       }
+       BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+               if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+                       BM_elem_select_set(em->bm, v, TRUE);
+               }
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_next_loop(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Next Loop";
+       ot->idname = "MESH_OT_select_next_loop";
+       ot->description = "";
+       /* api callbacks */
+       ot->exec = select_next_loop;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int region_to_loop(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMFace *f;
+       BMEdge *e;
+       BMIter iter;
+       ViewContext vc;
+       
+       em_setup_viewcontext(C, &vc);
+       
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               BM_elem_flag_disable(e, BM_ELEM_TAG);
+       }
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               BMLoop *l1, *l2;
+               BMIter liter1, liter2;
+               
+               BM_ITER(l1, &liter1, em->bm, BM_LOOPS_OF_FACE, f) {
+                       int tot = 0, totsel = 0;
+                       
+                       BM_ITER(l2, &liter2, em->bm, BM_LOOPS_OF_EDGE, l1->e) {
+                               tot++;
+                               totsel += BM_elem_flag_test(l2->f, BM_ELEM_SELECT) != 0;
+                       }
+                       
+                       if ((tot != totsel && totsel > 0) || (totsel == 1 && tot == 1))
+                               BM_elem_flag_enable(l1->e, BM_ELEM_TAG);
+               }
+       }
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(e, BM_ELEM_TAG) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN))
+                       BM_edge_select_set(em->bm, e, TRUE);
+       }
+       /* If in face-only select mode, switch to edge select mode so that
+          an edge-only selection is not inconsistent state */
+       if (em->selectmode == SCE_SELECT_FACE) {
+               em->selectmode = SCE_SELECT_EDGE;
+               EDBM_selectmode_set(em);
+               EDBM_selectmode_to_scene(C);
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_region_to_loop(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Boundary Loop";
+       ot->idname = "MESH_OT_region_to_loop";
+       /* api callbacks */
+       ot->exec = region_to_loop;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int loop_find_region(BMEditMesh *em, BMLoop *l, int flag, 
+       SmallHash *fhash, BMFace ***region_out)
+ {
+       BLI_array_declare(region);
+       BLI_array_declare(stack);
+       BMFace **region = NULL, *f;
+       BMFace **stack = NULL;
+       
+       BLI_array_append(stack, l->f);
+       BLI_smallhash_insert(fhash, (uintptr_t)l->f, NULL);
+       
+       while (BLI_array_count(stack) > 0) {
+               BMIter liter1, liter2;
+               BMLoop *l1, *l2;
+               
+               f = BLI_array_pop(stack);
+               BLI_array_append(region, f);
+               
+               BM_ITER(l1, &liter1, em->bm, BM_LOOPS_OF_FACE, f) {
+                       if (BM_elem_flag_test(l1->e, flag))
+                               continue;
+                       
+                       BM_ITER(l2, &liter2, em->bm, BM_LOOPS_OF_EDGE, l1->e) {
+                               if (BLI_smallhash_haskey(fhash, (uintptr_t)l2->f))
+                                       continue;
+                               
+                               BLI_array_append(stack, l2->f);
+                               BLI_smallhash_insert(fhash, (uintptr_t)l2->f, NULL);
+                       }
+               }
+       }
+       
+       BLI_array_free(stack);
+       
+       *region_out = region;
+       return BLI_array_count(region);
+ }
+ static int verg_radial(const void *va, const void *vb)
+ {
+       BMEdge *e1 = *((void **)va);
+       BMEdge *e2 = *((void **)vb);
+       int a, b;
+       
+       a = BM_edge_face_count(e1);
+       b = BM_edge_face_count(e2);
+       
+       if (a > b)  return -1;
+       if (a == b) return  0;
+       if (a < b)  return  1;
+       
+       return -1;
+ }
+ static int loop_find_regions(BMEditMesh *em, int selbigger)
+ {
+       SmallHash visithash;
+       BMIter iter;
+       BMEdge *e, **edges = NULL;
+       BLI_array_declare(edges);
+       BMFace *f;
+       int count = 0, i;
+       
+       BLI_smallhash_init(&visithash);
+       
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               BM_elem_flag_disable(f, BM_ELEM_TAG);
+       }
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+                       BLI_array_append(edges, e);
+                       BM_elem_flag_enable(e, BM_ELEM_TAG);
+               }
+               else {
+                       BM_elem_flag_disable(e, BM_ELEM_TAG);
+               }
+       }
+       
+       /* sort edges by radial cycle length */
+       qsort(edges,  BLI_array_count(edges), sizeof(void *), verg_radial);
+       
+       for (i = 0; i < BLI_array_count(edges); i++) {
+               BMIter liter;
+               BMLoop *l;
+               BMFace **region = NULL, **r;
+               int c, tot = 0;
+               
+               e = edges[i];
+               
+               if (!BM_elem_flag_test(e, BM_ELEM_TAG))
+                       continue;
+               
+               BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_EDGE, e) {
+                       if (BLI_smallhash_haskey(&visithash, (uintptr_t)l->f))
+                               continue;
+                                               
+                       c = loop_find_region(em, l, BM_ELEM_SELECT, &visithash, &r);
+                       if (!region || (selbigger ? c >= tot : c < tot)) {
+                               /* this region is the best seen so far */
+                               tot = c;
+                               if (region) {
+                                       /* free the previous best */
+                                       MEM_freeN(region);
+                               }
+                               /* track the current region as the new best */
+                               region = r;
+                       }
+                       else {
+                               /* this region is not as good as best so far, just free it */
+                               MEM_freeN(r);
+                       }
+               }
+               
+               if (region) {
+                       int j;
+                       
+                       for (j = 0; j < tot; j++) {
+                               BM_elem_flag_enable(region[j], BM_ELEM_TAG);
+                               BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, region[j]) {
+                                       BM_elem_flag_disable(l->e, BM_ELEM_TAG);
+                               }
+                       }
+                       
+                       count += tot;
+                       
+                       MEM_freeN(region);
+               }
+       }
+       
+       BLI_array_free(edges);
+       BLI_smallhash_release(&visithash);
+       
+       return count;
+ }
+ static int loop_to_region(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMIter iter;
+       BMFace *f;
+       int selbigger = RNA_boolean_get(op->ptr, "select_bigger");
+       int a, b;
+       /* find the set of regions with smallest number of total faces */
+       a = loop_find_regions(em, selbigger);
+       b = loop_find_regions(em, !selbigger);
+       
+       if ((a <= b) ^ selbigger) {
+               loop_find_regions(em, selbigger);
+       }
+       
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(f, BM_ELEM_TAG) && !BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+                       BM_face_select_set(em->bm, f, TRUE);
+               }
+       }
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_loop_to_region(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Loop Inner-Region";
+       ot->idname = "MESH_OT_loop_to_region";
+       /* api callbacks */
+       ot->exec = loop_to_region;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_boolean(ot->srna, "select_bigger", 0, "Select Bigger", "Select bigger regions instead of smaller ones");
+ }
index 0000000000000000000000000000000000000000,f97a8e1b9a9b012f623852e39bc0b99c0182ff0d..b403aaca5dce5f240db96e02fb9ea13dcdaf06c8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,4582 +1,4690 @@@
+ /*
+  * ***** 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 Blender Foundation.
+  * All rights reserved.
+  *
+  * The Original Code is: all of this file.
+  *
+  * Contributor(s): Joseph Eagar
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ #include "MEM_guardedalloc.h"
+ #include "DNA_material_types.h"
+ #include "DNA_mesh_types.h"
+ #include "DNA_modifier_types.h"
+ #include "DNA_object_types.h"
+ #include "DNA_scene_types.h"
+ #include "RNA_define.h"
+ #include "RNA_access.h"
+ #include "BLI_blenlib.h"
+ #include "BLI_math.h"
+ #include "BLI_rand.h"
+ #include "BKE_material.h"
+ #include "BKE_context.h"
+ #include "BKE_cdderivedmesh.h"
+ #include "BKE_depsgraph.h"
+ #include "BKE_object.h"
+ #include "BKE_report.h"
+ #include "BKE_texture.h"
+ #include "BKE_main.h"
+ #include "BKE_tessmesh.h"
+ #include "WM_api.h"
+ #include "WM_types.h"
+ #include "ED_mesh.h"
+ #include "ED_view3d.h"
+ #include "ED_screen.h"
+ #include "ED_transform.h"
+ #include "ED_object.h"
+ #include "RE_render_ext.h"
+ #include "mesh_intern.h"
+ static void add_normal_aligned(float nor[3], const float add[3])
+ {
+       if (dot_v3v3(nor, add) < -0.9999f)
+               sub_v3_v3(nor, add);
+       else
+               sub_v3_v3(nor, add);
+ }
+ static int subdivide_exec(bContext *C, wmOperator *op)
+ {
+       ToolSettings *ts = CTX_data_tool_settings(C);
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       int cuts = RNA_int_get(op->ptr,"number_cuts");
+       float smooth = 0.292f * RNA_float_get(op->ptr, "smoothness");
+       float fractal = RNA_float_get(op->ptr, "fractal")/2.5;
+       int flag = 0;
+       if (smooth != 0.0f)
+               flag |= B_SMOOTH;
+       if (fractal != 0.0f)
+               flag |= B_FRACTAL;
+       
+       if (RNA_boolean_get(op->ptr, "quadtri") && 
+           RNA_enum_get(op->ptr, "quadcorner") == SUBD_STRAIGHT_CUT)
+       {
+               RNA_enum_set(op->ptr, "quadcorner", SUBD_INNERVERT);
+       }
+       
+       BM_mesh_esubdivideflag(obedit, em->bm, BM_ELEM_SELECT,
+                         smooth, fractal,
+                         ts->editbutflag|flag, 
+                         cuts, 0, RNA_enum_get(op->ptr, "quadcorner"), 
+                         RNA_boolean_get(op->ptr, "quadtri"),
+                         1, RNA_int_get(op->ptr, "seed"));
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ /* Note, these values must match delete_mesh() event values */
+ static EnumPropertyItem prop_mesh_cornervert_types[] = {
+       {SUBD_INNERVERT,     "INNERVERT", 0,      "Inner Vert", ""},
+       {SUBD_PATH,          "PATH", 0,           "Path", ""},
+       {SUBD_STRAIGHT_CUT,  "STRAIGHT_CUT", 0,   "Straight Cut", ""},
+       {SUBD_FAN,           "FAN", 0,            "Fan", ""},
+       {0, NULL, 0, NULL, NULL}
+ };
+ void MESH_OT_subdivide(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Subdivide";
+       ot->description = "Subdivide selected edges";
+       ot->idname = "MESH_OT_subdivide";
+       /* api callbacks */
+       ot->exec = subdivide_exec;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* properties */
+       RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
+       /* BMESH_TODO, this currently does nothing, just add to stop UI from erroring out! */
+       RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, FLT_MAX, "Smoothness", "Smoothness factor (BMESH TODO)", 0.0f, 1.0f);
+       RNA_def_boolean(ot->srna, "quadtri", 0, "Quad/Tri Mode", "Tries to prevent ngons");
+       RNA_def_enum(ot->srna, "quadcorner", prop_mesh_cornervert_types, SUBD_STRAIGHT_CUT,
+                    "Quad Corner Type", "How to subdivide quad corners (anything other then Straight Cut will prevent ngons)");
+       RNA_def_float(ot->srna, "fractal", 0.0f, 0.0f, FLT_MAX, "Fractal", "Fractal randomness factor", 0.0f, 1000.0f);
+       RNA_def_int(ot->srna, "seed", 0, 0, 10000, "Random Seed", "Seed for the random number generator", 0, 50);
+ }
+ void EMBM_project_snap_verts(bContext *C, ARegion *ar, Object *obedit, BMEditMesh *em)
+ {
+       BMIter iter;
+       BMVert *eve;
+       BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
+               if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+                       float mval[2], vec[3], no_dummy[3];
+                       int dist_dummy;
+                       mul_v3_m4v3(vec, obedit->obmat, eve->co);
+                       project_float_noclip(ar, vec, mval);
+                       if (snapObjectsContext(C, mval, &dist_dummy, vec, no_dummy, SNAP_NOT_OBEDIT)) {
+                               mul_v3_m4v3(eve->co, obedit->imat, vec);
+                       }
+               }
+       }
+ }
+ /* individual face extrude */
+ /* will use vertex normals for extrusion directions, so *nor is unaffected */
+ static short EDBM_Extrude_face_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
+ {
+       BMOIter siter;
+       BMIter liter;
+       BMFace *f;
+       BMLoop *l;
+       BMOperator bmop;
+       EDBM_InitOpf(em, &bmop, op, "extrude_face_indiv faces=%hf", hflag);
+       /* deselect original verts */
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       BMO_op_exec(em->bm, &bmop);
+       
+       BMO_ITER(f, &siter, em->bm, &bmop, "faceout", BM_FACE) {
+               BM_elem_select_set(em->bm, f, TRUE);
+               /* set face vertex normals to face normal */
+               BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
+                       copy_v3_v3(l->v->no, f->no);
+               }
+       }
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return 0;
+       }
+       return 's'; // s is shrink/fatten
+ }
+ /* extrudes individual edges */
+ static short EDBM_Extrude_edges_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
+ {
+       BMOperator bmop;
+       EDBM_InitOpf(em, &bmop, op, "extrude_edge_only edges=%he", hflag);
+       /* deselect original verts */
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       BMO_op_exec(em->bm, &bmop);
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "geomout", BM_ELEM_SELECT, BM_VERT|BM_EDGE);
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return 0;
+       }
+       return 'n'; // n is normal grab
+ }
+ /* extrudes individual vertices */
+ static short EDBM_Extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
+ {
+       BMOperator bmop;
+       EDBM_InitOpf(em, &bmop, op, "extrude_vert_indiv verts=%hv", hflag);
+       /* deselect original verts */
+       BMO_slot_buffer_hflag_disable(em->bm, &bmop, "verts", BM_ELEM_SELECT, BM_VERT);
+       BMO_op_exec(em->bm, &bmop);
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "vertout", BM_ELEM_SELECT, BM_VERT);
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return 0;
+       }
+       return 'g'; // g is grab
+ }
+ static short EDBM_Extrude_edge(Object *obedit, BMEditMesh *em, const char hflag, float nor[3])
+ {
+       BMesh *bm = em->bm;
+       BMIter iter;
+       BMOIter siter;
+       BMOperator extop;
+       BMEdge *edge;
+       BMFace *f;
+       ModifierData *md;
+       BMHeader *el;
+       
+       BMO_op_init(bm, &extop, "extrudefaceregion");
+       BMO_slot_from_hflag(bm, &extop, "edgefacein",
+                              hflag, BM_VERT|BM_EDGE|BM_FACE);
+       /* If a mirror modifier with clipping is on, we need to adjust some 
+        * of the cases above to handle edges on the line of symmetry.
+        */
+       md = obedit->modifiers.first;
+       for (; md; md = md->next) {
+               if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
+                       MirrorModifierData *mmd = (MirrorModifierData *) md;
+               
+                       if (mmd->flag & MOD_MIR_CLIPPING) {
+                               float mtx[4][4];
+                               if (mmd->mirror_ob) {
+                                       float imtx[4][4];
+                                       invert_m4_m4(imtx, mmd->mirror_ob->obmat);
+                                       mult_m4_m4m4(mtx, imtx, obedit->obmat);
+                               }
+                               for (edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL);
+                                    edge;
+                                    edge = BM_iter_step(&iter))
+                               {
+                                       if (BM_elem_flag_test(edge, hflag)) {
+                                               float co1[3], co2[3];
+                                               copy_v3_v3(co1, edge->v1->co);
+                                               copy_v3_v3(co2, edge->v2->co);
+                                               if (mmd->mirror_ob) {
+                                                       mul_v3_m4v3(co1, mtx, co1);
+                                                       mul_v3_m4v3(co2, mtx, co2);
+                                               }
+                                               if (mmd->flag & MOD_MIR_AXIS_X) {
+                                                       if ( (fabs(co1[0]) < mmd->tolerance) &&
+                                                                (fabs(co2[0]) < mmd->tolerance) )
+                                                       {
+                                                               BMO_slot_map_ptr_insert(bm, &extop, "exclude", edge, NULL);
+                                                       }
+                                               }
+                                               if (mmd->flag & MOD_MIR_AXIS_Y) {
+                                                       if ( (fabs(co1[1]) < mmd->tolerance) &&
+                                                                (fabs(co2[1]) < mmd->tolerance) )
+                                                       {
+                                                               BMO_slot_map_ptr_insert(bm, &extop, "exclude", edge, NULL);
+                                                       }
+                                               }
+                                               if (mmd->flag & MOD_MIR_AXIS_Z) {
+                                                       if ( (fabs(co1[2]) < mmd->tolerance) &&
+                                                                (fabs(co2[2]) < mmd->tolerance) )
+                                                       {
+                                                               BMO_slot_map_ptr_insert(bm, &extop, "exclude", edge, NULL);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       BMO_op_exec(bm, &extop);
+       nor[0] = nor[1] = nor[2] = 0.0f;
+       
+       BMO_ITER(el, &siter, bm, &extop, "geomout", BM_ALL) {
+               BM_elem_select_set(bm, el, TRUE);
+               if (el->htype == BM_FACE) {
+                       f = (BMFace *)el;
+                       add_normal_aligned(nor, f->no);
+               };
+       }
+       normalize_v3(nor);
+       BMO_op_finish(bm, &extop);
+       if (nor[0] == 0.0f && nor[1] == 0.0f && nor[2] == 0.0f) return 'g'; // grab
+       return 'n'; // normal constraint 
+ }
+ static short EDBM_Extrude_vert(Object *obedit, BMEditMesh *em, const char hflag, float *nor)
+ {
+       BMIter iter;
+       BMEdge *eed;
+               
+       /* ensure vert flags are consistent for edge selections */
+       eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
+       for ( ; eed; eed = BM_iter_step(&iter)) {
+               if (BM_elem_flag_test(eed, hflag)) {
+                       if (hflag & BM_ELEM_SELECT) {
+                               BM_elem_select_set(em->bm, eed->v1, TRUE);
+                               BM_elem_select_set(em->bm, eed->v2, TRUE);
+                       }
+                       BM_elem_flag_enable(eed->v1, hflag & ~BM_ELEM_SELECT);
+                       BM_elem_flag_enable(eed->v2, hflag & ~BM_ELEM_SELECT);
+               }
+               else {
+                       if (BM_elem_flag_test(eed->v1, hflag) && BM_elem_flag_test(eed->v2, hflag)) {
+                               if (hflag & BM_ELEM_SELECT) {
+                                       BM_elem_select_set(em->bm, eed, TRUE);
+                               }
+                               BM_elem_flag_enable(eed, hflag & ~BM_ELEM_SELECT);
+                       }
+               }
+       }
+       return EDBM_Extrude_edge(obedit, em, hflag, nor);
+ }
+ static int extrude_repeat_mesh(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       RegionView3D *rv3d = CTX_wm_region_view3d(C);
+               
+       int steps = RNA_int_get(op->ptr,"steps");
+       
+       float offs = RNA_float_get(op->ptr,"offset");
+       float dvec[3], tmat[3][3], bmat[3][3], nor[3] = {0.0, 0.0, 0.0};
+       short a;
+       /* dvec */
+       normalize_v3_v3(dvec, rv3d->persinv[2]);
+       mul_v3_fl(dvec, offs);
+       /* base correction */
+       copy_m3_m4(bmat, obedit->obmat);
+       invert_m3_m3(tmat, bmat);
+       mul_m3_v3(tmat, dvec);
+       for (a = 0; a < steps; a++) {
+               EDBM_Extrude_edge(obedit, em, BM_ELEM_SELECT, nor);
+               //BMO_op_callf(em->bm, "extrudefaceregion edgefacein=%hef", BM_ELEM_SELECT);
+               BMO_op_callf(em->bm, "translate vec=%v verts=%hv", (float *)dvec, BM_ELEM_SELECT);
+               //extrudeflag(obedit, em, SELECT, nor);
+               //translateflag(em, SELECT, dvec);
+       }
+       
+       EDBM_RecalcNormals(em);
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_extrude_repeat(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Extrude Repeat Mesh";
+       ot->description = "Extrude selected vertices, edges or faces repeatedly";
+       ot->idname = "MESH_OT_extrude_repeat";
+       
+       /* api callbacks */
+       ot->exec = extrude_repeat_mesh;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, 100.0f, "Offset", "", 0.0f, FLT_MAX);
+       RNA_def_int(ot->srna, "steps", 10, 0, 180, "Steps", "", 0, INT_MAX);
+ }
+ /* generic extern called extruder */
+ static int EDBM_Extrude_Mesh(Scene *scene, Object *obedit, BMEditMesh *em, wmOperator *op, float *norin)
+ {
+       short nr, transmode = 0;
+       float stacknor[3] = {0.0f, 0.0f, 0.0f};
+       float *nor = norin ? norin : stacknor;
+       nor[0] = nor[1] = nor[2] = 0.0f;
+       if (em->selectmode & SCE_SELECT_VERTEX) {
+               if (em->bm->totvertsel == 0) nr = 0;
+               else if (em->bm->totvertsel == 1) nr = 4;
+               else if (em->bm->totedgesel == 0) nr = 4;
+               else if (em->bm->totfacesel == 0)
+                       nr = 3; // pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
+               else if (em->bm->totfacesel == 1)
+                       nr = 1; // pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
+               else 
+                       nr = 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
+       }
+       else if (em->selectmode & SCE_SELECT_EDGE) {
+               if (em->bm->totedgesel == 0) nr = 0;
+               
+               nr = 1;
+               /* else if (em->totedgesel == 1) nr = 3;
+               else if (em->totfacesel == 0) nr = 3;
+               else if (em->totfacesel == 1)
+                       nr = 1; // pupmenu("Extrude %t|Region %x1|Only Edges%x3");
+               else
+                       nr = 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
+               */
+       }
+       else {
+               if (em->bm->totfacesel == 0) nr = 0;
+               else if (em->bm->totfacesel == 1) nr = 1;
+               else
+                       nr = 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
+       }
+       if (nr < 1) return 'g';
+       if (nr == 1 && em->selectmode & SCE_SELECT_VERTEX)
+               transmode = EDBM_Extrude_vert(obedit, em, BM_ELEM_SELECT, nor);
+       else if (nr == 1) transmode = EDBM_Extrude_edge(obedit, em, BM_ELEM_SELECT, nor);
+       else if (nr == 4) transmode = EDBM_Extrude_verts_indiv(em, op, BM_ELEM_SELECT, nor);
+       else if (nr == 3) transmode = EDBM_Extrude_edges_indiv(em, op, BM_ELEM_SELECT, nor);
+       else transmode = EDBM_Extrude_face_indiv(em, op, BM_ELEM_SELECT, nor);
+       
+       if (transmode == 0) {
+               BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
+       }
+       else {
+               
+                       /* 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(scene, obedit, OB_RECALC_DATA);
+               object_handle_update(scene, 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(obedit->obmat, nor);
+                               sub_v3_v3v3(nor, nor, obedit->obmat[3]);
+ //                            BIF_setSingleAxisConstraint(nor, "along normal");
+                       }
+ //                    Transform();
+               }
+       }
+       
+       return transmode;
+ }
+ /* extrude without transform */
+ static int mesh_extrude_region_exec(bContext *C, wmOperator *op)
+ {
+       Scene *scene = CTX_data_scene(C);
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       
+       EDBM_Extrude_Mesh(scene, obedit, em, op, NULL);
+       /* This normally happens when pushing undo but modal operators
+        * like this one don't push undo data until after modal mode is
+        * done.*/
+       EDBM_RecalcNormals(em);
+       BMEdit_RecalcTesselation(em);
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_extrude_region(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Extrude Region";
+       ot->idname = "MESH_OT_extrude_region";
+       
+       /* api callbacks */
+       //ot->invoke = mesh_extrude_region_invoke;
+       ot->exec = mesh_extrude_region_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
+ }
+ static int mesh_extrude_verts_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       float nor[3];
+       EDBM_Extrude_verts_indiv(em, op, BM_ELEM_SELECT, nor);
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Extrude Only Vertices";
+       ot->idname = "MESH_OT_extrude_verts_indiv";
+       
+       /* api callbacks */
+       ot->exec = mesh_extrude_verts_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* to give to transform */
+       RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
+ }
+ static int mesh_extrude_edges_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       float nor[3];
+       EDBM_Extrude_edges_indiv(em, op, BM_ELEM_SELECT, nor);
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Extrude Only Edges";
+       ot->idname = "MESH_OT_extrude_edges_indiv";
+       
+       /* api callbacks */
+       ot->exec = mesh_extrude_edges_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* to give to transform */
+       RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
+ }
+ static int mesh_extrude_faces_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       float nor[3];
+       EDBM_Extrude_face_indiv(em, op, BM_ELEM_SELECT, nor);
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Extrude Individual Faces";
+       ot->idname = "MESH_OT_extrude_faces_indiv";
+       
+       /* api callbacks */
+       ot->exec = mesh_extrude_faces_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
+ }
+ /* ******************** (de)select all operator **************** */
+ void EDBM_toggle_select_all(BMEditMesh *em) /* exported for UV */
+ {
+       if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel)
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       else 
+               EDBM_flag_enable_all(em, BM_ELEM_SELECT);
+ }
+ static int mesh_select_all_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       int action = RNA_enum_get(op->ptr, "action");
+       
+       switch (action) {
+       case SEL_TOGGLE:
+               EDBM_toggle_select_all(em);
+               break;
+       case SEL_SELECT:
+               EDBM_flag_enable_all(em, BM_ELEM_SELECT);
+               break;
+       case SEL_DESELECT:
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+               break;
+       case SEL_INVERT:
+               EDBM_select_swap(em);
+               break;
+       }
+       
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_select_all(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select/Deselect All";
+       ot->idname = "MESH_OT_select_all";
+       ot->description = "(de)select all vertices, edges or faces";
+       
+       /* api callbacks */
+       ot->exec = mesh_select_all_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       WM_operator_properties_select_all(ot);
+ }
+ static int mesh_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       if (EDBM_select_interior_faces(em)) {
+               WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void MESH_OT_select_interior_faces(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Select Interior Faces";
+       ot->idname = "MESH_OT_select_interior_faces";
+       ot->description = "Select faces where all edges have more than 2 face users";
+       /* api callbacks */
+       ot->exec = mesh_faces_select_interior_exec;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ /* *************** add-click-mesh (extrude) operator ************** */
+ /* in trunk see: 'editmesh_add.c' */
+ static int dupli_extrude_cursor(bContext *C, wmOperator *op, wmEvent *event)
+ {
+       ViewContext vc;
+       BMVert *v1;
+       BMIter iter;
+       float min[3], max[3];
+       int done = 0;
+       short use_proj;
+       
+       em_setup_viewcontext(C, &vc);
+       
+       use_proj = (vc.scene->toolsettings->snap_flag & SCE_SNAP) &&    (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE);
+       INIT_MINMAX(min, max);
+       
+       BM_ITER(v1, &iter, vc.em->bm, BM_VERTS_OF_MESH, NULL) {
+               if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
+                       DO_MINMAX(v1->co, min, max);
+                       done = 1;
+               }
+       }
+       /* call extrude? */
+       if (done) {
+               const short rot_src = RNA_boolean_get(op->ptr, "rotate_source");
+               BMEdge *eed;
+               float vec[3], cent[3], mat[3][3];
+               float nor[3] = {0.0, 0.0, 0.0};
+               /* 2D normal calc */
+               float mval_f[2];
+               mval_f[0] = (float)event->mval[0];
+               mval_f[1] = (float)event->mval[1];
+               /* check for edges that are half selected, use for rotation */
+               done = 0;
+               BM_ITER(eed, &iter, vc.em->bm, BM_EDGES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+                               float co1[3], co2[3];
+                               mul_v3_m4v3(co1, vc.obedit->obmat, eed->v1->co);
+                               mul_v3_m4v3(co2, vc.obedit->obmat, eed->v2->co);
+                               project_float_noclip(vc.ar, co1, co1);
+                               project_float_noclip(vc.ar, co2, co2);
+                               /* 2D rotate by 90d while adding.
+                                *  (x, y) = (y, -x)
+                                *
+                                * accumulate the screenspace normal in 2D,
+                                * with screenspace edge length weighting the result. */
+                               if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
+                                       nor[0] +=  (co1[1] - co2[1]);
+                                       nor[1] += -(co1[0] - co2[0]);
+                               }
+                               else {
+                                       nor[0] +=  (co2[1] - co1[1]);
+                                       nor[1] += -(co2[0] - co1[0]);
+                               }
+                       }
+                       done = 1;
+               }
+               if (done) {
+                       float view_vec[3], cross[3];
+                       /* convert the 2D nomal into 3D */
+                       mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
+                       mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
+                       /* correct the normal to be aligned on the view plane */
+                       copy_v3_v3(view_vec, vc.rv3d->viewinv[2]);
+                       mul_mat3_m4_v3(vc.obedit->imat, view_vec);
+                       cross_v3_v3v3(cross, nor, view_vec);
+                       cross_v3_v3v3(nor, view_vec, cross);
+                       normalize_v3(nor);
+               }
+               
+               /* center */
+               mid_v3_v3v3(cent, min, max);
+               copy_v3_v3(min, cent);
+               mul_m4_v3(vc.obedit->obmat, min);       // view space
+               view3d_get_view_aligned_coordinate(&vc, min, event->mval, TRUE);
+               mul_m4_v3(vc.obedit->imat, min); // back in object space
+               sub_v3_v3(min, cent);
+               
+               /* calculate rotation */
+               unit_m3(mat);
+               if (done) {
+                       float dot;
+                       copy_v3_v3(vec, min);
+                       normalize_v3(vec);
+                       dot = dot_v3v3(vec, nor);
+                       if (fabsf(dot) < 0.999f) {
+                               float cross[3], si, q1[4];
+                               cross_v3_v3v3(cross, nor, vec);
+                               normalize_v3(cross);
+                               dot = 0.5f * saacos(dot);
+                               /* halve the rotation if its applied twice */
+                               if (rot_src) dot *= 0.5f;
+                               si = sinf(dot);
+                               q1[0] = cosf(dot);
+                               q1[1] = cross[0] * si;
+                               q1[2] = cross[1] * si;
+                               q1[3] = cross[2] * si;
+                               quat_to_mat3(mat, q1);
+                       }
+               }
+               
+               if (rot_src) {
+                       EDBM_CallOpf(vc.em, op, "rotate verts=%hv cent=%v mat=%m3",
+                               BM_ELEM_SELECT, cent, mat);
+                       /* also project the source, for retopo workflow */
+                       if (use_proj)
+                               EMBM_project_snap_verts(C, vc.ar, vc.obedit, vc.em);
+               }
+               EDBM_Extrude_edge(vc.obedit, vc.em, BM_ELEM_SELECT, nor);
+               EDBM_CallOpf(vc.em, op, "rotate verts=%hv cent=%v mat=%m3",
+                       BM_ELEM_SELECT, cent, mat);
+               EDBM_CallOpf(vc.em, op, "translate verts=%hv vec=%v",
+                       BM_ELEM_SELECT, min);
+       }
+       else {
+               float *curs = give_cursor(vc.scene, vc.v3d);
+               BMOperator bmop;
+               BMOIter oiter;
+               
+               copy_v3_v3(min, curs);
+               view3d_get_view_aligned_coordinate(&vc, min, event->mval, 0);
+               invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
+               mul_m4_v3(vc.obedit->imat, min); // back in object space
+               
+               EDBM_InitOpf(vc.em, &bmop, op, "makevert co=%v", min);
+               BMO_op_exec(vc.em->bm, &bmop);
+               BMO_ITER(v1, &oiter, vc.em->bm, &bmop, "newvertout", BM_VERT) {
+                       BM_elem_select_set(vc.em->bm, v1, TRUE);
+               }
+               if (!EDBM_FinishOp(vc.em, &bmop, op, TRUE)) {
+                       return OPERATOR_CANCELLED;
+               }
+       }
+       if (use_proj)
+               EMBM_project_snap_verts(C, vc.ar, vc.obedit, vc.em);
+       /* This normally happens when pushing undo but modal operators
+        * like this one don't push undo data until after modal mode is
+        * done. */
+       EDBM_RecalcNormals(vc.em);
+       BMEdit_RecalcTesselation(vc.em);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, vc.obedit->data);
+       DAG_id_tag_update(vc.obedit->data, OB_RECALC_DATA);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Duplicate or Extrude at 3D Cursor";
+       ot->idname = "MESH_OT_dupli_extrude_cursor";
+       
+       /* api callbacks */
+       ot->invoke = dupli_extrude_cursor;
+       ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_boolean(ot->srna, "rotate_source", 1, "Rotate Source", "Rotate initial selection giving better shape");
+ }
+ static int delete_mesh(bContext *C, Object *obedit, wmOperator *op, int event, Scene *UNUSED(scene))
+ {
+       BMEditMesh *bem = ((Mesh *)obedit->data)->edit_btmesh;
+       
+       if (event < 1) return OPERATOR_CANCELLED;
+       if (event == 10) {
+               //"Erase Vertices";
+               if (!EDBM_CallOpf(bem, op, "del geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS))
+                       return OPERATOR_CANCELLED;
+       } 
+       else if (event == 11) {
+               //"Edge Loop"
+               if (!EDBM_CallOpf(bem, op, "dissolveedgeloop edges=%he", BM_ELEM_SELECT))
+                       return OPERATOR_CANCELLED;
+       }
+       else if (event == 7) {
+               int use_verts = RNA_boolean_get(op->ptr, "use_verts");
+               //"Dissolve"
+               if (bem->selectmode & SCE_SELECT_FACE) {
+                       if (!EDBM_CallOpf(bem, op, "dissolvefaces faces=%hf use_verts=%i", BM_ELEM_SELECT, use_verts))
+                               return OPERATOR_CANCELLED;
+               }
+               else if (bem->selectmode & SCE_SELECT_EDGE) {
+                       if (!EDBM_CallOpf(bem, op, "dissolveedges edges=%he use_verts=%i", BM_ELEM_SELECT, use_verts))
+                               return OPERATOR_CANCELLED;
+               }
+               else if (bem->selectmode & SCE_SELECT_VERTEX) {
+                       if (!EDBM_CallOpf(bem, op, "dissolveverts verts=%hv", BM_ELEM_SELECT))
+                               return OPERATOR_CANCELLED;
+               }
+       }
+       else if (event == 4) {
+               //Edges and Faces
+               if (!EDBM_CallOpf(bem, op, "del geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))
+                       return OPERATOR_CANCELLED;
+       } 
+       else if (event == 1) {
+               //"Erase Edges"
+               if (!EDBM_CallOpf(bem, op, "del geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES))
+                       return OPERATOR_CANCELLED;
+       }
+       else if (event == 2) {
+               //"Erase Faces";
+               if (!EDBM_CallOpf(bem, op, "del geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES))
+                       return OPERATOR_CANCELLED;
+       }
+       else if (event == 5) {
+               //"Erase Only Faces";
+               if (!EDBM_CallOpf(bem, op, "del geom=%hf context=%d",
+                                 BM_ELEM_SELECT, DEL_ONLYFACES))
+                       return OPERATOR_CANCELLED;
+       }
+       
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ /* Note, these values must match delete_mesh() event values */
+ static EnumPropertyItem prop_mesh_delete_types[] = {
+       {7, "DISSOLVE",         0, "Dissolve", ""},
+       {12, "COLLAPSE", 0, "Collapse", ""},
+       {10,"VERT",             0, "Vertices", ""},
+       {1, "EDGE",             0, "Edges", ""},
+       {2, "FACE",             0, "Faces", ""},
+       {11, "EDGE_LOOP", 0, "Edge Loop", ""},
+       {4, "EDGE_FACE", 0, "Edges & Faces", ""},
+       {5, "ONLY_FACE", 0, "Only Faces", ""},
+       {0, NULL, 0, NULL, NULL}
+ };
+ static int delete_mesh_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       Scene *scene = CTX_data_scene(C);
+       int type = RNA_enum_get(op->ptr, "type");
+       
+       if (type != 12) {
+               if (delete_mesh(C, obedit, op, type, scene) == OPERATOR_CANCELLED)
+                       return OPERATOR_CANCELLED;
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       }
+       else {
+               if (!EDBM_CallOpf(em, op, "collapse edges=%he", BM_ELEM_SELECT))
+                       return OPERATOR_CANCELLED;
+               DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA|ND_SELECT, obedit);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_delete(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Delete";
+       ot->description = "Delete selected vertices, edges or faces";
+       ot->idname = "MESH_OT_delete";
+       
+       /* api callbacks */
+       ot->invoke = WM_menu_invoke;
+       ot->exec = delete_mesh_exec;
+       
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* props */
+       ot->prop = RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, 10, "Type", "Method used for deleting mesh data");
+       /* TODO, move dissolve into its own operator so this doesnt confuse non-dissolve options */
+       RNA_def_boolean(ot->srna, "use_verts", 0, "Dissolve Verts",
+                       "When dissolving faaces/edges, also dissolve remaining vertices");
+ }
+ static int addedgeface_mesh_exec(bContext *C, wmOperator *op)
+ {
+       BMOperator bmop;
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       
+       if (!EDBM_InitOpf(em, &bmop, op, "contextual_create geom=%hfev", BM_ELEM_SELECT))
+               return OPERATOR_CANCELLED;
+       
+       BMO_op_exec(em->bm, &bmop);
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "faceout", BM_ELEM_SELECT, BM_FACE);
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_edge_face_add(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Make Edge/Face";
+       ot->description = "Add an edge or face to selected";
+       ot->idname = "MESH_OT_edge_face_add";
+       
+       /* api callbacks */
+       ot->exec = addedgeface_mesh_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ /* ************************* SEAMS AND EDGES **************** */
+ static int editbmesh_mark_seam(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       Mesh *me = ((Mesh *)obedit->data);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMesh *bm = em->bm;
+       BMEdge *eed;
+       BMIter iter;
+       int clear = RNA_boolean_get(op->ptr, "clear");
+       
+       /* auto-enable seams drawing */
+       if (clear == 0) {
+               me->drawflag |= ME_DRAWSEAMS;
+       }
+       if (clear) {
+               BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
+                               continue;
+                       
+                       BM_elem_flag_disable(eed, BM_ELEM_SEAM);
+               }
+       }
+       else {
+               BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
+                               continue;
+                       BM_elem_flag_enable(eed, BM_ELEM_SEAM);
+               }
+       }
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_mark_seam(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Mark Seam";
+       ot->idname = "MESH_OT_mark_seam";
+       ot->description = "(un)mark selected edges as a seam";
+       
+       /* api callbacks */
+       ot->exec = editbmesh_mark_seam;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_boolean(ot->srna, "clear", 0, "Clear", "");
+ }
+ static int editbmesh_mark_sharp(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       Mesh *me = ((Mesh *)obedit->data);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMesh *bm = em->bm;
+       BMEdge *eed;
+       BMIter iter;
+       int clear = RNA_boolean_get(op->ptr, "clear");
+       /* auto-enable sharp edge drawing */
+       if (clear == 0) {
+               me->drawflag |= ME_DRAWSHARP;
+       }
+       if (!clear) {
+               BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
+                               continue;
+                       
+                       BM_elem_flag_disable(eed, BM_ELEM_SMOOTH);
+               }
+       }
+       else {
+               BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+                       if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
+                               continue;
+                       
+                       BM_elem_flag_enable(eed, BM_ELEM_SMOOTH);
+               }
+       }
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_mark_sharp(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Mark Sharp";
+       ot->idname = "MESH_OT_mark_sharp";
+       ot->description = "(un)mark selected edges as sharp";
+       
+       /* api callbacks */
+       ot->exec = editbmesh_mark_sharp;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_boolean(ot->srna, "clear", 0, "Clear", "");
+ }
+ static int editbmesh_vert_connect(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMesh *bm = em->bm;
+       BMOperator bmop;
+       int len = 0;
+       
+       if (!EDBM_InitOpf(em, &bmop, op, "connectverts verts=%hv", BM_ELEM_SELECT)) {
+               return OPERATOR_CANCELLED;
+       }
+       BMO_op_exec(bm, &bmop);
+       len = BMO_slot_get(&bmop, "edgeout")->len;
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ void MESH_OT_vert_connect(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Vertex Connect";
+       ot->idname = "MESH_OT_vert_connect";
+       
+       /* api callbacks */
+       ot->exec = editbmesh_vert_connect;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int editbmesh_edge_split(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMesh *bm = em->bm;
+       BMOperator bmop;
+       int len = 0;
+       
+       if (!EDBM_InitOpf(em, &bmop, op, "edgesplit edges=%he numcuts=%d", 
+                       BM_ELEM_SELECT, RNA_int_get(op->ptr,"number_cuts"))) {
+               return OPERATOR_CANCELLED;
+       }
+       BMO_op_exec(bm, &bmop);
+       len = BMO_slot_get(&bmop, "outsplit")->len;
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ void MESH_OT_edge_split(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Edge Split";
+       ot->idname = "MESH_OT_edge_split";
+       
+       /* api callbacks */
+       ot->exec = editbmesh_edge_split;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, INT_MAX);
+ }
+ /****************** add duplicate operator ***************/
+ static int mesh_duplicate_exec(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       EDBM_InitOpf(em, &bmop, op, "dupe geom=%hvef", BM_ELEM_SELECT);
+       
+       BMO_op_exec(em->bm, &bmop);
+       EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "newout", BM_ELEM_SELECT, BM_ALL);
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
+       
+       return OPERATOR_FINISHED;
+ }
+ static int mesh_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+ {
+       WM_cursor_wait(1);
+       mesh_duplicate_exec(C, op);
+       WM_cursor_wait(0);
+       
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_duplicate(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Duplicate";
+       ot->description = "Duplicate selected vertices, edges or faces";
+       ot->idname = "MESH_OT_duplicate";
+       
+       /* api callbacks */
+       ot->invoke = mesh_duplicate_invoke;
+       ot->exec = mesh_duplicate_exec;
+       
+       ot->poll = ED_operator_editmesh;
+       
+       /* to give to transform */
+       RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
+ }
+ static int flip_normals(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
+       
+       if (!EDBM_CallOpf(em, op, "reversefaces faces=%hf", BM_ELEM_SELECT))
+               return OPERATOR_CANCELLED;
+       
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_flip_normals(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Flip Normals";
+       ot->description = "Flip the direction of selected face's vertex and face normals";
+       ot->idname = "MESH_OT_flip_normals";
+       
+       /* api callbacks */
+       ot->exec = flip_normals;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static const EnumPropertyItem direction_items[] = {
+       {DIRECTION_CW, "CW", 0, "Clockwise", ""},
+       {DIRECTION_CCW, "CCW", 0, "Counter Clockwise", ""},
+       {0, NULL, 0, NULL, NULL}};
+ /* only accepts 1 selected edge, or 2 selected faces */
+ static int edge_rotate_selected(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMOperator bmop;
+       BMEdge *eed;
+       BMIter iter;
+       const int do_ccw = RNA_enum_get(op->ptr, "direction") == 1;
+       int do_deselect = FALSE; /* do we deselect */
+       
+       if (!(em->bm->totfacesel == 2 || em->bm->totedgesel == 1)) {
+               BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces");
+               return OPERATOR_CANCELLED;
+       }
+       /* first see if we have two adjacent faces */
+       BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               if (BM_edge_face_count(eed) == 2) {
+                       if ((BM_elem_flag_test(eed->l->f, BM_ELEM_SELECT) && BM_elem_flag_test(eed->l->radial_next->f, BM_ELEM_SELECT))
+                                && !(BM_elem_flag_test(eed->l->f, BM_ELEM_HIDDEN) || BM_elem_flag_test(eed->l->radial_next->f, BM_ELEM_HIDDEN)))
+                       {
+                               break;
+                       }
+               }
+       }
+       
+       /* ok, we don't have two adjacent faces, but we do have two selected ones.
+        * that's an error condition.*/
+       if (!eed && em->bm->totfacesel == 2) {
+               BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces");
+               return OPERATOR_CANCELLED;
+       }
+       if (!eed) {
+               BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+                       if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+                               /* de-select the edge before */
+                               do_deselect = TRUE;
+                               break;
+                       }
+               }
+       }
+       /* this should never happen */
+       if (!eed)
+               return OPERATOR_CANCELLED;
+       
+       EDBM_InitOpf(em, &bmop, op, "edgerotate edges=%e ccw=%d", eed, do_ccw);
+       /* avoid adding to the selection if we start off with only a selected edge,
+        * we could also just deselect the single edge easily but use the BMO api
+        * since it seems this is more 'correct' */
+       if (do_deselect) BMO_slot_buffer_hflag_disable(em->bm, &bmop, "edges", BM_ELEM_SELECT, BM_EDGE);
+       BMO_op_exec(em->bm, &bmop);
+       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "edgeout", BM_ELEM_SELECT, BM_EDGE);
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_edge_rotate(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Rotate Selected Edge";
+       ot->description = "Rotate selected edge or adjoining faces";
+       ot->idname = "MESH_OT_edge_rotate";
+       /* api callbacks */
+       ot->exec = edge_rotate_selected;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* props */
+       RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "direction", "direction to rotate edge around");
+ }
+ /* swap is 0 or 1, if 1 it hides not selected */
+ void EDBM_hide_mesh(BMEditMesh *em, int swap)
+ {
+       BMIter iter;
+       BMHeader *h;
+       int itermode;
+       if (em == NULL) return;
+       
+       if (em->selectmode & SCE_SELECT_VERTEX)
+               itermode = BM_VERTS_OF_MESH;
+       else if (em->selectmode & SCE_SELECT_EDGE)
+               itermode = BM_EDGES_OF_MESH;
+       else
+               itermode = BM_FACES_OF_MESH;
+       BM_ITER(h, &iter, em->bm, itermode, NULL) {
+               if (BM_elem_flag_test(h, BM_ELEM_SELECT) ^ swap)
+                       BM_elem_hide_set(em->bm, h, TRUE);
+       }
+       EDBM_selectmode_flush(em);
+       /* original hide flushing comment (OUTDATED):
+        * hide happens on least dominant select mode, and flushes up, not down! (helps preventing errors in subsurf) */
+       /*  - vertex hidden, always means edge is hidden too
+               - edge hidden, always means face is hidden too
+               - face hidden, only set face hide
+               - then only flush back down what's absolute hidden
+       */
+ }
+ static int hide_mesh_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
+       
+       EDBM_hide_mesh(em, RNA_boolean_get(op->ptr, "unselected"));
+               
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_hide(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Hide Selection";
+       ot->idname = "MESH_OT_hide";
+       
+       /* api callbacks */
+       ot->exec = hide_mesh_exec;
+       ot->poll = ED_operator_editmesh;
+        ot->description = "Hide (un)selected vertices, edges or faces";
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       /* props */
+       RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
+ }
+ void EDBM_reveal_mesh(BMEditMesh *em)
+ {
+       const char iter_types[3] = {BM_VERTS_OF_MESH,
+                                   BM_EDGES_OF_MESH,
+                                   BM_FACES_OF_MESH};
+       int sels[3] = {(em->selectmode & SCE_SELECT_VERTEX),
+                      (em->selectmode & SCE_SELECT_EDGE),
+                      (em->selectmode & SCE_SELECT_FACE),
+                     };
+       BMIter iter;
+     BMHeader *ele;
+       int i;
+       /* Use index field to remember what was hidden before all is revealed. */
+       for (i = 0; i < 3; i++) {
+               BM_ITER(ele, &iter, em->bm, iter_types[i], NULL) {
+                       if (BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
+                               BM_elem_flag_enable(ele, BM_ELEM_TAG);
+                       }
+                       else {
+                               BM_elem_flag_disable(ele, BM_ELEM_TAG);
+                       }
+               }
+       }
+       /* Reveal everything */
+       EDBM_flag_disable_all(em, BM_ELEM_HIDDEN);
+       /* Select relevant just-revealed elements */
+       for (i = 0; i < 3; i++) {
+               if (!sels[i]) {
+                       continue;
+               }
+               BM_ITER(ele, &iter, em->bm, iter_types[i], NULL) {
+                       if (BM_elem_flag_test(ele, BM_ELEM_TAG)) {
+                               BM_elem_select_set(em->bm, ele, TRUE);
+                       }
+               }
+       }
+       EDBM_selectmode_flush(em);
+ }
+ static int reveal_mesh_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
+       
+       EDBM_reveal_mesh(em);
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_reveal(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Reveal Hidden";
+       ot->idname = "MESH_OT_reveal";
+       ot->description = "Reveal all hidden vertices, edges and faces";
+       
+       /* api callbacks */
+       ot->exec = reveal_mesh_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int normals_make_consistent_exec(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       
+       /* doflip has to do with bmesh_rationalize_normals, it's an internal
+        * thing */
+       if (!EDBM_CallOpf(em, op, "righthandfaces faces=%hf doflip=%d", BM_ELEM_SELECT, 1))
+               return OPERATOR_CANCELLED;
+       if (RNA_boolean_get(op->ptr, "inside"))
+               EDBM_CallOpf(em, op, "reversefaces faces=%hf", BM_ELEM_SELECT);
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_normals_make_consistent(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Make Normals Consistent";
+       ot->description = "Make face and vertex normals point either outside or inside the mesh";
+       ot->idname = "MESH_OT_normals_make_consistent";
+       
+       /* api callbacks */
+       ot->exec = normals_make_consistent_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_boolean(ot->srna, "inside", 0, "Inside", "");
+ }
+ static int do_smooth_vertex(bContext *C, wmOperator *op)
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       ModifierData *md;
+       int mirrx = 0, mirry = 0, mirrz = 0;
+       int i, repeat;
+       float clipdist = 0.0f;
+       /* mirror before smooth */
+       if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) {
+               EDBM_CacheMirrorVerts(em, TRUE);
+       }
+       /* if there is a mirror modifier with clipping, flag the verts that
+        * are within tolerance of the plane(s) of reflection 
+        */
+       for (md = obedit->modifiers.first; md; md = md->next) {
+               if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
+                       MirrorModifierData *mmd = (MirrorModifierData *)md;
+               
+                       if (mmd->flag & MOD_MIR_CLIPPING) {
+                               if (mmd->flag & MOD_MIR_AXIS_X)
+                                       mirrx = 1;
+                               if (mmd->flag & MOD_MIR_AXIS_Y)
+                                       mirry = 1;
+                               if (mmd->flag & MOD_MIR_AXIS_Z)
+                                       mirrz = 1;
+                               clipdist = mmd->tolerance;
+                       }
+               }
+       }
+       repeat = RNA_int_get(op->ptr,"repeat");
+       if (!repeat)
+               repeat = 1;
+       
+       for (i = 0; i < repeat; i++) {
+               if (!EDBM_CallOpf(em, op,
+                                 "vertexsmooth verts=%hv mirror_clip_x=%d mirror_clip_y=%d mirror_clip_z=%d clipdist=%f",
+                                 BM_ELEM_SELECT, mirrx, mirry, mirrz, clipdist))
+               {
+                       return OPERATOR_CANCELLED;
+               }
+       }
+       /* apply mirror */
+       if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) {
+               EDBM_ApplyMirrorCache(em, BM_ELEM_SELECT, 0);
+               EDBM_EndMirrorCache(em);
+       }
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }     
+       
+ void MESH_OT_vertices_smooth(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Smooth Vertex";
+       ot->description = "Flatten angles of selected vertices";
+       ot->idname = "MESH_OT_vertices_smooth";
+       
+       /* api callbacks */
+       ot->exec = do_smooth_vertex;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       RNA_def_int(ot->srna, "repeat", 1, 1, 100, "Number of times to smooth the mesh", "", 1, INT_MAX);
+ }
+ static int bm_test_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       ARegion *ar = CTX_wm_region(C);
+       View3D *v3d = CTX_wm_view3d(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       BMBVHTree *tree = BMBVH_NewBVH(em, 0, NULL, NULL);
+       BMIter iter;
+       BMEdge *e;
+       /* hide all back edges */
+       BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
+               if (!BM_elem_flag_test(e, BM_ELEM_SELECT))
+                       continue;
+               if (!BMBVH_EdgeVisible(tree, e, ar, v3d, obedit))
+                       BM_elem_select_set(em->bm, e, FALSE);
+       }
+       BMBVH_FreeBVH(tree);
+       
+ #if 0 //uv island walker test
+       BMIter iter, liter;
+       BMFace *f;
+       BMLoop *l, *l2;
+       MLoopUV *luv;
+       BMWalker walker;
+       int i = 0;
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
+                       luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
+               }
+       }
+       BMW_init(&walker, em->bm, BMW_UVISLAND, BMW_NIL_LAY);
+       BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
+                       luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
+                       if (luv->flag & MLOOPUV_VERTSEL) {
+                               l2 = BMW_begin(&walker, l);
+                               for (; l2; l2 = BMW_step(&walker)) {
+                                       luv = CustomData_bmesh_get(&em->bm->ldata, l2->head.data, CD_MLOOPUV);
+                                       luv->flag |= MLOOPUV_VERTSEL;
+                               }                               
+                       }
+               }
+       }
+       BMW_end(&walker);
+ #endif
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }     
+       
+ void MESH_OT_bm_test(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "BMesh Test Operator";
+       ot->idname = "MESH_OT_bm_test";
+       
+       /* api callbacks */
+       ot->exec = bm_test_exec;
+       ot->poll = ED_operator_editmesh;
+       
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       //RNA_def_int(ot->srna, "repeat", 1, 1, 100, "Number of times to smooth the mesh", "", 1, INT_MAX);
+ }
+ /********************** Smooth/Solid Operators *************************/
+ static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
+ {
+       BMIter iter;
+       BMFace *efa;
+       if (em == NULL) return;
+       
+       BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+               if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+                       if (smooth)
+                               BM_elem_flag_enable(efa, BM_ELEM_SMOOTH);
+                       else
+                               BM_elem_flag_disable(efa, BM_ELEM_SMOOTH);
+               }
+       }
+ }
+ static int mesh_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       mesh_set_smooth_faces(em, 1);
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_faces_shade_smooth(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Shade Smooth";
+        ot->description = "Display faces smooth (using vertex normals)";
+       ot->idname = "MESH_OT_faces_shade_smooth";
+       /* api callbacks */
+       ot->exec = mesh_faces_shade_smooth_exec;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ static int mesh_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
+       mesh_set_smooth_faces(em, 0);
+       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_faces_shade_flat(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Shade Flat";
+       ot->description = "Display faces flat";
+       ot->idname = "MESH_OT_faces_shade_flat";
+       /* api callbacks */
+       ot->exec = mesh_faces_shade_flat_exec;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+ }
+ /********************** UV/Color Operators *************************/
+ static const EnumPropertyItem axis_items[] = {
+       {OPUVC_AXIS_X, "X", 0, "X", ""},
+       {OPUVC_AXIS_Y, "Y", 0, "Y", ""},
+       {0, NULL, 0, NULL, NULL}};
+ static int mesh_rotate_uvs(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* get the direction from RNA */
+       int dir = RNA_enum_get(op->ptr, "direction");
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "meshrotateuvs faces=%hf dir=%d", BM_ELEM_SELECT, dir);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       /* dependencies graph and notification stuff */
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }
+ static int mesh_reverse_uvs(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "meshreverseuvs faces=%hf", BM_ELEM_SELECT);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       /* dependencies graph and notification stuff */
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }
+ static int mesh_rotate_colors(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* get the direction from RNA */
+       int dir = RNA_enum_get(op->ptr, "direction");
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "meshrotatecolors faces=%hf dir=%d", BM_ELEM_SELECT, dir);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       /* dependencies graph and notification stuff */
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
+ /*    DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT | ND_GEOM_SELECT, ob);
+ */
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }
+ static int mesh_reverse_colors(bContext *C, wmOperator *op)
+ {
+       Object *ob = CTX_data_edit_object(C);
+       BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
+       BMOperator bmop;
+       /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
+       EDBM_InitOpf(em, &bmop, op, "meshreversecolors faces=%hf", BM_ELEM_SELECT);
+       /* execute the operator */
+       BMO_op_exec(em->bm, &bmop);
+       /* finish the operator */
+       if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
+               return OPERATOR_CANCELLED;
+       }
+       DAG_id_tag_update(ob->data, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
+       /* we succeeded */
+       return OPERATOR_FINISHED;
+ }
+ void MESH_OT_uvs_rotate(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Rotate UVs";
+       ot->idname = "MESH_OT_uvs_rotate";
+       /* api callbacks */
+       ot->exec = mesh_rotate_uvs;
+       ot->poll = ED_operator_editmesh;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* props */
+       RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "Direction", "Direction to rotate UVs around");
+ }
+ //void MESH_OT_uvs_mirror(wmOperatorType *ot)
+ void MESH_OT_uvs_reverse(wmOperatorType&nb