Blender V4.3
mathutils_Quaternion.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10
11#include <Python.h>
12
13#include "mathutils.hh"
14
15#include "BLI_math_base_safe.h"
16#include "BLI_math_matrix.h"
17#include "BLI_math_rotation.h"
18#include "BLI_math_vector.h"
19#include "BLI_utildefines.h"
20
23
24#ifndef MATH_STANDALONE
25# include "BLI_dynstr.h"
26#endif
27
28#define QUAT_SIZE 4
29
30static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *),
32static void quat__axis_angle_sanitize(float axis[3], float *angle);
33static PyObject *Quaternion_copy(QuaternionObject *self);
34static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args);
35
36/* -------------------------------------------------------------------- */
40static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *),
42{
43 PyObject *ret = Quaternion_copy(self);
44 PyObject *ret_dummy = quat_func((QuaternionObject *)ret);
45 if (ret_dummy) {
46 Py_DECREF(ret_dummy);
47 return ret;
48 }
49 /* error */
50 Py_DECREF(ret);
51 return nullptr;
52}
53
55static void quat__axis_angle_sanitize(float axis[3], float *angle)
56{
57 if (axis) {
58 if (is_zero_v3(axis) || !isfinite(axis[0]) || !isfinite(axis[1]) || !isfinite(axis[2])) {
59 axis[0] = 1.0f;
60 axis[1] = 0.0f;
61 axis[2] = 0.0f;
62 }
63 else if (EXPP_FloatsAreEqual(axis[0], 0.0f, 10) && EXPP_FloatsAreEqual(axis[1], 0.0f, 10) &&
64 EXPP_FloatsAreEqual(axis[2], 0.0f, 10))
65 {
66 axis[0] = 1.0f;
67 }
68 }
69
70 if (angle) {
71 if (!isfinite(*angle)) {
72 *angle = 0.0f;
73 }
74 }
75}
76
80static PyObject *Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits)
81{
82 PyObject *ret;
83 int i;
84
85 ret = PyTuple_New(QUAT_SIZE);
86
87 if (ndigits >= 0) {
88 for (i = 0; i < QUAT_SIZE; i++) {
89 PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round(double(self->quat[i]), ndigits)));
90 }
91 }
92 else {
93 for (i = 0; i < QUAT_SIZE; i++) {
94 PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->quat[i]));
95 }
96 }
97
98 return ret;
99}
100
103/* -------------------------------------------------------------------- */
107static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
108{
109 PyObject *seq = nullptr;
110 double angle = 0.0f;
111 float quat[QUAT_SIZE];
112 unit_qt(quat);
113
114 if (kwds && PyDict_Size(kwds)) {
115 PyErr_SetString(PyExc_TypeError,
116 "mathutils.Quaternion(): "
117 "takes no keyword args");
118 return nullptr;
119 }
120
121 if (!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle)) {
122 return nullptr;
123 }
124
125 switch (PyTuple_GET_SIZE(args)) {
126 case 0:
127 break;
128 case 1: {
129 int size;
130
131 if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == -1)
132 {
133 return nullptr;
134 }
135
136 if (size == 4) {
137 /* 4d: Quaternion (common case) */
138 }
139 else {
140 /* 3d: Interpret as exponential map */
141 BLI_assert(size == 3);
142 expmap_to_quat(quat, quat);
143 }
144
145 break;
146 }
147 case 2: {
148 float axis[3];
149 if (mathutils_array_parse(axis, 3, 3, seq, "mathutils.Quaternion()") == -1) {
150 return nullptr;
151 }
152 angle = angle_wrap_rad(angle); /* clamp because of precision issues */
153 axis_angle_to_quat(quat, axis, angle);
154 break;
155 /* PyArg_ParseTuple assures no more than 2 */
156 }
157 }
158 return Quaternion_CreatePyObject(quat, type);
159}
160
163/* -------------------------------------------------------------------- */
168 /* Wrap. */
169 Quaternion_to_euler_doc,
170 ".. method:: to_euler(order, euler_compat)\n"
171 "\n"
172 " Return Euler representation of the quaternion.\n"
173 "\n"
174 " :arg order: Optional rotation order argument in\n"
175 " ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX'].\n"
176 " :type order: str\n"
177 " :arg euler_compat: Optional euler argument the new euler will be made\n"
178 " compatible with (no axis flipping between them).\n"
179 " Useful for converting a series of matrices to animation curves.\n"
180 " :type euler_compat: :class:`Euler`\n"
181 " :return: Euler representation of the quaternion.\n"
182 " :rtype: :class:`Euler`\n");
183static PyObject *Quaternion_to_euler(QuaternionObject *self, PyObject *args)
184{
185 float tquat[4];
186 float eul[3];
187 const char *order_str = nullptr;
188 short order = EULER_ORDER_XYZ;
189 EulerObject *eul_compat = nullptr;
190
191 if (!PyArg_ParseTuple(args, "|sO!:to_euler", &order_str, &euler_Type, &eul_compat)) {
192 return nullptr;
193 }
194
195 if (BaseMath_ReadCallback(self) == -1) {
196 return nullptr;
197 }
198
199 if (order_str) {
200 order = euler_order_from_string(order_str, "Quaternion.to_euler()");
201
202 if (order == -1) {
203 return nullptr;
204 }
205 }
206
207 normalize_qt_qt(tquat, self->quat);
208
209 if (eul_compat) {
210 if (BaseMath_ReadCallback(eul_compat) == -1) {
211 return nullptr;
212 }
213
214 if (order == EULER_ORDER_XYZ) {
215 quat_to_compatible_eul(eul, eul_compat->eul, tquat);
216 }
217 else {
218 quat_to_compatible_eulO(eul, eul_compat->eul, order, tquat);
219 }
220 }
221 else {
222 if (order == EULER_ORDER_XYZ) {
223 quat_to_eul(eul, tquat);
224 }
225 else {
226 quat_to_eulO(eul, order, tquat);
227 }
228 }
229
230 return Euler_CreatePyObject(eul, order, nullptr);
231}
232
235/* -------------------------------------------------------------------- */
240 /* Wrap. */
241 Quaternion_to_matrix_doc,
242 ".. method:: to_matrix()\n"
243 "\n"
244 " Return a matrix representation of the quaternion.\n"
245 "\n"
246 " :return: A 3x3 rotation matrix representation of the quaternion.\n"
247 " :rtype: :class:`Matrix`\n");
249{
250 float mat[9]; /* all values are set */
251
252 if (BaseMath_ReadCallback(self) == -1) {
253 return nullptr;
254 }
255
256 quat_to_mat3((float(*)[3])mat, self->quat);
257 return Matrix_CreatePyObject(mat, 3, 3, nullptr);
258}
259
262/* -------------------------------------------------------------------- */
267 /* Wrap. */
268 Quaternion_to_axis_angle_doc,
269 ".. method:: to_axis_angle()\n"
270 "\n"
271 " Return the axis, angle representation of the quaternion.\n"
272 "\n"
273 " :return: Axis, angle.\n"
274 " :rtype: tuple[:class:`Vector`, float]\n");
276{
277 PyObject *ret;
278
279 float tquat[4];
280
281 float axis[3];
282 float angle;
283
284 if (BaseMath_ReadCallback(self) == -1) {
285 return nullptr;
286 }
287
288 normalize_qt_qt(tquat, self->quat);
289 quat_to_axis_angle(axis, &angle, tquat);
290
291 quat__axis_angle_sanitize(axis, &angle);
292
293 ret = PyTuple_New(2);
294 PyTuple_SET_ITEMS(ret, Vector_CreatePyObject(axis, 3, nullptr), PyFloat_FromDouble(angle));
295 return ret;
296}
297
300/* -------------------------------------------------------------------- */
305 /* Wrap. */
306 Quaternion_to_swing_twist_doc,
307 ".. method:: to_swing_twist(axis)\n"
308 "\n"
309 " Split the rotation into a swing quaternion with the specified\n"
310 " axis fixed at zero, and the remaining twist rotation angle.\n"
311 "\n"
312 " :arg axis: Twist axis as a string in ['X', 'Y', 'Z'].\n"
313 " :type axis: str\n"
314 " :return: Swing, twist angle.\n"
315 " :rtype: tuple[:class:`Quaternion`, float]\n");
316static PyObject *Quaternion_to_swing_twist(QuaternionObject *self, PyObject *axis_arg)
317{
318 PyObject *ret;
319
320 const char *axis_str = nullptr;
321 float swing[4], twist;
322 int axis;
323
324 if (axis_arg && PyUnicode_Check(axis_arg)) {
325 axis_str = PyUnicode_AsUTF8(axis_arg);
326 }
327
328 if (axis_str && axis_str[0] >= 'X' && axis_str[0] <= 'Z' && axis_str[1] == 0) {
329 axis = axis_str[0] - 'X';
330 }
331 else {
332 PyErr_SetString(PyExc_ValueError,
333 "Quaternion.to_swing_twist(): "
334 "the axis argument must be "
335 "a string in 'X', 'Y', 'Z'");
336 return nullptr;
337 }
338
339 if (BaseMath_ReadCallback(self) == -1) {
340 return nullptr;
341 }
342
343 twist = quat_split_swing_and_twist(self->quat, axis, swing, nullptr);
344
345 ret = PyTuple_New(2);
347 ret, Quaternion_CreatePyObject(swing, Py_TYPE(self)), PyFloat_FromDouble(twist));
348 return ret;
349}
350
353/* -------------------------------------------------------------------- */
358 /* Wrap. */
359 Quaternion_to_exponential_map_doc,
360 ".. method:: to_exponential_map()\n"
361 "\n"
362 " Return the exponential map representation of the quaternion.\n"
363 "\n"
364 " This representation consist of the rotation axis multiplied by the rotation angle.\n"
365 " Such a representation is useful for interpolation between multiple orientations.\n"
366 "\n"
367 " :return: exponential map.\n"
368 " :rtype: :class:`Vector` of size 3\n"
369 "\n"
370 " To convert back to a quaternion, pass it to the :class:`Quaternion` constructor.\n");
372{
373 float expmap[3];
374
375 if (BaseMath_ReadCallback(self) == -1) {
376 return nullptr;
377 }
378
379 quat_to_expmap(expmap, self->quat);
380 return Vector_CreatePyObject(expmap, 3, nullptr);
381}
382
385/* -------------------------------------------------------------------- */
390 /* Wrap. */
391 Quaternion_cross_doc,
392 ".. method:: cross(other)\n"
393 "\n"
394 " Return the cross product of this quaternion and another.\n"
395 "\n"
396 " :arg other: The other quaternion to perform the cross product with.\n"
397 " :type other: :class:`Quaternion`\n"
398 " :return: The cross product.\n"
399 " :rtype: :class:`Quaternion`\n");
400static PyObject *Quaternion_cross(QuaternionObject *self, PyObject *value)
401{
402 float quat[QUAT_SIZE], tquat[QUAT_SIZE];
403
404 if (BaseMath_ReadCallback(self) == -1) {
405 return nullptr;
406 }
407
409 tquat, QUAT_SIZE, QUAT_SIZE, value, "Quaternion.cross(other), invalid 'other' arg") ==
410 -1)
411 {
412 return nullptr;
413 }
414
415 mul_qt_qtqt(quat, self->quat, tquat);
416 return Quaternion_CreatePyObject(quat, Py_TYPE(self));
417}
418
421/* -------------------------------------------------------------------- */
426 /* Wrap. */
427 Quaternion_dot_doc,
428 ".. method:: dot(other)\n"
429 "\n"
430 " Return the dot product of this quaternion and another.\n"
431 "\n"
432 " :arg other: The other quaternion to perform the dot product with.\n"
433 " :type other: :class:`Quaternion`\n"
434 " :return: The dot product.\n"
435 " :rtype: float\n");
436static PyObject *Quaternion_dot(QuaternionObject *self, PyObject *value)
437{
438 float tquat[QUAT_SIZE];
439
440 if (BaseMath_ReadCallback(self) == -1) {
441 return nullptr;
442 }
443
445 tquat, QUAT_SIZE, QUAT_SIZE, value, "Quaternion.dot(other), invalid 'other' arg") == -1)
446 {
447 return nullptr;
448 }
449
450 return PyFloat_FromDouble(dot_qtqt(self->quat, tquat));
451}
452
455/* -------------------------------------------------------------------- */
460 /* Wrap. */
461 Quaternion_rotation_difference_doc,
462 ".. function:: rotation_difference(other)\n"
463 "\n"
464 " Returns a quaternion representing the rotational difference.\n"
465 "\n"
466 " :arg other: second quaternion.\n"
467 " :type other: :class:`Quaternion`\n"
468 " :return: the rotational difference between the two quat rotations.\n"
469 " :rtype: :class:`Quaternion`\n");
470static PyObject *Quaternion_rotation_difference(QuaternionObject *self, PyObject *value)
471{
472 float tquat[QUAT_SIZE], quat[QUAT_SIZE];
473
474 if (BaseMath_ReadCallback(self) == -1) {
475 return nullptr;
476 }
477
478 if (mathutils_array_parse(tquat,
479 QUAT_SIZE,
480 QUAT_SIZE,
481 value,
482 "Quaternion.rotation_difference(other), invalid 'other' arg") == -1)
483 {
484 return nullptr;
485 }
486
487 rotation_between_quats_to_quat(quat, self->quat, tquat);
488
489 return Quaternion_CreatePyObject(quat, Py_TYPE(self));
490}
491
494/* -------------------------------------------------------------------- */
499 /* Wrap. */
500 Quaternion_slerp_doc,
501 ".. function:: slerp(other, factor)\n"
502 "\n"
503 " Returns the interpolation of two quaternions.\n"
504 "\n"
505 " :arg other: value to interpolate with.\n"
506 " :type other: :class:`Quaternion`\n"
507 " :arg factor: The interpolation value in [0.0, 1.0].\n"
508 " :type factor: float\n"
509 " :return: The interpolated rotation.\n"
510 " :rtype: :class:`Quaternion`\n");
511static PyObject *Quaternion_slerp(QuaternionObject *self, PyObject *args)
512{
513 PyObject *value;
514 float tquat[QUAT_SIZE], quat[QUAT_SIZE], fac;
515
516 if (!PyArg_ParseTuple(args, "Of:slerp", &value, &fac)) {
517 PyErr_SetString(PyExc_TypeError,
518 "quat.slerp(): "
519 "expected Quaternion types and float");
520 return nullptr;
521 }
522
523 if (BaseMath_ReadCallback(self) == -1) {
524 return nullptr;
525 }
526
528 tquat, QUAT_SIZE, QUAT_SIZE, value, "Quaternion.slerp(other), invalid 'other' arg") ==
529 -1)
530 {
531 return nullptr;
532 }
533
534 if (fac > 1.0f || fac < 0.0f) {
535 PyErr_SetString(PyExc_ValueError,
536 "quat.slerp(): "
537 "interpolation factor must be between 0.0 and 1.0");
538 return nullptr;
539 }
540
541 interp_qt_qtqt(quat, self->quat, tquat, fac);
542
543 return Quaternion_CreatePyObject(quat, Py_TYPE(self));
544}
545
548/* -------------------------------------------------------------------- */
553 /* Wrap. */
554 Quaternion_rotate_doc,
555 ".. method:: rotate(other)\n"
556 "\n"
557 " Rotates the quaternion by another mathutils value.\n"
558 "\n"
559 " :arg other: rotation component of mathutils value\n"
560 " :type other: :class:`Euler` | :class:`Quaternion` | :class:`Matrix`\n");
561static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value)
562{
563 float self_rmat[3][3], other_rmat[3][3], rmat[3][3];
564 float tquat[4], length;
565
567 return nullptr;
568 }
569
570 if (mathutils_any_to_rotmat(other_rmat, value, "Quaternion.rotate(value)") == -1) {
571 return nullptr;
572 }
573
574 length = normalize_qt_qt(tquat, self->quat);
575 quat_to_mat3(self_rmat, tquat);
576 mul_m3_m3m3(rmat, other_rmat, self_rmat);
577 mat3_to_quat(self->quat, rmat);
578 mul_qt_fl(self->quat, length); /* maintain length after rotating */
579
581 Py_RETURN_NONE;
582}
583
585 /* Wrap. */
586 Quaternion_make_compatible_doc,
587 ".. method:: make_compatible(other)\n"
588 "\n"
589 " Make this quaternion compatible with another,\n"
590 " so interpolating between them works as intended.\n");
591static PyObject *Quaternion_make_compatible(QuaternionObject *self, PyObject *value)
592{
593 float quat[QUAT_SIZE];
594 float tquat[QUAT_SIZE];
595
597 return nullptr;
598 }
599
600 if (mathutils_array_parse(tquat,
601 QUAT_SIZE,
602 QUAT_SIZE,
603 value,
604 "Quaternion.make_compatible(other), invalid 'other' arg") == -1)
605 {
606 return nullptr;
607 }
608
609 /* Can only operate on unit length quaternions. */
610 const float quat_len = normalize_qt_qt(quat, self->quat);
611 quat_to_compatible_quat(self->quat, quat, tquat);
612 mul_qt_fl(self->quat, quat_len);
613
615
616 Py_RETURN_NONE;
617}
618
621/* -------------------------------------------------------------------- */
629 /* Wrap. */
630 Quaternion_normalize_doc,
631 ".. function:: normalize()\n"
632 "\n"
633 " Normalize the quaternion.\n");
635{
637 return nullptr;
638 }
639
640 normalize_qt(self->quat);
641
643 Py_RETURN_NONE;
644}
646 /* Wrap. */
647 Quaternion_normalized_doc,
648 ".. function:: normalized()\n"
649 "\n"
650 " Return a new normalized quaternion.\n"
651 "\n"
652 " :return: a normalized copy.\n"
653 " :rtype: :class:`Quaternion`\n");
658
661/* -------------------------------------------------------------------- */
669 /* Wrap. */
670 Quaternion_invert_doc,
671 ".. function:: invert()\n"
672 "\n"
673 " Set the quaternion to its inverse.\n");
675{
677 return nullptr;
678 }
679
680 invert_qt(self->quat);
681
683 Py_RETURN_NONE;
684}
686 /* Wrap. */
687 Quaternion_inverted_doc,
688 ".. function:: inverted()\n"
689 "\n"
690 " Return a new, inverted quaternion.\n"
691 "\n"
692 " :return: the inverted value.\n"
693 " :rtype: :class:`Quaternion`\n");
698
701/* -------------------------------------------------------------------- */
706 /* Wrap. */
707 Quaternion_identity_doc,
708 ".. function:: identity()\n"
709 "\n"
710 " Set the quaternion to an identity quaternion.\n");
712{
714 return nullptr;
715 }
716
717 unit_qt(self->quat);
718
720 Py_RETURN_NONE;
721}
722
725/* -------------------------------------------------------------------- */
730 /* Wrap. */
731 Quaternion_negate_doc,
732 ".. function:: negate()\n"
733 "\n"
734 " Set the quaternion to its negative.\n");
736{
738 return nullptr;
739 }
740
741 mul_qt_fl(self->quat, -1.0f);
742
744 Py_RETURN_NONE;
745}
746
749/* -------------------------------------------------------------------- */
754 /* Wrap. */
755 Quaternion_conjugate_doc,
756 ".. function:: conjugate()\n"
757 "\n"
758 " Set the quaternion to its conjugate (negate x, y, z).\n");
760{
762 return nullptr;
763 }
764
765 conjugate_qt(self->quat);
766
768 Py_RETURN_NONE;
769}
771 /* Wrap. */
772 Quaternion_conjugated_doc,
773 ".. function:: conjugated()\n"
774 "\n"
775 " Return a new conjugated quaternion.\n"
776 "\n"
777 " :return: a new quaternion.\n"
778 " :rtype: :class:`Quaternion`\n");
783
786/* -------------------------------------------------------------------- */
791 /* Wrap. */
792 Quaternion_copy_doc,
793 ".. function:: copy()\n"
794 "\n"
795 " Returns a copy of this quaternion.\n"
796 "\n"
797 " :return: A copy of the quaternion.\n"
798 " :rtype: :class:`Quaternion`\n"
799 "\n"
800 " .. note:: use this to get a copy of a wrapped quaternion with\n"
801 " no reference to the original data.\n");
803{
804 if (BaseMath_ReadCallback(self) == -1) {
805 return nullptr;
806 }
807
808 return Quaternion_CreatePyObject(self->quat, Py_TYPE(self));
809}
810static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args)
811{
812 if (!PyC_CheckArgs_DeepCopy(args)) {
813 return nullptr;
814 }
815 return Quaternion_copy(self);
816}
817
820/* -------------------------------------------------------------------- */
825{
826 PyObject *ret, *tuple;
827
828 if (BaseMath_ReadCallback(self) == -1) {
829 return nullptr;
830 }
831
832 tuple = Quaternion_to_tuple_ext(self, -1);
833
834 ret = PyUnicode_FromFormat("Quaternion(%R)", tuple);
835
836 Py_DECREF(tuple);
837 return ret;
838}
839
840#ifndef MATH_STANDALONE
842{
843 DynStr *ds;
844
845 if (BaseMath_ReadCallback(self) == -1) {
846 return nullptr;
847 }
848
849 ds = BLI_dynstr_new();
850
852 "<Quaternion (w=%.4f, x=%.4f, y=%.4f, z=%.4f)>",
853 self->quat[0],
854 self->quat[1],
855 self->quat[2],
856 self->quat[3]);
857
858 return mathutils_dynstr_to_py(ds); /* frees ds */
859}
860#endif
861
864/* -------------------------------------------------------------------- */
868static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op)
869{
870 PyObject *res;
871 int ok = -1; /* zero is true */
872
876
877 if (BaseMath_ReadCallback(quatA) == -1 || BaseMath_ReadCallback(quatB) == -1) {
878 return nullptr;
879 }
880
881 ok = EXPP_VectorsAreEqual(quatA->quat, quatB->quat, QUAT_SIZE, 1) ? 0 : -1;
882 }
883
884 switch (op) {
885 case Py_NE:
886 ok = !ok;
888 case Py_EQ:
889 res = ok ? Py_False : Py_True;
890 break;
891
892 case Py_LT:
893 case Py_LE:
894 case Py_GT:
895 case Py_GE:
896 res = Py_NotImplemented;
897 break;
898 default:
899 PyErr_BadArgument();
900 return nullptr;
901 }
902
903 return Py_NewRef(res);
904}
905
908/* -------------------------------------------------------------------- */
913{
914 if (BaseMath_ReadCallback(self) == -1) {
915 return -1;
916 }
917
919 return -1;
920 }
921
922 return mathutils_array_hash(self->quat, QUAT_SIZE);
923}
924
927/* -------------------------------------------------------------------- */
932static Py_ssize_t Quaternion_len(QuaternionObject * /*self*/)
933{
934 return QUAT_SIZE;
935}
936
938static PyObject *Quaternion_item(QuaternionObject *self, Py_ssize_t i)
939{
940 if (i < 0) {
941 i = QUAT_SIZE - i;
942 }
943
944 if (i < 0 || i >= QUAT_SIZE) {
945 PyErr_SetString(PyExc_IndexError,
946 "quaternion[attribute]: "
947 "array index out of range");
948 return nullptr;
949 }
950
951 if (BaseMath_ReadIndexCallback(self, i) == -1) {
952 return nullptr;
953 }
954
955 return PyFloat_FromDouble(self->quat[i]);
956}
957
959static int Quaternion_ass_item(QuaternionObject *self, Py_ssize_t i, PyObject *ob)
960{
961 float f;
962
963 if (BaseMath_Prepare_ForWrite(self) == -1) {
964 return -1;
965 }
966
967 f = float(PyFloat_AsDouble(ob));
968
969 if (f == -1.0f && PyErr_Occurred()) { /* parsed item not a number */
970 PyErr_SetString(PyExc_TypeError,
971 "quaternion[index] = x: "
972 "assigned value not a number");
973 return -1;
974 }
975
976 if (i < 0) {
977 i = QUAT_SIZE - i;
978 }
979
980 if (i < 0 || i >= QUAT_SIZE) {
981 PyErr_SetString(PyExc_IndexError,
982 "quaternion[attribute] = x: "
983 "array assignment index out of range");
984 return -1;
985 }
986 self->quat[i] = f;
987
988 if (BaseMath_WriteIndexCallback(self, i) == -1) {
989 return -1;
990 }
991
992 return 0;
993}
994
996static PyObject *Quaternion_slice(QuaternionObject *self, int begin, int end)
997{
998 PyObject *tuple;
999 int count;
1000
1001 if (BaseMath_ReadCallback(self) == -1) {
1002 return nullptr;
1003 }
1004
1005 CLAMP(begin, 0, QUAT_SIZE);
1006 if (end < 0) {
1007 end = (QUAT_SIZE + 1) + end;
1008 }
1009 CLAMP(end, 0, QUAT_SIZE);
1010 begin = std::min(begin, end);
1011
1012 tuple = PyTuple_New(end - begin);
1013 for (count = begin; count < end; count++) {
1014 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(self->quat[count]));
1015 }
1016
1017 return tuple;
1018}
1019
1021static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyObject *seq)
1022{
1023 int i, size;
1024 float quat[QUAT_SIZE];
1025
1027 return -1;
1028 }
1029
1030 CLAMP(begin, 0, QUAT_SIZE);
1031 if (end < 0) {
1032 end = (QUAT_SIZE + 1) + end;
1033 }
1034 CLAMP(end, 0, QUAT_SIZE);
1035 begin = std::min(begin, end);
1036
1037 if ((size = mathutils_array_parse(
1038 quat, 0, QUAT_SIZE, seq, "mathutils.Quaternion[begin:end] = []")) == -1)
1039 {
1040 return -1;
1041 }
1042
1043 if (size != (end - begin)) {
1044 PyErr_SetString(PyExc_ValueError,
1045 "quaternion[begin:end] = []: "
1046 "size mismatch in slice assignment");
1047 return -1;
1048 }
1049
1050 /* Parsed well, now set in vector. */
1051 for (i = 0; i < size; i++) {
1052 self->quat[begin + i] = quat[i];
1053 }
1054
1056 return 0;
1057}
1058
1060static PyObject *Quaternion_subscript(QuaternionObject *self, PyObject *item)
1061{
1062 if (PyIndex_Check(item)) {
1063 Py_ssize_t i;
1064 i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1065 if (i == -1 && PyErr_Occurred()) {
1066 return nullptr;
1067 }
1068 if (i < 0) {
1069 i += QUAT_SIZE;
1070 }
1071 return Quaternion_item(self, i);
1072 }
1073 if (PySlice_Check(item)) {
1074 Py_ssize_t start, stop, step, slicelength;
1075
1076 if (PySlice_GetIndicesEx(item, QUAT_SIZE, &start, &stop, &step, &slicelength) < 0) {
1077 return nullptr;
1078 }
1079
1080 if (slicelength <= 0) {
1081 return PyTuple_New(0);
1082 }
1083 if (step == 1) {
1084 return Quaternion_slice(self, start, stop);
1085 }
1086
1087 PyErr_SetString(PyExc_IndexError, "slice steps not supported with quaternions");
1088 return nullptr;
1089 }
1090
1091 PyErr_Format(
1092 PyExc_TypeError, "quaternion indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
1093 return nullptr;
1094}
1095
1097static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyObject *value)
1098{
1099 if (PyIndex_Check(item)) {
1100 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1101 if (i == -1 && PyErr_Occurred()) {
1102 return -1;
1103 }
1104 if (i < 0) {
1105 i += QUAT_SIZE;
1106 }
1107 return Quaternion_ass_item(self, i, value);
1108 }
1109 if (PySlice_Check(item)) {
1110 Py_ssize_t start, stop, step, slicelength;
1111
1112 if (PySlice_GetIndicesEx(item, QUAT_SIZE, &start, &stop, &step, &slicelength) < 0) {
1113 return -1;
1114 }
1115
1116 if (step == 1) {
1117 return Quaternion_ass_slice(self, start, stop, value);
1118 }
1119
1120 PyErr_SetString(PyExc_IndexError, "slice steps not supported with quaternion");
1121 return -1;
1122 }
1123
1124 PyErr_Format(
1125 PyExc_TypeError, "quaternion indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
1126 return -1;
1127}
1128
1131/* -------------------------------------------------------------------- */
1136static PyObject *Quaternion_add(PyObject *q1, PyObject *q2)
1137{
1138 float quat[QUAT_SIZE];
1139 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1140
1142 PyErr_Format(PyExc_TypeError,
1143 "Quaternion addition: (%s + %s) "
1144 "invalid type for this operation",
1145 Py_TYPE(q1)->tp_name,
1146 Py_TYPE(q2)->tp_name);
1147 return nullptr;
1148 }
1149 quat1 = (QuaternionObject *)q1;
1150 quat2 = (QuaternionObject *)q2;
1151
1152 if (BaseMath_ReadCallback(quat1) == -1 || BaseMath_ReadCallback(quat2) == -1) {
1153 return nullptr;
1154 }
1155
1156 add_qt_qtqt(quat, quat1->quat, quat2->quat, 1.0f);
1157 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1158}
1159
1161static PyObject *Quaternion_sub(PyObject *q1, PyObject *q2)
1162{
1163 int x;
1164 float quat[QUAT_SIZE];
1165 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1166
1168 PyErr_Format(PyExc_TypeError,
1169 "Quaternion subtraction: (%s - %s) "
1170 "invalid type for this operation",
1171 Py_TYPE(q1)->tp_name,
1172 Py_TYPE(q2)->tp_name);
1173 return nullptr;
1174 }
1175
1176 quat1 = (QuaternionObject *)q1;
1177 quat2 = (QuaternionObject *)q2;
1178
1179 if (BaseMath_ReadCallback(quat1) == -1 || BaseMath_ReadCallback(quat2) == -1) {
1180 return nullptr;
1181 }
1182
1183 for (x = 0; x < QUAT_SIZE; x++) {
1184 quat[x] = quat1->quat[x] - quat2->quat[x];
1185 }
1186
1187 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1188}
1189
1190static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar)
1191{
1192 float tquat[4];
1193 copy_qt_qt(tquat, quat->quat);
1194 mul_qt_fl(tquat, scalar);
1195 return Quaternion_CreatePyObject(tquat, Py_TYPE(quat));
1196}
1197
1199static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
1200{
1201 float scalar;
1202 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1203
1205 quat1 = (QuaternionObject *)q1;
1206 if (BaseMath_ReadCallback(quat1) == -1) {
1207 return nullptr;
1208 }
1209 }
1210 if (QuaternionObject_Check(q2)) {
1211 quat2 = (QuaternionObject *)q2;
1212 if (BaseMath_ReadCallback(quat2) == -1) {
1213 return nullptr;
1214 }
1215 }
1216
1217 if (quat1 && quat2) { /* QUAT * QUAT (element-wise product) */
1218 float quat[QUAT_SIZE];
1219 mul_vn_vnvn(quat, quat1->quat, quat2->quat, QUAT_SIZE);
1220 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1221 }
1222 /* the only case this can happen (for a supported type is "FLOAT * QUAT") */
1223 if (quat2) { /* FLOAT * QUAT */
1224 if (((scalar = PyFloat_AsDouble(q1)) == -1.0f && PyErr_Occurred()) == 0) {
1225 return quat_mul_float(quat2, scalar);
1226 }
1227 }
1228 else if (quat1) { /* QUAT * FLOAT */
1229 if (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0) {
1230 return quat_mul_float(quat1, scalar);
1231 }
1232 }
1233
1234 PyErr_Format(PyExc_TypeError,
1235 "Element-wise multiplication: "
1236 "not supported between '%.200s' and '%.200s' types",
1237 Py_TYPE(q1)->tp_name,
1238 Py_TYPE(q2)->tp_name);
1239 return nullptr;
1240}
1241
1243static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2)
1244{
1245 float scalar;
1246 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1247
1249 quat1 = (QuaternionObject *)q1;
1250 if (BaseMath_ReadCallback(quat1) == -1) {
1251 return nullptr;
1252 }
1253 }
1254 if (QuaternionObject_Check(q2)) {
1255 quat2 = (QuaternionObject *)q2;
1256 if (BaseMath_ReadCallback(quat2) == -1) {
1257 return nullptr;
1258 }
1259 }
1260
1261 if (quat1 && quat2) { /* QUAT *= QUAT (in-place element-wise product). */
1262 mul_vn_vn(quat1->quat, quat2->quat, QUAT_SIZE);
1263 }
1264 else if (quat1 && (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
1265 /* QUAT *= FLOAT */
1266 mul_qt_fl(quat1->quat, scalar);
1267 }
1268 else {
1269 PyErr_Format(PyExc_TypeError,
1270 "Element-wise multiplication: "
1271 "not supported between '%.200s' and '%.200s' types",
1272 Py_TYPE(q1)->tp_name,
1273 Py_TYPE(q2)->tp_name);
1274 return nullptr;
1275 }
1276
1277 (void)BaseMath_WriteCallback(quat1);
1278 Py_INCREF(q1);
1279 return q1;
1280}
1281
1283static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2)
1284{
1285 float quat[QUAT_SIZE];
1286 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1287
1289 quat1 = (QuaternionObject *)q1;
1290 if (BaseMath_ReadCallback(quat1) == -1) {
1291 return nullptr;
1292 }
1293 }
1294 if (QuaternionObject_Check(q2)) {
1295 quat2 = (QuaternionObject *)q2;
1296 if (BaseMath_ReadCallback(quat2) == -1) {
1297 return nullptr;
1298 }
1299 }
1300
1301 if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
1302 mul_qt_qtqt(quat, quat1->quat, quat2->quat);
1303 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1304 }
1305 if (quat1) {
1306 /* QUAT @ VEC */
1307 if (VectorObject_Check(q2)) {
1308 VectorObject *vec2 = (VectorObject *)q2;
1309 float tvec[3];
1310
1311 if (vec2->vec_num != 3) {
1312 PyErr_SetString(PyExc_ValueError,
1313 "Vector multiplication: "
1314 "only 3D vector rotations (with quats) "
1315 "currently supported");
1316 return nullptr;
1317 }
1318 if (BaseMath_ReadCallback(vec2) == -1) {
1319 return nullptr;
1320 }
1321
1322 copy_v3_v3(tvec, vec2->vec);
1323 mul_qt_v3(quat1->quat, tvec);
1324
1325 return Vector_CreatePyObject(tvec, 3, Py_TYPE(vec2));
1326 }
1327 }
1328
1329 PyErr_Format(PyExc_TypeError,
1330 "Quaternion multiplication: "
1331 "not supported between '%.200s' and '%.200s' types",
1332 Py_TYPE(q1)->tp_name,
1333 Py_TYPE(q2)->tp_name);
1334 return nullptr;
1335}
1336
1338static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2)
1339{
1340 float quat[QUAT_SIZE];
1341 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1342
1344 quat1 = (QuaternionObject *)q1;
1345 if (BaseMath_ReadCallback(quat1) == -1) {
1346 return nullptr;
1347 }
1348 }
1349 if (QuaternionObject_Check(q2)) {
1350 quat2 = (QuaternionObject *)q2;
1351 if (BaseMath_ReadCallback(quat2) == -1) {
1352 return nullptr;
1353 }
1354 }
1355
1356 if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
1357 mul_qt_qtqt(quat, quat1->quat, quat2->quat);
1358 copy_qt_qt(quat1->quat, quat);
1359 }
1360 else {
1361 PyErr_Format(PyExc_TypeError,
1362 "In place quaternion multiplication: "
1363 "not supported between '%.200s' and '%.200s' types",
1364 Py_TYPE(q1)->tp_name,
1365 Py_TYPE(q2)->tp_name);
1366 return nullptr;
1367 }
1368
1369 (void)BaseMath_WriteCallback(quat1);
1370 Py_INCREF(q1);
1371 return q1;
1372}
1373
1376{
1377 float tquat[QUAT_SIZE];
1378
1379 if (BaseMath_ReadCallback(self) == -1) {
1380 return nullptr;
1381 }
1382
1383 negate_v4_v4(tquat, self->quat);
1384 return Quaternion_CreatePyObject(tquat, Py_TYPE(self));
1385}
1386
1389/* -------------------------------------------------------------------- */
1393static PySequenceMethods Quaternion_SeqMethods = {
1394 /*sq_length*/ (lenfunc)Quaternion_len,
1395 /*sq_concat*/ nullptr,
1396 /*sq_repeat*/ nullptr,
1397 /*sq_item*/ (ssizeargfunc)Quaternion_item,
1398 /*was_sq_slice*/ nullptr, /* DEPRECATED. */
1399 /*sq_ass_item*/ (ssizeobjargproc)Quaternion_ass_item,
1400 /*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
1401 /*sq_contains*/ nullptr,
1402 /*sq_inplace_concat*/ nullptr,
1403 /*sq_inplace_repeat*/ nullptr,
1404};
1405
1406static PyMappingMethods Quaternion_AsMapping = {
1407 /*mp_length*/ (lenfunc)Quaternion_len,
1408 /*mp_subscript*/ (binaryfunc)Quaternion_subscript,
1409 /*mp_ass_subscript*/ (objobjargproc)Quaternion_ass_subscript,
1410};
1411
1412static PyNumberMethods Quaternion_NumMethods = {
1413 /*nb_add*/ (binaryfunc)Quaternion_add,
1414 /*nb_subtract*/ (binaryfunc)Quaternion_sub,
1415 /*nb_multiply*/ (binaryfunc)Quaternion_mul,
1416 /*nb_remainder*/ nullptr,
1417 /*nb_divmod*/ nullptr,
1418 /*nb_power*/ nullptr,
1419 /*nb_negative*/ (unaryfunc)Quaternion_neg,
1420 /*nb_positive*/ (unaryfunc)Quaternion_copy,
1421 /*nb_absolute*/ nullptr,
1422 /*nb_bool*/ nullptr,
1423 /*nb_invert*/ nullptr,
1424 /*nb_lshift*/ nullptr,
1425 /*nb_rshift*/ nullptr,
1426 /*nb_and*/ nullptr,
1427 /*nb_xor*/ nullptr,
1428 /*nb_or*/ nullptr,
1429 /*nb_int*/ nullptr,
1430 /*nb_reserved*/ nullptr,
1431 /*nb_float*/ nullptr,
1432 /*nb_inplace_add*/ nullptr,
1433 /*nb_inplace_subtract*/ nullptr,
1434 /*nb_inplace_multiply*/ (binaryfunc)Quaternion_imul,
1435 /*nb_inplace_remainder*/ nullptr,
1436 /*nb_inplace_power*/ nullptr,
1437 /*nb_inplace_lshift*/ nullptr,
1438 /*nb_inplace_rshift*/ nullptr,
1439 /*nb_inplace_and*/ nullptr,
1440 /*nb_inplace_xor*/ nullptr,
1441 /*nb_inplace_or*/ nullptr,
1442 /*nb_floor_divide*/ nullptr,
1443 /*nb_true_divide*/ nullptr,
1444 /*nb_inplace_floor_divide*/ nullptr,
1445 /*nb_inplace_true_divide*/ nullptr,
1446 /*nb_index*/ nullptr,
1447 /*nb_matrix_multiply*/ (binaryfunc)Quaternion_matmul,
1448 /*nb_inplace_matrix_multiply*/ (binaryfunc)Quaternion_imatmul,
1449};
1450
1453/* -------------------------------------------------------------------- */
1458 /* Wrap. */
1459 Quaternion_axis_doc,
1460 "Quaternion axis value.\n"
1461 "\n"
1462 ":type: float");
1463static PyObject *Quaternion_axis_get(QuaternionObject *self, void *type)
1464{
1465 return Quaternion_item(self, POINTER_AS_INT(type));
1466}
1467
1468static int Quaternion_axis_set(QuaternionObject *self, PyObject *value, void *type)
1469{
1470 return Quaternion_ass_item(self, POINTER_AS_INT(type), value);
1471}
1472
1474 /* Wrap. */
1475 Quaternion_magnitude_doc,
1476 "Size of the quaternion (read-only).\n"
1477 "\n"
1478 ":type: float");
1479static PyObject *Quaternion_magnitude_get(QuaternionObject *self, void * /*closure*/)
1480{
1481 if (BaseMath_ReadCallback(self) == -1) {
1482 return nullptr;
1483 }
1484
1485 return PyFloat_FromDouble(sqrtf(dot_qtqt(self->quat, self->quat)));
1486}
1487
1489 /* Wrap. */
1490 Quaternion_angle_doc,
1491 "Angle of the quaternion.\n"
1492 "\n"
1493 ":type: float");
1494static PyObject *Quaternion_angle_get(QuaternionObject *self, void * /*closure*/)
1495{
1496 float tquat[4];
1497 float angle;
1498
1499 if (BaseMath_ReadCallback(self) == -1) {
1500 return nullptr;
1501 }
1502
1503 normalize_qt_qt(tquat, self->quat);
1504
1505 angle = 2.0f * safe_acosf(tquat[0]);
1506
1507 quat__axis_angle_sanitize(nullptr, &angle);
1508
1509 return PyFloat_FromDouble(angle);
1510}
1511
1512static int Quaternion_angle_set(QuaternionObject *self, PyObject *value, void * /*closure*/)
1513{
1514 float tquat[4];
1515 float len;
1516
1517 float axis[3], angle_dummy;
1518 float angle;
1519
1521 return -1;
1522 }
1523
1524 len = normalize_qt_qt(tquat, self->quat);
1525 quat_to_axis_angle(axis, &angle_dummy, tquat);
1526
1527 angle = PyFloat_AsDouble(value);
1528
1529 if (angle == -1.0f && PyErr_Occurred()) { /* parsed item not a number */
1530 PyErr_SetString(PyExc_TypeError, "Quaternion.angle = value: float expected");
1531 return -1;
1532 }
1533
1534 angle = angle_wrap_rad(angle);
1535
1536 quat__axis_angle_sanitize(axis, &angle);
1537
1538 axis_angle_to_quat(self->quat, axis, angle);
1539 mul_qt_fl(self->quat, len);
1540
1541 if (BaseMath_WriteCallback(self) == -1) {
1542 return -1;
1543 }
1544
1545 return 0;
1546}
1547
1549 /* Wrap. */
1550 Quaternion_axis_vector_doc,
1551 "Quaternion axis as a vector.\n"
1552 "\n"
1553 ":type: :class:`Vector`");
1554static PyObject *Quaternion_axis_vector_get(QuaternionObject *self, void * /*closure*/)
1555{
1556 float tquat[4];
1557
1558 float axis[3];
1559 float angle_dummy;
1560
1561 if (BaseMath_ReadCallback(self) == -1) {
1562 return nullptr;
1563 }
1564
1565 normalize_qt_qt(tquat, self->quat);
1566 quat_to_axis_angle(axis, &angle_dummy, tquat);
1567
1568 quat__axis_angle_sanitize(axis, nullptr);
1569
1570 return Vector_CreatePyObject(axis, 3, nullptr);
1571}
1572
1573static int Quaternion_axis_vector_set(QuaternionObject *self, PyObject *value, void * /*closure*/)
1574{
1575 float tquat[4];
1576 float len;
1577
1578 float axis[3];
1579 float angle;
1580
1582 return -1;
1583 }
1584
1585 len = normalize_qt_qt(tquat, self->quat);
1586 quat_to_axis_angle(axis, &angle, tquat); /* axis value is unused */
1587
1588 if (mathutils_array_parse(axis, 3, 3, value, "quat.axis = other") == -1) {
1589 return -1;
1590 }
1591
1592 quat__axis_angle_sanitize(axis, &angle);
1593
1594 axis_angle_to_quat(self->quat, axis, angle);
1595 mul_qt_fl(self->quat, len);
1596
1597 if (BaseMath_WriteCallback(self) == -1) {
1598 return -1;
1599 }
1600
1601 return 0;
1602}
1603
1606/* -------------------------------------------------------------------- */
1610static PyGetSetDef Quaternion_getseters[] = {
1611 {"w",
1612 (getter)Quaternion_axis_get,
1613 (setter)Quaternion_axis_set,
1614 Quaternion_axis_doc,
1615 POINTER_FROM_INT(0)},
1616 {"x",
1617 (getter)Quaternion_axis_get,
1618 (setter)Quaternion_axis_set,
1619 Quaternion_axis_doc,
1620 POINTER_FROM_INT(1)},
1621 {"y",
1622 (getter)Quaternion_axis_get,
1623 (setter)Quaternion_axis_set,
1624 Quaternion_axis_doc,
1625 POINTER_FROM_INT(2)},
1626 {"z",
1627 (getter)Quaternion_axis_get,
1628 (setter)Quaternion_axis_set,
1629 Quaternion_axis_doc,
1630 POINTER_FROM_INT(3)},
1631 {"magnitude",
1633 (setter) nullptr,
1634 Quaternion_magnitude_doc,
1635 nullptr},
1636 {"angle",
1637 (getter)Quaternion_angle_get,
1638 (setter)Quaternion_angle_set,
1639 Quaternion_angle_doc,
1640 nullptr},
1641 {"axis",
1644 Quaternion_axis_vector_doc,
1645 nullptr},
1646 {"is_wrapped",
1648 (setter) nullptr,
1650 nullptr},
1651 {"is_frozen",
1653 (setter) nullptr,
1655 nullptr},
1656 {"is_valid",
1658 (setter) nullptr,
1660 nullptr},
1661 {"owner",
1663 (setter) nullptr,
1665 nullptr},
1666 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
1667};
1668
1671/* -------------------------------------------------------------------- */
1675#if (defined(__GNUC__) && !defined(__clang__))
1676# pragma GCC diagnostic push
1677# pragma GCC diagnostic ignored "-Wcast-function-type"
1678#endif
1679
1680static PyMethodDef Quaternion_methods[] = {
1681 /* In place only. */
1682 {"identity", (PyCFunction)Quaternion_identity, METH_NOARGS, Quaternion_identity_doc},
1683 {"negate", (PyCFunction)Quaternion_negate, METH_NOARGS, Quaternion_negate_doc},
1684
1685 /* Operate on original or copy. */
1686 {"conjugate", (PyCFunction)Quaternion_conjugate, METH_NOARGS, Quaternion_conjugate_doc},
1687 {"conjugated", (PyCFunction)Quaternion_conjugated, METH_NOARGS, Quaternion_conjugated_doc},
1688
1689 {"invert", (PyCFunction)Quaternion_invert, METH_NOARGS, Quaternion_invert_doc},
1690 {"inverted", (PyCFunction)Quaternion_inverted, METH_NOARGS, Quaternion_inverted_doc},
1691
1692 {"normalize", (PyCFunction)Quaternion_normalize, METH_NOARGS, Quaternion_normalize_doc},
1693 {"normalized", (PyCFunction)Quaternion_normalized, METH_NOARGS, Quaternion_normalized_doc},
1694
1695 /* Return converted representation. */
1696 {"to_euler", (PyCFunction)Quaternion_to_euler, METH_VARARGS, Quaternion_to_euler_doc},
1697 {"to_matrix", (PyCFunction)Quaternion_to_matrix, METH_NOARGS, Quaternion_to_matrix_doc},
1698 {"to_axis_angle",
1699 (PyCFunction)Quaternion_to_axis_angle,
1700 METH_NOARGS,
1701 Quaternion_to_axis_angle_doc},
1702 {"to_swing_twist",
1703 (PyCFunction)Quaternion_to_swing_twist,
1704 METH_O,
1705 Quaternion_to_swing_twist_doc},
1706 {"to_exponential_map",
1707 (PyCFunction)Quaternion_to_exponential_map,
1708 METH_NOARGS,
1709 Quaternion_to_exponential_map_doc},
1710
1711 /* Operation between 2 or more types. */
1712 {"cross", (PyCFunction)Quaternion_cross, METH_O, Quaternion_cross_doc},
1713 {"dot", (PyCFunction)Quaternion_dot, METH_O, Quaternion_dot_doc},
1714 {"rotation_difference",
1715 (PyCFunction)Quaternion_rotation_difference,
1716 METH_O,
1717 Quaternion_rotation_difference_doc},
1718 {"slerp", (PyCFunction)Quaternion_slerp, METH_VARARGS, Quaternion_slerp_doc},
1719 {"rotate", (PyCFunction)Quaternion_rotate, METH_O, Quaternion_rotate_doc},
1720 {"make_compatible",
1721 (PyCFunction)Quaternion_make_compatible,
1722 METH_O,
1723 Quaternion_make_compatible_doc},
1724
1725 /* Base-math methods. */
1726 {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc},
1727
1728 {"copy", (PyCFunction)Quaternion_copy, METH_NOARGS, Quaternion_copy_doc},
1729 {"__copy__", (PyCFunction)Quaternion_copy, METH_NOARGS, Quaternion_copy_doc},
1730 {"__deepcopy__", (PyCFunction)Quaternion_deepcopy, METH_VARARGS, Quaternion_copy_doc},
1731 {nullptr, nullptr, 0, nullptr},
1732};
1733
1734#if (defined(__GNUC__) && !defined(__clang__))
1735# pragma GCC diagnostic pop
1736#endif
1737
1740/* -------------------------------------------------------------------- */
1744#ifdef MATH_STANDALONE
1745# define Quaternion_str nullptr
1746#endif
1747
1749 /* Wrap. */
1750 quaternion_doc,
1751 ".. class:: Quaternion([seq, [angle]])\n"
1752 "\n"
1753 " This object gives access to Quaternions in Blender.\n"
1754 "\n"
1755 " :arg seq: size 3 or 4\n"
1756 " :type seq: :class:`Vector`\n"
1757 " :arg angle: rotation angle, in radians\n"
1758 " :type angle: float\n"
1759 "\n"
1760 " The constructor takes arguments in various forms:\n"
1761 "\n"
1762 " (), *no args*\n"
1763 " Create an identity quaternion\n"
1764 " (*wxyz*)\n"
1765 " Create a quaternion from a ``(w, x, y, z)`` vector.\n"
1766 " (*exponential_map*)\n"
1767 " Create a quaternion from a 3d exponential map vector.\n"
1768 "\n"
1769 " .. seealso:: :meth:`to_exponential_map`\n"
1770 " (*axis, angle*)\n"
1771 " Create a quaternion representing a rotation of *angle* radians over *axis*.\n"
1772 "\n"
1773 " .. seealso:: :meth:`to_axis_angle`\n");
1774PyTypeObject quaternion_Type = {
1775 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
1776 /*tp_name*/ "Quaternion",
1777 /*tp_basicsize*/ sizeof(QuaternionObject),
1778 /*tp_itemsize*/ 0,
1779 /*tp_dealloc*/ (destructor)BaseMathObject_dealloc,
1780 /*tp_vectorcall_offset*/ 0,
1781 /*tp_getattr*/ nullptr,
1782 /*tp_setattr*/ nullptr,
1783 /*tp_as_async*/ nullptr,
1784 /*tp_repr*/ (reprfunc)Quaternion_repr,
1785 /*tp_as_number*/ &Quaternion_NumMethods,
1786 /*tp_as_sequence*/ &Quaternion_SeqMethods,
1787 /*tp_as_mapping*/ &Quaternion_AsMapping,
1788 /*tp_hash*/ (hashfunc)Quaternion_hash,
1789 /*tp_call*/ nullptr,
1790 /*tp_str*/ (reprfunc)Quaternion_str,
1791 /*tp_getattro*/ nullptr,
1792 /*tp_setattro*/ nullptr,
1793 /*tp_as_buffer*/ nullptr,
1794 /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
1795 /*tp_doc*/ quaternion_doc,
1796 /*tp_traverse*/ (traverseproc)BaseMathObject_traverse,
1797 /*tp_clear*/ (inquiry)BaseMathObject_clear,
1798 /*tp_richcompare*/ (richcmpfunc)Quaternion_richcmpr,
1799 /*tp_weaklistoffset*/ 0,
1800 /*tp_iter*/ nullptr,
1801 /*tp_iternext*/ nullptr,
1802 /*tp_methods*/ Quaternion_methods,
1803 /*tp_members*/ nullptr,
1804 /*tp_getset*/ Quaternion_getseters,
1805 /*tp_base*/ nullptr,
1806 /*tp_dict*/ nullptr,
1807 /*tp_descr_get*/ nullptr,
1808 /*tp_descr_set*/ nullptr,
1809 /*tp_dictoffset*/ 0,
1810 /*tp_init*/ nullptr,
1811 /*tp_alloc*/ nullptr,
1812 /*tp_new*/ Quaternion_new,
1813 /*tp_free*/ nullptr,
1814 /*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
1815 /*tp_bases*/ nullptr,
1816 /*tp_mro*/ nullptr,
1817 /*tp_cache*/ nullptr,
1818 /*tp_subclasses*/ nullptr,
1819 /*tp_weaklist*/ nullptr,
1820 /*tp_del*/ nullptr,
1821 /*tp_version_tag*/ 0,
1822 /*tp_finalize*/ nullptr,
1823 /*tp_vectorcall*/ nullptr,
1824};
1825
1826#ifdef MATH_STANDALONE
1827# undef Quaternion_str
1828#endif
1829
1832/* -------------------------------------------------------------------- */
1836PyObject *Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type)
1837{
1839 float *quat_alloc;
1840
1841 quat_alloc = static_cast<float *>(PyMem_Malloc(QUAT_SIZE * sizeof(float)));
1842 if (UNLIKELY(quat_alloc == nullptr)) {
1843 PyErr_SetString(PyExc_MemoryError,
1844 "Quaternion(): "
1845 "problem allocating data");
1846 return nullptr;
1847 }
1848
1850 if (self) {
1851 self->quat = quat_alloc;
1852 /* init callbacks as nullptr */
1853 self->cb_user = nullptr;
1854 self->cb_type = self->cb_subtype = 0;
1855
1856 /* NEW */
1857 if (!quat) { /* new empty */
1858 unit_qt(self->quat);
1859 }
1860 else {
1861 copy_qt_qt(self->quat, quat);
1862 }
1864 }
1865 else {
1866 PyMem_Free(quat_alloc);
1867 }
1868
1869 return (PyObject *)self;
1870}
1871
1872PyObject *Quaternion_CreatePyObject_wrap(float quat[4], PyTypeObject *base_type)
1873{
1875
1877 if (self) {
1878 /* init callbacks as nullptr */
1879 self->cb_user = nullptr;
1880 self->cb_type = self->cb_subtype = 0;
1881
1882 /* WRAP */
1883 self->quat = quat;
1885 }
1886 return (PyObject *)self;
1887}
1888
1889PyObject *Quaternion_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar cb_subtype)
1890{
1892 if (self) {
1893 Py_INCREF(cb_user);
1894 self->cb_user = cb_user;
1895 self->cb_type = cb_type;
1896 self->cb_subtype = cb_subtype;
1897 BLI_assert(!PyObject_GC_IsTracked((PyObject *)self));
1898 PyObject_GC_Track(self);
1899 }
1900
1901 return (PyObject *)self;
1902}
1903
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ATTR_FALLTHROUGH
A dynamically sized string ADT.
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.c:37
void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format,...) ATTR_PRINTF_FORMAT(2
double double_round(double x, int ndigits)
Definition math_base.c:28
MINLINE float safe_acosf(float a)
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
void quat_to_compatible_eulO(float eul[3], const float oldrot[3], short order, const float quat[4])
void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q2[4])
void interp_qt_qtqt(float q[4], const float a[4], const float b[4], float t)
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void quat_to_mat3(float m[3][3], const float q[4])
@ EULER_ORDER_XYZ
void mat3_to_quat(float q[4], const float mat[3][3])
void quat_to_eulO(float e[3], short order, const float q[4])
float normalize_qt(float q[4])
void invert_qt(float q[4])
void mul_qt_fl(float q[4], float f)
void mul_qt_v3(const float q[4], float r[3])
void unit_qt(float q[4])
void quat_to_eul(float eul[3], const float quat[4])
float normalize_qt_qt(float r[4], const float q[4])
float dot_qtqt(const float a[4], const float b[4])
float quat_split_swing_and_twist(const float q_in[4], int axis, float r_swing[4], float r_twist[4])
void mul_qt_qtqt(float q[4], const float a[4], const float b[4])
void quat_to_axis_angle(float axis[3], float *angle, const float q[4])
void quat_to_expmap(float expmap[3], const float q[4])
void add_qt_qtqt(float q[4], const float a[4], const float b[4], float t)
void conjugate_qt(float q[4])
void quat_to_compatible_eul(float eul[3], const float oldrot[3], const float quat[4])
void copy_qt_qt(float q[4], const float a[4])
float angle_wrap_rad(float angle)
void quat_to_compatible_quat(float q[4], const float a[4], const float old[4])
void expmap_to_quat(float r[4], const float expmap[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v4_v4(float r[4], const float a[4])
void mul_vn_vn(float *array_tar, const float *array_src, int size)
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
void mul_vn_vnvn(float *array_tar, const float *array_src_a, const float *array_src_b, int size)
unsigned char uchar
#define CLAMP(a, b, c)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define UNLIKELY(x)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
PyObject * self
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
local_group_size(16, 16) .push_constant(Type b
#define sqrtf(x)
int len
draw_view in_light_buf[] float
int count
int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
Definition mathutils.cc:471
int BaseMathObject_is_gc(BaseMathObject *self)
Definition mathutils.cc:711
Py_hash_t mathutils_array_hash(const float *array, size_t array_len)
Definition mathutils.cc:69
void BaseMathObject_dealloc(BaseMathObject *self)
Definition mathutils.cc:689
int EXPP_VectorsAreEqual(const float *vecA, const float *vecB, int size, int floatSteps)
Definition mathutils.cc:490
int mathutils_array_parse(float *array, int array_num_min, int array_num_max, PyObject *value, const char *error_prefix)
Definition mathutils.cc:97
char BaseMathObject_is_valid_doc[]
Definition mathutils.cc:639
char BaseMathObject_is_wrapped_doc[]
Definition mathutils.cc:625
PyObject * BaseMathObject_owner_get(BaseMathObject *self, void *)
Definition mathutils.cc:619
char BaseMathObject_is_frozen_doc[]
Definition mathutils.cc:632
PyObject * mathutils_dynstr_to_py(DynStr *ds)
Definition mathutils.cc:502
int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix)
Definition mathutils.cc:409
PyObject * BaseMathObject_is_frozen_get(BaseMathObject *self, void *)
Definition mathutils.cc:634
PyObject * BaseMathObject_freeze(BaseMathObject *self)
Definition mathutils.cc:653
PyObject * BaseMathObject_is_wrapped_get(BaseMathObject *self, void *)
Definition mathutils.cc:627
char BaseMathObject_owner_doc[]
Definition mathutils.cc:618
char BaseMathObject_freeze_doc[]
Definition mathutils.cc:645
PyObject * BaseMathObject_is_valid_get(BaseMathObject *self, void *)
Definition mathutils.cc:640
int BaseMathObject_clear(BaseMathObject *self)
Definition mathutils.cc:671
int BaseMathObject_traverse(BaseMathObject *self, visitproc visit, void *arg)
Definition mathutils.cc:665
@ BASE_MATH_FLAG_IS_WRAP
Definition mathutils.hh:36
#define BaseMath_ReadCallback_ForWrite(_self)
Definition mathutils.hh:135
#define BaseMath_ReadIndexCallback(_self, _index)
Definition mathutils.hh:129
#define BaseMath_WriteCallback(_self)
Definition mathutils.hh:127
#define BASE_MATH_NEW(struct_name, root_type, base_type)
Definition mathutils.hh:26
#define BaseMathObject_Prepare_ForHash(_self)
Definition mathutils.hh:150
#define BASE_MATH_FLAG_DEFAULT
Definition mathutils.hh:43
#define BaseMath_Prepare_ForWrite(_self)
Definition mathutils.hh:145
#define BaseMath_ReadCallback(_self)
Definition mathutils.hh:125
#define BaseMath_WriteIndexCallback(_self, _index)
Definition mathutils.hh:131
short euler_order_from_string(const char *str, const char *error_prefix)
PyTypeObject euler_Type
PyObject * Euler_CreatePyObject(const float eul[3], const short order, PyTypeObject *base_type)
PyObject * Matrix_CreatePyObject(const float *mat, const ushort col_num, const ushort row_num, PyTypeObject *base_type)
static PyObject * Quaternion_add(PyObject *q1, PyObject *q2)
static PyObject * Quaternion_slice(QuaternionObject *self, int begin, int end)
static PyObject * Quaternion_angle_get(QuaternionObject *self, void *)
static PyObject * Quaternion_conjugate(QuaternionObject *self)
static PyObject * quat_mul_float(QuaternionObject *quat, const float scalar)
static PyObject * Quaternion_imatmul(PyObject *q1, PyObject *q2)
static PyObject * Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits)
static PyObject * Quaternion_str(QuaternionObject *self)
static PyObject * Quaternion_rotate(QuaternionObject *self, PyObject *value)
static PyObject * Quaternion_richcmpr(PyObject *a, PyObject *b, int op)
static PyObject * Quaternion_to_matrix(QuaternionObject *self)
static PySequenceMethods Quaternion_SeqMethods
static int Quaternion_axis_vector_set(QuaternionObject *self, PyObject *value, void *)
static PyObject * Quaternion_rotation_difference(QuaternionObject *self, PyObject *value)
static PyMethodDef Quaternion_methods[]
static PyMappingMethods Quaternion_AsMapping
static PyObject * Quaternion_axis_vector_get(QuaternionObject *self, void *)
static PyGetSetDef Quaternion_getseters[]
static PyObject * Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static PyObject * Quaternion_axis_get(QuaternionObject *self, void *type)
static PyObject * Quaternion_to_exponential_map(QuaternionObject *self)
static int Quaternion_ass_item(QuaternionObject *self, Py_ssize_t i, PyObject *ob)
static PyObject * Quaternion_matmul(PyObject *q1, PyObject *q2)
static PyObject * Quaternion_make_compatible(QuaternionObject *self, PyObject *value)
static PyObject * Quaternion_to_euler(QuaternionObject *self, PyObject *args)
static PyObject * Quaternion_sub(PyObject *q1, PyObject *q2)
PyDoc_STRVAR(Quaternion_to_euler_doc, ".. method:: to_euler(order, euler_compat)\n" "\n" " Return Euler representation of the quaternion.\n" "\n" " :arg order: Optional rotation order argument in\n" " ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX'].\n" " :type order: str\n" " :arg euler_compat: Optional euler argument the new euler will be made\n" " compatible with (no axis flipping between them).\n" " Useful for converting a series of matrices to animation curves.\n" " :type euler_compat: :class:`Euler`\n" " :return: Euler representation of the quaternion.\n" " :rtype: :class:`Euler`\n")
static PyObject * Quaternion_magnitude_get(QuaternionObject *self, void *)
static PyObject * Quaternion_conjugated(QuaternionObject *self)
static void quat__axis_angle_sanitize(float axis[3], float *angle)
static PyObject * Quaternion_neg(QuaternionObject *self)
static PyObject * Quaternion_cross(QuaternionObject *self, PyObject *value)
#define QUAT_SIZE
static PyObject * Quaternion_negate(QuaternionObject *self)
static PyObject * Quaternion_subscript(QuaternionObject *self, PyObject *item)
static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyObject *value)
static PyObject * Quaternion_inverted(QuaternionObject *self)
static PyObject * Quaternion_deepcopy(QuaternionObject *self, PyObject *args)
static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyObject *seq)
static PyObject * Quaternion_normalized(QuaternionObject *self)
static PyObject * Quaternion_mul(PyObject *q1, PyObject *q2)
static PyNumberMethods Quaternion_NumMethods
PyObject * Quaternion_CreatePyObject_wrap(float quat[4], PyTypeObject *base_type)
static PyObject * Quaternion_to_axis_angle(QuaternionObject *self)
static int Quaternion_angle_set(QuaternionObject *self, PyObject *value, void *)
PyObject * Quaternion_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar cb_subtype)
PyTypeObject quaternion_Type
static PyObject * Quaternion_repr(QuaternionObject *self)
static int Quaternion_axis_set(QuaternionObject *self, PyObject *value, void *type)
static PyObject * quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *), QuaternionObject *self)
static PyObject * Quaternion_slerp(QuaternionObject *self, PyObject *args)
static PyObject * Quaternion_normalize(QuaternionObject *self)
static PyObject * Quaternion_identity(QuaternionObject *self)
static PyObject * Quaternion_to_swing_twist(QuaternionObject *self, PyObject *axis_arg)
static PyObject * Quaternion_invert(QuaternionObject *self)
static Py_hash_t Quaternion_hash(QuaternionObject *self)
PyObject * Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type)
static PyObject * Quaternion_dot(QuaternionObject *self, PyObject *value)
static PyObject * Quaternion_copy(QuaternionObject *self)
static PyObject * Quaternion_item(QuaternionObject *self, Py_ssize_t i)
static Py_ssize_t Quaternion_len(QuaternionObject *)
static PyObject * Quaternion_imul(PyObject *q1, PyObject *q2)
#define QuaternionObject_Check(v)
PyObject * Vector_CreatePyObject(const float *vec, const int vec_num, PyTypeObject *base_type)
#define VectorObject_Check(v)
int PyC_CheckArgs_DeepCopy(PyObject *args)
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret