- New additions to Mesh module
authorKen Hughes <khughes@pacific.edu>
Wed, 19 Oct 2005 21:24:18 +0000 (21:24 +0000)
committerKen Hughes <khughes@pacific.edu>
Wed, 19 Oct 2005 21:24:18 +0000 (21:24 +0000)
  - new methods from NMesh (transform, getFromObject, findEdges)
  - new methods for deleting groups of verts, edges and faces
  - new methods for accessing mesh editing tools: fill, flipNormals,
    recalcNormals, remDoubles, smooth, subdivide, toSphere
- Added PVertType to Types module (not my favorite name; any suggestions?)

source/blender/python/api2_2x/Mesh.c
source/blender/python/api2_2x/Mesh.h
source/blender/python/api2_2x/Types.c
source/blender/python/api2_2x/Types.h
source/blender/python/api2_2x/doc/Mesh.py
source/blender/python/api2_2x/doc/Types.py

index 14e0248a55124c4a6cbe206f3fcf65af52f9f920..ae982e5d42eb8381f102c610750cffd42315fb1d 100644 (file)
@@ -40,6 +40,7 @@
 #include "DNA_oops_types.h"
 #include "DNA_space_types.h"
 #include "DNA_curve_types.h"
+#include "DNA_meta_types.h"
 
 #include "BDR_editface.h"      /* make_tfaces */
 #include "BDR_vpaint.h"
@@ -48,7 +49,7 @@
 #include "BIF_editdeform.h"
 #include "BIF_editkey.h"       /* insert_meshkey */
 #include "BIF_editview.h"
-#include "BIF_space.h" /* allqueue */
+#include "BIF_editmesh.h"
 
 #include "BKE_deform.h"
 #include "BKE_mesh.h"
 #include "BKE_utildefines.h"
 #include "BKE_depsgraph.h"
 #include "BSE_edit.h"          /* for void countall(); */
+#include "BKE_curve.h" /* copy_curve(); */
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
 
 #include "blendef.h"
 #include "mydevice.h"
+#include "butspace.h"          /* for mesh tools */
 #include "Object.h"
 #include "Key.h"
 #include "Image.h"
@@ -77,6 +80,8 @@
 #include "constant.h"
 #include "gen_utils.h"
 
+#define MESH_TOOLS                     /* add access to mesh tools */
+
 /* EXPP Mesh defines */
 
 #define MESH_SMOOTHRESH               30
 #define MESH_HASMCOL                   1
 #define MESH_HASVERTUV                 2
 
+#define MESH_TOOL_TOSPHERE             0
+#define MESH_TOOL_VERTEXSMOOTH         1
+#define MESH_TOOL_FLIPNORM             2
+#define MESH_TOOL_SUBDIV               3
+#define MESH_TOOL_REMDOUB              4
+#define MESH_TOOL_FILL                 5
+#define MESH_TOOL_RECALCNORM           6
+
 /************************************************************************
  *
  * internal utilities
 typedef struct SrchEdges {
        unsigned int v[2];              /* indices for verts */
        unsigned char swap;             /* non-zero if verts swapped */
-#if 0
        unsigned int index;             /* index in original param list of this edge */
                                                        /* (will be used by findEdges) */
-#endif
 } SrchEdges;
 
 typedef struct SrchFaces {
@@ -114,6 +125,12 @@ typedef struct SrchFaces {
        unsigned char order;    /* order of original verts, bitpacked */
 } SrchFaces;
 
+typedef struct FaceEdges {
+       unsigned int v[2];              /* search key (vert indices) */
+       unsigned int index;             /* location in edge list */
+       unsigned char sel;              /* selection state */
+} FaceEdges;
+
 /*
  * compare edges by vertex indices
  */
@@ -170,40 +187,32 @@ int mface_comp( const void *va, const void *vb )
 }
 
 /*
- * update the DAG for all objects linked to this mesh
+ * compare edges by vertex indices
  */
 
-static void mesh_update( Mesh * mesh )
+int faceedge_comp( const void *va, const void *vb )
 {
-       allqueue( REDRAWVIEW3D, 1);
-       Object_updateDag( (void *) mesh );
+       const unsigned int *a = ((FaceEdges *)va)->v;
+       const unsigned int *b = ((FaceEdges *)vb)->v;
+
+       /* compare first index for differences */
+
+       if (a[0] < b[0]) return -1;     
+       else if (a[0] > b[0]) return 1;
+
+       /* if first indices equal, compare second index for differences */
+
+       else if (a[1] < b[1]) return -1;
+       else return (a[1] > b[1]);
 }
 
 /*
- * Since all faces must have 3 or 4 verts, we can't have v3 or v4 be zero.
- * If that happens during the deletion, we have to shuffle the vertices
- * around; otherwise it can cause an Eeekadoodle or worse.
- */
-
-static void eeek_fix( MFace *tmpface , int len4 )
-{
-       if( len4 && !tmpface->v4 ) {
-               unsigned int tmp = tmpface->v1;
-               tmpface->v1 = tmpface->v4;
-               tmpface->v4 = tmpface->v3;
-               tmpface->v3 = tmpface->v2;
-               tmpface->v2 = tmp;
-       } else if( !tmpface->v3 ) {
-               unsigned int tmp = tmpface->v1;
-               tmpface->v1 = tmpface->v2;
-               tmpface->v2 = tmpface->v3;
-               if( !len4 ) {
-                       tmpface->v3 = tmp;
-               } else {
-                       tmpface->v3 = tmpface->v4;
-                       tmpface->v4 = tmp;
-               }
-       }
+ * update the DAG for all objects linked to this mesh
+ */
+
+static void mesh_update( Mesh * mesh )
+{
+       Object_updateDag( (void *) mesh );
 }
 
 #ifdef CHECK_DVERTS /* not clear if this code is needed */
@@ -239,6 +248,347 @@ static void check_dverts(Mesh *me, int old_totvert)
 }
 #endif
 
