/* * $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 "BIF_space.h" /* allqueue */ #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 ) { allqueue( REDRAWVIEW3D, 1); Object_updateDag( (void *) mesh ); } /* * Since all faces must have 3 or 4 verts, we can't have v3 or v4 be zero. * If that happens during the deletion, we have to shuffle the vertices * around; otherwise it can cause an Eeekadoodle or worse. */ static void eeek_fix( MFace *tmpface , int len4 ) { if( len4 && !tmpface->v4 ) { unsigned int tmp = tmpface->v1; tmpface->v1 = tmpface->v4; tmpface->v4 = tmpface->v3; tmpface->v3 = tmpface->v2; tmpface->v2 = tmp; } else if( !tmpface->v3 ) { unsigned int tmp = tmpface->v1; tmpface->v1 = tmpface->v2; tmpface->v2 = tmpface->v3; if( !len4 ) { tmpface->v3 = tmp; } else { tmpface->v3 = tmpface->v4; tmpface->v4 = tmp; } } } #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 MCol_getAttr", (int)type ); 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 MCol_setAttr", (int)type ); 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 "." */ "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; } /************************************************************************ * * BPy_MVert attributes * ************************************************************************/ /* * get a vertex's coordinate */ static PyObject *MVert_getCoord( BPy_MVert * self ) { MVert *v; if( BPy_MVert_Check( self ) ) v = &((Mesh *)self->data)->mvert[self->index]; else v = (MVert *)self->data; return newVectorObject( v->co, 3, Py_WRAP ); } /* * set a vertex's coordinate */ static int MVert_setCoord( BPy_MVert * self, VectorObject * value ) { int i; MVert *v; if( BPy_MVert_Check( self ) ) v = &((Mesh *)self->data)->mvert[self->index]; else v = (MVert *)self->data; 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; 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 ) { float no[3]; int i; MVert *v; if( BPy_MVert_Check( self ) ) v = &((Mesh *)self->data)->mvert[self->index]; else v = (MVert *)self->data; 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 ) { MVert *v; if( BPy_MVert_Check( self ) ) v = &((Mesh *)self->data)->mvert[self->index]; else v = (MVert *)self->data; return EXPP_getBitfield( &v->flag, SELECT, 'b' ); } /* * set a vertex's select status */ static int MVert_setSel( BPy_MVert *self, PyObject *value ) { MVert *v; if( BPy_MVert_Check( self ) ) v = &((Mesh *)self->data)->mvert[self->index]; else v = (MVert *)self->data; return EXPP_setBitfield( value, &v->flag, SELECT, 'b' ); } /* * get a vertex's UV coordinates */ static PyObject *MVert_getUVco( BPy_MVert *self ) { Mesh *me = (Mesh *)self->data; if( !me->msticky ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "mesh has no 'sticky' coordinates" ); return newVectorObject( me->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}; Mesh *me = (Mesh *)self->data; struct MSticky *v; int i; /* * at least for now, don't allow creation of sticky coordinates if they * don't already exist */ if( !me->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 = &me->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 */ }; static PyGetSetDef BPy_PVert_getseters[] = { {"co", (getter)MVert_getCoord, (setter)MVert_setCoord, "vertex's coordinate", NULL}, {"no", (getter)MVert_getNormal, (setter)NULL, "vertex's normal", NULL}, {"sel", (getter)MVert_getSel, (setter)MVert_setSel, "vertex's select status", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /************************************************************************ * * Python MVert_Type standard operations * ************************************************************************/ static void MVert_dealloc( BPy_MVert * self ) { if( BPy_PVert_Check( self ) ) /* free memory of thick objects */ MEM_freeN ( self->data ); PyObject_DEL( self ); } static int MVert_compare( BPy_MVert * a, BPy_MVert * b ) { return( a->data == b->data && a->index == b->index ) ? 0 : -1; } static PyObject *MVert_repr( BPy_MVert * self ) { char format[512]; char index[24]; MVert *v; if( BPy_MVert_Check( self ) ) { v = &((Mesh *)self->data)->mvert[self->index]; sprintf ( index, "%d", self->index ); } else { v = (MVert *)self->data; BLI_strncpy( index, "(None)", 24 ); } sprintf( format, "[MVert (%f %f %f) (%f %f %f) %s]", 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), 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 "." */ "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 }; /************************************************************************ * * Python PVert_Type standard operations * ************************************************************************/ static int PVert_compare( BPy_MVert * a, BPy_MVert * b ) { return( a->data == b->data ) ? 0 : -1; } /************************************************************************ * * Python PVert_Type structure definition * ************************************************************************/ PyTypeObject PVert_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender PVert", /* 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 ) PVert_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_PVert_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 }; /* * create 'thin' or 'thick' MVert objects * * there are two types of objects; thin (wrappers for mesh vertex) and thick * (not contains in mesh). Thin objects are MVert_Type and thick are * PVert_Type. For thin objects, data is a pointer to a Mesh and index * is the vertex's index in mesh->mvert. For thick objects, data is a * pointer to an MVert; index is unused. */ /* * create a thin MVert object */ static PyObject *MVert_CreatePyObject( Mesh *mesh, int i ) { BPy_MVert *obj = (BPy_MVert *)PyObject_NEW( BPy_MVert, &MVert_Type ); if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); obj->index = i; obj->data = mesh; return (PyObject *)obj; } /* * create a thick MVert object */ static PyObject *PVert_CreatePyObject( MVert *vert ) { BPy_MVert *obj = PyObject_NEW( BPy_MVert, &PVert_Type ); MVert *newvert; if( !obj ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyObject_New() failed" ); newvert = (MVert *)MEM_callocN( sizeof( MVert ), "MVert" ); if( !newvert ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "MEM_callocN() failed" ); memcpy( newvert, vert, sizeof( MVert ) ); obj->data = newvert; return (PyObject *)obj; } /************************************************************************ * * Vertex sequence * ************************************************************************/ static int MVertSeq_len( BPy_MVertSeq * self ) { return self->mesh->totvert; } /* * retrive a single MVert from somewhere in the vertex list */ 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 ); }; /* * retrieve a slice of the vertex list (as a Python list) * * Python is nice enough to handle negative indices for us: if the user * specifies -1, Python will pass us len()-1. So we can just check for * indices in the range 0:len()-1. Of course, we should never actually * return the high index, but up to one less. */ static PyObject *MVertSeq_slice( BPy_MVertSeq *self, int low, int high ) { PyObject *list; int i; /* * Python list slice operator returns empty list when asked for a slice * outside the list, or if indices are reversed (low > high). Clamp * our input to do the same. */ if( low < 0 ) low = 0; if( high > self->mesh->totvert ) high = self->mesh->totvert; if( low > high ) low = high; list = PyList_New( high-low ); if( !list ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "PyList_New() failed" ); /* * return Py_NEW copies of requested vertices */ for( i = low; i < high; ++i ) PyList_SET_ITEM( list, i-low, PVert_CreatePyObject( (void *)&self->mesh->mvert[i] ) ); return list; } /* * assign a single MVert to somewhere in the vertex list */ static int MVertSeq_assign_item( BPy_MVertSeq * self, int i, BPy_MVert *v ) { MVert *dst = &self->mesh->mvert[i]; MVert *src; if( !v ) return EXPP_ReturnIntError( PyExc_IndexError, "del() not supported" ); if( i < 0 || i >= self->mesh->totvert ) return EXPP_ReturnIntError( PyExc_IndexError, "array index out of range" ); if( BPy_MVert_Check( v ) ) src = &((Mesh *)v->data)->mvert[v->index]; else src = (MVert *)v->data; memcpy( dst, src, sizeof(MVert) ); // mesh_update( self->mesh ); return 0; }; static int MVertSeq_assign_slice( BPy_MVertSeq *self, int low, int high, PyObject *args ) { int len, i; if( !PyList_Check( args ) ) return EXPP_ReturnIntError( PyExc_IndexError, "can only assign lists of MVerts" ); len = PyList_Size( args ); /* * Python list slice assign operator allows for changing the size of the * destination list, by replacement and appending.... * * >>> l=[1,2,3,4] * >>> m=[11,12,13,14] * >>> l[5:7]=m * >>> print l * [1, 2, 3, 4, 11, 12, 13, 14] * >>> l=[1,2,3,4] * >>> l[2:3]=m * >>> print l * [1, 2, 11, 12, 13, 14, 4] * * We don't want the size of the list to change (at least not at time * point in development) so we are a little more strict: * - low and high indices must be in range [0:len()] * - high-low == PyList_Size(v) */ if( low < 0 || high > self->mesh->totvert || low > high ) return EXPP_ReturnIntError( PyExc_IndexError, "invalid slice range" ); if( high-low != len ) return EXPP_ReturnIntError( PyExc_IndexError, "slice range and input list sizes must be equal" ); for( i = low; i < high; ++i ) { BPy_MVert *v = (BPy_MVert *)PyList_GET_ITEM( args, i-low ); MVert *dst = &self->mesh->mvert[i]; MVert *src; if( BPy_MVert_Check( v ) ) src = &((Mesh *)v->data)->mvert[v->index]; else src = (MVert *)v->data; memcpy( dst, src, sizeof(MVert) ); } // mesh_update( self->mesh ); return 0; } static PySequenceMethods MVertSeq_as_sequence = { ( inquiry ) MVertSeq_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) MVertSeq_item, /* sq_item */ ( intintargfunc ) MVertSeq_slice, /* sq_slice */ ( intobjargproc ) MVertSeq_assign_item, /* sq_ass_item */ ( intintobjargproc ) MVertSeq_assign_slice, /* 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, newlen; 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 */ tmp = PyTuple_GET_ITEM( args, 0 ); if( !VectorObject_Check ( tmp ) ) { if( !PySequence_Check ( tmp ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected a sequence of tuple triplets" ); args = tmp; } 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" ); } newlen = mesh->totvert + len; newvert = MEM_callocN( sizeof( MVert )*newlen, "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] = (float)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 */ memcpy( tmpvert->co, co, sizeof(co) ); /* 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; /* * maybe not quite done; if there are keys, have to fix those lists up */ if( mesh->key ) { KeyBlock *currkey = mesh->key->block.first; float *fp, *newkey; while( currkey ) { /* create key list, copy existing data if any */ newkey = MEM_callocN(mesh->key->elemsize*newlen, "keydata"); if( currkey->data ) { memcpy( newkey, currkey->data, mesh->totvert*mesh->key->elemsize ); MEM_freeN( currkey->data ); currkey->data = newkey; } /* add data for new vertices */ fp = (float *)((char *)currkey->data + mesh->key->elemsize*mesh->totvert ); tmpvert = mesh->mvert + mesh->totvert; for( i = newlen - mesh->totvert; i > 0; --i ) { VECCOPY(fp, tmpvert->co); fp += 3; tmpvert++; } currkey->totelem = newlen; currkey = currkey->next; } } /* set final vertex list size */ mesh->totvert = newlen; #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 vertices 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 "." */ "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 = (short)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 "." */ "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 "." */ "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 = (char)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 = (short)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; iuv[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; iuv[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