[Bf-committers] python mathutils api update proposal

Bassam Kurdali bkurdali at freefactory.org
Thu Feb 3 23:23:52 CET 2011


+1 on the changes, with some qualifications:

-for qualifying in place vs. returning new, whatabout verb/noun instead
of present/past tense of verb:
matrix.invert() inverts the matrix, returns none
matrix.inverse() returns the new inverse matrix.

in the rename methods we go from translation_part() to to_translation()
however, (nit picking) the 'to' implies that the matrix is being
changed, we are getting into the same region of ambiguity that you were
attempting to solve in the earlier example.
a suggustion is to use the verb 'derive'
as in :
matrix.derive_quaternion()
matrix.derive_translation()
or some other similar verb.
cheers
Bassam
On Thu, 2011-02-03 at 21:54 +0000, Campbell Barton wrote:
> On Thu, Feb 3, 2011 at 11:25 AM, Alex Fraser <alex at phatcore.com> wrote:
> > On Thu, Feb 3, 2011 at 10:08 PM, Campbell Barton <ideasman42 at gmail.com> wrote:
> >> Here are some proposed changes to the mathutils API I'd like to make
> >
> > As discussed on IRC, these changes look fine to me. One more
> > suggestion: can we allow vector components to be specified without the
> > use of a tuple, i.e. Vector(x,y,z) instead of Vector((x,y,z))?
> >
> > Cheers,
> > Alex (z0r)
> 
> Short answer is it doesn't make sense for Vectors to have one triple
> arg, so - we could.
> 
> Longer answer:
> 
> The reason I made this change is that Quaternions and Eulers have
> optional arguments which are more cumbersome to parse in C (see
> example below)
> 
> Since if we support both we would want to allow the same for other types: eg.
> Quaternion(w, x, y, z) # 2.4x, NOT SUPPORTED IN 2.5x
> Quaternion((w, x, y, z))
> Quaternion((x, y, z), angle)
> Quaternion(someOtherQuat)
> 
> Euler(x, y, z) # 2.4x, NOT SUPPORTED IN 2.5x
> Euler((x, y, z))
> Euler((x, y, z), 'ZXY')
> Euler(x, y, z, 'ZXY') # 2.4x didnt have order argument but perhaps
> this would be expected to work too?
> Euler(someOtherEuler)
> 
> Since quats have axis/angle and eulers have optional order argument
> there needs to be checks for argument lengths '0, 1, 2, 4' for quats,
> and '0, 1, 2, 3, (possibly) 4' for eulers.
> 
> The likelyhood we add new arguments to , Vector(x,y,z,w) is low so it
> could be an exception, but then its confusing as to why Vector(x,y,z)
> and Euler(x,y,z) doesn't.
> 
> My preference is to have consistency between types rather then have
> Vector() as the exception, even though I can see its a bit annoying.
> 
> To see what I mean about sloppy argument parsing, see comparison of
> 2.4x and 2.5x code below - both the same function though 2.5x uses
> mathutils_array_parse() utility function.
> The 2.4x code even had a memory leak for some time (refcount error).
> 
> # --- 2.5x Code
> static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args,
> PyObject *kwds)
> {
> 	PyObject *seq= NULL;
> 	double angle = 0.0f;
> 	float quat[QUAT_SIZE]= {0.0f, 0.0f, 0.0f, 0.0f};
> 
> 	if(kwds && PyDict_Size(kwds)) {
> 		PyErr_SetString(PyExc_TypeError, "mathutils.Quaternion(): takes no
> keyword args");
> 		return NULL;
> 	}
> 	
> 	if(!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle))
> 		return NULL;
> 
> 	switch(PyTuple_GET_SIZE(args)) {
> 	case 0:
> 		break;
> 	case 1:
> 		if (mathutils_array_parse(quat, QUAT_SIZE, QUAT_SIZE, seq,
> "mathutils.Quaternion()") == -1)
> 			return NULL;
> 		break;
> 	case 2:
> 		if (mathutils_array_parse(quat, 3, 3, seq, "mathutils.Quaternion()") == -1)
> 			return NULL;
> 		angle= fmod(angle + M_PI*2, M_PI*4) - M_PI*2; /* clamp because of
> precision issues */
> 		axis_angle_to_quat(quat, quat, angle);
> 		break;
> 	/* PyArg_ParseTuple assures no more then 2 */
> 	}
> 	return newQuaternionObject(quat, Py_NEW, type);
> }
> 
> 
> 
> 
> # --- 2.4x Code:
> PyObject *M_Mathutils_Quaternion(PyObject * self, PyObject * args)
> {
> 	PyObject *listObject = NULL, *n, *q, *f;
> 	int size, i;
> 	float quat[4];
> 	double norm = 0.0f, angle = 0.0f;
> 
> 	size = PySequence_Length(args);
> 	if (size == 1 || size == 2) { //seq?
> 		listObject = PySequence_GetItem(args, 0);
> 		if (PySequence_Check(listObject)) {
> 			size = PySequence_Length(listObject);
> 			if ((size == 4 && PySequence_Length(args) !=1) ||
> 				(size == 3 && PySequence_Length(args) !=2) || (size >4 || size < 3)) {
> 				// invalid args/size
> 				Py_DECREF(listObject);
> 				PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 				return NULL;
> 			}
> 	   		if(size == 3){ //get angle in axis/angle
> 				n = PySequence_GetItem(args, 1);
> 				if(n == NULL) { // parsed item not a number or getItem fail
> 					Py_DECREF(listObject);
> 					PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 					return NULL;
> 				}
> 				
> 				angle = PyFloat_AsDouble(n);
> 				Py_DECREF(n);
> 				
> 				if (angle==-1 && PyErr_Occurred()) {
> 					Py_DECREF(listObject);
> 					PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 					return NULL;
> 				}
> 			}
> 		}else{
> 			Py_DECREF(listObject); /* assume the list is teh second arg */
> 			listObject = PySequence_GetItem(args, 1);
> 			if (size>1 && PySequence_Check(listObject)) {
> 				size = PySequence_Length(listObject);
> 				if (size != 3) {
> 					// invalid args/size
> 					Py_DECREF(listObject);
> 					PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 					return NULL;
> 				}
> 				n = PySequence_GetItem(args, 0);
> 				if(n == NULL) { // parsed item not a number or getItem fail
> 					Py_DECREF(listObject);
> 					PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 					return NULL;
> 				}
> 				angle = PyFloat_AsDouble(n);
> 				Py_DECREF(n);
> 				
> 				if (angle==-1 && PyErr_Occurred()) {
> 					Py_DECREF(listObject);
> 					PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 					return NULL;
> 				}
> 			} else { // argument was not a sequence
> 				Py_XDECREF(listObject);
> 				PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 				return NULL;
> 			}
> 		}
> 	} else if (size == 0) { //returns a new empty quat
> 		return newQuaternionObject(NULL, Py_NEW);
> 	} else {
> 		Py_INCREF(args);
> 		listObject = args;
> 	}
> 
> 	if (size == 3) { // invalid quat size
> 		if(PySequence_Length(args) != 2){
> 			Py_DECREF(listObject);
> 			PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 			return NULL;
> 		}
> 	}else{
> 		if(size != 4){
> 			Py_DECREF(listObject);
> 			PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 			return NULL;
> 		}
> 	}
> 
> 	for (i=0; i<size; i++) { //parse
> 		q = PySequence_GetItem(listObject, i);
> 		if (q == NULL) { // Failed to read sequence
> 			Py_DECREF(listObject);
> 			PyErr_SetString(PyExc_RuntimeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 			return NULL;
> 		}
> 
> 		f = PyNumber_Float(q);
> 		if(f == NULL) { // parsed item not a number
> 			Py_DECREF(q);
> 			Py_DECREF(listObject);
> 			PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d
> numeric sequence expected or 3d vector and number\n");
> 			return NULL;
> 		}
> 
> 		quat[i] = (float)PyFloat_AS_DOUBLE(f);
> 		Py_DECREF(f);
> 		Py_DECREF(q);
> 	}
> 	if(size == 3){ //calculate the quat based on axis/angle
> 		norm = sqrt(quat[0] * quat[0] + quat[1] * quat[1] + quat[2] * quat[2]);
> 		quat[0] /= (float)norm;
> 		quat[1] /= (float)norm;
> 		quat[2] /= (float)norm;
> 
> 		angle = angle * (Py_PI / 180);
> 		quat[3] =(float) (sin(angle/ 2.0f)) * quat[2];
> 		quat[2] =(float) (sin(angle/ 2.0f)) * quat[1];
> 		quat[1] =(float) (sin(angle/ 2.0f)) * quat[0];
> 		quat[0] =(float) (cos(angle/ 2.0f));
> 	}
> 
> 	Py_DECREF(listObject);
> 	return newQuaternionObject(quat, Py_NEW);
> }
> 




More information about the Bf-committers mailing list