[Bf-committers] python mathutils api update proposal

Campbell Barton ideasman42 at gmail.com
Thu Feb 3 22:54:30 CET 2011


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);
}

-- 
- Campbell


More information about the Bf-committers mailing list