From b63e26e109ba8c651ea20f553e3ca8e0604fc73f Mon Sep 17 00:00:00 2001 From: Joseph Gilbert Date: Tue, 22 Nov 2005 17:59:49 +0000 Subject: [PATCH] Added some equvalency tests for the other math objects * ==, != are defined for matrix, quat, euler. --- .../blender/python/api2_2x/doc/Mathutils.py | 6 + source/blender/python/api2_2x/euler.c | 101 ++++++++++++++-- source/blender/python/api2_2x/gen_utils.c | 30 +++++ source/blender/python/api2_2x/gen_utils.h | 3 + source/blender/python/api2_2x/matrix.c | 111 ++++++++++++++++-- source/blender/python/api2_2x/quat.c | 105 ++++++++++++++--- source/blender/python/api2_2x/vector.c | 17 +-- 7 files changed, 319 insertions(+), 54 deletions(-) diff --git a/source/blender/python/api2_2x/doc/Mathutils.py b/source/blender/python/api2_2x/doc/Mathutils.py index e2440aa9020..4b00d045825 100644 --- a/source/blender/python/api2_2x/doc/Mathutils.py +++ b/source/blender/python/api2_2x/doc/Mathutils.py @@ -522,6 +522,8 @@ class Euler: @ivar wrapped: Whether or not this object is wrapping data directly @note: You can access a euler object like a sequence - x = euler[0] + @note: Comparison operators can be done: + - ==, != test numeric values within epsilon @attention: Euler 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 @@ -594,6 +596,8 @@ class Quaternion: @ivar axis: Vector representing the axis of rotation. @ivar angle: A scalar representing the amount of rotation in degrees. + @note: Comparison operators can be done: + - ==, != test numeric values within epsilon @note: Math can be performed on Quaternion classes - quat + quat - quat - quat @@ -699,6 +703,8 @@ class Matrix: - mat * float/int - mat * vec - mat * mat + @note: Comparison operators can be done: + - ==, != test numeric values within epsilon @note: You can access a quaternion object like a 2d sequence - x = matrix[0][1] - vector = matrix[2] diff --git a/source/blender/python/api2_2x/euler.c b/source/blender/python/api2_2x/euler.c index 3ea0e30d56c..99855f93b0d 100644 --- a/source/blender/python/api2_2x/euler.c +++ b/source/blender/python/api2_2x/euler.c @@ -248,6 +248,47 @@ static PyObject *Euler_repr(EulerObject * self) return PyString_FromString(str); } +//------------------------tp_richcmpr +//returns -1 execption, 0 false, 1 true +static PyObject* Euler_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) +{ + EulerObject *eulA = NULL, *eulB = NULL; + int result = 0; + + if (!EulerObject_Check(objectA) || !EulerObject_Check(objectB)){ + if (comparison_type == Py_NE){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } + } + eulA = (EulerObject*)objectA; + eulB = (EulerObject*)objectB; + + switch (comparison_type){ + case Py_EQ: + result = EXPP_VectorsAreEqual(eulA->eul, eulB->eul, 3, 1); + break; + case Py_NE: + result = EXPP_VectorsAreEqual(eulA->eul, eulB->eul, 3, 1); + if (result == 0){ + result = 1; + }else{ + result = 0; + } + break; + default: + printf("The result of the comparison could not be evaluated"); + break; + } + if (result == 1){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } +} +//------------------------tp_doc +static char EulerObject_doc[] = "This is a wrapper for euler objects."; //---------------------SEQUENCE PROTOCOLS------------------------ //----------------------------len(object)------------------------ //sequence length @@ -360,19 +401,53 @@ static PySequenceMethods Euler_SeqMethods = { }; //------------------PY_OBECT DEFINITION-------------------------- PyTypeObject euler_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size */ - "euler", /*tp_name */ - sizeof(EulerObject), /*tp_basicsize */ - 0, /*tp_itemsize */ - (destructor) Euler_dealloc, /*tp_dealloc */ - (printfunc) 0, /*tp_print */ - (getattrfunc) Euler_getattr, /*tp_getattr */ - (setattrfunc) Euler_setattr, /*tp_setattr */ - 0, /*tp_compare */ - (reprfunc) Euler_repr, /*tp_repr */ - 0, /*tp_as_number */ - &Euler_SeqMethods, /*tp_as_sequence */ + PyObject_HEAD_INIT(NULL) //tp_head + 0, //tp_internal + "euler", //tp_name + sizeof(EulerObject), //tp_basicsize + 0, //tp_itemsize + (destructor)Euler_dealloc, //tp_dealloc + 0, //tp_print + (getattrfunc)Euler_getattr, //tp_getattr + (setattrfunc) Euler_setattr, //tp_setattr + 0, //tp_compare + (reprfunc) Euler_repr, //tp_repr + 0, //tp_as_number + &Euler_SeqMethods, //tp_as_sequence + 0, //tp_as_mapping + 0, //tp_hash + 0, //tp_call + 0, //tp_str + 0, //tp_getattro + 0, //tp_setattro + 0, //tp_as_buffer + Py_TPFLAGS_DEFAULT, //tp_flags + EulerObject_doc, //tp_doc + 0, //tp_traverse + 0, //tp_clear + (richcmpfunc)Euler_richcmpr, //tp_richcompare + 0, //tp_weaklistoffset + 0, //tp_iter + 0, //tp_iternext + 0, //tp_methods + 0, //tp_members + 0, //tp_getset + 0, //tp_base + 0, //tp_dict + 0, //tp_descr_get + 0, //tp_descr_set + 0, //tp_dictoffset + 0, //tp_init + 0, //tp_alloc + 0, //tp_new + 0, //tp_free + 0, //tp_is_gc + 0, //tp_bases + 0, //tp_mro + 0, //tp_cache + 0, //tp_subclasses + 0, //tp_weaklist + 0 //tp_del }; //------------------------newEulerObject (internal)------------- //creates a new euler object diff --git a/source/blender/python/api2_2x/gen_utils.c b/source/blender/python/api2_2x/gen_utils.c index b88d8e22010..de29e4ba001 100644 --- a/source/blender/python/api2_2x/gen_utils.c +++ b/source/blender/python/api2_2x/gen_utils.c @@ -41,6 +41,36 @@ #include "constant.h" +//---------------------- EXPP_FloatsAreEqual ------------------------- +//Floating point comparisons +//floatStep = number of representable floats allowable in between +// float A and float B to be considered equal. +int EXPP_FloatsAreEqual(float A, float B, int floatSteps) +{ + int a, b, delta; + assert(floatSteps > 0 && floatSteps < (4 * 1024 * 1024)); + a = *(int*)&A; + if (a < 0) + a = 0x80000000 - a; + b = *(int*)&B; + if (b < 0) + b = 0x80000000 - b; + delta = abs(a - b); + if (delta <= floatSteps) + return 1; + return 0; +} +//---------------------- EXPP_VectorsAreEqual ------------------------- +//Builds on EXPP_FloatsAreEqual to test vectors +int EXPP_VectorsAreEqual(float *vecA, float *vecB, int size, int floatSteps){ + + int x; + for (x=0; x< size; x++){ + if (EXPP_FloatsAreEqual(vecA[x], vecB[x], floatSteps) == 0) + return 0; + } + return 1; +} //---------------------- EXPP_GetModuleConstant ------------------------- //Helper function for returning a module constant PyObject *EXPP_GetModuleConstant(char *module, char *constant) diff --git a/source/blender/python/api2_2x/gen_utils.h b/source/blender/python/api2_2x/gen_utils.h index bf68d1a3c9b..5aa2cf5a878 100644 --- a/source/blender/python/api2_2x/gen_utils.h +++ b/source/blender/python/api2_2x/gen_utils.h @@ -62,6 +62,9 @@ #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False #endif +int EXPP_FloatsAreEqual(float A, float B, int floatSteps); +int EXPP_VectorsAreEqual(float *vecA, float *vecB, int size, int floatSteps); + PyObject *EXPP_GetModuleConstant(char *module, char *constant); int StringEqual( const char *string1, const char *string2 ); diff --git a/source/blender/python/api2_2x/matrix.c b/source/blender/python/api2_2x/matrix.c index a0a02a98575..6b90a3a6494 100644 --- a/source/blender/python/api2_2x/matrix.c +++ b/source/blender/python/api2_2x/matrix.c @@ -374,7 +374,58 @@ static PyObject *Matrix_repr(MatrixObject * self) return PyString_FromString(str); } +//------------------------tp_richcmpr +//returns -1 execption, 0 false, 1 true +static PyObject* Matrix_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) +{ + MatrixObject *matA = NULL, *matB = NULL; + int result = 0; + if (!MatrixObject_Check(objectA) || !MatrixObject_Check(objectB)){ + if (comparison_type == Py_NE){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } + } + matA = (MatrixObject*)objectA; + matB = (MatrixObject*)objectB; + + if (matA->colSize != matB->colSize && matA->rowSize != matB->rowSize){ + if (comparison_type == Py_NE){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } + } + + switch (comparison_type){ + case Py_EQ: + //contigPtr is basically a really long vector + result = EXPP_VectorsAreEqual(matA->contigPtr, matB->contigPtr, + (matA->rowSize * matA->colSize), 1); + break; + case Py_NE: + result = EXPP_VectorsAreEqual(matA->contigPtr, matB->contigPtr, + (matA->rowSize * matA->colSize), 1); + if (result == 0){ + result = 1; + }else{ + result = 0; + } + break; + default: + printf("The result of the comparison could not be evaluated"); + break; + } + if (result == 1){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } +} +//------------------------tp_doc +static char MatrixObject_doc[] = "This is a wrapper for matrix objects."; //---------------------SEQUENCE PROTOCOLS------------------------ //----------------------------len(object)------------------------ //sequence length @@ -734,19 +785,53 @@ static PyNumberMethods Matrix_NumMethods = { }; //------------------PY_OBECT DEFINITION-------------------------- PyTypeObject matrix_Type = { - PyObject_HEAD_INIT(NULL) /* required python macro */ - 0, /*ob_size */ - "Matrix", /*tp_name */ - sizeof(MatrixObject), /*tp_basicsize */ - 0, /*tp_itemsize */ - (destructor) Matrix_dealloc, /*tp_dealloc */ - (printfunc) 0, /*tp_print */ - (getattrfunc) Matrix_getattr, /*tp_getattr */ - (setattrfunc) Matrix_setattr, /*tp_setattr */ - 0, /*tp_compare */ - (reprfunc) Matrix_repr, /*tp_repr */ - &Matrix_NumMethods, /*tp_as_number */ - &Matrix_SeqMethods, /*tp_as_sequence */ + PyObject_HEAD_INIT(NULL) //tp_head + 0, //tp_internal + "matrix", //tp_name + sizeof(MatrixObject), //tp_basicsize + 0, //tp_itemsize + (destructor)Matrix_dealloc, //tp_dealloc + 0, //tp_print + (getattrfunc)Matrix_getattr, //tp_getattr + (setattrfunc) Matrix_setattr, //tp_setattr + 0, //tp_compare + (reprfunc) Matrix_repr, //tp_repr + &Matrix_NumMethods, //tp_as_number + &Matrix_SeqMethods, //tp_as_sequence + 0, //tp_as_mapping + 0, //tp_hash + 0, //tp_call + 0, //tp_str + 0, //tp_getattro + 0, //tp_setattro + 0, //tp_as_buffer + Py_TPFLAGS_DEFAULT, //tp_flags + MatrixObject_doc, //tp_doc + 0, //tp_traverse + 0, //tp_clear + (richcmpfunc)Matrix_richcmpr, //tp_richcompare + 0, //tp_weaklistoffset + 0, //tp_iter + 0, //tp_iternext + 0, //tp_methods + 0, //tp_members + 0, //tp_getset + 0, //tp_base + 0, //tp_dict + 0, //tp_descr_get + 0, //tp_descr_set + 0, //tp_dictoffset + 0, //tp_init + 0, //tp_alloc + 0, //tp_new + 0, //tp_free + 0, //tp_is_gc + 0, //tp_bases + 0, //tp_mro + 0, //tp_cache + 0, //tp_subclasses + 0, //tp_weaklist + 0 //tp_del }; //------------------------newMatrixObject (internal)------------- //creates a new matrix object diff --git a/source/blender/python/api2_2x/quat.c b/source/blender/python/api2_2x/quat.c index 2b5fcc5966c..3c7cb4b839a 100644 --- a/source/blender/python/api2_2x/quat.c +++ b/source/blender/python/api2_2x/quat.c @@ -187,7 +187,9 @@ static PyObject *Quaternion_getattr(QuaternionObject * self, char *name) } Normalise(vec); //If the axis of rotation is 0,0,0 set it to 1,0,0 - for zero-degree rotations - if (vec[0] < 1e-44 && vec[1] < 1e-44 && vec[2] < 1e-44){ + if( EXPP_FloatsAreEqual(vec[0], 0.0f, 10) && + EXPP_FloatsAreEqual(vec[1], 0.0f, 10) && + EXPP_FloatsAreEqual(vec[2], 0.0f, 10) ){ vec[0] = 1.0f; } return (PyObject *) newVectorObject(vec, 3, Py_NEW); @@ -251,6 +253,47 @@ static PyObject *Quaternion_repr(QuaternionObject * self) return PyString_FromString(str); } +//------------------------tp_richcmpr +//returns -1 execption, 0 false, 1 true +static PyObject* Quaternion_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) +{ + QuaternionObject *quatA = NULL, *quatB = NULL; + int result = 0; + + if (!QuaternionObject_Check(objectA) || !QuaternionObject_Check(objectB)){ + if (comparison_type == Py_NE){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } + } + quatA = (QuaternionObject*)objectA; + quatB = (QuaternionObject*)objectB; + + switch (comparison_type){ + case Py_EQ: + result = EXPP_VectorsAreEqual(quatA->quat, quatB->quat, 4, 1); + break; + case Py_NE: + result = EXPP_VectorsAreEqual(quatA->quat, quatB->quat, 4, 1); + if (result == 0){ + result = 1; + }else{ + result = 0; + } + break; + default: + printf("The result of the comparison could not be evaluated"); + break; + } + if (result == 1){ + return EXPP_incr_ret(Py_True); + }else{ + return EXPP_incr_ret(Py_False); + } +} +//------------------------tp_doc +static char QuaternionObject_doc[] = "This is a wrapper for quaternion objects."; //---------------------SEQUENCE PROTOCOLS------------------------ //----------------------------len(object)------------------------ //sequence length @@ -529,19 +572,53 @@ static PyNumberMethods Quaternion_NumMethods = { }; //------------------PY_OBECT DEFINITION-------------------------- PyTypeObject quaternion_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size */ - "quaternion", /*tp_name */ - sizeof(QuaternionObject), /*tp_basicsize */ - 0, /*tp_itemsize */ - (destructor) Quaternion_dealloc, /*tp_dealloc */ - (printfunc) 0, /*tp_print */ - (getattrfunc) Quaternion_getattr, /*tp_getattr */ - (setattrfunc) Quaternion_setattr, /*tp_setattr */ - 0, /*tp_compare */ - (reprfunc) Quaternion_repr, /*tp_repr */ - &Quaternion_NumMethods, /*tp_as_number */ - &Quaternion_SeqMethods, /*tp_as_sequence */ +PyObject_HEAD_INIT(NULL) //tp_head + 0, //tp_internal + "quaternion", //tp_name + sizeof(QuaternionObject), //tp_basicsize + 0, //tp_itemsize + (destructor)Quaternion_dealloc, //tp_dealloc + 0, //tp_print + (getattrfunc)Quaternion_getattr, //tp_getattr + (setattrfunc) Quaternion_setattr, //tp_setattr + 0, //tp_compare + (reprfunc) Quaternion_repr, //tp_repr + &Quaternion_NumMethods, //tp_as_number + &Quaternion_SeqMethods, //tp_as_sequence + 0, //tp_as_mapping + 0, //tp_hash + 0, //tp_call + 0, //tp_str + 0, //tp_getattro + 0, //tp_setattro + 0, //tp_as_buffer + Py_TPFLAGS_DEFAULT, //tp_flags + QuaternionObject_doc, //tp_doc + 0, //tp_traverse + 0, //tp_clear + (richcmpfunc)Quaternion_richcmpr, //tp_richcompare + 0, //tp_weaklistoffset + 0, //tp_iter + 0, //tp_iternext + 0, //tp_methods + 0, //tp_members + 0, //tp_getset + 0, //tp_base + 0, //tp_dict + 0, //tp_descr_get + 0, //tp_descr_set + 0, //tp_dictoffset + 0, //tp_init + 0, //tp_alloc + 0, //tp_new + 0, //tp_free + 0, //tp_is_gc + 0, //tp_bases + 0, //tp_mro + 0, //tp_cache + 0, //tp_subclasses + 0, //tp_weaklist + 0 //tp_del }; //------------------------newQuaternionObject (internal)------------- //creates a new quaternion object diff --git a/source/blender/python/api2_2x/vector.c b/source/blender/python/api2_2x/vector.c index af151d87d4e..a0afaf60c1e 100644 --- a/source/blender/python/api2_2x/vector.c +++ b/source/blender/python/api2_2x/vector.c @@ -687,18 +687,6 @@ static double vec_magnitude(float *data, int size) } return (double)sqrt(dot); } -//------------------------vec_equality(internal) -static int vec_equality(float *dataA, float *dataB, int size, double epsilon) -{ - int i; - - for(i=0; i dataB[i]) && ((dataA[i] - epsilon) < dataB[i]))){ - return 0; - } - } - return 1; -} //------------------------tp_richcmpr //returns -1 execption, 0 false, 1 true PyObject* Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) @@ -744,10 +732,10 @@ PyObject* Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_t } break; case Py_EQ: - result = vec_equality(vecA->vec, vecB->vec, vecA->size, epsilon); + result = EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->size, 1); break; case Py_NE: - result = vec_equality(vecA->vec, vecB->vec, vecA->size, epsilon); + result = EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->size, 1); if (result == 0){ result = 1; }else{ @@ -771,6 +759,7 @@ PyObject* Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_t } break; default: + printf("The result of the comparison could not be evaluated"); break; } if (result == 1){