+/*
+ * delete vertices from mesh, then delete edges/keys/faces which used those
+ * vertices
+ *
+ * Deletion is done by "smart compaction"; groups of verts/edges/faces which
+ * remain in the list are copied to new list instead of one at a time.  Since
+ * Blender has no realloc we would have to copy things anyway, so there's no
+ * point trying to fill empty entries with data from the end of the lists.
+ *
+ * vert_table is a lookup table for mapping old verts to new verts (after the
+ * vextex list has deleted vertices removed).  Each entry contains the
+ * vertex's new index.
+ */
+
+static void delete_verts( Mesh *mesh, unsigned int *vert_table, int to_delete )
+{
+       /*
+        * (1) allocate vertex table (initialize contents to 0)
+        * (2) mark each vertex being deleted in vertex table (= UINT_MAX)
+        * (3) update remaining table entries with "new" vertex index (after
+        *     compaction)
+        * (4) allocate new vertex list
+        * (5) do "smart copy" of vertices from old to new
+        *     * each moved vertex is entered into vertex table: if vertex i is
+        *       moving to index j in new list
+        *       vert_table[i] = j;
+        * (6) if keys, do "smart copy" of keys
+        * (7) process edge list
+        *      update vert index
+        *      delete edges which delete verts
+        * (7) allocate new edge list
+        * (8) do "smart copy" of edges
+        * (9) allocate new face list
+        * (10) do "smart copy" of face
+        */
+       unsigned int *tmpvert;
+       int i;
+       char state;
+       MVert *newvert, *srcvert, *dstvert;
+       int count;
+
+       newvert = (MVert *)MEM_mallocN(
+                       sizeof( MVert )*( mesh->totvert-to_delete ), "MVerts" );
+
+       /*
+        * do "smart compaction" of the table; find and copy groups of vertices
+        * which are not being deleted
+        */
+
+       dstvert = newvert;
+       srcvert = mesh->mvert;
+       tmpvert = vert_table;
+       count = 0;
+       state = 1;
+       for( i = 0; i < mesh->totvert; ++i, ++tmpvert ) {
+               switch( state ) {
+               case 0:         /* skipping verts */
+                       if( *tmpvert == UINT_MAX ) {
+                               ++count;
+                       } else {
+                               srcvert = mesh->mvert + i;
+                               count = 1;
+                               state = 1;
+                       }
+                       break;
+               case 1:         /* gathering verts */
+                       if( *tmpvert != UINT_MAX ) {
+                               ++count;
+                       } else {
+                               if( count ) {
+                                       memcpy( dstvert, srcvert, sizeof( MVert ) * count );
+                                       dstvert += count;
+                               }
+                               count = 1;
+                               state = 0;
+                       }
+               }
+       }
+
+       /* if we were gathering verts at the end of the loop, copy those */
+       if( state && count )
+               memcpy( dstvert, srcvert, sizeof( MVert ) * count );
+
+       /* delete old vertex list, install the new one, update vertex count */
+
+       MEM_freeN( mesh->mvert );
+       mesh->mvert = newvert;
+       mesh->totvert -= to_delete;
+}
+
+static void delete_edges( Mesh *mesh, unsigned int *vert_table, int to_delete )
+{
+       int i;
+       MEdge *tmpedge;
+
+       /* if not given, then mark and count edges to be deleted */
+       if( !to_delete ) {
+               tmpedge = mesh->medge;
+               for( i = mesh->totedge; i-- ; ++tmpedge )
+                       if( vert_table[tmpedge->v1] == UINT_MAX ||
+                                       vert_table[tmpedge->v2] == UINT_MAX ) {
+                               tmpedge->v1 = UINT_MAX;
+                               ++to_delete;
+                       }
+       }
+
+       /* if there are edges to delete, handle it */
+       if( to_delete ) {
+               MEdge *newedge, *srcedge, *dstedge;
+               int count, state;
+
+       /* allocate new edge list and populate */
+               newedge = (MEdge *)MEM_mallocN(
+                               sizeof( MEdge )*( mesh->totedge-to_delete ), "MEdges" );
+
+       /*
+        * do "smart compaction" of the edges; find and copy groups of edges
+        * which are not being deleted
+        */
+
+               dstedge = newedge;
+               srcedge = mesh->medge;
+               tmpedge = srcedge;
+               count = 0;
+               state = 1;
+               for( i = 0; i < mesh->totedge; ++i, ++tmpedge ) {
+                       switch( state ) {
+                       case 0:         /* skipping edges */
+                               if( tmpedge->v1 == UINT_MAX ) {
+                                       ++count;
+                               } else {
+                                       srcedge = tmpedge;
+                                       count = 1;
+                                       state = 1;
+                               }
+                               break;
+                       case 1:         /* gathering edges */
+                               if( tmpedge->v1 != UINT_MAX ) {
+                                       ++count;
+                               } else {
+                                       if( count ) {
+                                               memcpy( dstedge, srcedge, sizeof( MEdge ) * count );
+                                               dstedge += count;
+                                       }
+                                       count = 1;
+                                       state = 0;
+                               }
+                       }
+               /* if edge is good, update vertex indices */
+               }
+
+       /* copy any pending good edges */
+               if( state && count )
+                       memcpy( dstedge, srcedge, sizeof( MEdge ) * count );
+
+       /* delete old vertex list, install the new one, update vertex count */
+               MEM_freeN( mesh->medge );
+               mesh->medge = newedge;
+               mesh->totedge -= to_delete;
+       }
+
+       /* if vertices were deleted, update edge's vertices */
+       if( vert_table ) {
+               tmpedge = mesh->medge;
+               for( i = mesh->totedge; i--; ++tmpedge ) {
+                       tmpedge->v1 = vert_table[tmpedge->v1];
+                       tmpedge->v2 = vert_table[tmpedge->v2];
+               }
+       }
+}
+
+/*
+ * Since all faces must have 3 or 4 verts, we can't have v3 or v4 be zero.
+ * If that happens during the deletion, we have to shuffle the vertices
+ * around; otherwise it can cause an Eeekadoodle or worse.  If there are
+ * texture faces as well, they have to be shuffled as well.
+ *
+ * (code borrowed from test_index_face() in mesh.c, but since we know the
+ * faces already have correct number of vertices, this is a little faster)
+ */
+
+static void eeek_fix( MFace *mface, TFace *tface, int len4 )
+{
+       /* if 4 verts, then neither v3 nor v4 can be zero */
+       if( len4 ) {
+               if( !mface->v3 || !mface->v4 ) {
+                       SWAP( int, mface->v1, mface->v3 );
+                       SWAP( int, mface->v2, mface->v4 );
+                       if( tface ) {
+                               SWAP( float, tface->uv[0][0], tface->uv[2][0] );
+                               SWAP( float, tface->uv[0][1], tface->uv[2][1] );
+                               SWAP( float, tface->uv[1][0], tface->uv[3][0] );
+                               SWAP( float, tface->uv[1][1], tface->uv[3][1] );
+                               SWAP( unsigned int, tface->col[0], tface->col[2] );
+                               SWAP( unsigned int, tface->col[1], tface->col[3] );
+                       }
+               }
+       } else if( !mface->v3 ) {
+       /* if 2 verts, then just v3 cannot be zero (v4 MUST be zero) */
+               SWAP( int, mface->v1, mface->v2 );
+               SWAP( int, mface->v2, mface->v3 );
+               if( tface ) {
+                       SWAP( float, tface->uv[0][0], tface->uv[1][0] );
+                       SWAP( float, tface->uv[0][1], tface->uv[1][1] );
+                       SWAP( float, tface->uv[2][0], tface->uv[1][0] );
+                       SWAP( float, tface->uv[2][1], tface->uv[1][1] );
+                       SWAP( unsigned int, tface->col[0], tface->col[1] );
+                       SWAP( unsigned int, tface->col[1], tface->col[2] );
+               }
+       }
+}
+
+static void delete_faces( Mesh *mesh, unsigned int *vert_table, int to_delete )
+{
+       int i;
+       MFace *tmpface;
+       TFace *tmptface;
+
+               /* if there are faces to delete, handle it */
+       if( to_delete ) {
+               MFace *newface, *srcface, *dstface;
+               TFace *newtface = NULL, *srctface, *dsttface;
+               char state;
+               int count;
+
+               newface = (MFace *)MEM_mallocN( ( mesh->totface-to_delete )
+                               * sizeof( MFace ), "MFace" );
+               if( mesh->tface )
+                       newtface = (TFace *)MEM_mallocN( ( mesh->totface-to_delete )
+                                       * sizeof( TFace ), "TFace" );
+
+               /*
+                * do "smart compaction" of the faces; find and copy groups of faces
+                * which are not being deleted
+                */
+
+               dstface = newface;
+               srcface = mesh->mface;
+               tmpface = srcface;
+               dsttface = newtface;
+               srctface = mesh->tface;
+               tmptface = srctface;
+
+               count = 0;
+               state = 1;
+               for( i = 0; i < mesh->totface; ++i ) {
+                       switch( state ) {
+                       case 0:         /* skipping faces */
+                               if( tmpface->v1 == UINT_MAX ) {
+                                       ++count;
+                               } else {
+                                       srcface = tmpface;
+                                       srctface = tmptface;
+                                       count = 1;
+                                       state = 1;
+                               }
+                               break;
+                       case 1:         /* gathering faces */
+                               if( tmpface->v1 != UINT_MAX ) {
+                                       ++count;
+                               } else {
+                                       if( count ) {
+                                               memcpy( dstface, srcface, sizeof( MFace ) * count );
+                                               dstface += count;
+                                               if( newtface ) {
+                                                       memcpy( dsttface, srctface, sizeof( TFace )
+                                                                       * count );
+                                                       dsttface += count;
+                                               }
+                                       }
+                                       count = 1;
+                                       state = 0;
+                               }
+                       }
+                       ++tmpface; 
+                       ++tmptface; 
+               }
+
+       /* if we were gathering faces at the end of the loop, copy those */
+               if ( state && count ) {
+                       memcpy( dstface, srcface, sizeof( MFace ) * count );
+                       if( newtface )
+                               memcpy( dsttface, srctface, sizeof( TFace ) * count );
+               }
+
+       /* delete old face list, install the new one, update face count */
+
+               MEM_freeN( mesh->mface );
+               mesh->mface = newface;
+               mesh->totface -= to_delete;
+               if( newtface ) {
+                       MEM_freeN( mesh->tface );
+                       mesh->tface = newtface;
+               }
+       }
+
+       /* if vertices were deleted, update face's vertices */
+       if( vert_table ) {
+               tmpface = mesh->mface;
+               tmptface = mesh->tface;
+               for( i = mesh->totface; i--; ) {
+                       int len4 = tmpface->v4;
+                       tmpface->v1 = vert_table[tmpface->v1];
+                       tmpface->v2 = vert_table[tmpface->v2];
+                       tmpface->v3 = vert_table[tmpface->v3];
+                       tmpface->v4 = vert_table[tmpface->v4];
+
+                       eeek_fix( tmpface, tmptface, len4 );
+
+                       ++tmpface;
+                       if( mesh->tface )
+                               ++tmptface;
+               }
+       }
+}
+
+/*
+ * fill up vertex lookup table with old-to-new mappings
+ *
+ * returns the number of vertices marked for deletion
+ */
+
+static unsigned int make_vertex_table( unsigned int *vert_table, int count )
+{
+       int i;
+       unsigned int *tmpvert = vert_table;
+       unsigned int to_delete = 0;
+       unsigned int new_index = 0;
+
+       /* fill the lookup table with old->new index mappings */
+       for( i = count; i; --i, ++tmpvert ) {
+               if( *tmpvert == UINT_MAX ) {
+                       ++to_delete;
+               } else {
+                       *tmpvert = new_index;
+                       ++new_index;
+               }
+       }
+       return to_delete;
+}
+
 /************************************************************************
  *
  * Color attributes
@@ -936,8 +1286,8 @@ static PyObject *MVert_CreatePyObject( Mesh *mesh, int i )
 
 static PyObject *PVert_CreatePyObject( MVert *vert )
 {
-       BPy_MVert *obj = PyObject_NEW( BPy_MVert, &PVert_Type );
        MVert *newvert;
+       BPy_MVert *obj = (BPy_MVert *)PyObject_NEW( BPy_MVert, &PVert_Type );
 
        if( !obj )
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
@@ -1198,10 +1548,11 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
        tmpvert = &newvert[mesh->totvert];
        for( i = 0; i < len; ++i ) {
                float co[3];
-               tmp = PySequence_Fast_GET_ITEM( args, i );
+               tmp = PySequence_GetItem( args, i );
                if( VectorObject_Check( tmp ) ) {
                        if( ((VectorObject *)tmp)->size != 3 ) {
                                MEM_freeN( newvert );
+                               Py_DECREF ( tmp );
                                Py_DECREF ( args );
                                return EXPP_ReturnPyObjError( PyExc_ValueError,
                                        "expected vector of size 3" );
@@ -1225,10 +1576,12 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
                        if( !ok ) {
                                MEM_freeN( newvert );
                                Py_DECREF ( args );
+                               Py_DECREF ( tmp );
                                return EXPP_ReturnPyObjError( PyExc_ValueError,
                                        "expected tuple triplet of floats" );
                        }
                }
+               Py_DECREF ( tmp );
 
        /* add the coordinate to the new list */
                memcpy( tmpvert->co, co, sizeof(co) );
@@ -1270,7 +1623,7 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
 
                        /* add data for new vertices */
                        fp = (float *)((char *)currkey->data +
-                                       mesh->key->elemsize*mesh->totvert );
+                                       (mesh->key->elemsize*mesh->totvert));
                        tmpvert = mesh->mvert + mesh->totvert;
                        for( i = newlen - mesh->totvert; i > 0; --i ) {
                                VECCOPY(fp, tmpvert->co);
@@ -1294,9 +1647,100 @@ static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args )
        return EXPP_incr_ret( Py_None );
 }
 
