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
This commit is contained in:
parent
693ecdf7d3
commit
aa5a96430e
Notes:
blender-bot
2023-02-14 19:27:40 +01:00
Referenced by issue blender/blender-addons#56276, Python: Add support for @ infix operator matrix multiplication
|
@ -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,6 +2332,114 @@ static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar)
|
|||
static PyObject *Matrix_mul(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 */
|
||||
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;
|
||||
|
@ -2348,15 +2456,15 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2)
|
|||
}
|
||||
|
||||
if (mat1 && mat2) {
|
||||
/* MATRIX * MATRIX */
|
||||
/* 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,21 +2500,70 @@ 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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(!"internal error");
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Matrix multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(m1)->tp_name, Py_TYPE(m2)->tp_name);
|
||||
"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 {
|
||||
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;
|
||||
}
|
||||
|
||||
(void)BaseMath_WriteCallback(mat1);
|
||||
Py_INCREF(m1);
|
||||
return m1;
|
||||
}
|
||||
|
||||
/*-----------------PROTOCOL DECLARATIONS--------------------------*/
|
||||
static PySequenceMethods Matrix_SeqMethods = {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
/*------------------------obj *= obj------------------------------
|
||||
* inplace multiplication */
|
||||
static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2)
|
||||
{
|
||||
float scalar;
|
||||
QuaternionObject *quat1 = NULL, *quat2 = NULL;
|
||||
|
||||
if (QuaternionObject_Check(q1)) {
|
||||
quat1 = (QuaternionObject *)q1;
|
||||
if (BaseMath_ReadCallback(quat1) == -1)
|
||||
return NULL;
|
||||
}
|
||||
if (QuaternionObject_Check(q2)) {
|
||||
quat2 = (QuaternionObject *)q2;
|
||||
if (BaseMath_ReadCallback(quat2) == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (quat1 && quat2) { /* QUAT *= QUAT (inplace element-wise product) */
|
||||
#ifdef USE_MATHUTILS_ELEM_MUL
|
||||
mul_vn_vn(quat1->quat, quat2->quat, QUAT_SIZE);
|
||||
#else
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Inplace element-wise multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
else if (quat1 && (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
|
||||
/* QUAT *= FLOAT */
|
||||
mul_qt_fl(quat1->quat, scalar);
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
|
||||
(void)BaseMath_WriteCallback(quat1);
|
||||
Py_INCREF(q1);
|
||||
return q1;
|
||||
}
|
||||
/*------------------------obj @ obj------------------------------
|
||||
* quaternion multiplication */
|
||||
static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2)
|
||||
{
|
||||
float quat[QUAT_SIZE];
|
||||
QuaternionObject *quat1 = NULL, *quat2 = NULL;
|
||||
|
||||
if (QuaternionObject_Check(q1)) {
|
||||
quat1 = (QuaternionObject *)q1;
|
||||
if (BaseMath_ReadCallback(quat1) == -1)
|
||||
return NULL;
|
||||
}
|
||||
if (QuaternionObject_Check(q2)) {
|
||||
quat2 = (QuaternionObject *)q2;
|
||||
if (BaseMath_ReadCallback(quat2) == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
|
||||
mul_qt_qtqt(quat, quat1->quat, quat2->quat);
|
||||
return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
|
||||
}
|
||||
else if (quat1) {
|
||||
/* QUAT * VEC */
|
||||
/* QUAT @ VEC */
|
||||
if (VectorObject_Check(q2)) {
|
||||
VectorObject *vec2 = (VectorObject *)q2;
|
||||
float tvec[3];
|
||||
|
||||
if (vec2->size != 3) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Vector multiplication: "
|
||||
"only 3D vector rotations (with quats) "
|
||||
"currently supported");
|
||||
"Vector multiplication: "
|
||||
"only 3D vector rotations (with quats) "
|
||||
"currently supported");
|
||||
return NULL;
|
||||
}
|
||||
if (BaseMath_ReadCallback(vec2) == -1) {
|
||||
|
@ -880,21 +962,48 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
|
|||
|
||||
return Vector_CreatePyObject(tvec, 3, Py_TYPE(vec2));
|
||||
}
|
||||
/* QUAT * FLOAT */
|
||||
else if ((((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
|
||||
return quat_mul_float(quat1, scalar);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(!"internal error");
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Quaternion multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
|
||||
"Quaternion multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
/*------------------------obj @= obj------------------------------
|
||||
* inplace quaternion multiplication */
|
||||
static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2)
|
||||
{
|
||||
float quat[QUAT_SIZE];
|
||||
QuaternionObject *quat1 = NULL, *quat2 = NULL;
|
||||
|
||||
if (QuaternionObject_Check(q1)) {
|
||||
quat1 = (QuaternionObject *)q1;
|
||||
if (BaseMath_ReadCallback(quat1) == -1)
|
||||
return NULL;
|
||||
}
|
||||
if (QuaternionObject_Check(q2)) {
|
||||
quat2 = (QuaternionObject *)q2;
|
||||
if (BaseMath_ReadCallback(quat2) == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
|
||||
mul_qt_qtqt(quat, quat1->quat, quat2->quat);
|
||||
copy_qt_qt(quat1->quat, quat);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Inplace quaternion multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(q1)->tp_name, Py_TYPE(q2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)BaseMath_WriteCallback(quat1);
|
||||
Py_INCREF(q1);
|
||||
return q1;
|
||||
}
|
||||
|
||||
/* -obj
|
||||
* returns the negative of this object*/
|
||||
|
@ -952,7 +1061,7 @@ static PyNumberMethods Quaternion_NumMethods = {
|
|||
NULL, /*nb_float*/
|
||||
NULL, /* nb_inplace_add */
|
||||
NULL, /* nb_inplace_subtract */
|
||||
NULL, /* nb_inplace_multiply */
|
||||
(binaryfunc) Quaternion_imul, /* nb_inplace_multiply */
|
||||
NULL, /* nb_inplace_remainder */
|
||||
NULL, /* nb_inplace_power */
|
||||
NULL, /* nb_inplace_lshift */
|
||||
|
@ -965,6 +1074,8 @@ static PyNumberMethods Quaternion_NumMethods = {
|
|||
NULL, /* nb_inplace_floor_divide */
|
||||
NULL, /* nb_inplace_true_divide */
|
||||
NULL, /* nb_index */
|
||||
(binaryfunc) Quaternion_matmul, /* nb_matrix_multiply */
|
||||
(binaryfunc) Quaternion_imatmul, /* nb_inplace_matrix_multiply */
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(Quaternion_axis_doc,
|
||||
|
|
|
@ -1706,11 +1706,132 @@ static PyObject *vector_mul_float(VectorObject *vec, const float scalar)
|
|||
mul_vn_vn_fl(tvec, vec->vec, vec->size, scalar);
|
||||
return Vector_CreatePyObject_alloc(tvec, vec->size, Py_TYPE(vec));
|
||||
}
|
||||
#ifdef USE_MATHUTILS_ELEM_MUL
|
||||
static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2)
|
||||
{
|
||||
float *tvec = PyMem_Malloc(vec1->size * sizeof(float));
|
||||
if (tvec == NULL) {
|
||||
PyErr_SetString(PyExc_MemoryError,
|
||||
"vec * vec: "
|
||||
"problem allocating pointer space");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mul_vn_vnvn(tvec, vec1->vec, vec2->vec, vec1->size);
|
||||
return Vector_CreatePyObject_alloc(tvec, vec1->size, Py_TYPE(vec1));
|
||||
}
|
||||
#endif
|
||||
static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
|
||||
{
|
||||
VectorObject *vec1 = NULL, *vec2 = NULL;
|
||||
float scalar;
|
||||
|
||||
if (VectorObject_Check(v1)) {
|
||||
vec1 = (VectorObject *)v1;
|
||||
if (BaseMath_ReadCallback(vec1) == -1)
|
||||
return NULL;
|
||||
}
|
||||
if (VectorObject_Check(v2)) {
|
||||
vec2 = (VectorObject *)v2;
|
||||
if (BaseMath_ReadCallback(vec2) == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Intentionally don't support (Quaternion) here, uses reverse order instead. */
|
||||
|
||||
/* make sure v1 is always the vector */
|
||||
if (vec1 && vec2) {
|
||||
#ifdef USE_MATHUTILS_ELEM_MUL
|
||||
if (vec1->size != vec2->size) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Vector multiplication: "
|
||||
"vectors must have the same dimensions for this operation");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* element-wise product */
|
||||
return vector_mul_vec(vec1, vec2);
|
||||
#endif
|
||||
}
|
||||
else if (vec1) {
|
||||
if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC * FLOAT */
|
||||
return vector_mul_float(vec1, scalar);
|
||||
}
|
||||
}
|
||||
else if (vec2) {
|
||||
if (((scalar = PyFloat_AsDouble(v1)) == -1.0f && PyErr_Occurred()) == 0) { /* FLOAT * VEC */
|
||||
return vector_mul_float(vec2, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Element-wise multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* multiplication in-place: obj *= obj */
|
||||
static PyObject *Vector_imul(PyObject *v1, PyObject *v2)
|
||||
{
|
||||
VectorObject *vec1 = NULL, *vec2 = NULL;
|
||||
float scalar;
|
||||
|
||||
if (VectorObject_Check(v1)) {
|
||||
vec1 = (VectorObject *)v1;
|
||||
if (BaseMath_ReadCallback(vec1) == -1)
|
||||
return NULL;
|
||||
}
|
||||
if (VectorObject_Check(v2)) {
|
||||
vec2 = (VectorObject *)v2;
|
||||
if (BaseMath_ReadCallback(vec2) == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BaseMath_ReadCallback_ForWrite(vec1) == -1)
|
||||
return NULL;
|
||||
|
||||
/* Intentionally don't support (Quaternion, Matrix) here, uses reverse order instead. */
|
||||
|
||||
if (vec1 && vec2) {
|
||||
#ifdef USE_MATHUTILS_ELEM_MUL
|
||||
if (vec1->size != vec2->size) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Vector multiplication: "
|
||||
"vectors must have the same dimensions for this operation");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* element-wise product inplace */
|
||||
mul_vn_vn(vec1->vec, vec2->vec, vec1->size);
|
||||
#else
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Inplace element-wise multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
else if (vec1 && (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0)) { /* VEC *= FLOAT */
|
||||
mul_vn_fl(vec1->vec, vec1->size, scalar);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Inplace element-wise multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)BaseMath_WriteCallback(vec1);
|
||||
Py_INCREF(v1);
|
||||
return v1;
|
||||
}
|
||||
|
||||
static PyObject *Vector_matmul(PyObject *v1, PyObject *v2)
|
||||
{
|
||||
VectorObject *vec1 = NULL, *vec2 = NULL;
|
||||
int vec_size;
|
||||
|
||||
if (VectorObject_Check(v1)) {
|
||||
|
@ -1731,8 +1852,8 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
|
|||
if (vec1 && vec2) {
|
||||
if (vec1->size != vec2->size) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Vector multiplication: "
|
||||
"vectors must have the same dimensions for this operation");
|
||||
"Vector multiplication: "
|
||||
"vectors must have the same dimensions for this operation");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1741,7 +1862,7 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
|
|||
}
|
||||
else if (vec1) {
|
||||
if (MatrixObject_Check(v2)) {
|
||||
/* VEC * MATRIX */
|
||||
/* VEC @ MATRIX */
|
||||
float tvec[MAX_DIMENSIONS];
|
||||
|
||||
if (BaseMath_ReadCallback((MatrixObject *)v2) == -1)
|
||||
|
@ -1759,53 +1880,22 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2)
|
|||
|
||||
return Vector_CreatePyObject(tvec, vec_size, Py_TYPE(vec1));
|
||||
}
|
||||
else if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC * FLOAT */
|
||||
return vector_mul_float(vec1, scalar);
|
||||
}
|
||||
}
|
||||
else if (vec2) {
|
||||
if (((scalar = PyFloat_AsDouble(v1)) == -1.0f && PyErr_Occurred()) == 0) { /* FLOAT * VEC */
|
||||
return vector_mul_float(vec2, scalar);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(!"internal error");
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Vector multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
"Vector multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* multiplication in-place: obj *= obj */
|
||||
static PyObject *Vector_imul(PyObject *v1, PyObject *v2)
|
||||
static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2)
|
||||
{
|
||||
VectorObject *vec = (VectorObject *)v1;
|
||||
float scalar;
|
||||
|
||||
if (BaseMath_ReadCallback_ForWrite(vec) == -1)
|
||||
return NULL;
|
||||
|
||||
/* Intentionally don't support (Quaternion, Matrix) here, uses reverse order instead. */
|
||||
|
||||
/* only support 'vec *= float'
|
||||
* vec*=vec result is a float so that wont work */
|
||||
if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC *= FLOAT */
|
||||
mul_vn_fl(vec->vec, vec->size, scalar);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Vector multiplication: (%s *= %s) "
|
||||
"invalid type for this operation",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)BaseMath_WriteCallback(vec);
|
||||
Py_INCREF(v1);
|
||||
return v1;
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Inplace vector multiplication: "
|
||||
"not supported between '%.200s' and '%.200s' types",
|
||||
Py_TYPE(v1)->tp_name, Py_TYPE(v2)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* divid: obj / obj */
|
||||
|
@ -2119,6 +2209,8 @@ static PyNumberMethods Vector_NumMethods = {
|
|||
NULL, /* nb_inplace_floor_divide */
|
||||
Vector_idiv, /* nb_inplace_true_divide */
|
||||
NULL, /* nb_index */
|
||||
(binaryfunc) Vector_matmul, /* nb_matrix_multiply */
|
||||
(binaryfunc) Vector_imatmul, /* nb_inplace_matrix_multiply */
|
||||
};
|
||||
|
||||
/*------------------PY_OBECT DEFINITION--------------------------*/
|
||||
|
|
|
@ -104,7 +104,7 @@ class MatrixTesting(unittest.TestCase):
|
|||
self.assertEqual(mat[1][3], 2)
|
||||
self.assertEqual(mat[2][3], 3)
|
||||
|
||||
def test_non_square_mult(self):
|
||||
def test_matrix_non_square_matmul(self):
|
||||
mat1 = Matrix(((1, 2, 3),
|
||||
(4, 5, 6)))
|
||||
mat2 = Matrix(((1, 2),
|
||||
|
@ -117,10 +117,10 @@ class MatrixTesting(unittest.TestCase):
|
|||
(19, 26, 33),
|
||||
(29, 40, 51)))
|
||||
|
||||
self.assertEqual(mat1 * mat2, prod_mat1)
|
||||
self.assertEqual(mat2 * mat1, prod_mat2)
|
||||
self.assertEqual(mat1 @ mat2, prod_mat1)
|
||||
self.assertEqual(mat2 @ mat1, prod_mat2)
|
||||
|
||||
def test_mat4x4_vec3D_mult(self):
|
||||
def test_mat4x4_vec3D_matmul(self):
|
||||
mat = Matrix(((1, 0, 2, 0),
|
||||
(0, 6, 0, 0),
|
||||
(0, 0, 1, 1),
|
||||
|
@ -131,23 +131,58 @@ class MatrixTesting(unittest.TestCase):
|
|||
prod_mat_vec = Vector((7, 12, 4))
|
||||
prod_vec_mat = Vector((1, 12, 5))
|
||||
|
||||
self.assertEqual(mat * vec, prod_mat_vec)
|
||||
self.assertEqual(vec * mat, prod_vec_mat)
|
||||
self.assertEqual(mat @ vec, prod_mat_vec)
|
||||
self.assertEqual(vec @ mat, prod_vec_mat)
|
||||
|
||||
def test_mat_vec_mult(self):
|
||||
def test_mat_vec_matmul(self):
|
||||
mat1 = Matrix()
|
||||
|
||||
vec = Vector((1, 2))
|
||||
|
||||
self.assertRaises(ValueError, mat1.__mul__, vec)
|
||||
self.assertRaises(ValueError, vec.__mul__, mat1)
|
||||
self.assertRaises(ValueError, mat1.__matmul__, vec)
|
||||
self.assertRaises(ValueError, vec.__matmul__, mat1)
|
||||
|
||||
mat2 = Matrix(((1, 2),
|
||||
(-2, 3)))
|
||||
|
||||
prod = Vector((5, 4))
|
||||
|
||||
self.assertEqual(mat2 * vec, prod)
|
||||
self.assertEqual(mat2 @ vec, prod)
|
||||
|
||||
def test_matrix_square_matmul(self):
|
||||
mat1 = Matrix(((1, 0),
|
||||
(1, 2)))
|
||||
mat2 = Matrix(((1, 2),
|
||||
(-2, 3)))
|
||||
|
||||
prod1 = Matrix(((1, 2),
|
||||
(-3, 8)))
|
||||
prod2 = Matrix(((3, 4),
|
||||
(1, 6)))
|
||||
|
||||
self.assertEqual(mat1 @ mat2, prod1)
|
||||
self.assertEqual(mat2 @ mat1, prod2)
|
||||
|
||||
"""
|
||||
# tests for element-wise multiplication
|
||||
|
||||
def test_matrix_mul(self):
|
||||
mat1 = Matrix(((1, 0),
|
||||
(1, 2)))
|
||||
mat2 = Matrix(((1, 2),
|
||||
(-2, 3)))
|
||||
mat3 = Matrix(((1, 0, 2, 0),
|
||||
(0, 6, 0, 0),
|
||||
(0, 0, 1, 1),
|
||||
(0, 0, 0, 1)))
|
||||
|
||||
prod = Matrix(((1, 0),
|
||||
(-2, 6)))
|
||||
|
||||
self.assertEqual(mat1 * mat2, prod)
|
||||
self.assertEqual(mat2 * mat1, prod)
|
||||
self.assertRaises(ValueError, mat1.__mul__, mat3)
|
||||
"""
|
||||
|
||||
def test_matrix_inverse(self):
|
||||
mat = Matrix(((1, 4, 0, -1),
|
||||
|
@ -185,7 +220,7 @@ class MatrixTesting(unittest.TestCase):
|
|||
|
||||
self.assertEqual(mat.inverted_safe(), inv_mat_safe)
|
||||
|
||||
def test_matrix_mult(self):
|
||||
def test_matrix_matmult(self):
|
||||
mat = Matrix(((1, 4, 0, -1),
|
||||
(2, -1, 2, -2),
|
||||
(0, 3, 8, 3),
|
||||
|
@ -196,7 +231,7 @@ class MatrixTesting(unittest.TestCase):
|
|||
(0, 48, 73, 18),
|
||||
(16, -14, 26, -13)))
|
||||
|
||||
self.assertEqual(mat * mat, prod_mat)
|
||||
self.assertEqual(mat @ mat, prod_mat)
|
||||
|
||||
|
||||
class VectorTesting(unittest.TestCase):
|
||||
|
@ -209,6 +244,49 @@ class VectorTesting(unittest.TestCase):
|
|||
if v.length_squared != 0.0:
|
||||
self.assertAlmostEqual(v.angle(v.orthogonal()), angle_90d)
|
||||
|
||||
def test_vector_matmul(self):
|
||||
# produces dot product for vectors
|
||||
vec1 = Vector((1, 3, 5))
|
||||
vec2 = Vector((1, 2))
|
||||
|
||||
self.assertRaises(ValueError, vec1.__matmul__, vec2)
|
||||
self.assertEqual(vec1 @ vec1, 35)
|
||||
self.assertEqual(vec2 @ vec2, 5)
|
||||
|
||||
def test_vector_imatmul(self):
|
||||
vec = Vector((1, 3, 5))
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
vec @= vec
|
||||
|
||||
"""
|
||||
# tests for element-wise multiplication
|
||||
|
||||
def test_vector_mul(self):
|
||||
# element-wise multiplication
|
||||
vec1 = Vector((1, 3, 5))
|
||||
vec2 = Vector((1, 2))
|
||||
|
||||
prod1 = Vector((1, 9, 25))
|
||||
prod2 = Vector((2, 6, 10))
|
||||
|
||||
self.assertRaises(ValueError, vec1.__mul__, vec2)
|
||||
self.assertEqual(vec1 * vec1, prod1)
|
||||
self.assertEqual(2 * vec1, prod2)
|
||||
|
||||
def test_vector_imul(self):
|
||||
# inplace element-wise multiplication
|
||||
vec = Vector((1, 3, 5))
|
||||
prod1 = Vector((1, 9, 25))
|
||||
prod2 = Vector((2, 18, 50))
|
||||
|
||||
vec *= vec
|
||||
self.assertEqual(vec, prod1)
|
||||
|
||||
vec *= 2
|
||||
self.assertEqual(vec, prod2)
|
||||
"""
|
||||
|
||||
|
||||
class QuaternionTesting(unittest.TestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue