Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-05-03 21:52:03 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-05-03 21:52:03 +0400
commit8a83aff9f56c5b8f8240847835578f1a23e2e9cf (patch)
tree894e8c4f8a4225aed4e7ad7ecd491cb8f8606384 /source
parent03a0770df57835ca3551642fc6ba879bdde9564a (diff)
[#18678] Swizzle properties for Mathutils.Vector
patch from Alex Fraser (z0r) eg. - vec.xyz = vec.zyx - vec.xy = vec.zw - vec.xxy = vec.wzz - vec.yzyz = vec.yxyx See http://en.wikipedia.org/wiki/Swizzling_(computer_graphics) made some minor modifications to this patch. tested access times and adding 336 attributes to vectors doesn't make a noticeable differences to speed of existing axis attributes (x,y,z,w) - thanks to python dict lookups.
Diffstat (limited to 'source')
-rw-r--r--source/blender/python/BPY_interface.c2
-rw-r--r--source/blender/python/api2_2x/Mathutils.c15
-rw-r--r--source/blender/python/api2_2x/Mathutils.h2
-rw-r--r--source/blender/python/api2_2x/doc/Mathutils.py17
-rw-r--r--source/blender/python/api2_2x/vector.c311
-rw-r--r--source/blender/python/api2_2x/vector.h4
-rw-r--r--source/gameengine/Ketsji/KX_PythonInit.cpp3
7 files changed, 347 insertions, 7 deletions
diff --git a/source/blender/python/BPY_interface.c b/source/blender/python/BPY_interface.c
index d86c317ee7e..1c98d6a5a59 100644
--- a/source/blender/python/BPY_interface.c
+++ b/source/blender/python/BPY_interface.c
@@ -286,6 +286,8 @@ void BPY_end_python( void )
free_libblock( &G.main->script, script );
}
+ Mathutils_Free(NULL);
+
Py_Finalize( );
BPyMenu_RemoveAllEntries( ); /* freeing bpymenu mem */
diff --git a/source/blender/python/api2_2x/Mathutils.c b/source/blender/python/api2_2x/Mathutils.c
index 9c73ca16e70..83a58193a8b 100644
--- a/source/blender/python/api2_2x/Mathutils.c
+++ b/source/blender/python/api2_2x/Mathutils.c
@@ -107,6 +107,11 @@ struct PyMethodDef M_Mathutils_methods[] = {
/*----------------------------MODULE INIT-------------------------*/
/* from can be Blender.Mathutils or GameLogic.Mathutils for the BGE */
+void Mathutils_Free(void * closure)
+{
+ Vector_Free();
+}
+
#if (PY_VERSION_HEX >= 0x03000000)
static struct PyModuleDef M_Mathutils_module_def = {
{}, /* m_base */
@@ -117,7 +122,7 @@ static struct PyModuleDef M_Mathutils_module_def = {
0, /* m_reload */
0, /* m_traverse */
0, /* m_clear */
- 0, /* m_free */
+ Mathutils_Free, /* m_free */
};
#endif
@@ -126,10 +131,13 @@ PyObject *Mathutils_Init(const char *from)
PyObject *submodule;
//seed the generator for the rand function
- BLI_srand((unsigned int) (PIL_check_seconds_timer() *
- 0x7FFFFFFF));
+ BLI_srand((unsigned int) (PIL_check_seconds_timer() * 0x7FFFFFFF));
/* needed for getseters */
+ if(!(vector_Type.tp_flags & Py_TPFLAGS_READY))
+ if (Vector_Init() != 0) /* setup dynamic getset array */
+ return NULL;
+
if( PyType_Ready( &vector_Type ) < 0 )
return NULL;
if( PyType_Ready( &matrix_Type ) < 0 )
@@ -147,6 +155,7 @@ PyObject *Mathutils_Init(const char *from)
return (submodule);
}
+
//-----------------------------METHODS----------------------------
//----------------column_vector_multiplication (internal)---------
//COLUMN VECTOR Multiplication (Matrix X Vector)
diff --git a/source/blender/python/api2_2x/Mathutils.h b/source/blender/python/api2_2x/Mathutils.h
index 7406c5c4810..ec6c3f548d9 100644
--- a/source/blender/python/api2_2x/Mathutils.h
+++ b/source/blender/python/api2_2x/Mathutils.h
@@ -38,6 +38,8 @@
#include "euler.h"
PyObject *Mathutils_Init( const char * from );
+void Mathutils_Free(void *);
+
PyObject *row_vector_multiplication(VectorObject* vec, MatrixObject * mat);
PyObject *column_vector_multiplication(MatrixObject * mat, VectorObject* vec);
PyObject *quat_rotation(PyObject *arg1, PyObject *arg2);
diff --git a/source/blender/python/api2_2x/doc/Mathutils.py b/source/blender/python/api2_2x/doc/Mathutils.py
index 524d1fb6d4c..fc0c4ed10fa 100644
--- a/source/blender/python/api2_2x/doc/Mathutils.py
+++ b/source/blender/python/api2_2x/doc/Mathutils.py
@@ -400,6 +400,7 @@ class Vector:
The Vector object
=================
This object gives access to Vectors in Blender.
+ @group Axises: x, y, z, w
@ivar x: The x value.
@ivar y: The y value.
@ivar z: The z value (if any).
@@ -420,6 +421,16 @@ class Vector:
- -vec
@note: You can access a vector object like a sequence
- x = vector[0]
+ - vec_a[:] vec_b
+ - vec2d[:] vec3d[:2]
+ @note: Vectors support 'swizzle' operations
+ - vec.xyz = vec.zyx
+ - vec.xy = vec.zw
+ - vec.xxy = vec.wzz
+ - vec.yzyz = vec.yxyx
+
+ See U{http://en.wikipedia.org/wiki/Swizzling_(computer_graphics)}
+
@attention: Vector data can be wrapped or non-wrapped. When a object is wrapped it
means that the object will give you direct access to the data inside of blender. Modification
of this object will directly change the data inside of blender. To copy a wrapped object
@@ -535,6 +546,7 @@ class Euler:
The Euler object
================
This object gives access to Eulers in Blender.
+ @group Axises: x, y, z, w
@ivar x: The heading value in degrees.
@ivar y: The pitch value in degrees.
@ivar z: The roll value in degrees.
@@ -596,7 +608,7 @@ class Euler:
"""
Return a matrix representation of the euler.
@rtype: Matrix object
- @return: A roation matrix representation of the euler.
+ @return: A 3x3 roation matrix representation of the euler.
"""
def toQuat():
@@ -611,6 +623,7 @@ class Quaternion:
The Quaternion object
=====================
This object gives access to Quaternions in Blender.
+ @group Axises: x, y, z, w
@ivar w: The w value.
@ivar x: The x value.
@ivar y: The y value.
@@ -716,7 +729,7 @@ class Quaternion:
"""
Return a matrix representation of the quaternion.
@rtype: Matrix object
- @return: A rotation matrix representation of the quaternion.
+ @return: A 3x3 rotation matrix representation of the quaternion.
"""
class Matrix:
diff --git a/source/blender/python/api2_2x/vector.c b/source/blender/python/api2_2x/vector.c
index dd53cca9054..bef2917b0fa 100644
--- a/source/blender/python/api2_2x/vector.c
+++ b/source/blender/python/api2_2x/vector.c
@@ -20,7 +20,7 @@
* All rights reserved.
*
*
- * Contributor(s): Willian P. Germano & Joseph Gilbert, Ken Hughes
+ * Contributor(s): Willian P. Germano, Joseph Gilbert, Ken Hughes, Alex Fraser, Campbell Barton
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -31,6 +31,24 @@
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
+#define MAX_DIMENSIONS 4
+/* Swizzle axes get packed into a single value that is used as a closure. Each
+ axis uses SWIZZLE_BITS_PER_AXIS bits. The first bit (SWIZZLE_VALID_AXIS) is
+ used as a sentinel: if it is unset, the axis is not valid. */
+#define SWIZZLE_BITS_PER_AXIS 3
+#define SWIZZLE_VALID_AXIS 0x4
+#define SWIZZLE_AXIS 0x3
+
+
+/* An array of getseters, some of which have members on the stack and some on
+ the heap.
+
+ Vector_dyn_getseters: The getseter structures. Terminated with a NULL sentinel.
+ Vector_dyn_names: All the names of the getseters that were allocated on the heap.
+ Each name is terminated with a null character, but there is
+ currently no way to find the length of this array. */
+PyGetSetDef* Vector_dyn_getseters = NULL;
+char* Vector_dyn_names = NULL;
/*-------------------------DOC STRINGS ---------------------------*/
char Vector_Zero_doc[] = "() - set all values in the vector to 0";
@@ -42,6 +60,7 @@ char Vector_Resize4D_doc[] = "() - resize a vector to [x,y,z,w]";
char Vector_ToTrackQuat_doc[] = "(track, up) - extract a quaternion from the vector and the track and up axis";
char Vector_reflect_doc[] = "(mirror) - return a vector reflected on the mirror normal";
char Vector_copy_doc[] = "() - return a copy of the vector";
+char Vector_swizzle_doc[] = "Swizzle: Get or set axes in specified order";
/*-----------------------METHOD DEFINITIONS ----------------------*/
struct PyMethodDef Vector_methods[] = {
{"zero", (PyCFunction) Vector_Zero, METH_NOARGS, Vector_Zero_doc},
@@ -1121,7 +1140,287 @@ static PyGetSetDef Vector_getseters[] = {
{NULL,NULL,NULL,NULL,NULL} /* Sentinel */
};
+/* Get a new Vector according to the provided swizzle. This function has little
+ error checking, as we are in control of the inputs: the closure is set by us
+ in Vector_createSwizzleGetSeter. */
+static PyObject *Vector_getSwizzle(VectorObject * self, void *closure)
+{
+ size_t axisA;
+ size_t axisB;
+ float vec[MAX_DIMENSIONS];
+ unsigned int swizzleClosure;
+
+ /* Unpack the axes from the closure into an array. */
+ axisA = 0;
+ swizzleClosure = (unsigned int) closure;
+ while (swizzleClosure & SWIZZLE_VALID_AXIS)
+ {
+ axisB = swizzleClosure & SWIZZLE_AXIS;
+ vec[axisA] = self->vec[axisB];
+ swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS;
+ axisA++;
+ }
+
+ return newVectorObject(vec, axisA, Py_NEW);
+}
+
+/* Set the items of this vector using a swizzle.
+ - If value is a vector or list this operates like an array copy, except that
+ the destination is effectively re-ordered as defined by the swizzle. At
+ most min(len(source), len(dest)) values will be copied.
+ - If the value is scalar, it is copied to all axes listed in the swizzle.
+ - If an axis appears more than once in the swizzle, the final occurrance is
+ the one that determines its value.
+
+ Returns 0 on success and -1 on failure. On failure, the vector will be
+ unchanged. */
+static int Vector_setSwizzle(VectorObject * self, PyObject * value, void *closure)
+{
+ VectorObject *vecVal;
+ PyObject *item;
+ size_t listLen;
+ float scalarVal;
+
+ size_t axisB;
+ size_t axisA;
+ unsigned int swizzleClosure;
+
+ float vecTemp[MAX_DIMENSIONS];
+
+ /* Check that the closure can be used with this vector: even 2D vectors have
+ swizzles defined for axes z and w, but they would be invalid. */
+ swizzleClosure = (unsigned int) closure;
+ while (swizzleClosure & SWIZZLE_VALID_AXIS)
+ {
+ axisA = swizzleClosure & SWIZZLE_AXIS;
+ if (axisA >= self->size)
+ {
+ PyErr_SetString(PyExc_AttributeError, "Error: vector does not have specified axis.\n");
+ return -1;
+ }
+ swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS;
+ }
+
+ if (VectorObject_Check(value))
+ {
+ /* Copy vector contents onto swizzled axes. */
+ vecVal = (VectorObject*) value;
+ axisB = 0;
+ swizzleClosure = (unsigned int) closure;
+ while (swizzleClosure & SWIZZLE_VALID_AXIS && axisB < vecVal->size)
+ {
+ axisA = swizzleClosure & SWIZZLE_AXIS;
+ vecTemp[axisA] = vecVal->vec[axisB];
+
+ swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS;
+ axisB++;
+ }
+ memcpy(self->vec, vecTemp, axisB * sizeof(float));
+ return 0;
+ }
+ else if (PyList_Check(value))
+ {
+ /* Copy list contents onto swizzled axes. */
+ listLen = PyList_Size(value);
+ swizzleClosure = (unsigned int) closure;
+ axisB = 0;
+ while (swizzleClosure & SWIZZLE_VALID_AXIS && axisB < listLen)
+ {
+ item = PyList_GetItem(value, axisB);
+ if (!PyNumber_Check(item))
+ {
+ PyErr_SetString(PyExc_AttributeError, "Error: vector does not have specified axis.\n");
+ return -1;
+ }
+ scalarVal = (float)PyFloat_AsDouble(item);
+
+ axisA = swizzleClosure & SWIZZLE_AXIS;
+ vecTemp[axisA] = scalarVal;
+
+ swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS;
+ axisB++;
+ }
+ memcpy(self->vec, vecTemp, axisB * sizeof(float));
+ return 0;
+ }
+ else if (PyNumber_Check(value))
+ {
+ /* Assign the same value to each axis. */
+ scalarVal = (float)PyFloat_AsDouble(value);
+ swizzleClosure = (unsigned int) closure;
+ while (swizzleClosure & SWIZZLE_VALID_AXIS)
+ {
+ axisA = swizzleClosure & SWIZZLE_AXIS;
+ self->vec[axisA] = scalarVal;
+
+ swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS;
+ }
+ return 0;
+ }
+ else
+ {
+ PyErr_SetString( PyExc_TypeError, "Expected a Vector, list or scalar value." );
+ return -1;
+ }
+}
+
+/* Create a getseter that operates on the axes defined in swizzle.
+ Parameters:
+ gsd: An empty PyGetSetDef object. This will be modified.
+ swizzle: An array of axis indices.
+ dimensions: The number of axes to swizzle. Must be >= 2 and <=
+ MAX_DIMENSIONS.
+ name: A pointer to string that the name will be stored in. This is
+ purely to reduce the number of allocations. Before this function
+ returns, name will be advanced to the point immediately after
+ the name of the new getseter. Therefore, do not attempt to read
+ its contents. */
+static void Vector_createSwizzleGetSeter
+(
+ PyGetSetDef *gsd,
+ unsigned short *swizzle,
+ size_t dimensions,
+ char **name
+)
+{
+ const char axes[] = {'x', 'y', 'z', 'w'};
+ unsigned int closure;
+ int i;
+
+ /* Convert the index array into named axes. Store the name in the string
+ that was passed in, and make the getseter structure point to the same
+ address. */
+ gsd->name = *name;
+ for (i = 0; i < dimensions; i++)
+ gsd->name[i] = axes[swizzle[i]];
+ gsd->name[i] = '\0';
+ /* Advance the name pointer to the next available address. */
+ (*name) = (*name) + dimensions + 1;
+
+ gsd->get = (getter)Vector_getSwizzle;
+ gsd->set = (setter)Vector_setSwizzle;
+
+ gsd->doc = Vector_swizzle_doc;
+
+ /* Pack the axes into a single value to use as the closure. Pack these in
+ in reverse so they come out in the right order when unpacked. */
+ closure = 0;
+ for (i = MAX_DIMENSIONS - 1; i >= 0; i--)
+ {
+ closure = closure << SWIZZLE_BITS_PER_AXIS;
+ if (i < dimensions)
+ closure = closure | swizzle[i] | SWIZZLE_VALID_AXIS;
+ }
+ gsd->closure = (void*) closure;
+}
+
+/* Create and append all implicit swizzle getseters to an existing array of
+ getseters.
+ Parameters:
+ Vector_getseters:
+ A null-terminated array of getseters that have been manually
+ defined.
+ resGds: The resultant array of getseters. This will be a combination of
+ static and manually-defined getseters. Use Vector_DelGetseters
+ to free this array.
+
+ Returns: 0 on success, -1 on failure. On failure, resGsd will be left
+ untouched. */
+static int Vector_AppendSwizzleGetseters(void)
+{
+ int len;
+ int len_orig;
+ int len_names;
+ unsigned int i;
+ unsigned short swizzle[MAX_DIMENSIONS];
+ char *name;
+
+ /* Count the explicit getseters. */
+ for (len_orig = 0; Vector_getseters[len_orig].name != NULL; len_orig++);
+
+ /* Then there are 4^4 + 4^3 + 4^2 = 336 swizzles. */
+ len = len_orig + 336 + 1; /* Plus one sentinel. */
+
+ /* That means 4^4 names of length 4 + 1, 4^3 names of length 3 + 1, and 4^2
+ names of length 2 + 1 (including a null character at the end of each)
+ = (4^4) * 5 + (4^3) * 4 + (4^2) * 3
+ = 1584 */
+ len_names = 1584;
+
+ if(Vector_dyn_getseters) { /* Should never happen */
+ PyMem_Del(Vector_dyn_getseters);
+ Vector_dyn_getseters= NULL;
+ }
+ Vector_dyn_getseters = PyMem_New(PyGetSetDef, len * sizeof(PyGetSetDef));
+ if (Vector_dyn_getseters == NULL) {
+ PyErr_SetString(PyExc_MemoryError, "Could not allocate memory for swizzle getseters.");
+ return -1;
+ }
+ memset(Vector_dyn_getseters, 0, len * sizeof(PyGetSetDef));
+
+ if(Vector_dyn_names) { /* Should never happen */
+ PyMem_Del(Vector_dyn_names);
+ Vector_dyn_getseters= NULL;
+ }
+ Vector_dyn_names = PyMem_New(char, len_names * sizeof(char));
+ if (Vector_dyn_names == NULL) {
+ PyErr_SetString(PyExc_MemoryError, "Could not allocate memory for swizzle getseter names.");
+ PyMem_Del(Vector_dyn_getseters);
+ return -1;
+ }
+ memset(Vector_dyn_names, 0, len_names * sizeof(char));
+
+ /* Do a shallow copy of the getseters. A deep clone can't be done because
+ we don't know how much memory each closure needs. */
+ memcpy(Vector_dyn_getseters, Vector_getseters, len_orig * sizeof(PyGetSetDef));
+
+ /* Create the swizzle functions. The pointer for name will be advanced by
+ Vector_createSwizzleGetSeter. */
+ name = Vector_dyn_names;
+ i = len_orig;
+ for (swizzle[0] = 0; swizzle[0] < MAX_DIMENSIONS; swizzle[0]++)
+ {
+ for (swizzle[1] = 0; swizzle[1] < MAX_DIMENSIONS; swizzle[1]++)
+ {
+ Vector_createSwizzleGetSeter(&Vector_dyn_getseters[i++], swizzle, 2, &name);
+ for (swizzle[2] = 0; swizzle[2] < MAX_DIMENSIONS; swizzle[2]++)
+ {
+ Vector_createSwizzleGetSeter(&Vector_dyn_getseters[i++], swizzle, 3, &name);
+ for (swizzle[3] = 0; swizzle[3] < MAX_DIMENSIONS; swizzle[3]++)
+ {
+ Vector_createSwizzleGetSeter(&Vector_dyn_getseters[i++], swizzle, 4, &name);
+ }
+ }
+ }
+ }
+
+ /* No need to add a sentinel - memory was initialised to zero above. */
+ vector_Type.tp_getset = Vector_dyn_getseters;
+
+ return 0;
+}
+/* Delete an array of getseters that was created by
+ Vector_AppendSwizzleGetseters. It is safe to call this even if the structure
+ members are NULL. */
+static void Vector_DelGetseters(void)
+{
+ /* Free strings that were allocated on the heap. */
+ if (Vector_dyn_names != NULL) {
+ PyMem_Del(Vector_dyn_names);
+ Vector_dyn_names = NULL;
+ }
+
+ /* Free the structure arrays themselves. */
+ if (Vector_dyn_getseters != NULL) {
+ PyMem_Del(Vector_dyn_getseters);
+ Vector_dyn_getseters = NULL;
+ }
+
+ /* for the blenderplayer that will initialize multiple times :| */
+ vector_Type.tp_getset = Vector_getseters;
+ vector_Type.tp_flags = 0; /* maybe python does this when finalizing? - wont hurt anyway */
+}
/* Note
Py_TPFLAGS_CHECKTYPES allows us to avoid casting all types to Vector when coercing
@@ -1189,7 +1488,7 @@ PyTypeObject vector_Type = {
/*** Attribute descriptor and subclassing stuff ***/
Vector_methods, /* struct PyMethodDef *tp_methods; */
NULL, /* struct PyMemberDef *tp_members; */
- Vector_getseters, /* struct PyGetSetDef *tp_getset; */
+ Vector_getseters, /* struct PyGetSetDef *tp_getset; */
NULL, /* struct _typeobject *tp_base; */
NULL, /* PyObject *tp_dict; */
NULL, /* descrgetfunc tp_descr_get; */
@@ -1268,3 +1567,11 @@ PyObject *Vector_Negate(VectorObject * self)
/*###################################################################
###########################DEPRECATED##############################*/
+int Vector_Init(void)
+{
+ return Vector_AppendSwizzleGetseters();
+}
+
+void Vector_Free() {
+ Vector_DelGetseters();
+}
diff --git a/source/blender/python/api2_2x/vector.h b/source/blender/python/api2_2x/vector.h
index 9ec2eff8047..16a4a1f462a 100644
--- a/source/blender/python/api2_2x/vector.h
+++ b/source/blender/python/api2_2x/vector.h
@@ -43,6 +43,10 @@ typedef struct {
short wrapped; /* is wrapped data? */
} VectorObject;
+void Vector_Free(void);
+int Vector_Init(void);
+
+
/*prototypes*/
PyObject *Vector_Zero( VectorObject * self );
PyObject *Vector_Normalize( VectorObject * self );
diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp
index 18fd990dc51..e4252b55fb1 100644
--- a/source/gameengine/Ketsji/KX_PythonInit.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInit.cpp
@@ -1708,6 +1708,9 @@ void exitGamePlayerPythonScripting()
/* since python restarts we cant let the python backup of the sys.path hang around in a global pointer */
restorePySysPath(); /* get back the original sys.path and clear the backup */
+ /* Modules that need freeing */
+ Mathutils_Free(NULL);
+
Py_Finalize();
bpy_import_main_set(NULL);
}