+static PyObject *MVertSeq_delete( BPy_MVertSeq * self, PyObject *args )
+{
+       unsigned int *vert_table;
+       int vert_delete, face_count;
+       int i;
+       Mesh *mesh = self->mesh;
+       MFace *tmpface;
+
+       Py_INCREF( args );              /* so we can safely DECREF later */
+
+       /* accept a sequence (lists or tuples) also */
+       if( PySequence_Size( args ) == 1 ) {
+               PyObject *tmp = PyTuple_GET_ITEM( args, 0 );
+               if( PySequence_Check ( tmp ) ) {
+                       Py_DECREF( args );              /* release previous reference */
+                       args = tmp;                             /* PyTuple_GET_ITEM returns new ref */
+               }
+       }
+
+       /* allocate vertex lookup table */
+       vert_table = (unsigned int *)MEM_callocN( 
+                       mesh->totvert*sizeof( unsigned int ), "vert_table" );
+
+       /* get the indices of vertices to be removed */
+       for( i = PySequence_Size( args ); i--; ) {
+               PyObject *tmp = PySequence_GetItem( args, i );
+               int index;
+               if( BPy_MVert_Check( tmp ) ) {
+                       if( (void *)self->mesh != ((BPy_MVert*)tmp)->data ) {
+                               MEM_freeN( vert_table );
+                               Py_DECREF( args );
+                               Py_DECREF( tmp );
+                               return EXPP_ReturnPyObjError( PyExc_ValueError,
+                                               "MVert belongs to a different mesh" );
+                       }
+                       index = ((BPy_MVert*)tmp)->index;
+               }
+               else if( PyInt_CheckExact( tmp ) )
+                       index = PyInt_AsLong ( tmp );
+               else {
+                       MEM_freeN( vert_table );
+                       Py_DECREF( args );
+                       Py_DECREF( tmp );
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected a sequence of ints or MVerts" );
+               }
+               Py_DECREF( tmp );
+               if( index < 0 || index >= mesh->totvert ) {
+                       MEM_freeN( vert_table );
+                       Py_DECREF( args );
+                       return EXPP_ReturnPyObjError( PyExc_ValueError,
+                                       "array index out of range" );
+               }
+               vert_table[index] = UINT_MAX;
+       }
+
+       /* delete things, then clean up and return */
+
+       vert_delete = make_vertex_table( vert_table, mesh->totvert );
+       if( vert_delete )
+               delete_verts( mesh, vert_table, vert_delete );
+
+       /* calculate edges to delete, fix vertex indices */
+       delete_edges( mesh, vert_table, 0 );
+
+       /*
+        * find number of faces which contain any of the deleted vertices,
+        * and mark them, then delete them
+        */
+       tmpface = mesh->mface;
+       face_count=0;
+       for( i = mesh->totface; i--; ++tmpface ) {
+               if( vert_table[tmpface->v1] == UINT_MAX ||
+                               vert_table[tmpface->v2] == UINT_MAX ||
+                               vert_table[tmpface->v3] == UINT_MAX ||
+                               vert_table[tmpface->v4] == UINT_MAX ) {
+                       tmpface->v1 = UINT_MAX;
+                       ++face_count;
+               }
+       }
+       delete_faces( mesh, vert_table, face_count );
+
+       /* clean up and exit */
+       MEM_freeN( vert_table );
+       mesh_update ( mesh );
+       Py_DECREF( args );
+       return EXPP_incr_ret( Py_None );
+}
+
 static struct PyMethodDef BPy_MVertSeq_methods[] = {
        {"extend", (PyCFunction)MVertSeq_extend, METH_VARARGS,
                "add vertices to mesh"},
+       {"delete", (PyCFunction)MVertSeq_delete, METH_VARARGS,
+               "delete vertices to mesh"},
        {NULL, NULL, 0, NULL}
 };
 
@@ -1740,6 +2184,7 @@ static PyObject *MEdgeSeq_item( BPy_MEdgeSeq * self, int i )
        return MEdge_CreatePyObject( self->mesh, i );
 }
 
