[Bf-blender-cvs] [aa5a96430ea] blender2.8: Python: Add support for @ infix operator matrix multiplication

Andrew Hale noreply at git.blender.org
Fri Aug 10 15:19:58 CEST 2018


Commit: aa5a96430ea0741fac39b87fe17fb0faddadd3cf
Author: Andrew Hale
Date:   Fri Aug 10 14:53:38 2018 +0200
Branches: blender2.8
https://developer.blender.org/rBaa5a96430ea0741fac39b87fe17fb0faddadd3cf

Python: Add support for @ infix operator matrix multiplication

This differential revision implements the code for T56276

Reviewers: campbellbarton

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D3587

===================================================================

M	source/blender/python/mathutils/mathutils_Matrix.c
M	source/blender/python/mathutils/mathutils_Quaternion.c
M	source/blender/python/mathutils/mathutils_Vector.c
M	tests/python/bl_pyapi_mathutils.py

===================================================================

diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 70c400f99b8..3bd40cca5c6 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -2321,7 +2321,7 @@ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2)
 	return Matrix_CreatePyObject(mat, mat1->num_col, mat1->num_row, Py_TYPE(mat1));
 }
 /*------------------------obj * obj------------------------------
- * multiplication */
+ * element-wise multiplication */
 static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar)
 {
 	float tmat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
@@ -2332,7 +2332,6 @@ static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar)
 static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
 {
 	float scalar;
-	int vec_size;
 
 	MatrixObject *mat1 = NULL, *mat2 = NULL;
 
@@ -2348,15 +2347,124 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
 	}
 
 	if (mat1 && mat2) {
+#ifdef USE_MATHUTILS_ELEM_MUL
 		/* MATRIX * MATRIX */
 		float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
 
+		if ((mat1->num_row != mat2->num_row) || (mat1->num_col != mat2->num_col)) {
+			PyErr_SetString(PyExc_ValueError,
+							"matrix1 * matrix2: matrix1 number of rows/columns "
+							"and the matrix2 number of rows/columns must be the same");
+			return NULL;
+		}
+
+		mul_vn_vnvn(mat, mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row);
+
+		return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1));
+#endif
+	}
+	else if (mat2) {
+		/*FLOAT/INT * MATRIX */
+		if (((scalar = PyFloat_AsDouble(m1)) == -1.0f && PyErr_Occurred()) == 0) {
+			return matrix_mul_float(mat2, scalar);
+		}
+	}
+	else if (mat1) {
+		/* MATRIX * FLOAT/INT */
+		if (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0) {
+			return matrix_mul_float(mat1, scalar);
+		}
+	}
+
+	PyErr_Format(PyExc_TypeError,
+				 "Element-wise multiplication: "
+				 "not supported between '%.200s' and '%.200s' types",
+				 Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
+	return NULL;
+}
+/*------------------------obj *= obj------------------------------
+ * Inplace element-wise multiplication */
+static PyObject *Matrix_imul(PyObject *m1, PyObject *m2)
+{
+	float scalar;
+
+	MatrixObject *mat1 = NULL, *mat2 = NULL;
+
+	if (MatrixObject_Check(m1)) {
+		mat1 = (MatrixObject *)m1;
+		if (BaseMath_ReadCallback(mat1) == -1)
+			return NULL;
+	}
+	if (MatrixObject_Check(m2)) {
+		mat2 = (MatrixObject *)m2;
+		if (BaseMath_ReadCallback(mat2) == -1)
+			return NULL;
+	}
+
+	if (mat1 && mat2) {
+#ifdef USE_MATHUTILS_ELEM_MUL
+		/* MATRIX *= MATRIX */
+		if ((mat1->num_row != mat2->num_row) || (mat1->num_col != mat2->num_col)) {
+			PyErr_SetString(PyExc_ValueError,
+							"matrix1 *= matrix2: matrix1 number of rows/columns "
+							"and the matrix2 number of rows/columns must be the same");
+			return NULL;
+		}
+
+		mul_vn_vn(mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row);
+#else
+		PyErr_Format(PyExc_TypeError,
+					 "Inplace element-wise multiplication: "
+					 "not supported between '%.200s' and '%.200s' types",
+					 Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
+		return NULL;
+#endif
+	}
+	else if (mat1 && (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0)) {
+		/* MATRIX *= FLOAT/INT */
+		mul_vn_fl(mat1->matrix, mat1->num_row * mat1->num_col, scalar);
+	}
+	else {
+		PyErr_Format(PyExc_TypeError,
+					 "Inplace element-wise multiplication: "
+					 "not supported between '%.200s' and '%.200s' types",
+					 Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
+		return NULL;
+	}
+
+	(void)BaseMath_WriteCallback(mat1);
+	Py_INCREF(m1);
+	return m1;
+}
+/*------------------------obj @ obj------------------------------
+ * matrix multiplication */
+static PyObject *Matrix_matmul(PyObject *m1, PyObject *m2)
+{
+	int vec_size;
+
+	MatrixObject *mat1 = NULL, *mat2 = NULL;
+
+	if (MatrixObject_Check(m1)) {
+		mat1 = (MatrixObject *)m1;
+		if (BaseMath_ReadCallback(mat1) == -1)
+			return NULL;
+	}
+	if (MatrixObject_Check(m2)) {
+		mat2 = (MatrixObject *)m2;
+		if (BaseMath_ReadCallback(mat2) == -1)
+			return NULL;
+	}
+
+	if (mat1 && mat2) {
+		/* MATRIX @ MATRIX */
+		float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
+
 		int col, row, item;
 
 		if (mat1->num_col != mat2->num_row) {
 			PyErr_SetString(PyExc_ValueError,
-			                "matrix1 * matrix2: matrix1 number of columns "
-			                "and the matrix2 number of rows must be the same");
+							"matrix1 * matrix2: matrix1 number of columns "
+							"and the matrix2 number of rows must be the same");
 			return NULL;
 		}
 
@@ -2372,14 +2480,8 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
 
 		return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1));
 	}
