[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