+
 static PySequenceMethods MEdgeSeq_as_sequence = {
        ( inquiry ) MEdgeSeq_len,       /* sq_length */
        ( binaryfunc ) 0,       /* sq_concat */
@@ -1841,11 +2286,12 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
        /* verify the param list and get a total count of number of edges */
        new_edge_count = 0;
        for( i = 0; i < len; ++i ) {
-               tmp = PySequence_Fast_GET_ITEM( args, i );
+               tmp = PySequence_GetItem( args, i );
 
                /* not a tuple of MVerts... error */
                if( !PyTuple_Check( tmp ) ||
                                EXPP_check_sequence_consistency( tmp, &MVert_Type ) != 1 ) {
+                       Py_DECREF( tmp );
                        Py_DECREF( args );
                        return EXPP_ReturnPyObjError( PyExc_ValueError,
                                "expected sequence of MVert tuples" );
@@ -1854,10 +2300,13 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
                /* not the right number of MVerts... error */
                nverts = PyTuple_Size( tmp );
                if( nverts < 2 || nverts > 4 ) {
+                       Py_DECREF( tmp );
                        Py_DECREF( args );
                        return EXPP_ReturnPyObjError( PyExc_ValueError,
                                "expected 2 to 4 MVerts per tuple" );
                }
+               Py_DECREF( tmp );
+
                if( nverts == 2 )
                        ++new_edge_count;       /* if only two vert, then add only edge */
                else
@@ -1872,12 +2321,13 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
        len = PySequence_Size( args );
        tmppair = newpair;
        for( i = 0; i < len; ++i ) {
-               tmp = PySequence_Fast_GET_ITEM( args, i );
+               tmp = PySequence_GetItem( args, i );
                nverts = PyTuple_Size( tmp );
 
                /* get copies of vertices */
                for(j = 0; j < nverts; ++j )
                        e[j] = (BPy_MVert *)PyTuple_GET_ITEM( tmp, j );
+               Py_DECREF( tmp );
 
                if( nverts == 2 )
                        nverts = 1;     /* again, two verts give just one edge */
@@ -2016,9 +2466,154 @@ static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
        return EXPP_incr_ret( Py_None );
 }
 
+static PyObject *MEdgeSeq_delete( BPy_MEdgeSeq * self, PyObject *args )
+{
+       Mesh *mesh = self->mesh;
+       MEdge *srcedge;
+       MFace *srcface;
+       unsigned int *vert_table, *del_table, *edge_table;
+       int i, len;
+       int face_count, edge_count, vert_count;
+
+       Py_INCREF( args );              /* so we can safely DECREF later */
+
+       /* accept a sequence (lists or tuples) also */
+       if( PySequence_Size( args ) == 1 ) {
+               PyObject *tmp = PyTuple_GET_ITEM( args, 0 );
+               if( PySequence_Check ( tmp ) ) {
+                       Py_DECREF( args );              /* release previous reference */
+                       args = tmp;                             /* PyTuple_GET_ITEM returns new ref */
+               }
+       }
+
+       /* see how many args we need to parse */
+       len = PySequence_Size( args );
+       edge_table = (unsigned int *)MEM_callocN( len*sizeof( unsigned int ),
+                       "edge_table" );
+
+       /* get the indices of edges to be removed */
+       for( i = len; i--; ) {
+               PyObject *tmp = PySequence_GetItem( args, i );
+               if( BPy_MEdge_Check( tmp ) )
+                       edge_table[i] = ((BPy_MEdge *)tmp)->index;
+               else if( PyInt_CheckExact( tmp ) )
+                       edge_table[i] = PyInt_AsLong ( tmp );
+               else {
+                       MEM_freeN( edge_table );
+                       Py_DECREF( tmp );
+                       Py_DECREF( args );
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected a sequence of ints or MEdges" );
+               }
+               Py_DECREF( tmp );
+
+               /* if index out-of-range, throw exception */
+               if( edge_table[i] >= (unsigned int)mesh->totedge ) {
+                       MEM_freeN( edge_table );
+                       Py_DECREF( args );
+                       return EXPP_ReturnPyObjError( PyExc_ValueError,
+                                       "array index out of range" );
+               }
+       }
+
+       /*
+        * build two tables: first table marks vertices which belong to an edge
+        * which is being deleted
+        */
+       del_table = (unsigned int *)MEM_callocN( 
+                       mesh->totvert*sizeof( unsigned int ), "vert_table" );
+
+       /*
+        * Borrow a trick from editmesh code: for each edge to be deleted, mark
+        * its vertices as well.  Then go through face list and look for two
+        * consecutive marked vertices.
+        */
+
+       /* mark each edge that's to be deleted */
+       srcedge = mesh->medge;
+       for( i = len; i--; ) {
+               unsigned int idx = edge_table[i];
+               del_table[srcedge[idx].v1] = UINT_MAX;
+               del_table[srcedge[idx].v2] = UINT_MAX;
+               srcedge[idx].v1 = UINT_MAX;
+       }
+
+       /*
+        * second table is used for vertices which become orphaned (belong to no
+        * edges) and need to be deleted; it's also the normal lookup table for
+        * old->new vertex indices
+        */
+
+       vert_table = (unsigned int *)MEM_mallocN( 
+                       mesh->totvert*sizeof( unsigned int ), "vert_table" );
+
+       /* assume all edges will be deleted (fills with UINT_MAX) */
+       memset( vert_table, UCHAR_MAX, mesh->totvert*sizeof( unsigned int ) );
+
+       /* unmark vertices of each "good" edge; count each "bad" edge */
+       edge_count = 0;
+       for( i = mesh->totedge; i--; ++srcedge )
+               if( srcedge->v1 != UINT_MAX )
+                       vert_table[srcedge->v1] = vert_table[srcedge->v2] = 0;
+               else
+                       ++edge_count;
+
+       /*
+        * find faces which no longer have all edges
+        */
+
+       face_count = 0;
+       srcface = mesh->mface;
+       for( i = 0; i < mesh->totface; ++i, ++srcface ) {
+               int len = srcface->v4 ? 4 : 3;
+               unsigned int id[4];
+               int del;
+
+               id[0] = del_table[srcface->v1];
+               id[1] = del_table[srcface->v2];
+               id[2] = del_table[srcface->v3];
+               id[3] = del_table[srcface->v4];
+
+               del = ( id[0] == UINT_MAX && id[1] == UINT_MAX ) ||
+                       ( id[1] == UINT_MAX && id[2] == UINT_MAX );
+               if( !del ) {
+                       if( len == 3 )
+                               del = ( id[2] == UINT_MAX && id[0] == UINT_MAX );
+                       else
+                               del = ( id[2] == UINT_MAX && id[3] == UINT_MAX ) ||
+                                       ( id[3] == UINT_MAX && id[0] == UINT_MAX );
+               }
+               if( del ) {
+                       srcface->v1 = UINT_MAX;
+                       ++face_count;
+               } 
+       }
+
+       /* fix the vertex lookup table, if any verts to delete, do so now */
+       vert_count = make_vertex_table( vert_table, mesh->totvert );
+       if( vert_count )
+               delete_verts( mesh, vert_table, vert_count );
+
+       /* delete faces which have a deleted edge */
+       delete_faces( mesh, vert_table, face_count );
+
+       /* now delete the edges themselves */
+       delete_edges( mesh, vert_table, edge_count );
+
+       /* clean up and return */
+       MEM_freeN( del_table );
+       MEM_freeN( vert_table );
+       MEM_freeN( edge_table );
+       Py_DECREF( args );
+       mesh_update ( mesh );
+       return EXPP_incr_ret( Py_None );
+}
+
 static struct PyMethodDef BPy_MEdgeSeq_methods[] = {
        {"extend", (PyCFunction)MEdgeSeq_extend, METH_VARARGS,
                "add edges to mesh"},
+       {"delete", (PyCFunction)MEdgeSeq_delete, METH_VARARGS,
+               "delete edges from mesh"},
        {NULL, NULL, 0, NULL}
 };
 
@@ -2297,7 +2892,7 @@ static int MFace_setImage( BPy_MFace *self, PyObject *value )
 }
 
 /*
- * get face's texture flags
+ * get face's texture flag
  */
 
 static PyObject *MFace_getFlag( BPy_MFace *self )
@@ -2318,7 +2913,7 @@ static PyObject *MFace_getFlag( BPy_MFace *self )
 }
 
 /*
- * set face's texture flags
+ * set face's texture flag
  */
 
 static int MFace_setFlag( BPy_MFace *self, PyObject *value )
@@ -2949,7 +3544,7 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
        /* verify the param list and get a total count of number of edges */
        new_face_count = 0;
        for( i = 0; i < len; ++i ) {
-               tmp = PySequence_Fast_GET_ITEM( args, i );
+               tmp = PySequence_GetItem( args, i );
 
                /* not a tuple of MVerts... error */
                if( !PyTuple_Check( tmp ) ||
@@ -2983,12 +3578,20 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
                MFace tmpface;
                unsigned int vert[4]={0,0,0,0};
                unsigned char order[4]={0,1,2,3};
-               tmp = PySequence_Fast_GET_ITEM( args, i );
+               tmp = PySequence_GetItem( args, i );
                nverts = PyTuple_Size( tmp );
 
                if( nverts == 2 )       /* again, ignore 2-vert tuples */
                        break;
 
+               /* get copies of vertices */
+#if 0
+               for( j = 0; j < nverts; ++j ) {
+                       BPy_MVert *e = (BPy_MVert *)PyTuple_GET_ITEM( tmp, j );
+                       vert[j] = e->index;
+               }
+#endif
+
                /*
                 * go through some contortions to guarantee the third and fourth
                 * vertices are not index 0
@@ -3004,7 +3607,9 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
                        e = (BPy_MVert *)PyTuple_GET_ITEM( tmp, 3 );
                        tmpface.v4 = e->index;
                }
-               eeek_fix( &tmpface, nverts==4 );
+               Py_DECREF( tmp );
+
+               eeek_fix( &tmpface, NULL, nverts==4 );
                vert[0] = tmpface.v1;
                vert[1] = tmpface.v2;
                vert[2] = tmpface.v3;
@@ -3150,6 +3755,7 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
                                tmpface->v2 = tmppair->v[index[1]];
                                tmpface->v3 = tmppair->v[index[2]];
                                tmpface->v4 = tmppair->v[index[3]];
+
                                tmpface->flag = 0;
                                mesh->totface++;
                                ++tmpface;
@@ -3166,9 +3772,218 @@ static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args )
        return EXPP_incr_ret( Py_None );
 }
 
+struct fourEdges
+{
+       FaceEdges *v[4];
+};
+
+static PyObject *MFaceSeq_delete( BPy_MFaceSeq * self, PyObject *args )
+{
+       unsigned int *face_table;
+       int i, len;
+       Mesh *mesh = self->mesh;
+       MFace *tmpface;
+       int face_count;
+       int edge_also = 0;
+
+       /* check for valid inputs */
+
+       if( PySequence_Size( args ) != 2 ||
+                       !PyArg_ParseTuple( args, "iO", &edge_also, &args ) )
+               return EXPP_ReturnPyObjError( PyExc_TypeError,
+                               "expected and int and a sequence of ints or MFaces" );
+   
+       /* see how many args we need to parse */
+       len = PySequence_Size( args );
+       if( len < 1 ) {
+               Py_DECREF( args );
+               return EXPP_ReturnPyObjError( PyExc_TypeError,
+                               "sequence must contain at least one int or MFace" );
+       }
+
+       face_table = (unsigned int *)MEM_callocN( len*sizeof( unsigned int ),
+                       "face_table" );
+
+       /* get the indices of faces to be removed */
+       for( i = len; i--; ) {
+               PyObject *tmp = PySequence_GetItem( args, i );
+               if( BPy_MEdge_Check( tmp ) )
+                       face_table[i] = ((BPy_MEdge *)tmp)->index;
+               else if( PyInt_CheckExact( tmp ) )
+                       face_table[i] = PyInt_AsLong ( tmp );
+               else {
+                       MEM_freeN( face_table );
+                       Py_DECREF( tmp );
+                       Py_DECREF( args );
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected a sequence of ints or MFaces" );
+               }
+               Py_DECREF( tmp );
+
+               /* if index out-of-range, throw exception */
+               if( face_table[i] >= (unsigned int)mesh->totface ) {
+                       MEM_freeN( face_table );
+                       Py_DECREF( args );
+                       return EXPP_ReturnPyObjError( PyExc_ValueError,
+                                       "array index out of range" );
+               }
+       }
+
+       if( edge_also ) {
+       /*
+        * long version
+        *
+        * (1) build sorted table of all edges
+        * (2) construct face->edge lookup table for all faces
+        *         face->e1 = mesh->medge[i]
+        * (3) (delete sorted table)
+        * (4) mark all edges as live
+        * (5) mark all edges for deleted faces as dead
+        * (6) mark all edges for remaining faces as live
+        * (7) delete all dead edges
+        * (8) (delete face lookup table)
+        *
+        */
+
+               FaceEdges *edge_table, *tmp_et;
+               MEdge *tmpedge;
+               FaceEdges **face_edges;
+               FaceEdges **tmp_fe;
+               struct fourEdges *fface;
+               int edge_count;
+
+               edge_table = MEM_mallocN( mesh->totedge*sizeof( FaceEdges ),
+                       "edge_table" );
+
+               tmpedge = mesh->medge;
+               tmp_et = edge_table;
+
+               for( i = 0; i < mesh->totedge; ++i ) {
+                       if( tmpedge->v1 < tmpedge->v2 ) { 
+                               tmp_et->v[0] = tmpedge->v1;
+                               tmp_et->v[1] = tmpedge->v2;
+                       } else {
+                               tmp_et->v[0] = tmpedge->v2;
+                               tmp_et->v[1] = tmpedge->v1;
+                       }
+                       tmp_et->index = i;
+                       tmp_et->sel = 1;                /* select each edge */
+                       ++tmpedge; 
+                       ++tmp_et;
+               }
+
+               /* sort the edge pairs */
+               qsort( edge_table, mesh->totedge, sizeof(FaceEdges), faceedge_comp );
+
+               /* build face translation table, lookup edges */
+               face_edges = MEM_callocN( 4*sizeof(FaceEdges*)*mesh->totface,
+                       "face_edges" ); 
+
+               tmp_fe = face_edges;
+               tmpface = mesh->mface;
+               for( i = mesh->totface; i--; ++tmpface ) {
+                       FaceEdges *ptrs[4];
+                       unsigned int verts[4];
+                       int j,k;
+                       FaceEdges target;
+                       int len=tmpface->v4 ? 4 : 3;
+
+                       ptrs[3] = NULL;
+                       verts[0] = tmpface->v1;
+                       verts[1] = tmpface->v2;
+                       verts[2] = tmpface->v3;
+                       if(len == 4)
+                               verts[3] = tmpface->v4;
+                       for( j = 0; j < len; ++j ) {
+                               k = (j+1) % len;
+                               if( verts[j] < verts[k] ) { 
+                                       target.v[0] = verts[j];
+                                       target.v[1] = verts[k];
+                               } else {
+                                       target.v[0] = verts[k];
+                                       target.v[1] = verts[j];
+                               }
+                               ptrs[j] = bsearch( &target, edge_table, mesh->totedge,
+                                                       sizeof(FaceEdges), faceedge_comp );
+                       }
+                       for( j = 0; j < 4; ++j, ++tmp_fe )
+                               *tmp_fe = ptrs[j];
+               }
+
+               /* for each face, deselect each edge */
+               tmpface = mesh->mface;
+               face_count = 0;
+               for( i = len; i--; ) {
+                       if( tmpface[face_table[i]].v1 != UINT_MAX ) {
+                               fface = (void *)face_edges;
+                               fface += face_table[i];
+                               fface->v[0]->sel = 0;
+                               fface->v[1]->sel = 0;
+                               fface->v[2]->sel = 0;
+                               if( fface->v[3] )
+                                       fface->v[3]->sel = 0;
+                               tmpface[face_table[i]].v1 = UINT_MAX;
+                               ++face_count;
+                       }
+               }
+
+               /* for each face, deselect each edge */
+               tmpface = mesh->mface;
+               fface = (struct fourEdges *)face_edges;
+               for( i = mesh->totface; i--; ++tmpface, ++fface ) {
+                       if( tmpface->v1 != UINT_MAX ) {
+                               FaceEdges (*face)[4];
+                               face = (void *)face_edges;
+                               face += face_table[i];
+                               fface->v[0]->sel = 1;
+                               fface->v[1]->sel = 1;
+                               fface->v[2]->sel = 1;
+                               if( fface->v[3] )
+                                       fface->v[3]->sel = 1;
+                       }
+               }
+
+               /* now mark the selected edges for deletion */
+
+               edge_count = 0;
+               for( i = 0; i < mesh->totedge; ++i ) {
+                       if( !edge_table[i].sel ) {
+                               mesh->medge[edge_table[i].index].v1 = UINT_MAX;
+                               ++edge_count;
+                       }
+               }
+
+               if( edge_count )
+                       delete_edges( mesh, NULL, edge_count );
+
+               MEM_freeN( face_edges );
+               MEM_freeN( edge_table );
+       } else {
+       /* mark faces to delete */
+               tmpface = mesh->mface;
+               face_count = 0;
+               for( i = len; i--; )
+                       if( tmpface[face_table[i]].v1 != UINT_MAX ) {
+                               tmpface[face_table[i]].v1 = UINT_MAX;
+                               ++face_count;
+                       }
+       }
+
+       /* delete faces which have a deleted edge */
+       delete_faces( mesh, NULL, face_count );
+
+       /* clean up and return */
+       MEM_freeN( face_table );
+       Py_DECREF( args );
+       mesh_update ( mesh );
+       return EXPP_incr_ret( Py_None );
+}
+
 static struct PyMethodDef BPy_MFaceSeq_methods[] = {
        {"extend", (PyCFunction)MFaceSeq_extend, METH_VARARGS,
-               "add faces and edges to mesh"},
+               "add faces to mesh"},
+       {"delete", (PyCFunction)MFaceSeq_delete, METH_VARARGS,
+               "delete faces to mesh"},
        {NULL, NULL, 0, NULL}
 };
 
@@ -3316,15 +4131,350 @@ static PyObject *Mesh_Update( BPy_Mesh * self )
        return EXPP_incr_ret( Py_None );
 }
 