-	else if (mat2) {
-		/*FLOAT/INT * MATRIX */
-		if (((scalar = PyFloat_AsDouble(m1)) == -1.0f && PyErr_Occurred()) == 0) {
-			return matrix_mul_float(mat2, scalar);
-		}
-	}
 	else if (mat1) {
-		/* MATRIX * VECTOR */
+		/* MATRIX @ VECTOR */
 		if (VectorObject_Check(m2)) {
 			VectorObject *vec2 = (VectorObject *)m2;
 			float tvec[MATRIX_MAX_DIM];
@@ -2398,20 +2500,69 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
 
 			return Vector_CreatePyObject(tvec, vec_size, Py_TYPE(m2));
 		}
-		/*FLOAT/INT * MATRIX */
-		else if (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0) {
-			return matrix_mul_float(mat1, scalar);
+	}
+
+	PyErr_Format(PyExc_TypeError,
+				 "Matrix multiplication: "
+				 "not supported between '%.200s' and '%.200s' types",
+				 Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
+	return NULL;
+}
+/*------------------------obj @= obj------------------------------
+ * inplace matrix multiplication */
+static PyObject *Matrix_imatmul(PyObject *m1, PyObject *m2)
+{
+	MatrixObject *mat1 = NULL, *mat2 = NULL;
+
+	if (MatrixObject_Check(m1)) {
+		mat1 = (MatrixObject *)m1;
+		if (BaseMath_ReadCallback(mat1) == -1)
+			return NULL;
+	}
+	if (MatrixObject_Check(m2)) {
+		mat2 = (MatrixObject *)m2;
+		if (BaseMath_ReadCallback(mat2) == -1)
+			return NULL;
+	}
+
+	if (mat1 && mat2) {
+		/* MATRIX @= MATRIX */
+		float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM];
+		int col, row, item;
+
+		if (mat1->num_col != mat2->num_row) {
+			PyErr_SetString(PyExc_ValueError,
+							"matrix1 * matrix2: matrix1 number of columns "
+							"and the matrix2 number of rows must be the same");
+			return NULL;
 		}
+
+		for (col = 0; col < mat2->num_col; col++) {
+			for (row = 0; row < mat1->num_row; row++) {
+				double dot = 0.0f;
+				for (item = 0; item < mat1->num_col; item++) {
+					dot += (double)(MATRIX_ITEM(mat1, row, item) * MATRIX_ITEM(mat2, item, col));
+				}
+				/* store in new matrix as overwriting original at this point will cause
+				 * subsequent iterations to use incorrect values */
+				mat[(col * mat1->num_row) + row] = (float)dot;
+			}
+		}
+
+		/* copy matrix back */
+		memcpy(mat1->matrix, mat, mat1->num_row * mat1->num_col);
 	}
 	else {
-		BLI_assert(!"internal error");
+		PyErr_Format(PyExc_TypeError,
+					 "Inplace matrix multiplication: "
+					 "not supported between '%.200s' and '%.200s' types",
+					 Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
+		return NULL;
 	}
 
-	PyErr_Format(PyExc_TypeError,
-	             "Matrix multiplication: "
-	             "not supported between '%.200s' and '%.200s' types",
-	             Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
-	return NULL;
+	(void)BaseMath_WriteCallback(mat1);
+	Py_INCREF(m1);
+	return m1;
 }
 
 /*-----------------PROTOCOL DECLARATIONS--------------------------*/
@@ -2527,7 +2678,7 @@ static PyNumberMethods Matrix_NumMethods = {
 	NULL,                   /*nb_float*/
 	NULL,                   /* nb_inplace_add */
 	NULL,                   /* nb_inplace_subtract */
-	NULL,                   /* nb_inplace_multiply */
+	(binaryfunc) Matrix_imul,  /* nb_inplace_multiply */
 	NULL,                   /* nb_inplace_remainder */
 	NULL,                   /* nb_inplace_power */
 	NULL,                   /* nb_inplace_lshift */
@@ -2540,6 +2691,8 @@ static PyNumberMethods Matrix_NumMethods = {
 	NULL,                   /* nb_inplace_floor_divide */
 	NULL,                   /* nb_inplace_true_divide */
 	NULL,                   /* nb_index */
+	(binaryfunc) Matrix_matmul,  /* nb_matrix_multiply */
+	(binaryfunc) Matrix_imatmul, /* nb_inplace_matrix_multiply */
 };
 
 PyDoc_STRVAR(Matrix_translation_doc,
diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c
index 48c18dd20c1..bb5983af535 100644
--- a/source/blender/python/mathutils/mathutils_Quaternion.c
+++ b/source/blender/python/mathutils/mathutils_Quaternion.c
@@ -834,7 +834,7 @@ static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar)
  * multiplication */
 static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
 {
-	float quat[QUAT_SIZE], scalar;
+	float scalar;
 	QuaternionObject *quat1 = NULL, *quat2 = NULL;
 
 	if (QuaternionObject_Check(q1)) {
@@ -848,9 +848,12 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
 			return NULL;
 	}
 
-	if (quat1 && quat2) { /* QUAT * QUAT (cross product) */
-		mul_qt_qtqt(quat, quat1->quat, quat2->quat);
+	if (quat1 && quat2) { /* QUAT * QUAT (element-wise product) */
+#ifdef USE_MATHUTILS_ELEM_MUL
+		float quat[QUAT_SIZE];
+		mul_vn_vnvn(quat, quat1->quat, quat2->quat, QUAT_SIZE);
 		return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
+#endif
 	}
 	/* the only case this can happen (for a supported type is "FLOAT * QUAT") */
 	else if (quat2) { /* FLOAT * QUAT */
@@ -858,17 +861,96 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
 			return quat_mul_float(quat2, scalar);
 		}
 	}
+	else if (quat1) { /* QUAT * FLOAT */
+		if ((((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
+			return quat_mul_float(quat1, scalar);
+		}
+	}
+
+	PyErr_Format(PyExc_TypeError,
+				 "Element-wise multiplication: "
+	             "not supported between '%.200s' and '%.200s' types",
+	             Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
+	return NULL;

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list