diff options
author | Ken Hughes <khughes@pacific.edu> | 2005-10-03 23:36:15 +0400 |
---|---|---|
committer | Ken Hughes <khughes@pacific.edu> | 2005-10-03 23:36:15 +0400 |
commit | f2af563f92b7c33d0ddf7b97bbd2c168cf21e5a6 (patch) | |
tree | 8e5864ba00d9cc9bebaedc3be4d50d0480f830b5 /source | |
parent | c7f4016349a93624542cf9179102ebcfe350ad19 (diff) |
Added new BPython thin mesh module
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/python/SConscript | 1 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Blender.c | 2 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Mesh.c | 3523 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Mesh.h | 123 | ||||
-rw-r--r-- | source/blender/python/api2_2x/doc/API_intro.py | 1 |
5 files changed, 3650 insertions, 0 deletions
diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript index f19751f0152..206e266bbb3 100644 --- a/source/blender/python/SConscript +++ b/source/blender/python/SConscript @@ -28,6 +28,7 @@ source_files = ['BPY_interface.c', 'api2_2x/MTex.c', 'api2_2x/Material.c', 'api2_2x/Mathutils.c', + 'api2_2x/Mesh.c', 'api2_2x/Metaball.c', 'api2_2x/NLA.c', 'api2_2x/Noise.c', diff --git a/source/blender/python/api2_2x/Blender.c b/source/blender/python/api2_2x/Blender.c index ed8721d511b..c4fe631dc6b 100644 --- a/source/blender/python/api2_2x/Blender.c +++ b/source/blender/python/api2_2x/Blender.c @@ -72,6 +72,7 @@ struct ID; /*keep me up here */ #include "Lamp.h" #include "Lattice.h" #include "Mathutils.h" +#include "Mesh.h" #include "Metaball.h" #include "NMesh.h" #include "Object.h" @@ -818,6 +819,7 @@ void M_Blender_Init(void) PyDict_SetItemString(dict, "Lattice", Lattice_Init()); PyDict_SetItemString(dict, "Library", Library_Init()); PyDict_SetItemString(dict, "Material", Material_Init()); + PyDict_SetItemString(dict, "Mesh", Mesh_Init()); PyDict_SetItemString(dict, "Metaball", Metaball_Init()); PyDict_SetItemString(dict, "Mathutils", Mathutils_Init()); PyDict_SetItemString(dict, "NMesh", NMesh_Init()); diff --git a/source/blender/python/api2_2x/Mesh.c b/source/blender/python/api2_2x/Mesh.c new file mode 100644 index 00000000000..15f46751de0 --- /dev/null +++ b/source/blender/python/api2_2x/Mesh.c @@ -0,0 +1,3523 @@ +/* + * $Id$ + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender, partially based on NMesh.c API. + * + * Contributor(s): Ken Hughes + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +#include "Mesh.h" /*This must come first*/ + +#include "MEM_guardedalloc.h" + +#include "DNA_key_types.h" +#include "DNA_armature_types.h" +#include "DNA_scene_types.h" +#include "DNA_oops_types.h" +#include "DNA_space_types.h" +#include "DNA_curve_types.h" + +#include "BDR_editface.h" /* make_tfaces */ +#include "BDR_vpaint.h" +#include "BDR_editobject.h" + +#include "BIF_editdeform.h" +#include "BIF_editkey.h" /* insert_meshkey */ +#include "BIF_editview.h" + +#include "BKE_deform.h" +#include "BKE_mesh.h" +#include "BKE_material.h" +#include "BKE_main.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_displist.h" +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_mball.h" +#include "BKE_utildefines.h" +#include "BKE_depsgraph.h" +#include "BSE_edit.h" /* for void countall(); */ + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "blendef.h" +#include "mydevice.h" +#include "Object.h" +#include "Key.h" +#include "Image.h" +#include "Material.h" +#include "Mathutils.h" +#include "constant.h" +#include "gen_utils.h" + +/* EXPP Mesh defines */ + +#define MESH_SMOOTHRESH 30 +#define MESH_SMOOTHRESH_MIN 1 +#define MESH_SMOOTHRESH_MAX 80 +#define MESH_SUBDIV 1 +#define MESH_SUBDIV_MIN 0 +#define MESH_SUBDIV_MAX 6 + +#define MESH_HASFACEUV 0 +#define MESH_HASMCOL 1 +#define MESH_HASVERTUV 2 + +/************************************************************************ + * + * internal utilities + * + ************************************************************************/ + +/* + * internal structures used for sorting edges and faces + */ + +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 { + unsigned int v[4]; /* indices for verts */ + unsigned char order; /* order of original verts, bitpacked */ +} SrchFaces; + +/* + * compare edges by vertex indices + */ + +int medge_comp( const void *va, const void *vb ) +{ + const unsigned int *a = ((SrchEdges *)va)->v; + const unsigned int *b = ((SrchEdges *)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]); +} + +/* + * compare faces by vertex indices + */ + +int mface_comp( const void *va, const void *vb ) +{ + const SrchFaces *a = va; + const SrchFaces *b = vb; + int i; + + /* compare indices, first to last, for differences */ + for( i = 0; i < 4; ++i ) { + if( a->v[i] < b->v[i] ) + return -1; + if( a->v[i] > b->v[i] ) + return 1; + } + + /* + * don't think this needs be done; if order is different then either + * (a) the face is good, just reversed or has a different starting + * vertex, or (b) face is bad (for 4 verts) and there's a "twist" + */ + +#if 0 + /* if all the same verts, compare their order */ + if( a->order < b->order ) + return -1; + if( a->order > b->order ) + return 1; +#endif + + return 0; +} + +/* + * 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 */ + +/* + * if verts have been added or deleted, fix dverts also + */ + +static void check_dverts(Mesh *me, int old_totvert) +{ + int totvert = me->totvert; + + /* if all verts have been deleted, free old dverts */ + if (totvert == 0) free_dverts(me->dvert, old_totvert); + /* if verts have been added, expand me->dvert */ + else if (totvert > old_totvert) { + MDeformVert *mdv = me->dvert; + me->dvert = NULL; + create_dverts(me); + copy_dverts(me->dvert, mdv, old_totvert); + free_dverts(mdv, old_totvert); + } + /* if verts have been deleted, shrink me->dvert */ + else { + MDeformVert *mdv = me->dvert; + me->dvert = NULL; + create_dverts(me); + copy_dverts(me->dvert, mdv, totvert); + free_dverts(mdv, old_totvert); + } + + return; +} +#endif + + +/************************************************************************ + * + * Color attributes + * + ************************************************************************/ + +/* + * get a color attribute + */ + +static PyObject *MCol_getAttr( BPy_MCol * self, void *type ) +{ + unsigned char param; + PyObject *attr; + + switch( (int)type ) { + case 'R': /* these are backwards, but that how it works */ + param = self->color->b; + break; + case 'G': + param = self->color->g; + break; + case 'B': /* these are backwards, but that how it works */ + param = self->color->r; + break; + case 'A': + param = self->color->a; + break; + default: + { + char errstr[1024]; + sprintf( errstr, "undefined type '%d' in %s", (int)type, + __FUNCTION__ ); + return EXPP_ReturnPyObjError( PyExc_RuntimeError, errstr ); + } + } + + attr = PyInt_FromLong( param ); + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed"); +} + +/* + * set a color attribute + */ + +static int MCol_setAttr( BPy_MCol * self, PyObject * value, void * type ) +{ + unsigned char *param; + + switch( (int)type ) { + case 'R': /* these are backwards, but that how it works */ + param = &self->color->b; + break; + case 'G': + param = &self->color->g; + break; + case 'B': /* these are backwards, but that how it works */ + param = &self->color->r; + break; + case 'A': + param = &self->color->a; + break; + default: + { + char errstr[1024]; + sprintf( errstr, "undefined type '%d' in %s", (int)type, + __FUNCTION__ ); + return EXPP_ReturnIntError( PyExc_RuntimeError, errstr ); + } + } + + return EXPP_setIValueClamped( value, param, 0, 255, 'b' ); +} + +/************************************************************************ + * + * Python MCol_Type attributes get/set structure + * + ************************************************************************/ + +static PyGetSetDef BPy_MCol_getseters[] = { + {"r", + (getter)MCol_getAttr, (setter)MCol_setAttr, + "red component", + (void *)'R'}, + {"g", + (getter)MCol_getAttr, (setter)MCol_setAttr, + "green component", + (void *)'G'}, + {"b", + (getter)MCol_getAttr, (setter)MCol_setAttr, + "blue component", + (void *)'B'}, + {"a", + (getter)MCol_getAttr, (setter)MCol_setAttr, + "alpha component", + (void *)'A'}, + {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ +}; + +/************************************************************************ + * + * Python MCol_Type methods + * + ************************************************************************/ + +static void MCol_dealloc( BPy_MCol * self ) +{ + PyObject_DEL( self ); +} + +static PyObject *MCol_repr( BPy_MCol * self ) +{ + return PyString_FromFormat( "[MCol %d %d %d %d]", + (int)self->color->r, (int)self->color->g, + (int)self->color->b, (int)self->color->a ); +} + +/************************************************************************ + * + * Python MCol_Type structure definition + * + ************************************************************************/ + +PyTypeObject MCol_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MCol", /* char *tp_name; */ + sizeof( BPy_MCol ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MCol_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) MCol_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + BPy_MCol_getseters, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +static PyObject *MCol_CreatePyObject( MCol * color ) +{ + BPy_MCol *obj = PyObject_NEW( BPy_MCol, &MCol_Type ); + + if( !obj ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyObject_New() failed" ); + + obj->color = color; + return (PyObject *)obj; +} + +/************************************************************************ + * + * Vertex attributes + * + ************************************************************************/ + +/* + * get a vertex's coordinate + */ + +static PyObject *MVert_getCoord( BPy_MVert * self ) +{ + struct MVert *v = &self->mesh->mvert[self->index]; + return newVectorObject( v->co, 3, Py_WRAP ); +} + +/* + * set a vertex's coordinate + */ + +static int MVert_setCoord( BPy_MVert * self, VectorObject * value ) +{ + struct MVert *v = &self->mesh->mvert[self->index]; + int i; + + if( !VectorObject_Check( value ) || value->size != 3 ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected vector argument of size 3" ); + + for( i=0; i<3 ; ++i) + v->co[i] = value->vec[i]; + + return 0; +} + +/* + * get a vertex's index + */ + +static PyObject *MVert_getIndex( BPy_MVert * self ) +{ + PyObject *attr = PyInt_FromLong( self->index ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * get a vertex's normal + */ + +static PyObject *MVert_getNormal( BPy_MVert * self ) +{ + struct MVert *v = &self->mesh->mvert[self->index]; + float no[3]; + int i; + + for( i=0; i<3; ++i ) + no[i] = (float)(v->no[i] / 32767.0); + return newVectorObject( no, 3, Py_NEW ); +} + +/* + * get a vertex's select status + */ + +static PyObject *MVert_getSel( BPy_MVert *self ) +{ + struct MVert *v = &self->mesh->mvert[self->index]; + return EXPP_getBitfield( &v->flag, SELECT, 'b' ); +} + +/* + * set a vertex's select status + */ + +static int MVert_setSel( BPy_MVert *self, PyObject *value ) +{ + struct MVert *v = &self->mesh->mvert[self->index]; + return EXPP_setBitfield( value, &v->flag, SELECT, 'b' ); +} + +/* + * get a vertex's UV coordinates + */ + +static PyObject *MVert_getUVco( BPy_MVert *self ) +{ + if( !self->mesh->msticky ) + return EXPP_ReturnPyObjError( PyExc_AttributeError, + "mesh has no 'sticky' coordinates" ); + + return newVectorObject( self->mesh->msticky[self->index].co, 2, Py_WRAP ); +} + +/* + * set a vertex's UV coordinates + */ + +static int MVert_setUVco( BPy_MVert *self, PyObject *value ) +{ + float uvco[3] = {0.0, 0.0}; + struct MSticky *v; + int i; + + /* + * at least for now, don't allow creation of sticky coordinates if they + * don't already exist + */ + + if( !self->mesh->msticky ) + return EXPP_ReturnIntError( PyExc_AttributeError, + "mesh has no 'sticky' coordinates" ); + + if( VectorObject_Check( value ) ) { + VectorObject *vect = (VectorObject *)value; + if( vect->size != 2 ) + return EXPP_ReturnIntError( PyExc_AttributeError, + "expected 2D vector" ); + for( i = 0; i < vect->size; ++i ) + uvco[i] = vect->vec[i]; + } else if( !PyArg_ParseTuple( value, "ff", + &uvco[0], &uvco[1] ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected 2D vector" ); + + v = &self->mesh->msticky[self->index]; + + for( i = 0; i < 2; ++i ) + v->co[i] = uvco[i]; + + return 0; +} + +/************************************************************************ + * + * Python MVert_Type attributes get/set structure + * + ************************************************************************/ + +static PyGetSetDef BPy_MVert_getseters[] = { + {"co", + (getter)MVert_getCoord, (setter)MVert_setCoord, + "vertex's coordinate", + NULL}, + {"index", + (getter)MVert_getIndex, (setter)NULL, + "vertex's index", + NULL}, + {"no", + (getter)MVert_getNormal, (setter)NULL, + "vertex's normal", + NULL}, + {"sel", + (getter)MVert_getSel, (setter)MVert_setSel, + "vertex's select status", + NULL}, + {"uvco", + (getter)MVert_getUVco, (setter)MVert_setUVco, + "vertex's UV coordinates", + NULL}, + {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ +}; + +/************************************************************************ + * + * Python MVert_Type standard operations + * + ************************************************************************/ + +static void MVert_dealloc( BPy_MVert * self ) +{ + PyObject_DEL( self ); +} + +static int MVert_compare( BPy_MVert * a, BPy_MVert * b ) +{ + return( a->mesh == b->mesh && a->index == b->index ) ? 0 : -1; +} + +static PyObject *MVert_repr( BPy_MVert * self ) +{ + struct MVert *v = &self->mesh->mvert[self->index]; + char format[512]; + + sprintf( format, "[MVert (%f %f %f) (%f %f %f) %d]", + v->co[0], v->co[1], v->co[2], (float)(v->no[0] / 32767.0), + (float)(v->no[1] / 32767.0), (float)(v->no[2] / 32767.0), + self->index ); + + return PyString_FromString( format ); +} + +/************************************************************************ + * + * Python MVert_Type structure definition + * + ************************************************************************/ + +PyTypeObject MVert_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MVert", /* char *tp_name; */ + sizeof( BPy_MVert ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MVert_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + ( cmpfunc ) MVert_compare, /* cmpfunc tp_compare; */ + ( reprfunc ) MVert_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + BPy_MVert_getseters, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +static PyObject *MVert_CreatePyObject( Mesh * mesh, int i ) +{ + BPy_MVert *obj = PyObject_NEW( BPy_MVert, &MVert_Type ); + + if( !obj ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyObject_New() failed" ); + + obj->mesh = mesh; + obj->index = i; + return (PyObject *)obj; +} + +/************************************************************************ + * + * Vertex sequence + * + ************************************************************************/ + +static int MVertSeq_len( BPy_MVertSeq * self ) +{ + return self->mesh->totvert; +} + +static PyObject *MVertSeq_item( BPy_MVertSeq * self, int i ) +{ + if( i < 0 || i >= self->mesh->totvert ) + return EXPP_ReturnPyObjError( PyExc_IndexError, + "array index out of range" ); + + return MVert_CreatePyObject( self->mesh, i ); +}; + +static PySequenceMethods MVertSeq_as_sequence = { + ( inquiry ) MVertSeq_len, /* sq_length */ + ( binaryfunc ) 0, /* sq_concat */ + ( intargfunc ) 0, /* sq_repeat */ + ( intargfunc ) MVertSeq_item, /* sq_item */ + ( intintargfunc ) 0, /* sq_slice */ + ( intobjargproc ) 0, /* sq_ass_item */ + ( intintobjargproc ) 0, /* sq_ass_slice */ + 0,0,0, +}; + +/************************************************************************ + * + * Python MVertSeq_Type iterator (iterates over vertices) + * + ************************************************************************/ + +/* + * Initialize the interator index + */ + +static PyObject *MVertSeq_getIter( BPy_MVertSeq * self ) +{ + self->iter = 0; + return EXPP_incr_ret ( (PyObject *) self ); +} + +/* + * Return next MVert. + */ + +static PyObject *MVertSeq_nextIter( BPy_MVertSeq * self ) +{ + if( self->iter == self->mesh->totvert ) + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + + return MVert_CreatePyObject( self->mesh, self->iter++ ); +} + +/************************************************************************ + * + * Python MVertSeq_Type methods + * + ************************************************************************/ + +static PyObject *MVertSeq_extend( BPy_MVertSeq * self, PyObject *args ) +{ + int len; + int i,j; + PyObject *tmp; + MVert *newvert, *tmpvert; + Mesh *mesh = self->mesh; + + /* make sure we get a sequence of tuples of something */ + + switch( PySequence_Size ( args ) ) { + case 1: /* better be a list or a tuple */ + args = PyTuple_GET_ITEM( args, 0 ); + if( !PySequence_Check ( args ) ) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a sequence of tuple triplets" ); + Py_INCREF( args ); /* so we can safely DECREF later */ + break; + case 3: /* take any three args and put into a tuple */ + tmp = PyTuple_GET_ITEM( args, 0 ); + if( PyTuple_Check( tmp ) ) { + Py_INCREF( args ); + break; + } + args = Py_BuildValue( "((OOO))", tmp, + PyTuple_GET_ITEM( args, 1 ), PyTuple_GET_ITEM( args, 2 ) ); + 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 triplets" ); + } + + len = PySequence_Size( args ); + if( len == 0 ) { + Py_DECREF ( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected at least one tuple" ); + } + + newvert = MEM_callocN( sizeof( MVert ) * (mesh->totvert+len), "MVerts" ); + + /* scan the input list and insert the new vertices */ + + tmpvert = &newvert[mesh->totvert]; + for( i = 0; i < len; ++i ) { + float co[3]; + tmp = PySequence_Fast_GET_ITEM( args, i ); + if( VectorObject_Check( tmp ) ) { + if( ((VectorObject *)tmp)->size != 3 ) { + MEM_freeN( newvert ); + Py_DECREF ( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected vector of size 3" ); + } + for( j = 0; j < 3; ++j ) + co[j] = ((VectorObject *)tmp)->vec[j]; + } else if( PyTuple_Check( tmp ) ) { + int ok=1; + PyObject *flt; + if( PyTuple_Size( tmp ) != 3 ) + ok = 0; + else + for( j = 0; ok && j < 3; ++j ) { + flt = PyTuple_GET_ITEM( tmp, j ); + if( !PyNumber_Check ( flt ) ) + ok = 0; + else + co[j] = PyFloat_AsDouble( flt ); + } + + if( !ok ) { + MEM_freeN( newvert ); + Py_DECREF ( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected tuple triplet of floats" ); + } + } + + /* add the coordinate to the new list */ +#if 0 + memcpy( tmpvert->co, co, sizeof(float)*3 ); +#else + { + int i=3; + while (i--) tmpvert->co[i] = co[i]; + } +#endif + + + + /* TODO: anything else which needs to be done when we add a vert? */ + /* probably not: NMesh's newvert() doesn't */ + ++tmpvert; + } + + /* if we got here we've added all the new verts, so just copy the old + * verts over and we're done */ + + if( mesh->mvert ) { + memcpy( newvert, mesh->mvert, mesh->totvert*sizeof(MVert) ); + MEM_freeN( mesh->mvert ); + } + mesh->mvert = newvert; + mesh->totvert += len; + +#ifdef CHECK_DVERTS + check_dverts( mesh, mesh->totvert - len ); +#endif + 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 edges to mesh"}, + {NULL, NULL, 0, NULL} +}; + +/************************************************************************ + * + * Python MVertSeq_Type standard operations + * + ************************************************************************/ + +static void MVertSeq_dealloc( BPy_MVertSeq * self ) +{ + PyObject_DEL( self ); +} + +/*****************************************************************************/ +/* Python NMVertSeq_Type structure definition: */ +/*****************************************************************************/ +PyTypeObject MVertSeq_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MVertSeq", /* char *tp_name; */ + sizeof( BPy_MVertSeq ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MVertSeq_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + &MVertSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + ( getiterfunc) MVertSeq_getIter, /* getiterfunc tp_iter; */ + ( iternextfunc ) MVertSeq_nextIter, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + BPy_MVertSeq_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +/************************************************************************ + * + * Edge attributes + * + ************************************************************************/ + +/* + * get an edge's crease value + */ + +static PyObject *MEdge_getCrease( BPy_MEdge * self ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + PyObject *attr = PyInt_FromLong( edge->crease ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * set an edge's crease value + */ + +static int MEdge_setCrease( BPy_MEdge * self, PyObject * value ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + return EXPP_setIValueClamped( value, &edge->crease, 0, 255, 'b' ); +} + +/* + * get an edge's flag + */ + +static PyObject *MEdge_getFlag( BPy_MEdge * self ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + PyObject *attr = PyInt_FromLong( edge->flag ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * set an edge's flag + */ + +static int MEdge_setFlag( BPy_MEdge * self, PyObject * value ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + short param; + static short bitmask = 1 /* 1=select */ + | ME_EDGEDRAW + | ME_EDGERENDER + | ME_SEAM + | ME_FGON; + + if( !PyInt_CheckExact ( value ) ) { + char errstr[128]; + sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); + return EXPP_ReturnIntError( PyExc_TypeError, errstr ); + } + param = PyInt_AS_LONG ( value ); + + if ( ( param & bitmask ) != param ) + return EXPP_ReturnIntError( PyExc_ValueError, + "invalid bit(s) set in mask" ); + + edge->flag = param; + + return 0; +} + +/* + * get an edge's first vertex + */ + +static PyObject *MEdge_getV1( BPy_MEdge * self ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + return MVert_CreatePyObject( self->mesh, edge->v1 ); +} + +/* + * set an edge's first vertex + */ + +static int MEdge_setV1( BPy_MEdge * self, BPy_MVert * value ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + edge->v1 = value->index; + return 0; +} + +/* + * get an edge's second vertex + */ + +static PyObject *MEdge_getV2( BPy_MEdge * self ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + return MVert_CreatePyObject( self->mesh, edge->v2 ); +} + +/* + * set an edge's second vertex + */ + +static int MEdge_setV2( BPy_MEdge * self, BPy_MVert * value ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + edge->v2 = value->index; + return 0; +} + +/* + * get an edges's index + */ + +static PyObject *MEdge_getIndex( BPy_MEdge * self ) +{ + PyObject *attr = PyInt_FromLong( self->index ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/************************************************************************ + * + * Python MEdge_Type attributes get/set structure + * + ************************************************************************/ + +static PyGetSetDef BPy_MEdge_getseters[] = { + {"crease", + (getter)MEdge_getCrease, (setter)MEdge_setCrease, + "edge's crease value", + NULL}, + {"flag", + (getter)MEdge_getFlag, (setter)MEdge_setFlag, + "edge's flags", + NULL}, + {"v1", + (getter)MEdge_getV1, (setter)MEdge_setV1, + "edge's first vertex", + NULL}, + {"v2", + (getter)MEdge_getV2, (setter)MEdge_setV2, + "edge's second vertex", + NULL}, + {"index", + (getter)MEdge_getIndex, (setter)NULL, + "edge's index", + NULL}, + {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ +}; + +/************************************************************************ + * + * Python MEdge_Type iterator (iterates over vertices) + * + ************************************************************************/ + +/* + * Initialize the interator index + */ + +static PyObject *MEdge_getIter( BPy_MEdge * self ) +{ + self->iter = 0; + return EXPP_incr_ret ( (PyObject *) self ); +} + +/* + * Return next MVert. Throw an exception after the second vertex. + */ + +static PyObject *MEdge_nextIter( BPy_MEdge * self ) +{ + if( self->iter == 2 ) + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + + self->iter++; + if( self->iter == 1 ) + return MEdge_getV1( self ); + else + return MEdge_getV2( self ); +} + +/************************************************************************ + * + * Python MEdge_Type standard operations + * + ************************************************************************/ + +static void MEdge_dealloc( BPy_MEdge * self ) +{ + PyObject_DEL( self ); +} + +static int MEdge_compare( BPy_MEdge * a, BPy_MEdge * b ) +{ + return( a->mesh == b->mesh && a->index == b->index ) ? 0 : -1; +} + +static PyObject *MEdge_repr( BPy_MEdge * self ) +{ + struct MEdge *edge = &self->mesh->medge[self->index]; + + return PyString_FromFormat( "[MEdge (%d %d) %d %d]", + (int)edge->v1, (int)edge->v2, (int)edge->crease, + (int)self->index ); +} + +/************************************************************************ + * + * Python MEdge_Type structure definition + * + ************************************************************************/ + +PyTypeObject MEdge_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MEdge", /* char *tp_name; */ + sizeof( BPy_MEdge ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MEdge_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + ( cmpfunc ) MEdge_compare, /* cmpfunc tp_compare; */ + ( reprfunc ) MEdge_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + ( getiterfunc) MEdge_getIter, /* getiterfunc tp_iter; */ + ( iternextfunc ) MEdge_nextIter, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + BPy_MEdge_getseters, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +static PyObject *MEdge_CreatePyObject( Mesh * mesh, int i ) +{ + BPy_MEdge *obj = PyObject_NEW( BPy_MEdge, &MEdge_Type ); + + if( !obj ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyObject_New() failed" ); + + obj->mesh = mesh; + obj->index = i; + return (PyObject *)obj; +} + +/************************************************************************ + * + * Edge sequence + * + ************************************************************************/ + +static int MEdgeSeq_len( BPy_MEdgeSeq * self ) +{ + return self->mesh->totedge; +} + +static PyObject *MEdgeSeq_item( BPy_MEdgeSeq * self, int i ) +{ + if( i < 0 || i >= self->mesh->totedge ) + return EXPP_ReturnPyObjError( PyExc_IndexError, + "array index out of range" ); + + return MEdge_CreatePyObject( self->mesh, i ); +} + +static PySequenceMethods MEdgeSeq_as_sequence = { + ( inquiry ) MEdgeSeq_len, /* sq_length */ + ( binaryfunc ) 0, /* sq_concat */ + ( intargfunc ) 0, /* sq_repeat */ + ( intargfunc ) MEdgeSeq_item, /* sq_item */ + ( intintargfunc ) 0, /* sq_slice */ + ( intobjargproc ) 0, /* sq_ass_item */ + ( intintobjargproc ) 0, /* sq_ass_slice */ + 0,0,0, +}; + +/************************************************************************ + * + * Python MEdgeSeq_Type iterator (iterates over edges) + * + ************************************************************************/ + +/* + * Initialize the interator index + */ + +static PyObject *MEdgeSeq_getIter( BPy_MEdgeSeq * self ) +{ + self->iter = 0; + return EXPP_incr_ret ( (PyObject *) self ); +} + +/* + * Return next MEdge. + */ + +static PyObject *MEdgeSeq_nextIter( BPy_MEdgeSeq * self ) +{ + if( self->iter == self->mesh->totedge ) + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + + return MEdge_CreatePyObject( self->mesh, self->iter++ ); +} + +/************************************************************************ + * + * Python MEdgeSeq_Type methods + * + ************************************************************************/ + +/* + * Create edges from tuples of vertices. Duplicate new edges, or + * edges which already exist, + */ + +static PyObject *MEdgeSeq_extend( BPy_MEdgeSeq * self, PyObject *args ) +{ + int len, nverts; + int i, j; + int new_edge_count, good_edges; + SrchEdges *oldpair, *newpair, *tmppair, *tmppair2; + PyObject *tmp; + BPy_MVert *e[4]; + MEdge *tmpedge; + Mesh *mesh = self->mesh; + + /* make sure we get a sequence of tuples of something */ + + switch( PySequence_Size ( args ) ) { + case 1: /* better be a list or a tuple */ + args = PyTuple_GET_ITEM( args, 0 ); + if( !PySequence_Check ( args ) ) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a sequence of tuple pairs" ); + Py_INCREF( args ); /* so we can safely DECREF later */ + break; + case 2: + case 3: + case 4: /* two to four args may be individual verts */ + tmp = PyTuple_GET_ITEM( args, 0 ); + if( PyTuple_Check( tmp ) ) {/* maybe just tuples, so use args as-is */ + Py_INCREF( args ); /* so we can safely DECREF later */ + break; + } + args = Py_BuildValue( "(O)", args ); + 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" ); + } + + /* make sure there is something to add */ + len = PySequence_Size( args ); + if( len == 0 ) { + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected at least one tuple" ); + } + + /* 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 ); + + /* not a tuple of MVerts... error */ + if( !PyTuple_Check( tmp ) || + EXPP_check_sequence_consistency( tmp, &MVert_Type ) != 1 ) { + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected sequence of MVert tuples" ); + } + + /* not the right number of MVerts... error */ + nverts = PyTuple_Size( tmp ); + if( nverts < 2 || nverts > 4 ) { + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected 2 to 4 MVerts per tuple" ); + } + if( nverts == 2 ) + ++new_edge_count; /* if only two vert, then add only edge */ + else + new_edge_count += nverts; /* otherwise, one edge per vert */ + } + + /* OK, commit to allocating the search structures */ + newpair = (SrchEdges *)MEM_callocN( sizeof(SrchEdges)*new_edge_count, + "MEdgePairs" ); + + /* scan the input list and build the new edge pair list */ + len = PySequence_Size( args ); + tmppair = newpair; + for( i = 0; i < len; ++i ) { + tmp = PySequence_Fast_GET_ITEM( 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 ); + + if( nverts == 2 ) + nverts = 1; /* again, two verts give just one edge */ + + /* now add the edges to the search list */ + for(j = 0; j < nverts; ++j ) { + int k = j+1; + if( k == nverts ) /* final edge */ + k = 0; + + /* sort verts into search list, abort if two are the same */ + if( e[j]->index < e[k]->index ) { + tmppair->v[0] = e[j]->index; + tmppair->v[1] = e[k]->index; + tmppair->swap = 0; + } else if( e[j]->index > e[k]->index ) { + tmppair->v[0] = e[k]->index; + tmppair->v[1] = e[j]->index; + tmppair->swap = 1; + } else { + MEM_freeN( newpair ); + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "tuple contains duplicate vertices" ); + } + tmppair++; + } + } + + /* sort the new edge pairs */ + qsort( newpair, new_edge_count, sizeof(SrchEdges), medge_comp ); + + /* + * find duplicates in the new list and mark. if it's a duplicate, + * then mark by setting second vert index to 0 (a real edge won't have + * second vert index of 0 since verts are sorted) + */ + + good_edges = new_edge_count; /* all edges are good at this point */ + + tmppair = newpair; /* "last good edge" */ + tmppair2 = &tmppair[1]; /* "current candidate edge" */ + for( i = 0; i < new_edge_count; ++i ) { + if( tmppair->v[0] != tmppair2->v[0] || + tmppair->v[1] != tmppair2->v[1] ) + tmppair = tmppair2; /* last != current, so current == last */ + else { + tmppair2->v[1] = 0; /* last == current, so mark as duplicate */ + --good_edges; /* one less good edge */ + } + tmppair2++; + } + + /* if mesh has edges, see if any of the new edges are already in it */ + if( mesh->totedge ) { + oldpair = (SrchEdges *)MEM_callocN( sizeof(SrchEdges)*mesh->totedge, + "MEdgePairs" ); + + /* + * build a search list of new edges (don't need to update "swap" + * field, since we're not creating edges here) + */ + 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; + } + ++tmpedge; + ++tmppair; + } + + /* sort the old edge pairs */ + qsort( oldpair, mesh->totedge, sizeof(SrchEdges), medge_comp ); + + /* eliminate new edges already in the mesh */ + tmppair = newpair; + for( i = new_edge_count; i-- ; ) { + if( tmppair->v[1] ) { + if( bsearch( tmppair, oldpair, mesh->totedge, + sizeof(SrchEdges), medge_comp ) ) { + tmppair->v[1] = 0; /* mark as duplicate */ + --good_edges; + } + } + tmppair++; + } + MEM_freeN( oldpair ); + } + + /* if any new edges are left, add to list */ + if( good_edges ) { + int totedge = mesh->totedge+good_edges; /* new edge count */ + + /* allocate new edge list */ + tmpedge = MEM_callocN(totedge*sizeof(MEdge), "NMesh_addEdges"); + + /* if we're appending, copy the old edge list and delete it */ + if( mesh->medge ) { + memcpy( tmpedge, mesh->medge, mesh->totedge*sizeof(MEdge)); + MEM_freeN( mesh->medge ); + } + mesh->medge = tmpedge; /* point to the new edge list */ + + /* point to the first edge we're going to add */ + tmpedge = &mesh->medge[mesh->totedge]; + tmppair = newpair; + + /* as we find a good edge, add it */ + while( good_edges ) { + if( tmppair->v[1] ) { /* not marked as duplicate ! */ + if( !tmppair->swap ) { + tmpedge->v1 = tmppair->v[0]; + tmpedge->v2 = tmppair->v[1]; + } else { + tmpedge->v1 = tmppair->v[1]; + tmpedge->v2 = tmppair->v[0]; + } + tmpedge->flag = ME_EDGEDRAW | ME_EDGERENDER; + mesh->totedge++; + --good_edges; + ++tmpedge; + } + tmppair++; + } + } + + /* clean up and leave */ + mesh_update( mesh ); + MEM_freeN( newpair ); + Py_DECREF ( args ); + return EXPP_incr_ret( Py_None ); +} + +static struct PyMethodDef BPy_MEdgeSeq_methods[] = { + {"extend", (PyCFunction)MEdgeSeq_extend, METH_VARARGS, + "add edges to mesh"}, + {NULL, NULL, 0, NULL} +}; + +/************************************************************************ + * + * Python MEdgeSeq_Type standard operators + * + ************************************************************************/ + +static void MEdgeSeq_dealloc( BPy_MEdgeSeq * self ) +{ + PyObject_DEL( self ); +} + +/*****************************************************************************/ +/* Python NMEdgeSeq_Type structure definition: */ +/*****************************************************************************/ +PyTypeObject MEdgeSeq_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MEdgeSeq", /* char *tp_name; */ + sizeof( BPy_MEdgeSeq ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MEdgeSeq_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + &MEdgeSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + ( getiterfunc) MEdgeSeq_getIter, /* getiterfunc tp_iter; */ + ( iternextfunc ) MEdgeSeq_nextIter, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + BPy_MEdgeSeq_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +/************************************************************************ + * + * Face attributes + * + ************************************************************************/ + +/* + * get a face's vertices + */ + +static PyObject *MFace_getVerts( BPy_MFace * self ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + PyObject *attr = PyTuple_New( face->v4 ? 4 : 3 ); + + if( !attr ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyTuple_New() failed" ); + + PyTuple_SetItem( attr, 0, MVert_CreatePyObject( self->mesh, face->v1 ) ); + PyTuple_SetItem( attr, 1, MVert_CreatePyObject( self->mesh, face->v2 ) ); + PyTuple_SetItem( attr, 2, MVert_CreatePyObject( self->mesh, face->v3 ) ); + if( face->v4 ) + PyTuple_SetItem( attr, 3, + MVert_CreatePyObject( self->mesh, face->v4 ) ); + + return attr; +} + +/* + * set a face's vertices + */ + +static int MFace_setVerts( BPy_MFace * self, PyObject * args ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + BPy_MVert *v1, *v2, *v3, *v4 = NULL; + + if( !PyArg_ParseTuple ( args, "O!O!O!|O!", &MVert_Type, &v1, + &MVert_Type, &v2, &MVert_Type, &v3, &MVert_Type, &v4 ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected tuple of 3 or 4 MVerts" ); + + face->v1 = v1->index; + face->v2 = v2->index; + face->v3 = v3->index; + if( v4 ) + face->v4 = v4->index; + return 0; +} + +/* + * get face's material index + */ + +static PyObject *MFace_getMat( BPy_MFace * self ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + PyObject *attr = PyInt_FromLong( face->mat_nr ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * set face's material index + */ + +static int MFace_setMat( BPy_MFace * self, PyObject * value ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + return EXPP_setIValueRange( value, &face->mat_nr, 0, 15, 'b' ); +} + +/* + * get a face's index + */ + +static PyObject *MFace_getIndex( BPy_MFace * self ) +{ + PyObject *attr = PyInt_FromLong( self->index ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * get face's normal index + */ + +static PyObject *MFace_getNormal( BPy_MFace * self ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + float *vert[4]; + float no[3]; + + vert[0] = self->mesh->mvert[face->v1].co; + vert[1] = self->mesh->mvert[face->v2].co; + vert[2] = self->mesh->mvert[face->v3].co; + vert[3] = self->mesh->mvert[face->v4].co; + if( face->v4 ) + CalcNormFloat4( vert[0], vert[1], vert[2], vert[3], no ); + else + CalcNormFloat( vert[0], vert[1], vert[2], no ); + + return newVectorObject( no, 3, Py_NEW ); +} + +/* + * get one of a face's mface flag bits + */ + +static PyObject *MFace_getMFlagBits( BPy_MFace * self, void * type ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + return EXPP_getBitfield( &face->flag, (int)type, 'b' ); +} + +/* + * set one of a face's mface flag bits + */ + +static int MFace_setMFlagBits( BPy_MFace * self, PyObject * value, + void * type ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + return EXPP_setBitfield( value, &face->flag, (int)type, 'b' ); +} + +/* + * get face's texture image + */ + +static PyObject *MFace_getImage( BPy_MFace *self ) +{ + TFace *face; + if( !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no texture values" ); + + face = &self->mesh->tface[self->index]; + + if( face->tpage ) + return Image_CreatePyObject( face->tpage ); + else + return EXPP_incr_ret( Py_None ); +} + +/* + * change or clear face's texture image + */ + +static int MFace_setImage( BPy_MFace *self, PyObject *value ) +{ + TFace *face; + if( !self->mesh->tface ) + return EXPP_ReturnIntError( PyExc_ValueError, + "face has no texture values" ); + + face = &self->mesh->tface[self->index]; + if( value == Py_None ) + face->tpage = NULL; /* should memory be freed? */ + else { + if( !BPy_Image_Check( value ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected image object" ); + face->tpage = ( ( BPy_Image * ) value )->image; + } + + return 0; +} + +/* + * get face's texture mode + */ + +static PyObject *MFace_getMode( BPy_MFace *self ) +{ + PyObject *attr; + + if( !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no texture values" ); + + attr = PyInt_FromLong( self->mesh->tface[self->index].mode ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * set face's texture mode + */ + +static int MFace_setMode( BPy_MFace *self, PyObject *value ) +{ + int param; + static short bitmask = TF_SELECT | TF_HIDE; + + if( !self->mesh->tface ) + return EXPP_ReturnIntError( PyExc_ValueError, + "face has no texture values" ); + + if( !PyInt_CheckExact ( value ) ) { + char errstr[128]; + sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); + return EXPP_ReturnIntError( PyExc_TypeError, errstr ); + } + param = PyInt_AS_LONG ( value ); + + /* only one face can be active, so don't allow that here */ + + if( ( param & bitmask ) == TF_ACTIVE ) + return EXPP_ReturnIntError( PyExc_ValueError, + "cannot make a face active; use 'activeFace' attribute" ); + + if( ( param & bitmask ) != param ) + return EXPP_ReturnIntError( PyExc_ValueError, + "invalid bit(s) set in mask" ); + + /* merge active setting with other new params */ + param |= (self->mesh->tface[self->index].flag & TF_ACTIVE); + self->mesh->tface[self->index].flag = param; + + return 0; +} + +/* + * get face's texture flags + */ + +static PyObject *MFace_getFlag( BPy_MFace *self ) +{ + PyObject *attr; + + if( !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no texture values" ); + + attr = PyInt_FromLong( self->mesh->tface[self->index].mode ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * set face's texture flag + */ + +static int MFace_setFlag( BPy_MFace *self, PyObject *value ) +{ + int param; + static short bitmask = TF_DYNAMIC + | TF_TEX + | TF_SHAREDVERT + | TF_LIGHT + | TF_SHAREDCOL + | TF_TILES + | TF_BILLBOARD + | TF_TWOSIDE + | TF_INVISIBLE + | TF_OBCOL + | TF_BILLBOARD2 + | TF_SHADOW + | TF_BMFONT; + + if( !self->mesh->tface ) + return EXPP_ReturnIntError( PyExc_ValueError, + "face has no texture values" ); + + if( !PyInt_CheckExact ( value ) ) { + char errstr[128]; + sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); + return EXPP_ReturnIntError( PyExc_TypeError, errstr ); + } + param = PyInt_AS_LONG ( value ); + + if( param == 0xffff ) /* if param is ALL, set everything but HALO */ + param = bitmask ^ TF_BILLBOARD; + else if( ( param & bitmask ) != param ) + return EXPP_ReturnIntError( PyExc_ValueError, + "invalid bit(s) set in mask" ); + + /* Blender UI doesn't allow these on at the same time */ + + if( ( param & (TF_BILLBOARD | TF_BILLBOARD2) ) == + (TF_BILLBOARD | TF_BILLBOARD2) ) + return EXPP_ReturnIntError( PyExc_ValueError, + "HALO and BILLBOARD cannot be enabled simultaneously" ); + + self->mesh->tface[self->index].mode = param; + + return 0; +} + +/* + * get face's texture transparency setting + */ + +static PyObject *MFace_getTransp( BPy_MFace *self ) +{ + PyObject *attr; + if( !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no texture values" ); + + attr = PyInt_FromLong( self->mesh->tface[self->index].transp ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +/* + * set face's texture transparency setting + */ + +static int MFace_setTransp( BPy_MFace *self, PyObject *value ) +{ + if( !self->mesh->tface ) + return EXPP_ReturnIntError( PyExc_ValueError, + "face has no texture values" ); + + return EXPP_setIValueRange( value, + &self->mesh->tface[self->index].transp, TF_SOLID, TF_SUB, 'b' ); +} + +/* + * get a face's texture UV values + */ + +static PyObject *MFace_getUV( BPy_MFace * self ) +{ + TFace *face; + PyObject *attr; + int length, i; + + if( !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no texture values" ); + + face = &self->mesh->tface[self->index]; + length = self->mesh->mface[self->index].v4 ? 4 : 3; + attr = PyTuple_New( length ); + + if( !attr ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyTuple_New() failed" ); + + for( i=0; i<length; ++i ) { + PyObject *vector = newVectorObject( face->uv[i], 2, Py_WRAP ); + if( !vector ) + return NULL; + PyTuple_SetItem( attr, i, vector ); + } + + return attr; +} + +/* + * set a face's texture UV values + */ + +static int MFace_setUV( BPy_MFace * self, PyObject * value ) +{ + TFace *face; + int length, i; + + if( !self->mesh->tface ) + return EXPP_ReturnIntError( PyExc_ValueError, + "face has no texture values" ); + + if( EXPP_check_sequence_consistency( value, &vector_Type ) != 1 ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected sequence of vectors" ); + + length = self->mesh->mface[self->index].v4 ? 4 : 3; + if( length != PyTuple_Size( value ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "size of vertex and UV lists differ" ); + + face = &self->mesh->tface[self->index]; + for( i=0; i<length; ++i ) { + VectorObject *vector = (VectorObject *)PyTuple_GET_ITEM( value, i ); + face->uv[i][0] = vector->vec[0]; + face->uv[i][1] = vector->vec[1]; + } + return 0; +} + +/* + * get a face's vertex colors. note that if mesh->tfaces is defined, then + * it takes precedent over mesh->mcol + */ + +static PyObject *MFace_getCol( BPy_MFace * self ) +{ + PyObject *attr; + int length, i; + MCol * mcol; + + /* if there's no mesh color vectors or texture faces, nothing to do */ + + if( !self->mesh->mcol && !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no vertex colors" ); + + if( self->mesh->tface ) + mcol = (MCol *) self->mesh->tface[self->index].col; + else + mcol = &self->mesh->mcol[self->index*4]; + + length = self->mesh->mface[self->index].v4 ? 4 : 3; + attr = PyTuple_New( length ); + + if( !attr ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyTuple_New() failed" ); + + for( i=0; i<length; ++i ) { + PyObject *color = MCol_CreatePyObject( &mcol[i] ); + if( !color ) + return NULL; + PyTuple_SetItem( attr, i, color ); + } + + return attr; +} + +/************************************************************************ + * + * Python MFace_Type attributes get/set structure + * + ************************************************************************/ + +static PyGetSetDef BPy_MFace_getseters[] = { + {"verts", + (getter)MFace_getVerts, (setter)MFace_setVerts, + "face's vertices", + NULL}, + {"v", + (getter)MFace_getVerts, (setter)MFace_setVerts, + "deprecated: see 'verts'", + NULL}, + {"mat", + (getter)MFace_getMat, (setter)MFace_setMat, + "face's material index", + NULL}, + {"index", + (getter)MFace_getIndex, (setter)NULL, + "face's index", + NULL}, + {"no", + (getter)MFace_getNormal, (setter)NULL, + "face's normal", + NULL}, + + {"hide", + (getter)MFace_getMFlagBits, (setter)MFace_setMFlagBits, + "face hidden in edit mode", + (void *)ME_HIDE}, + {"sel", + (getter)MFace_getMFlagBits, (setter)MFace_setMFlagBits, + "face selected in edit mode", + (void *)ME_FACE_SEL}, + {"smooth", + (getter)MFace_getMFlagBits, (setter)MFace_setMFlagBits, + "face smooth enabled", + (void *)ME_SMOOTH}, + + /* attributes for texture faces (mostly, I think) */ + + {"col", + (getter)MFace_getCol, (setter)NULL, + "face's vertex colors", + NULL}, + {"flag", + (getter)MFace_getFlag, (setter)MFace_setFlag, + "flags associated with texture faces", + NULL}, + {"image", + (getter)MFace_getImage, (setter)MFace_setImage, + "image associated with texture faces", + NULL}, + {"mode", + (getter)MFace_getMode, (setter)MFace_setMode, + "modes associated with texture faces", + NULL}, + {"transp", + (getter)MFace_getTransp, (setter)MFace_setTransp, + "transparency of texture faces", + NULL}, + {"uv", + (getter)MFace_getUV, (setter)MFace_setUV, + "face's UV coordinates", + NULL}, + + {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ +}; + +/************************************************************************ + * + * Python MFace_Type iterator (iterates over vertices) + * + ************************************************************************/ + +/* + * Initialize the interator index + */ + +static PyObject *MFace_getIter( BPy_MFace * self ) +{ + self->iter = 0; + return EXPP_incr_ret ( (PyObject *) self ); +} + +/* + * Return next MVert. Throw an exception after the final vertex. + */ + +static PyObject *MFace_nextIter( BPy_MFace * self ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + int len = self->mesh->mface[self->index].v4 ? 4 : 3; + + if( self->iter == len ) + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + + ++self->iter; + switch ( self->iter ) { + case 1: + return MVert_CreatePyObject( self->mesh, face->v1 ); + case 2: + return MVert_CreatePyObject( self->mesh, face->v2 ); + case 3: + return MVert_CreatePyObject( self->mesh, face->v3 ); + default : + return MVert_CreatePyObject( self->mesh, face->v4 ); + } +} + +/************************************************************************ + * + * Python MFace_Type methods + * + ************************************************************************/ + +/************************************************************************ + * + * Python MFace_Type standard operations + * + ************************************************************************/ + +static void MFace_dealloc( BPy_MFace * self ) +{ + PyObject_DEL( self ); +} + +static int MFace_compare( BPy_MFace * a, BPy_MFace * b ) +{ + return( a->mesh == b->mesh && a->index == b->index ) ? 0 : -1; +} + +static PyObject *MFace_repr( BPy_MFace* self ) +{ + struct MFace *face = &self->mesh->mface[self->index]; + + if( face->v4 ) + return PyString_FromFormat( "[MFace (%d %d %d %d) %d]", + (int)face->v1, (int)face->v2, + (int)face->v3, (int)face->v4, (int)self->index ); + else + return PyString_FromFormat( "[MFace (%d %d %d) %d]", + (int)face->v1, (int)face->v2, + (int)face->v3, (int)self->index ); +} + +/************************************************************************ + * + * Python MFace_Type structure definition + * + ************************************************************************/ + +PyTypeObject MFace_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MFace", /* char *tp_name; */ + sizeof( BPy_MFace ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MFace_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + ( cmpfunc ) MFace_compare, /* cmpfunc tp_compare; */ + ( reprfunc ) MFace_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + ( getiterfunc ) MFace_getIter, /* getiterfunc tp_iter; */ + ( iternextfunc ) MFace_nextIter, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + NULL, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + BPy_MFace_getseters, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +static PyObject *MFace_CreatePyObject( Mesh * mesh, int i ) +{ + BPy_MFace *obj = PyObject_NEW( BPy_MFace, &MFace_Type ); + + if( !obj ) + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyObject_New() failed" ); + + obj->mesh = mesh; + obj->index = i; + return (PyObject *)obj; +} + +/************************************************************************ + * + * Face sequence + * + ************************************************************************/ + +static int MFaceSeq_len( BPy_MFaceSeq * self ) +{ + return self->mesh->totface; +} + +static PyObject *MFaceSeq_item( BPy_MFaceSeq * self, int i ) +{ + if( i < 0 || i >= self->mesh->totface ) + return EXPP_ReturnPyObjError( PyExc_IndexError, + "array index out of range" ); + + return MFace_CreatePyObject( self->mesh, i ); +} + +static PySequenceMethods MFaceSeq_as_sequence = { + ( inquiry ) MFaceSeq_len, /* sq_length */ + ( binaryfunc ) 0, /* sq_concat */ + ( intargfunc ) 0, /* sq_repeat */ + ( intargfunc ) MFaceSeq_item, /* sq_item */ + ( intintargfunc ) 0, /* sq_slice */ + ( intobjargproc ) 0, /* sq_ass_item */ + ( intintobjargproc ) 0, /* sq_ass_slice */ + 0,0,0, +}; + +/************************************************************************ + * + * Python MFaceSeq_Type iterator (iterates over faces) + * + ************************************************************************/ + +/* + * Initialize the interator index + */ + +static PyObject *MFaceSeq_getIter( BPy_MFaceSeq * self ) +{ + self->iter = 0; + return EXPP_incr_ret ( (PyObject *) self ); +} + +/* + * Return next MFace. + */ + +static PyObject *MFaceSeq_nextIter( BPy_MFaceSeq * self ) +{ + if( self->iter == self->mesh->totface ) + return EXPP_ReturnPyObjError( PyExc_StopIteration, + "iterator at end" ); + + return MFace_CreatePyObject( self->mesh, self->iter++ ); +} + +/************************************************************************ + * + * Python MFaceSeq_Type methods + * + ************************************************************************/ + +static PyObject *MFaceSeq_extend( BPy_MEdgeSeq * self, PyObject *args ) +{ + /* + * (a) check input for valid edge objects, faces which consist of + * only three or four edges + * (b) check input to be sure edges form a closed face (each edge + * contains verts in two other different edges?) + * + * (1) build list of new faces; remove duplicates + * * use existing "v4=0 rule" for 3-vert faces + * (2) build list of existing faces for searching + * (3) from new face list, remove existing faces: + */ + + int len, nverts; + int i, j, k, new_face_count; + int good_faces; + SrchFaces *oldpair, *newpair, *tmppair, *tmppair2; + PyObject *tmp; + MFace *tmpface; + Mesh *mesh = self->mesh; + + /* make sure we get a sequence of tuples of something */ + + switch( PySequence_Size ( args ) ) { + case 1: /* better be a list or a tuple */ + args = PyTuple_GET_ITEM( args, 0 ); + if( !PySequence_Check ( args ) ) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected a sequence of tuple pairs" ); + Py_INCREF( args ); /* so we can safely DECREF later */ + break; + case 2: + case 3: + case 4: /* two to four args may be individual verts */ + tmp = PyTuple_GET_ITEM( args, 0 ); + if( PyTuple_Check( tmp ) ) {/* maybe just tuples, so use args as-is */ + Py_INCREF( args ); /* so we can safely DECREF later */ + break; + } + args = Py_BuildValue( "(O)", args ); + 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" ); + } + + /* make sure there is something to add */ + len = PySequence_Size( args ); + if( len == 0 ) { + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected at least one tuple" ); + } + + /* 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 ); + + /* not a tuple of MVerts... error */ + if( !PyTuple_Check( tmp ) || + EXPP_check_sequence_consistency( tmp, &MVert_Type ) != 1 ) { + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected sequence of MVert tuples" ); + } + + /* not the right number of MVerts... error */ + nverts = PyTuple_Size( tmp ); + if( nverts < 2 || nverts > 4 ) { + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "expected 2 to 4 MVerts per tuple" ); + } + + if( nverts != 2 ) /* new faces cannot have only 2 verts */ + ++new_face_count; + } + + /* OK, commit to allocating the search structures */ + newpair = (SrchFaces *)MEM_callocN( sizeof(SrchFaces)*new_face_count, + "MFacePairs" ); + + /* scan the input list and build the new face pair list */ + len = PySequence_Size( args ); + tmppair = newpair; + for( i = 0; i < len; ++i ) { + unsigned int vert[4]={0,0,0,0}; + unsigned char order[4]={0,1,2,3}; + tmp = PySequence_Fast_GET_ITEM( args, i ); + nverts = PyTuple_Size( tmp ); + + if( nverts == 2 ) /* again, ignore 2-vert tuples */ + break; + + /* get copies of vertices */ + for( j = 0; j < nverts; ++j ) { + BPy_MVert *e = (BPy_MVert *)PyTuple_GET_ITEM( tmp, j ); + vert[j] = e->index; + } + + /* convention says triangular faces always have v4 == 0 */ + if( nverts == 3 ) + tmppair->v[3] = 0; + + /* + * sort the verts before placing in pair list. the order of + * vertices in the face is very important, so keep track of + * the original order + */ + + for( j = nverts-1; j >= 0; --j ) { + for( k = 0; k < j; ++k ) { + if( vert[k] > vert[k+1] ) { + SWAP( int, vert[k], vert[k+1] ); + SWAP( char, order[k], order[k+1] ); + } else if( vert[k] == vert[k+1] ) { + MEM_freeN( newpair ); + Py_DECREF( args ); + return EXPP_ReturnPyObjError( PyExc_ValueError, + "tuple contains duplicate vertices" ); + } + } + tmppair->v[j] = vert[j]; + } + + /* pack order into a byte */ + tmppair->order = order[0]|(order[1]<<2)|(order[2]<<4)|(order[3]<<6); + ++tmppair; + } + + /* sort the new face pairs */ + qsort( newpair, new_face_count, sizeof(SrchFaces), mface_comp ); + + /* + * find duplicates in the new list and mark. if it's a duplicate, + * then mark by setting second vert index to 0 (a real edge won't have + * second vert index of 0 since verts are sorted) + */ + + good_faces = new_face_count; /* assume all faces good to start */ + + tmppair = newpair; /* "last good edge" */ + tmppair2 = &tmppair[1]; /* "current candidate edge" */ + for( i = 0; i < new_face_count; ++i ) { + if( mface_comp( tmppair, tmppair2 ) ) + tmppair = tmppair2; /* last != current, so current == last */ + else { + tmppair2->v[1] = 0; /* last == current, so mark as duplicate */ + --good_faces; /* one less good face */ + } + tmppair2++; + } + + /* if mesh has faces, see if any of the new faces are already in it */ + if( mesh->totface ) { + oldpair = (SrchFaces *)MEM_callocN( sizeof(SrchFaces)*mesh->totface, + "MFacePairs" ); + + tmppair = oldpair; + tmpface = mesh->mface; + for( i = 0; i < mesh->totface; ++i ) { + unsigned char order[4]={0,1,2,3}; + int verts[4]={tmpface->v1,tmpface->v2,tmpface->v3,tmpface->v4}; + + len = ( tmpface->v4 ) ? 3 : 2; + tmppair->v[3] = 0; /* for triangular faces */ + + /* sort the verts before placing in pair list here too */ + for( j = len; j >= 0; --j ) { + for( k = 0; k < j; ++k ) + if( verts[k] > verts[k+1] ) { + SWAP( int, verts[k], verts[k+1] ); + SWAP( unsigned char, order[k], order[k+1] ); + } + tmppair->v[j] = verts[j]; + } + + /* pack order into a byte */ + tmppair->order = order[0]|(order[1]<<2)|(order[2]<<4)|(order[3]<<6); + ++tmppair; + ++tmpface; + } + + /* sort the old face pairs */ + qsort( oldpair, mesh->totface, sizeof(SrchFaces), mface_comp ); + + /* eliminate new faces already in the mesh */ + tmppair = newpair; + for( i = len; i-- ; ) { + if( tmppair->v[1] ) { + if( bsearch( tmppair, oldpair, mesh->totface, + sizeof(SrchFaces), mface_comp ) ) { + tmppair->v[1] = 0; /* mark as duplicate */ + --good_faces; + } + } + tmppair++; + } + MEM_freeN( oldpair ); + } + + /* if any new faces are left, add to list */ + if( good_faces ) { + int totface = mesh->totface+good_faces; /* new face count */ + + /* allocate new face list */ + tmpface = MEM_callocN(totface*sizeof(MFace), "NMesh_addFaces"); + + /* if we're appending, copy the old face list and delete it */ + if( mesh->mface ) { + memcpy( tmpface, mesh->mface, mesh->totface*sizeof(MFace)); + MEM_freeN( mesh->mface ); + } + mesh->mface = tmpface; /* point to the new face list */ + + /* point to the first face we're going to add */ + tmpface = &mesh->mface[mesh->totface]; + tmppair = newpair; + + /* as we find a good face, add it */ + while ( good_faces ) { + if( tmppair->v[1] ) { + int i; + unsigned int index[4]; + unsigned char order = tmppair->order; + + /* unpack the order of the vertices */ + for( i = 0; i < 4; ++i ) { + index[(order & 0x03)] = i; + order >>= 2; + } + + /* now place vertices in the proper order */ + tmpface->v1 = tmppair->v[index[0]]; + tmpface->v2 = tmppair->v[index[1]]; + tmpface->v3 = tmppair->v[index[2]]; + tmpface->v4 = tmppair->v[index[3]]; + tmpface->flag = 0; + mesh->totface++; + ++tmpface; + --good_faces; + } + tmppair++; + } + } + + /* clean up and leave */ + mesh_update( mesh ); + Py_DECREF ( args ); + MEM_freeN( newpair ); + return EXPP_incr_ret( Py_None ); +} + +static struct PyMethodDef BPy_MFaceSeq_methods[] = { + {"extend", (PyCFunction)MFaceSeq_extend, METH_VARARGS, + "add edges to mesh"}, + {NULL, NULL, 0, NULL} +}; + +/************************************************************************ + * + * Python MFaceSeq_Type standard operations + * + ************************************************************************/ + +static void MFaceSeq_dealloc( BPy_MFaceSeq * self ) +{ + PyObject_DEL( self ); +} + +/*****************************************************************************/ +/* Python NMFaceSeq_Type structure definition: */ +/*****************************************************************************/ +PyTypeObject MFaceSeq_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender MFaceSeq", /* char *tp_name; */ + sizeof( BPy_MFaceSeq ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) MFaceSeq_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + NULL, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + &MFaceSeq_as_sequence, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + ( getiterfunc )MFaceSeq_getIter, /* getiterfunc tp_iter; */ + ( iternextfunc )MFaceSeq_nextIter, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + BPy_MFaceSeq_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +/************************************************************************ + * + * Python BPy_Mesh methods + * + ************************************************************************/ + +static PyObject *Mesh_calcNormals( BPy_Mesh * self ) +{ + Mesh *mesh = self->mesh; + + mesh_calc_normals( mesh->mvert, mesh->totvert, mesh->mface, + mesh->totface, NULL ); + return EXPP_incr_ret( Py_None ); +} + +static PyObject *Mesh_vertexShade( BPy_Mesh * self ) +{ + if( G.obedit ) + return EXPP_ReturnPyObjError(PyExc_RuntimeError, + "can't shade vertices while in edit mode" ); + + Base *base = FIRSTBASE; + while( base ) { + if( base->object->type == OB_MESH && + base->object->data == self->mesh ) { + base->flag |= SELECT; + set_active_base( base ); + make_vertexcol(); + countall(); + return EXPP_incr_ret( Py_None ); + } + base = base->next; + } + return EXPP_ReturnPyObjError(PyExc_RuntimeError, + "object not found in baselist!" ); +} + +/************************************************************************ + * + * Mesh attributes + * + ************************************************************************/ + +static PyObject *Mesh_getVerts( BPy_Mesh * self ) +{ + BPy_MVertSeq *seq = PyObject_NEW( BPy_MVertSeq, &MVertSeq_Type); + seq->mesh = self->mesh; + return (PyObject *)seq; +} + +static PyObject *Mesh_getEdges( BPy_Mesh * self ) +{ + BPy_MEdgeSeq *seq = PyObject_NEW( BPy_MEdgeSeq, &MEdgeSeq_Type); + seq->mesh = self->mesh; + return (PyObject *)seq; +} + +static PyObject *Mesh_getFaces( BPy_Mesh * self ) +{ + BPy_MFaceSeq *seq = PyObject_NEW( BPy_MFaceSeq, &MFaceSeq_Type); + seq->mesh = self->mesh; + return (PyObject *)seq; +} + +static PyObject *Mesh_getMaterials( BPy_Mesh *self ) +{ + return EXPP_PyList_fromMaterialList( self->mesh->mat, + self->mesh->totcol, 1 ); +} + +static int Mesh_setMaterials( BPy_Mesh *self, PyObject * value ) +{ + Material **matlist; + int len; + + if( !EXPP_check_sequence_consistency( value, &Material_Type ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "list should only contain materials or None)" ); + + len = PyList_Size( value ); + if( len > 16 ) + return EXPP_ReturnIntError( PyExc_TypeError, + "list can't have more than 16 materials" ); + + /* free old material list (if it exists) and adjust user counts */ + if( self->mesh->mat ) { + Mesh *me = self->mesh; + int i; + for( i = me->totcol; i-- > 0; ) + if( me->mat[i] ) + me->mat[i]->id.us--; + MEM_freeN( me->mat ); + } + + /* build the new material list, increment user count, store it */ + + matlist = EXPP_newMaterialList_fromPyList( value ); + EXPP_incr_mats_us( matlist, len ); + self->mesh->mat = matlist; + self->mesh->totcol = len; + +/**@ This is another ugly fix due to the weird material handling of blender. + * it makes sure that object material lists get updated (by their length) + * according to their data material lists, otherwise blender crashes. + * It just stupidly runs through all objects...BAD BAD BAD. + */ + + test_object_materials( ( ID * ) self->mesh ); + + return 0; +} + +static PyObject *Mesh_getMaxSmoothAngle( BPy_Mesh * self ) +{ + PyObject *attr = PyInt_FromLong( self->mesh->smoothresh ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); +} + +static int Mesh_setMaxSmoothAngle( BPy_Mesh *self, PyObject *value ) +{ + return EXPP_setIValueClamped( value, &self->mesh->smoothresh, + MESH_SMOOTHRESH_MIN, + MESH_SMOOTHRESH_MAX, 'h' ); +} + +static PyObject *Mesh_getSubDivLevels( BPy_Mesh * self ) +{ + PyObject *attr = Py_BuildValue( "(h,h)", + self->mesh->subdiv, self->mesh->subdivr ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "Py_BuildValue() failed" ); +} + +static int Mesh_setSubDivLevels( BPy_Mesh *self, PyObject *value ) +{ + int subdiv[2]; + int i; + PyObject *tmp; + +#if 0 + if( !PyArg_ParseTuple( value, "ii", &subdiv, &subdivr ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected (int, int) as argument" ); +#endif + if( !PyTuple_Check( value ) || PyTuple_Size( value ) != 2 ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected (int, int) as argument" ); + + for( i = 0; i < 2; i++ ) { + tmp = PyTuple_GET_ITEM( value, i ); + if( !PyInt_Check( tmp ) ) + return EXPP_ReturnIntError ( PyExc_TypeError, + "expected a list [int, int] as argument" ); + subdiv[i] = EXPP_ClampInt( PyInt_AsLong( tmp ), + MESH_SUBDIV_MIN, + MESH_SUBDIV_MAX ); + } + + self->mesh->subdiv = subdiv[0]; + self->mesh->subdivr = subdiv[1]; + return 0; +} + +static PyObject *Mesh_getName( BPy_Mesh * self ) +{ + PyObject *attr = PyString_FromString( self->mesh->id.name + 2 ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "couldn't get Mesh.name attribute" ); +} + +static int Mesh_setName( BPy_Mesh * self, PyObject * value ) +{ + char *name; + char buf[21]; + + name = PyString_AsString ( value ); + if( !name ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected string argument" ); + + PyOS_snprintf( buf, sizeof( buf ), "%s", name ); + + rename_id( &self->mesh->id, buf ); + + return 0; +} + +static PyObject *Mesh_getUsers( BPy_Mesh * self ) +{ + PyObject *attr = PyInt_FromLong( self->mesh->id.us ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "couldn't get Mesh.users attribute" ); +} + +static PyObject *Mesh_getFlag( BPy_Mesh * self, void *type ) +{ + PyObject *attr; + switch( (int)type ) { + case MESH_HASFACEUV: + attr = self->mesh->tface ? EXPP_incr_ret_True() : + EXPP_incr_ret_False(); + break; + case MESH_HASMCOL: + attr = self->mesh->mcol ? EXPP_incr_ret_True() : + EXPP_incr_ret_False(); + break; + case MESH_HASVERTUV: + attr = self->mesh->msticky ? EXPP_incr_ret_True() : + EXPP_incr_ret_False(); + break; + default: + attr = NULL; + } + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "couldn't get attribute" ); +} + +static PyObject *Mesh_getMode( BPy_Mesh * self ) +{ + PyObject *attr = PyInt_FromLong( self->mesh->flag ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "couldn't get Mesh.mode attribute" ); +} + +static int Mesh_setMode( BPy_Mesh *self, PyObject *value ) +{ + short param; + static short bitmask = ME_NOPUNOFLIP | ME_TWOSIDED | ME_AUTOSMOOTH; + + if( !PyInt_CheckExact ( value ) ) { + char errstr[128]; + sprintf ( errstr , "expected int bitmask of 0x%04x", bitmask ); + return EXPP_ReturnIntError( PyExc_TypeError, errstr ); + } + param = PyInt_AS_LONG ( value ); + + if( ( param & bitmask ) != param ) + return EXPP_ReturnIntError( PyExc_ValueError, + "invalid bit(s) set in mask" ); + + self->mesh->flag = param; + + return 0; +} + +static PyObject *Mesh_getActiveFace( BPy_Mesh * self ) +{ + TFace *face; + int i, totface; + + if( !self->mesh->tface ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "face has no texture values" ); + + face = self->mesh->tface; + totface = self->mesh->totface; + + for( i = 0; i < totface; ++face, ++i ) + if( face->flag & TF_ACTIVE ) { + PyObject *attr = PyInt_FromLong( i ); + + if( attr ) + return attr; + + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "PyInt_FromLong() failed" ); + } + + return EXPP_incr_ret( Py_None ); +} + +static int Mesh_setActiveFace( BPy_Mesh * self, PyObject * value ) +{ + TFace *face; + int param; + + /* if no texture faces, error */ + + if( !self->mesh->tface ) + return EXPP_ReturnIntError( PyExc_ValueError, + "face has no texture values" ); + + /* if param isn't an int, error */ + + if( !PyInt_CheckExact( value ) ) + return EXPP_ReturnIntError( PyExc_TypeError, + "expected an int argument" ); + + /* check for a valid index */ + + param = PyInt_AsLong( value ); + if( param < 0 || param > self->mesh->totface ) + return EXPP_ReturnIntError( PyExc_TypeError, + "face index out of range" ); + + face = self->mesh->tface; + + /* if requested face isn't already active, then inactivate all + * faces and activate the requested one */ + + if( !( face[param].flag & TF_ACTIVE ) ) { + int i; + for( i = self->mesh->totface; i > 0; ++face, --i ) + face->flag &= ~TF_ACTIVE; + self->mesh->tface[param].flag |= TF_ACTIVE; + } + return 0; +} + +static void Mesh_dealloc( BPy_Mesh * self ) +{ + PyObject_DEL( self ); +} + +static PyObject *Mesh_repr( BPy_Mesh * self ) +{ + return PyString_FromFormat( "[Mesh \"%s\"]", + self->mesh->id.name + 2 ); +} + +static struct PyMethodDef BPy_Mesh_methods[] = { + {"calcNormals", (PyCFunction)Mesh_calcNormals, METH_NOARGS, + "all recalculate vertex normals"}, + {"vertexShade", (PyCFunction)Mesh_vertexShade, METH_VARARGS, + "color vertices based on the current lighting setup"}, + {NULL, NULL, 0, NULL} +}; + +/*****************************************************************************/ +/* Python NMesh_Type attributes get/set structure: */ +/*****************************************************************************/ +static PyGetSetDef BPy_Mesh_getseters[] = { + {"verts", + (getter)Mesh_getVerts, (setter)NULL, + "The mesh's vertices (MVert)", + NULL}, + {"edges", + (getter)Mesh_getEdges, (setter)NULL, + "The mesh's edge data (MEdge)", + NULL}, + {"faces", + (getter)Mesh_getFaces, (setter)NULL, + "The mesh's face data (MFace)", + NULL}, + {"materials", + (getter)Mesh_getMaterials, (setter)Mesh_setMaterials, + "List of the mesh's materials", + NULL}, + {"degr", + (getter)Mesh_getMaxSmoothAngle, (setter)Mesh_setMaxSmoothAngle, + "The max angle for auto smoothing", + NULL}, + {"maxSmoothAngle", + (getter)Mesh_getMaxSmoothAngle, (setter)Mesh_setMaxSmoothAngle, + "deprecated: see 'degr'", + NULL}, + {"subDivLevels", + (getter)Mesh_getSubDivLevels, (setter)Mesh_setSubDivLevels, + "The display and rendering subdivision levels", + NULL}, + {"name", + (getter)Mesh_getName, (setter)Mesh_setName, + "The mesh's data name", + NULL}, + {"mode", + (getter)Mesh_getMode, (setter)Mesh_setMode, + "The mesh's mode bitfield", + NULL}, + + + {"faceUV", + (getter)Mesh_getFlag, (setter)NULL, + "UV-mapped textured faces enabled", + (void *)MESH_HASFACEUV}, + {"vertexColors", + (getter)Mesh_getFlag, (setter)NULL, + "Vertex colors for the mesh enabled", + (void *)MESH_HASMCOL}, + {"vertexUV", + (getter)Mesh_getFlag, (setter)NULL, + "'Sticky' flag for per vertex UV coordinates enabled", + (void *)MESH_HASVERTUV}, + {"activeFace", + (getter)Mesh_getActiveFace, (setter)Mesh_setActiveFace, + "Index of the mesh's active texture face (in UV editor)", + NULL}, + {"users", + (getter)Mesh_getUsers, (setter)NULL, + "Number of users of the mesh", + NULL}, + + {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ +}; + +/*****************************************************************************/ +/* Python Mesh_Type callback function prototypes: */ +/*****************************************************************************/ +static void Mesh_dealloc( BPy_Mesh * object ); + +/*****************************************************************************/ +/* Python Mesh_Type structure definition: */ +/*****************************************************************************/ +PyTypeObject Mesh_Type = { + PyObject_HEAD_INIT( NULL ) /* required py macro */ + 0, /* ob_size */ + /* For printing, in format "<module>.<name>" */ + "Blender Mesh", /* char *tp_name; */ + sizeof( BPy_Mesh ), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + ( destructor ) Mesh_dealloc,/* destructor tp_dealloc; */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + ( reprfunc ) Mesh_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + BPy_Mesh_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + BPy_Mesh_getseters, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + 0, /* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + +static PyObject *M_Mesh_Get( PyObject * self, PyObject * args ) +{ + char *name = NULL; + Mesh *mesh = NULL; + BPy_Mesh* obj; + + if( !PyArg_ParseTuple( args, "|s", &name ) ) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected zero or one string arguments" ); + + if( name ) { + mesh = ( Mesh * ) GetIdFromList( &( G.main->mesh ), name ); + + if( !mesh ) + return EXPP_incr_ret( Py_None ); + + obj = PyObject_NEW( BPy_Mesh, &Mesh_Type ); + obj->mesh = mesh; + return (PyObject *)obj; + } else { /* () - return a list with all meshes in the scene */ + PyObject *meshlist; + Link *link; + int index = 0; + + meshlist = PyList_New( BLI_countlist( &( G.main->mesh ) ) ); + + if( !meshlist ) + return EXPP_ReturnPyObjError( PyExc_MemoryError, + "couldn't create PyList" ); + + link = G.main->mesh.first; + index = 0; + while( link ) { + obj = ( BPy_Mesh * ) PyObject_NEW( BPy_Object, + &Mesh_Type ); + obj->mesh = ( Mesh * )link; + PyList_SetItem( meshlist, index, ( PyObject * ) obj ); + index++; + link = link->next; + } + return meshlist; + } +} + +#define SUBDIVIDE_EXPERIMENT +#undef SUBDIVIDE_EXPERIMENT + +#ifdef SUBDIVIDE_EXPERIMENT +#include <BIF_editmesh.h> + +/* + * test case + */ + +static PyObject *M_Mesh_Subdivide( PyObject * self, PyObject * args ) +{ + struct Object *object; + struct Base *basact; + char *name = NULL; + + PyArg_ParseTuple( args, "|s", &name ); + + object = GetObjectByName( name ); + + if( !object ) + return EXPP_ReturnPyObjError( PyExc_AttributeError, + "Unknown object specified." ); + + if( object->type != OB_MESH ) + return EXPP_ReturnPyObjError( PyExc_ValueError, + "Object specified is not a mesh." ); + + basact = BASACT; + + /* if already in edit mode, get out */ + + if( basact ) + exit_editmode( 1 ); + + /* enter mesh edit mode, apply subdivide, then exit edit mode */ + + G.obedit = object; + enter_editmode( ); + esubdivideflag(1, 0.0, G.scene->toolsettings->editbutflag & B_BEAUTY,1,0); + exit_editmode( 1 ); + + /* return to previous edit set-up (hopefully?) */ + + if( basact ) { + BASACT = basact; + enter_editmode( ); + } + + return EXPP_incr_ret( Py_None ); +} +#endif + +static struct PyMethodDef M_Mesh_methods[] = { + {"Get", (PyCFunction)M_Mesh_Get, METH_VARARGS, + "Get a mesh by name"}, +#ifdef SUBDIVIDE_EXPERIMENT + {"Subdivide", (PyCFunction)M_Mesh_Subdivide, METH_VARARGS, + "Subdivide selected edges in a mesh (experimental)"}, +#endif + {NULL, NULL, 0, NULL}, +}; + +static char M_Mesh_doc[] = "The Blender.Mesh submodule"; + +PyObject *Mesh_Init( void ) +{ + PyObject *submodule; + + if( PyType_Ready( &MCol_Type ) < 0 ) + return NULL; + if( PyType_Ready( &MVert_Type ) < 0 ) + return NULL; + if( PyType_Ready( &MVertSeq_Type ) < 0 ) + return NULL; + if( PyType_Ready( &MEdge_Type ) < 0 ) + return NULL; + if( PyType_Ready( &MEdgeSeq_Type ) < 0 ) + return NULL; + if( PyType_Ready( &MFace_Type ) < 0 ) + return NULL; + if( PyType_Ready( &MFaceSeq_Type ) < 0 ) + return NULL; + if( PyType_Ready( &Mesh_Type ) < 0 ) + return NULL; + + submodule = + Py_InitModule3( "Blender.Mesh", M_Mesh_methods, M_Mesh_doc ); + + return submodule; +} + +/* These are needed by Object.c */ + +PyObject *Mesh_CreatePyObject( Mesh * me ) +{ + BPy_Mesh *nmesh = PyObject_NEW( BPy_Mesh, &Mesh_Type ); + + if( !nmesh ) + return EXPP_ReturnPyObjError( PyExc_MemoryError, + "couldn't create BPy_Mesh object" ); + + nmesh->mesh = me; + + return ( PyObject * ) nmesh; +} + +int Mesh_CheckPyObject( PyObject * pyobj ) +{ + return ( pyobj->ob_type == &Mesh_Type ); +} + diff --git a/source/blender/python/api2_2x/Mesh.h b/source/blender/python/api2_2x/Mesh.h new file mode 100644 index 00000000000..d9b1f68800a --- /dev/null +++ b/source/blender/python/api2_2x/Mesh.h @@ -0,0 +1,123 @@ +/* + * $Id$ + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * This is a new part of Blender. + * + * Contributor(s): Ken Hughes + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + */ + +/* Most of this file comes from opy_nmesh.[ch] in the old bpython dir */ + +#ifndef EXPP_MESH_H +#define EXPP_MESH_H + +#include <Python.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "Material.h" +#include "Image.h" + +/* EXPP PyType Objects */ +extern PyTypeObject Mesh_Type; +extern PyTypeObject MVert_Type; +extern PyTypeObject MVertSeq_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_MVert_Check(v) ((v)->ob_type == &MVert_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 */ + +typedef struct { + PyObject_HEAD /* required python macro */ + MCol *color; +} BPy_MCol; /* a Mesh color: [r,g,b,a] */ + +typedef struct { + PyObject_VAR_HEAD /* required python macro */ + Mesh * mesh; + int index; +} BPy_MVert; /* a Mesh vertex */ + +typedef struct { + PyObject_VAR_HEAD /* required python macro */ + Mesh * mesh; + int iter; +} BPy_MVertSeq; /* a Mesh vertex sequence */ + +typedef struct { + PyObject_VAR_HEAD /* required python macro */ + Mesh * mesh; + int index; + int iter; +} BPy_MEdge; /* a Mesh edge */ + +typedef struct { + PyObject_VAR_HEAD /* required python macro */ + Mesh * mesh; + int iter; +} BPy_MEdgeSeq; /* a Mesh edge sequence */ + +typedef struct { + PyObject_VAR_HEAD /* required python macro */ + Mesh * mesh; + int index; + int iter; +} BPy_MFace; /* a Mesh face */ + +typedef struct { + PyObject_VAR_HEAD /* required python macro */ + Mesh * mesh; + int iter; +} BPy_MFaceSeq; /* a Mesh face sequence */ + +typedef struct { + PyObject_HEAD /* required python macro */ + Mesh * mesh; +} BPy_Mesh; + +/* PROTOS */ + +PyObject *Mesh_Init( void ); +PyObject *Mesh_CreatePyObject( Mesh * me ); +int Mesh_CheckPyObject( PyObject * pyobj ); + +#endif /* EXPP_MESH_H */ diff --git a/source/blender/python/api2_2x/doc/API_intro.py b/source/blender/python/api2_2x/doc/API_intro.py index 876c39b36b3..c2b4df3fbba 100644 --- a/source/blender/python/api2_2x/doc/API_intro.py +++ b/source/blender/python/api2_2x/doc/API_intro.py @@ -28,6 +28,7 @@ The Blender Python API Reference - L{Library} - L{Material} (*) - L{Mathutils} (*) + - L{Mesh} (*) - L{Metaball} - L{NMesh} (*) - L{Noise} |