-// #define MESH_TOOLS
+/*
+ * search for a single edge in mesh's edge list
+ */
+
+static PyObject *Mesh_findEdge( BPy_Mesh * self, PyObject *args )
+{
+       int i;
+       unsigned int v1, v2;
+       PyObject *tmp;
+       MEdge *edge = self->mesh->medge;
+
+       if( EXPP_check_sequence_consistency( args, &MVert_Type ) == 1 &&
+                       PySequence_Size( args ) == 2 ) {
+               tmp = PyTuple_GET_ITEM( args, 0 );
+               v1 = ((BPy_MVert *)tmp)->index;
+               tmp = PyTuple_GET_ITEM( args, 1 );
+               v2 = ((BPy_MVert *)tmp)->index;
+       } else if( PyArg_ParseTuple( args, "ii", &v1, &v2 ) ) {
+               if( (int)v1 >= self->mesh->totvert || (int)v2 >= self->mesh->totvert )
+                       return EXPP_ReturnPyObjError( PyExc_IndexError,
+                                       "index out of range" );
+       } else
+               return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                               "expected tuple of two ints or MVerts" );
+
+       for( i = 0; i < self->mesh->totedge; ++i ) {
+               if( ( edge->v1 == v1 && edge->v2 == v2 )
+                               || ( edge->v1 == v2 && edge->v2 == v1 ) ) {
+                       tmp = PyInt_FromLong( i );
+                       if( tmp )
+                               return tmp;
+                       return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                                       "PyInt_FromLong() failed" );
+               }
+               ++edge;
+       }
+       return EXPP_incr_ret( Py_None );
+}
+
+/*
+ * search for a group of edges in mesh's edge list
+ */
+
+static PyObject *Mesh_findEdges( PyObject * self, PyObject *args )
+{
+       int len;
+       int i;
+       SrchEdges *oldpair, *tmppair, target, *result;
+       PyObject *list, *tmp;
+       BPy_MVert *v1, *v2;
+       unsigned int index1, index2;
+       MEdge *tmpedge;
+       Mesh *mesh = ((BPy_Mesh *)self)->mesh;
+
+       /* if no edges, nothing to do */
+
+       if( !mesh->totedge )
+               return EXPP_ReturnPyObjError( PyExc_ValueError,
+                               "mesh has no edges" );
+
+       /* make sure we get a sequence of tuples of something */
+
+       tmp = PyTuple_GET_ITEM( args, 0 );
+       switch( PySequence_Size ( args ) ) {
+       case 1:         /* better be a list or a tuple */
+               if( !PySequence_Check ( tmp ) )
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected a sequence of tuple int or MVert pairs" );
+               args = tmp;
+               Py_INCREF( args );              /* so we can safely DECREF later */
+               break;
+       case 2:         /* take any two args and put into a tuple */
+               if( PyTuple_Check( tmp ) )
+                       Py_INCREF( args );      /* if first arg is a tuple, assume both are */
+               else {
+                       args = Py_BuildValue( "((OO))", tmp, PyTuple_GET_ITEM( args, 1 ) );
+                       if( !args )
+                               return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                                               "Py_BuildValue() failed" );
+               }
+               break;
+       default:        /* anything else is definitely wrong */
+               return EXPP_ReturnPyObjError( PyExc_TypeError,
+                               "expected a sequence of tuple pairs" );
+       }
+
+       len = PySequence_Size( args );
+       if( len == 0 ) {
+               Py_DECREF( args );
+               return EXPP_ReturnPyObjError( PyExc_ValueError,
+                               "expected at least one tuple" );
+       }
+
+       /* if a single edge, handle the simpler way */
+       if( len == 1 ) {
+               PyObject *result;
+               tmp = PySequence_GetItem( args, 0 );
+               result = Mesh_findEdge( (BPy_Mesh *)self, tmp );
+               Py_DECREF( tmp );
+               Py_DECREF( args );
+               return result;
+       }
+
+       /* build a list of all edges so we can search */
+       oldpair = (SrchEdges *)MEM_callocN( sizeof(SrchEdges)*mesh->totedge,
+                       "MEdgePairs" );
+
+       tmppair = oldpair;
+       tmpedge = mesh->medge;
+       for( i = 0; i < mesh->totedge; ++i ) {
+               if( tmpedge->v1 < tmpedge->v2 ) {
+                       tmppair->v[0] = tmpedge->v1;
+                       tmppair->v[1] = tmpedge->v2;
+               } else {
+                       tmppair->v[0] = tmpedge->v2;
+                       tmppair->v[1] = tmpedge->v1;
+               }
+               tmppair->index = i;
+               ++tmpedge;
+               ++tmppair;
+       }
+
+       /* sort the old edge pairs */
+       qsort( oldpair, mesh->totedge, sizeof(SrchEdges), medge_comp );
+
+       list = PyList_New( len );
+       if( !len )
+               return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                               "PyList_New() failed" );
+
+       /* scan the input list, find vert pairs, then search the edge list */
+
+       for( i = 0; i < len; ++i ) {
+               tmp = PySequence_GetItem( args, i );
+               if( !PyTuple_Check( tmp ) || PyTuple_Size( tmp ) != 2 ) {
+                       MEM_freeN( oldpair );
+                       Py_DECREF( tmp );
+                       Py_DECREF( args );
+                       Py_DECREF( list );
+                       return EXPP_ReturnPyObjError( PyExc_ValueError,
+                               "expected tuple pair" );
+               }
+
+               /* get objects, check that they are both MVerts of this mesh */
+               v1 = (BPy_MVert *)PyTuple_GET_ITEM( tmp, 0 );
+               v2 = (BPy_MVert *)PyTuple_GET_ITEM( tmp, 1 );
+               Py_DECREF ( tmp );
+               if( BPy_MVert_Check( v1 ) && BPy_MVert_Check( v2 ) ) {
+                       if( v1->data != (void *)mesh || v2->data != (void *)mesh ) {
+                               MEM_freeN( oldpair );
+                               Py_DECREF( args );
+                               Py_DECREF( list );
+                               return EXPP_ReturnPyObjError( PyExc_ValueError,
+                                       "one or both MVerts do not belong to this mesh" );
+                       }
+                       index1 = v1->index;
+                       index2 = v2->index;
+               } else if( PyInt_CheckExact( v1 ) && PyInt_CheckExact( v2 ) ) {
+                       index1 = PyInt_AsLong( (PyObject *)v1 );
+                       index2 = PyInt_AsLong( (PyObject *)v2 );
+                       if( (int)index1 >= mesh->totvert
+                                       || (int)index2 >= mesh->totvert ) {
+                               MEM_freeN( oldpair );
+                               Py_DECREF( args );
+                               Py_DECREF( list );
+                               return EXPP_ReturnPyObjError( PyExc_IndexError,
+                                               "index out of range" );
+                       }
+               } else {
+                       MEM_freeN( oldpair );
+                       Py_DECREF( args );
+                       Py_DECREF( list );
+                       return EXPP_ReturnPyObjError( PyExc_ValueError,
+                               "expected tuple to contain MVerts" );
+               }
+
+               /* sort verts into order */
+               if( index1 < index2 ) {
+                       target.v[0] = index1;
+                       target.v[1] = index2;
+               } else {
+                       target.v[0] = index2;
+                       target.v[1] = index1;
+               }
+
+               /* search edge list for a match; result is index or None */
+               result = bsearch( &target, oldpair, mesh->totedge,
+                               sizeof(SrchEdges), medge_comp );
+               if( result )
+                       tmp = PyInt_FromLong( result->index );
+               else
+                       tmp = EXPP_incr_ret( Py_None );
+               if( !tmp ) {
+                       MEM_freeN( oldpair );
+                       Py_DECREF( args );
+                       Py_DECREF( list );
+                       return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                               "PyInt_FromLong() failed" );
+               }
+               PyList_SET_ITEM( list, i, tmp );
+       }
+
+       MEM_freeN( oldpair );
+       Py_DECREF ( args );
+       return list;
+}
+
+/*
+ * replace mesh data with mesh data from another object
+ */
+
+static PyObject *Mesh_getFromObject( BPy_Mesh * self, PyObject * args )
+{
+       Object *ob;
+       char *name;
+       ID tmpid;
+       Mesh *tmpmesh;
+       Object *tmpobj = NULL;
+
+       if( !PyArg_ParseTuple( args, "s", &name ) )
+               return EXPP_ReturnPyObjError( PyExc_TypeError,
+                               "expected string argument" );
+
+       /* find the specified object */
+       ob = ( Object * ) GetIdFromList( &( G.main->object ), name );
+       if( !ob )
+               return EXPP_ReturnPyObjError( PyExc_AttributeError, name );
+
+       /* perform the mesh extraction based on type */
+       switch (ob->type) {
+       case OB_FONT:
+       case OB_CURVE:
+       case OB_SURF:
+               tmpobj = alloc_libblock( &( G.main->object ), ID_OB, "i_tmp" );
+               tmpobj->id.us = 1;
+               tmpobj->flag = 0;
+               tmpobj->type = ob->type;
+               tmpobj->data = copy_curve( (Curve *) ob->data );
+               makeDispListCurveTypes( tmpobj, 0 );
+               nurbs_to_mesh( tmpobj );
+               tmpmesh = tmpobj->data;
+               free_libblock_us( &G.main->object, tmpobj );
+               break;
+       case OB_MBALL:
+               ob = find_basis_mball( ob );
+               tmpmesh = add_mesh();
+               mball_to_mesh( &ob->disp, tmpmesh );
+               break;
+       case OB_MESH:
+               tmpmesh = copy_mesh( (Mesh *) ob->data );
+               tmpmesh->id.us = 0;
+               break;
+       default:
+               return EXPP_ReturnPyObjError( PyExc_AttributeError,
+                               "Object does not have geometry data" );
+       }
+
+       /* free mesh data in the original */
+       free_mesh( self->mesh );
+       /* save a copy of our ID, dup the temporary mesh, restore the ID */
+       tmpid = self->mesh->id;
+       memcpy( self->mesh, tmpmesh, sizeof( Mesh ) );
+       self->mesh->id= tmpid;
+       /* remove the temporary mesh */
+       BLI_remlink( &G.main->mesh, tmpmesh );
+       MEM_freeN( tmpmesh );
+
+       mesh_update( self->mesh );
+       return EXPP_incr_ret( Py_None );
+}
+
+/*
+ * apply a transform to the mesh's vertices
+ *
+ * WARNING: unlike NMesh, this method ALWAYS changes the original mesh
+ */
+
+static PyObject *Mesh_transform( BPy_Mesh *self, PyObject *args )
+{
+       Mesh *mesh = self->mesh;
+       MVert *mv;
+       PyObject *ob1 = NULL;
+       MatrixObject *mat;
+       int i, recalc_normals = 0;
+
+       if( !PyArg_ParseTuple( args, "O!|i", &matrix_Type, &ob1, &recalc_normals ) )
+               return ( EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected matrix and optionally an int as arguments" ) );
+
+       mat = ( MatrixObject * ) ob1;
+
+       if( mat->colSize != 4 || mat->rowSize != 4 )
+               return EXPP_ReturnPyObjError( PyExc_AttributeError,
+                               "matrix must be a 4x4 transformation matrix\n"
+                               "for example as returned by object.getMatrix()" );
+       
+       /* loop through all the verts and transform by the supplied matrix */
+       mv = mesh->mvert;
+       for( i = 0; i < mesh->totvert; i++, mv++ )
+               Mat4MulVecfl( (float(*)[4])*mat->matrix, mv->co );
+
+       if( recalc_normals ) {
+               /* loop through all the verts and transform normals by the inverse
+                * of the transpose of the supplied matrix */
+               float invmat[4][4];
+
+               /*
+                * we only need to invert a 3x3 submatrix, because the 4th component of
+                * affine vectors is 0, but Mat4Invert reports non invertible matrices
+                */
+
+               if (!Mat4Invert((float(*)[4])*invmat, (float(*)[4])*mat->matrix))
+                       return EXPP_ReturnPyObjError (PyExc_AttributeError,
+                               "given matrix is not invertible");
+
+               /*
+                * since normal is stored as shorts, convert to float 
+                */
+
+               mv = mesh->mvert;
+               for( i = 0; i < mesh->totvert; i++, mv++ ) {
+                       float vec[3];
+                       vec[0] = (float)mv->no[0] / 32767.0;
+                       vec[1] = (float)mv->no[1] / 32767.0;
+                       vec[2] = (float)mv->no[2] / 32767.0;
+                       Mat4MulVecfl( (float(*)[4])*invmat, vec );
+                       Normalise( vec );
+                       mv->no[0] = (short)(vec[0] * 32767.0);
+                       mv->no[1] = (short)(vec[1] * 32767.0);
+                       mv->no[2] = (short)(vec[2] * 32767.0);
+               }
+       }
+
+       return EXPP_incr_ret( Py_None );
+}
 
 #ifdef MESH_TOOLS
 
-static PyObject *Mesh_Tools( BPy_Mesh * self, int type )
+static PyObject *Mesh_Tools( BPy_Mesh * self, int type, void **args )
 {
+       Base *base;
+       int result;
        Object *object = NULL; 
-       Base *basact = BASACT;
-       Base *base = FIRSTBASE;
+       PyObject *attr = NULL;
 
        /* if already in edit mode, exit */
 
@@ -3352,15 +4502,38 @@ static PyObject *Mesh_Tools( BPy_Mesh * self, int type )
        G.obedit = object;
        enter_editmode( );
        switch( type ) {
-       case B_SUBDIV:
-               esubdivideflag(1, 0.0, 
-                               G.scene->toolsettings->editbutflag & B_BEAUTY,1,0);
+       case MESH_TOOL_TOSPHERE:
+               vertices_to_sphere();
                break;
-       case B_VERTEXSMOOTH:
+       case MESH_TOOL_VERTEXSMOOTH:
                vertexsmooth();
                break;
+       case MESH_TOOL_FLIPNORM:
+               /* would be simple to rewrite this to not use edit mesh */
+               /* see flipface() */
+               flip_editnormals();
+               break;
+       case MESH_TOOL_SUBDIV:
+               esubdivideflag( 1, 0.0, *((int *)args[0]), 1, 0 );
+               break;
+       case MESH_TOOL_REMDOUB:
+               result = removedoublesflag( 1, *((float *)args[0]) );
+
+       attr = PyInt_FromLong( result );
+               if( !attr )
+                       return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                                       "PyInt_FromLong() failed" );
+       case MESH_TOOL_FILL:
+               fill_mesh();
+               break;
+       case MESH_TOOL_RECALCNORM:
+               righthandfaces( *((int *)args[0]) );
+               break;
        }
        exit_editmode( 1 );
+       if( attr )
+               return attr;
+
        return EXPP_incr_ret( Py_None );
 }
 
@@ -3368,18 +4541,93 @@ static PyObject *Mesh_Tools( BPy_Mesh * self, int type )
  * "Subdivide" function
  */
 
-static PyObject *Mesh_Subdivide( BPy_Mesh * self )
+static PyObject *Mesh_subdivide( BPy_Mesh * self, PyObject * args )
 {
-       return Mesh_Tools( self, B_SUBDIV );
+       int beauty = 0;
+       void *params = &beauty;
+
+       if( !PyArg_ParseTuple( args, "|i", &beauty ) )
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected nothing or an int argument" );
+
+       return Mesh_Tools( self, MESH_TOOL_SUBDIV, &params );
 }
 
 /*
  * "Smooth" function
  */
 
-static PyObject *Mesh_Smooth( BPy_Mesh * self )
+static PyObject *Mesh_smooth( BPy_Mesh * self )
+{
+       return Mesh_Tools( self, MESH_TOOL_VERTEXSMOOTH, NULL );
+}
+
+/*
+ * "Remove doubles" function
+ */
+
+static PyObject *Mesh_removeDoubles( BPy_Mesh * self, PyObject *args )
+{
+       float limit;
+       void *params = &limit;
+
+       if( !PyArg_ParseTuple( args, "f", &limit ) )
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected float argument" );
+
+       limit = EXPP_ClampFloat( limit, 0.0f, 1.0f );
+
+       return Mesh_Tools( self, MESH_TOOL_REMDOUB, &params );
+}
+
+/*
+ * "recalc normals" function
+ */
+
+static PyObject *Mesh_recalcNormals( BPy_Mesh * self, PyObject *args )
+{
+       int direction = 0;
+       void *params = &direction;
+
+       if( !PyArg_ParseTuple( args, "|i", &direction ) )
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                       "expected nothing or an int in range [0,1]" );
+
+       if( direction < 0 || direction > 1 )
+                       return EXPP_ReturnPyObjError( PyExc_ValueError,
+                                       "expected int in range [0,1]" );
+
+       /* righthandfaces(1) = outward, righthandfaces(2) = inward */
+       ++direction;
+
+       return Mesh_Tools( self, MESH_TOOL_RECALCNORM, &params );
+}
+
+/*
+ * "Flip normals" function
+ */
+
+static PyObject *Mesh_flipNormals( BPy_Mesh * self )
 {
-       return Mesh_Tools( self, B_VERTEXSMOOTH );
+       return Mesh_Tools( self, MESH_TOOL_FLIPNORM, NULL );
+}
+
+/*
+ * "To sphere" function
+ */
+
+static PyObject *Mesh_toSphere( BPy_Mesh * self )
+{
+       return Mesh_Tools( self, MESH_TOOL_TOSPHERE, NULL );
+}
+
+/*
+ * "Fill" (scan fill) function
+ */
+
+static PyObject *Mesh_fill( BPy_Mesh * self )
+{
+       return Mesh_Tools( self, MESH_TOOL_FILL, NULL );
 }
 
 #endif
@@ -3389,13 +4637,29 @@ static struct PyMethodDef BPy_Mesh_methods[] = {
                "all recalculate vertex normals"},
        {"vertexShade", (PyCFunction)Mesh_vertexShade, METH_VARARGS,
                "color vertices based on the current lighting setup"},
+       {"findEdges", (PyCFunction)Mesh_findEdges, METH_VARARGS,
+               "find indices of an multiple edges in the mesh"},
+       {"getFromObject", (PyCFunction)Mesh_getFromObject, METH_VARARGS,
+               "Get a mesh by name"},
        {"update", (PyCFunction)Mesh_Update, METH_NOARGS,
                "Update display lists after changes to mesh"},
+       {"transform", (PyCFunction)Mesh_transform, METH_VARARGS,
+               "Applies a transformation matrix to mesh's vertices"},
 #ifdef MESH_TOOLS
-       {"subdivide", (PyCFunction)Mesh_Subdivide, METH_NOARGS,
-               "Subdivide selected edges in a mesh (experimental)"},
-       {"smooth", (PyCFunction)Mesh_Smooth, METH_NOARGS,
+       {"smooth", (PyCFunction)Mesh_smooth, METH_NOARGS,
                "Flattens angle of selected faces (experimental)"},
+       {"flipNormals", (PyCFunction)Mesh_flipNormals, METH_NOARGS,
+               "Toggles the direction of selected face's normals (experimental)"},
+       {"toSphere", (PyCFunction)Mesh_toSphere, METH_NOARGS,
+               "Moves selected vertices outward in a spherical shape (experimental)"},
+       {"fill", (PyCFunction)Mesh_fill, METH_NOARGS,
+               "Scan fill a closed edge loop (experimental)"},
+       {"subdivide", (PyCFunction)Mesh_subdivide, METH_VARARGS,
+               "Subdivide selected edges in a mesh (experimental)"},
+       {"remDoubles", (PyCFunction)Mesh_removeDoubles, METH_VARARGS,
+               "Removes duplicates from selected vertices (experimental)"},
+       {"recalcNormals", (PyCFunction)Mesh_recalcNormals, METH_VARARGS,
+               "Recalculates inside or outside normals (experimental)"},
 #endif
        {NULL, NULL, 0, NULL}
 };
@@ -3413,6 +4677,56 @@ static PyObject *Mesh_getVerts( BPy_Mesh * self )
        return (PyObject *)seq;
 }
 
+static int Mesh_setVerts( BPy_Mesh * self, PyObject * args )
+{
+       static int disabled = 0;
+       MVert *dst;
+       MVert *src;
+       char err[256];
+       int i;
+       
+       if( disabled ) {
+               sprintf( err, "attribute 'verts' of '%s' objects is not writable",
+                               self->ob_type->tp_name );
+               return EXPP_ReturnIntError( PyExc_TypeError, err );
+       }
+
+       if( PyList_Check( args ) ) {
+               if( EXPP_check_sequence_consistency( args, &MVert_Type ) != 1 &&
+                         EXPP_check_sequence_consistency( args, &PVert_Type ) != 1 )
+                       return EXPP_ReturnIntError( PyExc_TypeError, 
+                                       "expected a list of MVerts" );
+
+               if( PyList_Size( args ) != self->mesh->totvert )
+                       return EXPP_ReturnIntError( PyExc_TypeError, 
+                                       "list must have the same number of vertices as the mesh" );
+
+               dst = self->mesh->mvert;
+               for( i = 0; i < PyList_Size( args ); ++i ) {
+                       BPy_MVert *v = (BPy_MVert *)PyList_GET_ITEM( args, i );
+
+                       if( BPy_MVert_Check( v ) )
+                               src = &((Mesh *)v->data)->mvert[v->index];
+                       else
+                               src = (MVert *)v->data;
+
+                       memcpy( dst, src, sizeof(MVert) );
+                       ++dst;
+               }
+       } else if( args->ob_type == &MVertSeq_Type ) {
+               Mesh *mesh = ( (BPy_MVertSeq *) args)->mesh;
+
+               if( mesh->totvert != self->mesh->totvert )
+                       return EXPP_ReturnIntError( PyExc_TypeError, 
+                                       "vertex sequences must have the same number of vertices" );
+
+               memcpy( self->mesh->mvert, mesh->mvert, mesh->totvert*sizeof(MVert) );
+       } else
+               return EXPP_ReturnIntError( PyExc_TypeError, 
+                               "expected a list or sequence of MVerts" );
+       return 0;
+}
+
 static PyObject *Mesh_getEdges( BPy_Mesh * self )
 {
        BPy_MEdgeSeq *seq = PyObject_NEW( BPy_MEdgeSeq, &MEdgeSeq_Type);
@@ -3715,7 +5029,7 @@ static PyObject *Mesh_repr( BPy_Mesh * self )
 /*****************************************************************************/
 static PyGetSetDef BPy_Mesh_getseters[] = {
        {"verts",
-        (getter)Mesh_getVerts, (setter)NULL,
+        (getter)Mesh_getVerts, (setter)Mesh_setVerts,
         "The mesh's vertices (MVert)",
         NULL},
        {"edges",
@@ -3921,6 +5235,7 @@ static PyObject *M_Mesh_Get( PyObject * self, PyObject * args )
 static PyObject *M_Mesh_New( PyObject * self, PyObject * args )
 {
        char *name = "Mesh";
+       PyObject *ret = NULL;
        Mesh *mesh;
        BPy_Mesh *obj;
        char buf[21];
@@ -3969,7 +5284,7 @@ static PyObject *M_Mesh_MVert( PyObject * self, PyObject * args )
         */
 
        if( PyTuple_Size ( args ) == 1 ) {
-               PyObject *tmp = PySequence_Fast_GET_ITEM( args, 0 );
+               PyObject *tmp = PyTuple_GET_ITEM( args, 0 );
                if( !VectorObject_Check( tmp ) || ((VectorObject *)tmp)->size != 3 )
                        return EXPP_ReturnPyObjError( PyExc_ValueError,
                                "expected three floats or vector of size 3" );
index 714819036dd7579f009d942f3e7eb120c4ad743d..31810a57d44bddcfbbf04086ca83d1b25a4aebb3 100644 (file)
@@ -52,19 +52,19 @@ extern PyTypeObject Mesh_Type;
 extern PyTypeObject MVert_Type;
 extern PyTypeObject PVert_Type;
 extern PyTypeObject MVertSeq_Type;
+extern PyTypeObject MEdge_Type;
 extern PyTypeObject MFace_Type;
 extern PyTypeObject MCol_Type;
-extern PyTypeObject MEdge_Type;
 
 struct BPy_Object;
 
 /* Type checking for EXPP PyTypes */
 #define BPy_Mesh_Check(v)       ((v)->ob_type == &Mesh_Type)
 #define BPy_MFace_Check(v)      ((v)->ob_type == &MFace_Type)
+#define BPy_MEdge_Check(v)      ((v)->ob_type == &MEdge_Type)
 #define BPy_MVert_Check(v)      ((v)->ob_type == &MVert_Type)
 #define BPy_PVert_Check(v)      ((v)->ob_type == &PVert_Type)
 #define BPy_MCol_Check(v)       ((v)->ob_type == &MCol_Type)
-#define BPy_MEdge_Check(v)      ((v)->ob_type == &MEdge_Type)
 
 /* Typedefs for the new types */
 
@@ -87,7 +87,7 @@ typedef struct {
 
 typedef struct {
        PyObject_VAR_HEAD       /* required python macro   */
-       Mesh * mesh;
+       Mesh *mesh;                     /* points to a Mesh */
        int index;
        int iter;
 } BPy_MEdge;                   /* a Mesh edge */
index 1a770844e68551d78fe8a48b0e351e3db741a9e3..d41dd9b2c44357e016868d9c01c2c2adcce41897 100644 (file)
@@ -71,6 +71,7 @@ void types_InitAll( void )
        Mesh_Type.ob_type = &PyType_Type;
        MFace_Type.ob_type = &PyType_Type;
        MVert_Type.ob_type = &PyType_Type;
+       PVert_Type.ob_type = &PyType_Type;
        MEdge_Type.ob_type = &PyType_Type;
        MCol_Type.ob_type = &PyType_Type;
        Mesh_Type.ob_type = &PyType_Type;
@@ -130,6 +131,8 @@ PyObject *Types_Init( void )
                              ( PyObject * ) &MEdge_Type );
        PyDict_SetItemString( dict, "MVertType",
                              ( PyObject * ) &MVert_Type );
+       PyDict_SetItemString( dict, "PVertType",
+                             ( PyObject * ) &PVert_Type );
        PyDict_SetItemString( dict, "MColType", ( PyObject * ) &MCol_Type );
 
        PyDict_SetItemString( dict, "ArmatureType",
index d4ac545dbb243e7bbd16f17eca72fbf343e69c42..ea8a80a1a9899b3b0fef44216e15ea6f352e5714 100644 (file)
@@ -45,7 +45,8 @@ extern PyTypeObject Image_Type, Ipo_Type, IpoCurve_Type;
 extern PyTypeObject Lamp_Type, Lattice_Type;
 extern PyTypeObject Material_Type, Metaball_Type, MTex_Type;
 extern PyTypeObject NMFace_Type, NMVert_Type, NMCol_Type, NMesh_Type;
-extern PyTypeObject MFace_Type, MVert_Type, MEdge_Type, MCol_Type, Mesh_Type;
+extern PyTypeObject MFace_Type, MVert_Type, PVert_Type, MEdge_Type, MCol_Type,
+          Mesh_Type;
 extern PyTypeObject Object_Type;
 extern PyTypeObject Particle_Type;
 extern PyTypeObject Scene_Type, RenderData_Type;
index 0c0ab3b547a9feeb4e7029bb820a4d9845802e9a..1a8518c9a40bacbdd25bdfaa767a5e11a6f70e61 100644 (file)
@@ -3,6 +3,24 @@
 """
 The Blender.Mesh submodule.
 
+B{New}:
+ - L{transform()<Mesh.transform>}: apply transform matrix to mesh vertices
+ - L{getFromObject()<Mesh.getFromObject>}: get mesh data from other
+   geometry objects
+ - L{findEdges()<Mesh.findEdges>}: determine if and where edges exist in the
+   mesh's edge list
+ - delete methods for L{verts<MVertSeq.delete>}, L{edges<MEdgeSeq.delete>}
+   and L{faces<MFaceSeq.delete>}
+ - new experimental mesh tools:
+   L{fill()<Mesh.Mesh.fill>},
+   L{flipNormals()<Mesh.Mesh.flipNormals>},
+   L{recalcNormals()<Mesh.Mesh.recalcNormals>},
+   L{remDoubles()<Mesh.Mesh.remDoubles>},
+   L{smooth()<Mesh.Mesh.smooth>},
+   L{subdivide()<Mesh.Mesh.subdivide>} and
+   L{toSphere()<Mesh.Mesh.toSphere>}
+ - and if you're never used Mesh before, everything!
+
 Mesh Data
 =========
 
@@ -82,14 +100,15 @@ class MVert:
     if vertex coordinates are changed, it may be necessary to use
     L{Mesh.calcNormals()} to update the vertex normals.
   @type no: vector
-  @ivar uvco: The vertex texture "sticky" coordinates (x, y), if present. 
+  @ivar uvco: (MVerts only). The vertex texture "sticky" coordinates (x, y),
+    if present. 
     Use L{Mesh.vertexUV} to test for presence before trying to access;
     otherwise an exception will may be thrown.
     (Sticky coordinates can be set when the object is in the Edit mode;
     from the Editing Panel (F9), look under the "Mesh" properties for the 
     "Sticky" button).  
   @type uvco: vector
-  @ivar index: The vertex's index within the mesh.  Read-only.
+  @ivar index: (MVerts only). The vertex's index within the mesh.  Read-only.
   @type index: int
   @ivar sel: The vertex's selection state (selected=1).
    B{Note}: a Mesh will return the selection state of the mesh when EditMode 
@@ -107,15 +126,29 @@ class MVert:
 
   def __init__(coord):
     """
-    Create a new MVert object.
+    Create a new PVert object.  
+
+    @note: PVert-type objects are designed to be used for creating and
+    modifying a mesh's vertex list, but since they do not "wrap" any Blender
+    data there are some differences.  The B{index} and B{uvco} attributes 
+    are not defined for PVerts, and the B{no} attribute contains valid
+    data only if the PVert was created from an MVert (using a slice
+    operation on the mesh's vertex list.)  PVerts also cannot be used as an
+    argument to any method which expects data wrapping a Blender mesh, such
+    as L{MVertSeq.delete()}.
 
     Example::
       v = Blender.Mesh.MVert(1,0,0)
       v = Blender.Mesh.MVert(Blender.Mathutils.Vector([1,0,0]))
+
+      m = Blender.Mesh.Get('Mesh')
+      vlist = m.verts[:]   # slice operation also returns PVerts
+
     @type coord: three floats or a Vector object
     @param coord: the coordinate values for the new vertex
-    @rtype: MVert
-    @return: a new MVert object
+    @rtype: PVert
+    @return: a new PVert object
+
     """
 
 class MVertSeq:
@@ -128,7 +161,7 @@ class MVertSeq:
     a MVert object which "wraps" the actual vertex in the mesh; changing any
     of the vertex's attributes will immediately change the data in the mesh.
     When a slice of the vertex list is accessed, however, the operator[]
-    returns a list of MVert objects which are copies of the mesh's vertex
+    returns a list of PVert objects which are copies of the mesh's vertex
     data.  Changes to these objects have no effect on the mesh; they must be
     assigned back to the mesh's vertex list.
 
@@ -173,6 +206,20 @@ class MVertSeq:
        - a sequence (list or tuple) of either of the above.
     """
 
+  def delete(verts):
+    """
+    Deletes one or more vertices from the mesh.  Any edge or face which
+    uses the specified vertices are also deleted.
+
+    @type verts: multiple ints or MVerts
+    @param verts: can be
+       - a single MVert belonging to the mesh (B{note:} will not work with
+         PVerts)
+       - a single integer, specifying an index into the mesh's vertex list
+       - a sequence (list or tuple) containing two or more of either of
+         the above.
+    """
+
 class MEdge:
   """
   The MEdge object
@@ -228,6 +275,20 @@ class MEdgeSeq:
     of tuples each containing two to four MVerts.
     """
 
+  def delete(edges):
+    """
+    Deletes one or more edges from the mesh.  In addition, also delete:
+      - any faces which uses the specified edge(s)
+      - any "orphan" vertices (belonging only to specified edge(s))
+
+    @type edges: multiple ints or MEdges
+    @param edges: can be
+       - a single MEdge belonging to the mesh
+       - a single integer, specifying an index into the mesh's edge list
+       - a sequence (list or tuple) containing two or more of either of
+         the above.
+    """
+
 class MFace:
   """
   The MFace object
@@ -383,6 +444,20 @@ class MFaceSeq:
     of tuples each containing two to four MVerts.
     """
 
+  def delete(deledges, faces):
+    """
+    Deletes one or more faces (and optionally the edges associated with
+    the face(s)) from the mesh.  
+
+    @type deledges: int
+    @param deledges: controls whether just the faces (deledges=0)
+    or the faces and edges (deledges=1) are deleted.  These correspond to the
+    "Only Faces" and "Edges & Faces" options in the Edit Mode pop-up menu
+    @type faces: multiple ints or MFaces
+    @param faces: a sequence (list or tuple) containing one or more of:
+       - an MEdge belonging to the mesh
+       - a integer, specifying an index into the mesh's face list
+    """
 
 class Mesh:
   """
@@ -433,11 +508,63 @@ class Mesh:
   @type activeFace: int
   """
 
+  def getFromObject(name):
+    """
+    Replace the mesh's existing data with the raw mesh data from a Blender
+    Object.  This method support all the geometry based objects (mesh, text,
+    curve, surface, and meta).
+    @note: The mesh coordinates are in i{local space}, not the world space of
+    its object.  For world space vertex coordinates, each vertex location must
+    be multiplied by the object's 4x4 transform matrix (see L{transform}).
+    @type name: string
+    @param name: name of the Blender object which contains the geometry data.
+    """
+
   def calcNormals():
     """
     Recalculates the vertex normals using face data.
     """
 
+  def transform(matrix, recalc_normals = False):
+    """
+    Transforms the mesh by the specified 4x4 matrix (such as returned by
+    L{Object.Object.getMatrix}).  The matrix should be invertible.
+    Ideal usage for this is exporting to an external file where
+    global vertex locations are required for each object.
+    Sometimes external renderers or file formats do not use vertex normals.
+    In this case, you can skip transforming the vertex normals by leaving
+    the optional parameter recalc_normals as False or 0 (the default value).
+
+    Example::
+     # This script outputs deformed meshes worldspace vertex locations
+     # for a selected object without changing the object
+     import Blender
+     from Blender import Mesh, Object
+     
+     ob = Object.GetSelected()[0] # Get the first selected object
+     me = Mesh.New()              # Create a new mesh
+     me.getFromObject(ob.name)    # Get the object's mesh data
+     verts = me.verts[:]          # Save a copy of the vertices
+     me.transform(ob.matrix)      # Convert verts to world space
+     for v in me.verts:
+       print 'worldspace vert', v.co
+     me.verts = verts             # Restore the original verts
+    
+    @type matrix: Py_Matrix
+    @param matrix: 4x4 Matrix which can contain location, scale and rotation. 
+    @type recalc_normals: int
+    @param recalc_normals: if True or 1, also transform vertex normals.
+    @warn: unlike L{NMesh.transform()<NMesh.NMesh.transform>}, this method
+    I{will immediately modify the mesh data} when it is used.  If you
+    transform the mesh using the object's matrix to get the vertices'
+    world positions, the result will be a "double transform".  To avoid
+    this you either need to set the object's matrix to the identity
+    matrix, perform the inverse transform after outputting the transformed
+    vertices, or make a copy of the vertices prior to using this method
+    and restore them after outputting the transformed vertices (as shown
+    in the example).
+    """
+
   def vertexShade(object):
     """
     Colors vertices based on the current lighting setup, like when there
@@ -456,3 +583,69 @@ class Mesh:
     releases.
     """
 
+  def findEdges(edges):
+    """
+    Quickly search for the location of an edge.  
+    @type edges: tuple(s) of ints or MVerts
+    @param edges: can be tuples of MVerts or integer indexes (B{note:} will
+       not work with PVerts) or a sequence (list or tuple) containing two or
+       tuples.
+    @rtype: int, None or list
+    @return: if an edge is found, its index is returned; otherwise None is
+    returned.  If a sequence of edges is passed, a list is returned.
+    """
+
+  def smooth():
+    """
+    Flattens angle of selected faces. Experimental mesh tool.
+    An exception is thrown if called while in EditMode.
+    """
+
+  def flipNormals():
+    """
+    Toggles the direction of selected face's normals. Experimental mesh tool.
+    An exception is thrown if called while in EditMode.
+    """
+
+  def toSphere():
+    """
+    Moves selected vertices outward in a spherical shape. Experimental mesh
+    tool.
+    An exception is thrown if called while in EditMode.
+    """
+
+  def subdivide(beauty=0):
+    """
+    Subdivide selected edges in a mesh. Experimental mesh tool.
+    An exception is thrown if called while in EditMode.
+    @type beauty: int
+    @param beauty: specifies whether a "beauty" subdivide should be
+    enabled (disabled is default).  Value must be in the range [0,1].
+    """
+
+  def remDoubles(limit):
+    """
+    Removes duplicates from selected vertices. Experimental mesh tool.
+    An exception is thrown if called while in EditMode.
+    @type limit: float
+    @param limit: specifies the maximum distance considered for vertices
+    to be "doubles".  Value is clamped to the range [0.0,1.0].
+    @rtype: int
+    @return: the number of vertices deleted
+    """
+
+  def fill():
+    """
+    Scan fill a closed selected edge loop. Experimental mesh tool.
+    An exception is thrown if called while in EditMode.
+    """
+
+  def recalcNormals(direction=0):
+    """
+    Recalculates inside or outside normals for selected faces. Experimental
+    mesh tool.
+    An exception is thrown if called while in EditMode.
+    @type direction: int
+    @param direction: specifies outward (0) or inward (1) normals.  Outward
+    is the default.  Value must be in the range [0,1].
+    """
index 145a26b20abec84c8721a51fda12490edc701867..18e5352f1425b042136af0f6c454e7c0f786abf3 100644 (file)
@@ -36,7 +36,10 @@ Example::
 @var MFaceType: Blender MFace. A mesh face, with 
      three (a triangular face) or four (a quad face) vertices.
 @var MEdgeType: Blender MEdge. A mesh edge, with two vertices
-@var MVertType: Blender MVert. A mesh vertex.
+@var MVertType: Blender MVert. A mesh vertex which wraps a Blender mesh vertex
+     (typically an object returned from the mesh.verts sequence).
+@var PVertType: Blender MVert. A mesh vertex which does not wrap a Blender
+     mesh vertex (returned from L{Blender.Mesh.MVert()<Mesh.MVert.__init__>}).
 @var MColType: Blender MCol. A mesh rgba color.
 @var ArmatureType: Blender Armature. The "skeleton", for animating and deforming
 objects.