Blender V5.0
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
8
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/* -------------------------------------------------------------------- */
39
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 */
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
102
103/* -------------------------------------------------------------------- */
106
107static PyObject *Quaternion_vectorcall(PyObject *type,
108 PyObject *const *args,
109 const size_t nargsf,
110 PyObject *kwnames)
111{
112 if (UNLIKELY(kwnames && PyTuple_GET_SIZE(kwnames))) {
113 PyErr_SetString(PyExc_TypeError,
114 "mathutils.Quaternion(): "
115 "takes no keyword args");
116 return nullptr;
117 }
118
119 double angle = 0.0f;
120 float quat[QUAT_SIZE];
121 unit_qt(quat);
122
123 const size_t nargs = PyVectorcall_NARGS(nargsf);
124 switch (nargs) {
125 case 0: {
126 break;
127 }
128 case 1: {
129 const int size = mathutils_array_parse(
130 quat, 3, QUAT_SIZE, args[0], "mathutils.Quaternion()");
131
132 if (UNLIKELY(size == -1)) {
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, args[0], "mathutils.Quaternion()") == -1) {
150 return nullptr;
151 }
152 angle = PyFloat_AsDouble(args[1]);
153 if (UNLIKELY(angle == -1.0 && PyErr_Occurred())) {
154 PyErr_Format(PyExc_TypeError,
155 "mathutils.Quaternion(): "
156 "angle must be a real number, not '%.200s'",
157 Py_TYPE(args[1])->tp_name);
158 return nullptr;
159 }
160 angle = angle_wrap_rad(angle); /* clamp because of precision issues */
161 axis_angle_to_quat(quat, axis, angle);
162 break;
163 }
164 default: {
165 PyErr_Format(PyExc_TypeError,
166 "mathutils.Quaternion() "
167 "takes at most 2 arguments (%zd given)",
168 nargs);
169 return nullptr;
170 }
171 }
172 return Quaternion_CreatePyObject(quat, (PyTypeObject *)type);
173}
174
175static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
176{
177 /* Only called on sub-classes. */
178 if (UNLIKELY(kwds && PyDict_GET_SIZE(kwds))) {
179 PyErr_SetString(PyExc_TypeError,
180 "mathutils.Quaternion(): "
181 "takes no keyword args");
182 return nullptr;
183 }
184 PyObject *const *args_array = &PyTuple_GET_ITEM(args, 0);
185 const size_t args_array_num = PyTuple_GET_SIZE(args);
187 reinterpret_cast<PyObject *>(type), args_array, args_array_num, nullptr);
188}
189
191
192/* -------------------------------------------------------------------- */
195
197 /* Wrap. */
198 Quaternion_to_euler_doc,
199 ".. method:: to_euler(order='XYZ', euler_compat=None, /)\n"
200 "\n"
201 " Return Euler representation of the quaternion.\n"
202 "\n"
203 " :arg order: Rotation order.\n"
204 " :type order: Literal['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']\n"
205 " :arg euler_compat: Optional euler argument the new euler will be made\n"
206 " compatible with (no axis flipping between them).\n"
207 " Useful for converting a series of matrices to animation curves.\n"
208 " :type euler_compat: :class:`Euler`\n"
209 " :return: Euler representation of the quaternion.\n"
210 " :rtype: :class:`Euler`\n");
211static PyObject *Quaternion_to_euler(QuaternionObject *self, PyObject *args)
212{
213 float tquat[4];
214 float eul[3];
215 const char *order_str = nullptr;
216 short order = EULER_ORDER_XYZ;
217 EulerObject *eul_compat = nullptr;
218
219 if (!PyArg_ParseTuple(args, "|sO!:to_euler", &order_str, &euler_Type, &eul_compat)) {
220 return nullptr;
221 }
222
223 if (BaseMath_ReadCallback(self) == -1) {
224 return nullptr;
225 }
226
227 if (order_str) {
228 order = euler_order_from_string(order_str, "Quaternion.to_euler()");
229
230 if (order == -1) {
231 return nullptr;
232 }
233 }
234
235 normalize_qt_qt(tquat, self->quat);
236
237 if (eul_compat) {
238 if (BaseMath_ReadCallback(eul_compat) == -1) {
239 return nullptr;
240 }
241
242 if (order == EULER_ORDER_XYZ) {
243 quat_to_compatible_eul(eul, eul_compat->eul, tquat);
244 }
245 else {
246 quat_to_compatible_eulO(eul, eul_compat->eul, order, tquat);
247 }
248 }
249 else {
250 if (order == EULER_ORDER_XYZ) {
251 quat_to_eul(eul, tquat);
252 }
253 else {
254 quat_to_eulO(eul, order, tquat);
255 }
256 }
257
258 return Euler_CreatePyObject(eul, order, nullptr);
259}
260
262
263/* -------------------------------------------------------------------- */
266
268 /* Wrap. */
269 Quaternion_to_matrix_doc,
270 ".. method:: to_matrix()\n"
271 "\n"
272 " Return a matrix representation of the quaternion.\n"
273 "\n"
274 " :return: A 3x3 rotation matrix representation of the quaternion.\n"
275 " :rtype: :class:`Matrix`\n");
277{
278 float mat[9]; /* all values are set */
279
280 if (BaseMath_ReadCallback(self) == -1) {
281 return nullptr;
282 }
283
284 quat_to_mat3((float (*)[3])mat, self->quat);
285 return Matrix_CreatePyObject(mat, 3, 3, nullptr);
286}
287
289
290/* -------------------------------------------------------------------- */
293
295 /* Wrap. */
296 Quaternion_to_axis_angle_doc,
297 ".. method:: to_axis_angle()\n"
298 "\n"
299 " Return the axis, angle representation of the quaternion.\n"
300 "\n"
301 " :return: Axis, angle.\n"
302 " :rtype: tuple[:class:`Vector`, float]\n");
304{
305 PyObject *ret;
306
307 float tquat[4];
308
309 float axis[3];
310 float angle;
311
312 if (BaseMath_ReadCallback(self) == -1) {
313 return nullptr;
314 }
315
316 normalize_qt_qt(tquat, self->quat);
317 quat_to_axis_angle(axis, &angle, tquat);
318
320
321 ret = PyTuple_New(2);
322 PyTuple_SET_ITEMS(ret, Vector_CreatePyObject(axis, 3, nullptr), PyFloat_FromDouble(angle));
323 return ret;
324}
325
327
328/* -------------------------------------------------------------------- */
331
333 /* Wrap. */
334 Quaternion_to_swing_twist_doc,
335 ".. method:: to_swing_twist(axis, /)\n"
336 "\n"
337 " Split the rotation into a swing quaternion with the specified\n"
338 " axis fixed at zero, and the remaining twist rotation angle.\n"
339 "\n"
340 " :arg axis: Twist axis as a string.\n"
341 " :type axis: Literal['X', 'Y', 'Z']\n"
342 " :return: Swing, twist angle.\n"
343 " :rtype: tuple[:class:`Quaternion`, float]\n");
344static PyObject *Quaternion_to_swing_twist(QuaternionObject *self, PyObject *axis_arg)
345{
346 PyObject *ret;
347
348 const char *axis_str = nullptr;
349 float swing[4], twist;
350 int axis;
351
352 if (axis_arg && PyUnicode_Check(axis_arg)) {
353 axis_str = PyUnicode_AsUTF8(axis_arg);
354 }
355
356 if (axis_str && axis_str[0] >= 'X' && axis_str[0] <= 'Z' && axis_str[1] == 0) {
357 axis = axis_str[0] - 'X';
358 }
359 else {
360 PyErr_SetString(PyExc_ValueError,
361 "Quaternion.to_swing_twist(): "
362 "the axis argument must be "
363 "a string in 'X', 'Y', 'Z'");
364 return nullptr;
365 }
366
367 if (BaseMath_ReadCallback(self) == -1) {
368 return nullptr;
369 }
370
371 twist = quat_split_swing_and_twist(self->quat, axis, swing, nullptr);
372
373 ret = PyTuple_New(2);
375 ret, Quaternion_CreatePyObject(swing, Py_TYPE(self)), PyFloat_FromDouble(twist));
376 return ret;
377}
378
380
381/* -------------------------------------------------------------------- */
384
386 /* Wrap. */
387 Quaternion_to_exponential_map_doc,
388 ".. method:: to_exponential_map()\n"
389 "\n"
390 " Return the exponential map representation of the quaternion.\n"
391 "\n"
392 " This representation consist of the rotation axis multiplied by the rotation angle.\n"
393 " Such a representation is useful for interpolation between multiple orientations.\n"
394 "\n"
395 " :return: exponential map.\n"
396 " :rtype: :class:`Vector` of size 3\n"
397 "\n"
398 " To convert back to a quaternion, pass it to the :class:`Quaternion` constructor.\n");
400{
401 float expmap[3];
402
403 if (BaseMath_ReadCallback(self) == -1) {
404 return nullptr;
405 }
406
407 quat_to_expmap(expmap, self->quat);
408 return Vector_CreatePyObject(expmap, 3, nullptr);
409}
410
412
413/* -------------------------------------------------------------------- */
416
418 /* Wrap. */
419 Quaternion_cross_doc,
420 ".. method:: cross(other, /)\n"
421 "\n"
422 " Return the cross product of this quaternion and another.\n"
423 "\n"
424 " :arg other: The other quaternion to perform the cross product with.\n"
425 " :type other: :class:`Quaternion`\n"
426 " :return: The cross product.\n"
427 " :rtype: :class:`Quaternion`\n");
428static PyObject *Quaternion_cross(QuaternionObject *self, PyObject *value)
429{
430 float quat[QUAT_SIZE], tquat[QUAT_SIZE];
431
432 if (BaseMath_ReadCallback(self) == -1) {
433 return nullptr;
434 }
435
437 tquat, QUAT_SIZE, QUAT_SIZE, value, "Quaternion.cross(other), invalid 'other' arg") ==
438 -1)
439 {
440 return nullptr;
441 }
442
443 mul_qt_qtqt(quat, self->quat, tquat);
444 return Quaternion_CreatePyObject(quat, Py_TYPE(self));
445}
446
448
449/* -------------------------------------------------------------------- */
452
454 /* Wrap. */
455 Quaternion_dot_doc,
456 ".. method:: dot(other, /)\n"
457 "\n"
458 " Return the dot product of this quaternion and another.\n"
459 "\n"
460 " :arg other: The other quaternion to perform the dot product with.\n"
461 " :type other: :class:`Quaternion`\n"
462 " :return: The dot product.\n"
463 " :rtype: float\n");
464static PyObject *Quaternion_dot(QuaternionObject *self, PyObject *value)
465{
466 float tquat[QUAT_SIZE];
467
468 if (BaseMath_ReadCallback(self) == -1) {
469 return nullptr;
470 }
471
473 tquat, QUAT_SIZE, QUAT_SIZE, value, "Quaternion.dot(other), invalid 'other' arg") == -1)
474 {
475 return nullptr;
476 }
477
478 return PyFloat_FromDouble(dot_qtqt(self->quat, tquat));
479}
480
482
483/* -------------------------------------------------------------------- */
486
488 /* Wrap. */
489 Quaternion_rotation_difference_doc,
490 ".. function:: rotation_difference(other, /)\n"
491 "\n"
492 " Returns a quaternion representing the rotational difference.\n"
493 "\n"
494 " :arg other: second quaternion.\n"
495 " :type other: :class:`Quaternion`\n"
496 " :return: the rotational difference between the two quat rotations.\n"
497 " :rtype: :class:`Quaternion`\n");
498static PyObject *Quaternion_rotation_difference(QuaternionObject *self, PyObject *value)
499{
500 float tquat[QUAT_SIZE], quat[QUAT_SIZE];
501
502 if (BaseMath_ReadCallback(self) == -1) {
503 return nullptr;
504 }
505
506 if (mathutils_array_parse(tquat,
507 QUAT_SIZE,
508 QUAT_SIZE,
509 value,
510 "Quaternion.rotation_difference(other), invalid 'other' arg") == -1)
511 {
512 return nullptr;
513 }
514
515 rotation_between_quats_to_quat(quat, self->quat, tquat);
516
517 return Quaternion_CreatePyObject(quat, Py_TYPE(self));
518}
519
521
522/* -------------------------------------------------------------------- */
525
527 /* Wrap. */
528 Quaternion_slerp_doc,
529 ".. function:: slerp(other, factor, /)\n"
530 "\n"
531 " Returns the interpolation of two quaternions.\n"
532 "\n"
533 " :arg other: value to interpolate with.\n"
534 " :type other: :class:`Quaternion`\n"
535 " :arg factor: The interpolation value in [0.0, 1.0].\n"
536 " :type factor: float\n"
537 " :return: The interpolated rotation.\n"
538 " :rtype: :class:`Quaternion`\n");
539static PyObject *Quaternion_slerp(QuaternionObject *self, PyObject *args)
540{
541 PyObject *value;
542 float tquat[QUAT_SIZE], quat[QUAT_SIZE], fac;
543
544 if (!PyArg_ParseTuple(args, "Of:slerp", &value, &fac)) {
545 PyErr_SetString(PyExc_TypeError,
546 "quat.slerp(): "
547 "expected Quaternion types and float");
548 return nullptr;
549 }
550
551 if (BaseMath_ReadCallback(self) == -1) {
552 return nullptr;
553 }
554
556 tquat, QUAT_SIZE, QUAT_SIZE, value, "Quaternion.slerp(other), invalid 'other' arg") ==
557 -1)
558 {
559 return nullptr;
560 }
561
562 if (fac > 1.0f || fac < 0.0f) {
563 PyErr_SetString(PyExc_ValueError,
564 "quat.slerp(): "
565 "interpolation factor must be between 0.0 and 1.0");
566 return nullptr;
567 }
568
569 interp_qt_qtqt(quat, self->quat, tquat, fac);
570
571 return Quaternion_CreatePyObject(quat, Py_TYPE(self));
572}
573
575
576/* -------------------------------------------------------------------- */
579
581 /* Wrap. */
582 Quaternion_rotate_doc,
583 ".. method:: rotate(other, /)\n"
584 "\n"
585 " Rotates the quaternion by another mathutils value.\n"
586 "\n"
587 " :arg other: rotation component of mathutils value\n"
588 " :type other: :class:`Euler` | :class:`Quaternion` | :class:`Matrix`\n");
589static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value)
590{
591 float self_rmat[3][3], other_rmat[3][3], rmat[3][3];
592 float tquat[4], length;
593
595 return nullptr;
596 }
597
598 if (mathutils_any_to_rotmat(other_rmat, value, "Quaternion.rotate(value)") == -1) {
599 return nullptr;
600 }
601
602 length = normalize_qt_qt(tquat, self->quat);
603 quat_to_mat3(self_rmat, tquat);
604 mul_m3_m3m3(rmat, other_rmat, self_rmat);
605 mat3_to_quat(self->quat, rmat);
606 mul_qt_fl(self->quat, length); /* maintain length after rotating */
607
609 Py_RETURN_NONE;
610}
611
613 /* Wrap. */
614 Quaternion_make_compatible_doc,
615 ".. method:: make_compatible(other, /)\n"
616 "\n"
617 " Make this quaternion compatible with another,\n"
618 " so interpolating between them works as intended.\n"
619 "\n"
620 " :arg other: The other quaternion to make compatible with.\n"
621 " :type other: :class:`Quaternion`\n");
622static PyObject *Quaternion_make_compatible(QuaternionObject *self, PyObject *value)
623{
624 float quat[QUAT_SIZE];
625 float tquat[QUAT_SIZE];
626
628 return nullptr;
629 }
630
631 if (mathutils_array_parse(tquat,
632 QUAT_SIZE,
633 QUAT_SIZE,
634 value,
635 "Quaternion.make_compatible(other), invalid 'other' arg") == -1)
636 {
637 return nullptr;
638 }
639
640 /* Can only operate on unit length quaternions. */
641 const float quat_len = normalize_qt_qt(quat, self->quat);
642 quat_to_compatible_quat(self->quat, quat, tquat);
643 mul_qt_fl(self->quat, quat_len);
644
646
647 Py_RETURN_NONE;
648}
649
651
652/* -------------------------------------------------------------------- */
658
660 /* Wrap. */
661 Quaternion_normalize_doc,
662 ".. function:: normalize()\n"
663 "\n"
664 " Normalize the quaternion.\n");
666{
668 return nullptr;
669 }
670
671 normalize_qt(self->quat);
672
674 Py_RETURN_NONE;
675}
677 /* Wrap. */
678 Quaternion_normalized_doc,
679 ".. function:: normalized()\n"
680 "\n"
681 " Return a new normalized quaternion.\n"
682 "\n"
683 " :return: a normalized copy.\n"
684 " :rtype: :class:`Quaternion`\n");
689
691
692/* -------------------------------------------------------------------- */
698
700 /* Wrap. */
701 Quaternion_invert_doc,
702 ".. function:: invert()\n"
703 "\n"
704 " Set the quaternion to its inverse.\n");
706{
708 return nullptr;
709 }
710
711 invert_qt(self->quat);
712
714 Py_RETURN_NONE;
715}
717 /* Wrap. */
718 Quaternion_inverted_doc,
719 ".. function:: inverted()\n"
720 "\n"
721 " Return a new, inverted quaternion.\n"
722 "\n"
723 " :return: the inverted value.\n"
724 " :rtype: :class:`Quaternion`\n");
729
731
732/* -------------------------------------------------------------------- */
735
737 /* Wrap. */
738 Quaternion_identity_doc,
739 ".. function:: identity()\n"
740 "\n"
741 " Set the quaternion to an identity quaternion.\n");
743{
745 return nullptr;
746 }
747
748 unit_qt(self->quat);
749
751 Py_RETURN_NONE;
752}
753
755
756/* -------------------------------------------------------------------- */
759
761 /* Wrap. */
762 Quaternion_negate_doc,
763 ".. function:: negate()\n"
764 "\n"
765 " Set the quaternion to its negative.\n");
767{
769 return nullptr;
770 }
771
772 mul_qt_fl(self->quat, -1.0f);
773
775 Py_RETURN_NONE;
776}
777
779
780/* -------------------------------------------------------------------- */
783
785 /* Wrap. */
786 Quaternion_conjugate_doc,
787 ".. function:: conjugate()\n"
788 "\n"
789 " Set the quaternion to its conjugate (negate x, y, z).\n");
791{
793 return nullptr;
794 }
795
796 conjugate_qt(self->quat);
797
799 Py_RETURN_NONE;
800}
802 /* Wrap. */
803 Quaternion_conjugated_doc,
804 ".. function:: conjugated()\n"
805 "\n"
806 " Return a new conjugated quaternion.\n"
807 "\n"
808 " :return: a new quaternion.\n"
809 " :rtype: :class:`Quaternion`\n");
814
816
817/* -------------------------------------------------------------------- */
820
822 /* Wrap. */
823 Quaternion_copy_doc,
824 ".. function:: copy()\n"
825 "\n"
826 " Returns a copy of this quaternion.\n"
827 "\n"
828 " :return: A copy of the quaternion.\n"
829 " :rtype: :class:`Quaternion`\n"
830 "\n"
831 " .. note:: use this to get a copy of a wrapped quaternion with\n"
832 " no reference to the original data.\n");
834{
835 if (BaseMath_ReadCallback(self) == -1) {
836 return nullptr;
837 }
838
839 return Quaternion_CreatePyObject(self->quat, Py_TYPE(self));
840}
841static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args)
842{
843 if (!PyC_CheckArgs_DeepCopy(args)) {
844 return nullptr;
845 }
846 return Quaternion_copy(self);
847}
848
850
851/* -------------------------------------------------------------------- */
854
856{
857 PyObject *ret, *tuple;
858
859 if (BaseMath_ReadCallback(self) == -1) {
860 return nullptr;
861 }
862
863 tuple = Quaternion_to_tuple_ext(self, -1);
864
865 ret = PyUnicode_FromFormat("Quaternion(%R)", tuple);
866
867 Py_DECREF(tuple);
868 return ret;
869}
870
871#ifndef MATH_STANDALONE
873{
874 DynStr *ds;
875
876 if (BaseMath_ReadCallback(self) == -1) {
877 return nullptr;
878 }
879
880 ds = BLI_dynstr_new();
881
883 "<Quaternion (w=%.4f, x=%.4f, y=%.4f, z=%.4f)>",
884 self->quat[0],
885 self->quat[1],
886 self->quat[2],
887 self->quat[3]);
888
889 return mathutils_dynstr_to_py(ds); /* frees ds */
890}
891#endif
892
894
895/* -------------------------------------------------------------------- */
898
899static int Quaternion_getbuffer(PyObject *obj, Py_buffer *view, int flags)
900{
903 return -1;
904 }
905 if (UNLIKELY(BaseMath_ReadCallback(self) == -1)) {
906 return -1;
907 }
908
909 memset(view, 0, sizeof(*view));
910
911 view->obj = (PyObject *)self;
912 view->buf = (void *)self->quat;
913 view->len = Py_ssize_t(QUAT_SIZE * sizeof(float));
914 view->itemsize = sizeof(float);
915 view->ndim = 1;
916 if ((flags & PyBUF_WRITABLE) == 0) {
917 view->readonly = 1;
918 }
919 if (flags & PyBUF_FORMAT) {
920 view->format = (char *)"f";
921 }
922
924
925 Py_INCREF(self);
926 return 0;
927}
928
929static void Quaternion_releasebuffer(PyObject * /*exporter*/, Py_buffer *view)
930{
933
934 if (view->readonly == 0) {
936 PyErr_Print();
937 }
938 }
939}
940
941static PyBufferProcs Quaternion_as_buffer = {
942 (getbufferproc)Quaternion_getbuffer,
943 (releasebufferproc)Quaternion_releasebuffer,
944};
945
947
948/* -------------------------------------------------------------------- */
951
952static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op)
953{
954 PyObject *res;
955 int ok = -1; /* zero is true */
956
960
961 if (BaseMath_ReadCallback(quatA) == -1 || BaseMath_ReadCallback(quatB) == -1) {
962 return nullptr;
963 }
964
965 ok = EXPP_VectorsAreEqual(quatA->quat, quatB->quat, QUAT_SIZE, 1) ? 0 : -1;
966 }
967
968 switch (op) {
969 case Py_NE: {
970 ok = !ok;
972 }
973 case Py_EQ: {
974 res = ok ? Py_False : Py_True;
975 break;
976 }
977 case Py_LT:
978 case Py_LE:
979 case Py_GT:
980 case Py_GE: {
981 res = Py_NotImplemented;
982 break;
983 }
984 default: {
985 PyErr_BadArgument();
986 return nullptr;
987 }
988 }
989
990 return Py_NewRef(res);
991}
992
994
995/* -------------------------------------------------------------------- */
998
1000{
1001 if (BaseMath_ReadCallback(self) == -1) {
1002 return -1;
1003 }
1004
1006 return -1;
1007 }
1008
1009 return mathutils_array_hash(self->quat, QUAT_SIZE);
1010}
1011
1013
1014/* -------------------------------------------------------------------- */
1017
1019static Py_ssize_t Quaternion_len(QuaternionObject * /*self*/)
1020{
1021 return QUAT_SIZE;
1022}
1023
1025static PyObject *Quaternion_item(QuaternionObject *self, Py_ssize_t i)
1026{
1027 if (i < 0) {
1028 i = QUAT_SIZE - i;
1029 }
1030
1031 if (i < 0 || i >= QUAT_SIZE) {
1032 PyErr_SetString(PyExc_IndexError,
1033 "quaternion[attribute]: "
1034 "array index out of range");
1035 return nullptr;
1036 }
1037
1038 if (BaseMath_ReadIndexCallback(self, i) == -1) {
1039 return nullptr;
1040 }
1041
1042 return PyFloat_FromDouble(self->quat[i]);
1043}
1044
1046static int Quaternion_ass_item(QuaternionObject *self, Py_ssize_t i, PyObject *ob)
1047{
1048 float f;
1049
1050 if (BaseMath_Prepare_ForWrite(self) == -1) {
1051 return -1;
1052 }
1053
1054 f = float(PyFloat_AsDouble(ob));
1055
1056 if (f == -1.0f && PyErr_Occurred()) { /* parsed item not a number */
1057 PyErr_SetString(PyExc_TypeError,
1058 "quaternion[index] = x: "
1059 "assigned value not a number");
1060 return -1;
1061 }
1062
1063 if (i < 0) {
1064 i = QUAT_SIZE - i;
1065 }
1066
1067 if (i < 0 || i >= QUAT_SIZE) {
1068 PyErr_SetString(PyExc_IndexError,
1069 "quaternion[attribute] = x: "
1070 "array assignment index out of range");
1071 return -1;
1072 }
1073 self->quat[i] = f;
1074
1075 if (BaseMath_WriteIndexCallback(self, i) == -1) {
1076 return -1;
1077 }
1078
1079 return 0;
1080}
1081
1083static PyObject *Quaternion_slice(QuaternionObject *self, int begin, int end)
1084{
1085 PyObject *tuple;
1086 int count;
1087
1088 if (BaseMath_ReadCallback(self) == -1) {
1089 return nullptr;
1090 }
1091
1092 CLAMP(begin, 0, QUAT_SIZE);
1093 if (end < 0) {
1094 end = (QUAT_SIZE + 1) + end;
1095 }
1096 CLAMP(end, 0, QUAT_SIZE);
1097 begin = std::min(begin, end);
1098
1099 tuple = PyTuple_New(end - begin);
1100 for (count = begin; count < end; count++) {
1101 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(self->quat[count]));
1102 }
1103
1104 return tuple;
1105}
1106
1108static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyObject *seq)
1109{
1110 int i, size;
1111 float quat[QUAT_SIZE];
1112
1114 return -1;
1115 }
1116
1117 CLAMP(begin, 0, QUAT_SIZE);
1118 if (end < 0) {
1119 end = (QUAT_SIZE + 1) + end;
1120 }
1121 CLAMP(end, 0, QUAT_SIZE);
1122 begin = std::min(begin, end);
1123
1125 quat, 0, QUAT_SIZE, seq, "mathutils.Quaternion[begin:end] = []")) == -1)
1126 {
1127 return -1;
1128 }
1129
1130 if (size != (end - begin)) {
1131 PyErr_SetString(PyExc_ValueError,
1132 "quaternion[begin:end] = []: "
1133 "size mismatch in slice assignment");
1134 return -1;
1135 }
1136
1137 /* Parsed well, now set in vector. */
1138 for (i = 0; i < size; i++) {
1139 self->quat[begin + i] = quat[i];
1140 }
1141
1143 return 0;
1144}
1145
1147static PyObject *Quaternion_subscript(QuaternionObject *self, PyObject *item)
1148{
1149 if (PyIndex_Check(item)) {
1150 Py_ssize_t i;
1151 i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1152 if (i == -1 && PyErr_Occurred()) {
1153 return nullptr;
1154 }
1155 if (i < 0) {
1156 i += QUAT_SIZE;
1157 }
1158 return Quaternion_item(self, i);
1159 }
1160 if (PySlice_Check(item)) {
1161 Py_ssize_t start, stop, step, slicelength;
1162
1163 if (PySlice_GetIndicesEx(item, QUAT_SIZE, &start, &stop, &step, &slicelength) < 0) {
1164 return nullptr;
1165 }
1166
1167 if (slicelength <= 0) {
1168 return PyTuple_New(0);
1169 }
1170 if (step == 1) {
1171 return Quaternion_slice(self, start, stop);
1172 }
1173
1174 PyErr_SetString(PyExc_IndexError, "slice steps not supported with quaternions");
1175 return nullptr;
1176 }
1177
1178 PyErr_Format(
1179 PyExc_TypeError, "quaternion indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
1180 return nullptr;
1181}
1182
1184static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyObject *value)
1185{
1186 if (PyIndex_Check(item)) {
1187 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1188 if (i == -1 && PyErr_Occurred()) {
1189 return -1;
1190 }
1191 if (i < 0) {
1192 i += QUAT_SIZE;
1193 }
1194 return Quaternion_ass_item(self, i, value);
1195 }
1196 if (PySlice_Check(item)) {
1197 Py_ssize_t start, stop, step, slicelength;
1198
1199 if (PySlice_GetIndicesEx(item, QUAT_SIZE, &start, &stop, &step, &slicelength) < 0) {
1200 return -1;
1201 }
1202
1203 if (step == 1) {
1204 return Quaternion_ass_slice(self, start, stop, value);
1205 }
1206
1207 PyErr_SetString(PyExc_IndexError, "slice steps not supported with quaternion");
1208 return -1;
1209 }
1210
1211 PyErr_Format(
1212 PyExc_TypeError, "quaternion indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
1213 return -1;
1214}
1215
1217
1218/* -------------------------------------------------------------------- */
1221
1223static PyObject *Quaternion_add(PyObject *q1, PyObject *q2)
1224{
1225 float quat[QUAT_SIZE];
1226 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1227
1229 PyErr_Format(PyExc_TypeError,
1230 "Quaternion addition: (%s + %s) "
1231 "invalid type for this operation",
1232 Py_TYPE(q1)->tp_name,
1233 Py_TYPE(q2)->tp_name);
1234 return nullptr;
1235 }
1236 quat1 = (QuaternionObject *)q1;
1237 quat2 = (QuaternionObject *)q2;
1238
1239 if (BaseMath_ReadCallback(quat1) == -1 || BaseMath_ReadCallback(quat2) == -1) {
1240 return nullptr;
1241 }
1242
1243 add_qt_qtqt(quat, quat1->quat, quat2->quat, 1.0f);
1244 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1245}
1246
1248static PyObject *Quaternion_sub(PyObject *q1, PyObject *q2)
1249{
1250 int x;
1251 float quat[QUAT_SIZE];
1252 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1253
1255 PyErr_Format(PyExc_TypeError,
1256 "Quaternion subtraction: (%s - %s) "
1257 "invalid type for this operation",
1258 Py_TYPE(q1)->tp_name,
1259 Py_TYPE(q2)->tp_name);
1260 return nullptr;
1261 }
1262
1263 quat1 = (QuaternionObject *)q1;
1264 quat2 = (QuaternionObject *)q2;
1265
1266 if (BaseMath_ReadCallback(quat1) == -1 || BaseMath_ReadCallback(quat2) == -1) {
1267 return nullptr;
1268 }
1269
1270 for (x = 0; x < QUAT_SIZE; x++) {
1271 quat[x] = quat1->quat[x] - quat2->quat[x];
1272 }
1273
1274 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1275}
1276
1277static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar)
1278{
1279 float tquat[4];
1280 copy_qt_qt(tquat, quat->quat);
1281 mul_qt_fl(tquat, scalar);
1282 return Quaternion_CreatePyObject(tquat, Py_TYPE(quat));
1283}
1284
1286static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2)
1287{
1288 float scalar;
1289 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1290
1291 if (QuaternionObject_Check(q1)) {
1292 quat1 = (QuaternionObject *)q1;
1293 if (BaseMath_ReadCallback(quat1) == -1) {
1294 return nullptr;
1295 }
1296 }
1297 if (QuaternionObject_Check(q2)) {
1298 quat2 = (QuaternionObject *)q2;
1299 if (BaseMath_ReadCallback(quat2) == -1) {
1300 return nullptr;
1301 }
1302 }
1303
1304 if (quat1 && quat2) { /* QUAT * QUAT (element-wise product) */
1305 float quat[QUAT_SIZE];
1306 mul_vn_vnvn(quat, quat1->quat, quat2->quat, QUAT_SIZE);
1307 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1308 }
1309 /* the only case this can happen (for a supported type is "FLOAT * QUAT") */
1310 if (quat2) { /* FLOAT * QUAT */
1311 if (((scalar = PyFloat_AsDouble(q1)) == -1.0f && PyErr_Occurred()) == 0) {
1312 return quat_mul_float(quat2, scalar);
1313 }
1314 }
1315 else if (quat1) { /* QUAT * FLOAT */
1316 if (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0) {
1317 return quat_mul_float(quat1, scalar);
1318 }
1319 }
1320
1321 PyErr_Format(PyExc_TypeError,
1322 "Element-wise multiplication: "
1323 "not supported between '%.200s' and '%.200s' types",
1324 Py_TYPE(q1)->tp_name,
1325 Py_TYPE(q2)->tp_name);
1326 return nullptr;
1327}
1328
1330static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2)
1331{
1332 float scalar;
1333 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1334
1335 if (QuaternionObject_Check(q1)) {
1336 quat1 = (QuaternionObject *)q1;
1337 if (BaseMath_ReadCallback(quat1) == -1) {
1338 return nullptr;
1339 }
1340 }
1341 if (QuaternionObject_Check(q2)) {
1342 quat2 = (QuaternionObject *)q2;
1343 if (BaseMath_ReadCallback(quat2) == -1) {
1344 return nullptr;
1345 }
1346 }
1347
1348 if (quat1 && quat2) { /* QUAT *= QUAT (in-place element-wise product). */
1349 mul_vn_vn(quat1->quat, quat2->quat, QUAT_SIZE);
1350 }
1351 else if (quat1 && (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) {
1352 /* QUAT *= FLOAT */
1353 mul_qt_fl(quat1->quat, scalar);
1354 }
1355 else {
1356 PyErr_Format(PyExc_TypeError,
1357 "Element-wise multiplication: "
1358 "not supported between '%.200s' and '%.200s' types",
1359 Py_TYPE(q1)->tp_name,
1360 Py_TYPE(q2)->tp_name);
1361 return nullptr;
1362 }
1363
1364 (void)BaseMath_WriteCallback(quat1);
1365 Py_INCREF(q1);
1366 return q1;
1367}
1368
1370static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2)
1371{
1372 float quat[QUAT_SIZE];
1373 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1374
1375 if (QuaternionObject_Check(q1)) {
1376 quat1 = (QuaternionObject *)q1;
1377 if (BaseMath_ReadCallback(quat1) == -1) {
1378 return nullptr;
1379 }
1380 }
1381 if (QuaternionObject_Check(q2)) {
1382 quat2 = (QuaternionObject *)q2;
1383 if (BaseMath_ReadCallback(quat2) == -1) {
1384 return nullptr;
1385 }
1386 }
1387
1388 if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
1389 mul_qt_qtqt(quat, quat1->quat, quat2->quat);
1390 return Quaternion_CreatePyObject(quat, Py_TYPE(q1));
1391 }
1392 if (quat1) {
1393 /* QUAT @ VEC */
1394 if (VectorObject_Check(q2)) {
1395 VectorObject *vec2 = (VectorObject *)q2;
1396 float tvec[3];
1397
1398 if (vec2->vec_num != 3) {
1399 PyErr_SetString(PyExc_ValueError,
1400 "Vector multiplication: "
1401 "only 3D vector rotations (with quats) "
1402 "currently supported");
1403 return nullptr;
1404 }
1405 if (BaseMath_ReadCallback(vec2) == -1) {
1406 return nullptr;
1407 }
1408
1409 copy_v3_v3(tvec, vec2->vec);
1410 mul_qt_v3(quat1->quat, tvec);
1411
1412 return Vector_CreatePyObject(tvec, 3, Py_TYPE(vec2));
1413 }
1414 }
1415
1416 PyErr_Format(PyExc_TypeError,
1417 "Quaternion multiplication: "
1418 "not supported between '%.200s' and '%.200s' types",
1419 Py_TYPE(q1)->tp_name,
1420 Py_TYPE(q2)->tp_name);
1421 return nullptr;
1422}
1423
1425static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2)
1426{
1427 float quat[QUAT_SIZE];
1428 QuaternionObject *quat1 = nullptr, *quat2 = nullptr;
1429
1430 if (QuaternionObject_Check(q1)) {
1431 quat1 = (QuaternionObject *)q1;
1432 if (BaseMath_ReadCallback(quat1) == -1) {
1433 return nullptr;
1434 }
1435 }
1436 if (QuaternionObject_Check(q2)) {
1437 quat2 = (QuaternionObject *)q2;
1438 if (BaseMath_ReadCallback(quat2) == -1) {
1439 return nullptr;
1440 }
1441 }
1442
1443 if (quat1 && quat2) { /* QUAT @ QUAT (cross product) */
1444 mul_qt_qtqt(quat, quat1->quat, quat2->quat);
1445 copy_qt_qt(quat1->quat, quat);
1446 }
1447 else {
1448 PyErr_Format(PyExc_TypeError,
1449 "In place quaternion multiplication: "
1450 "not supported between '%.200s' and '%.200s' types",
1451 Py_TYPE(q1)->tp_name,
1452 Py_TYPE(q2)->tp_name);
1453 return nullptr;
1454 }
1455
1456 (void)BaseMath_WriteCallback(quat1);
1457 Py_INCREF(q1);
1458 return q1;
1459}
1460
1463{
1464 float tquat[QUAT_SIZE];
1465
1466 if (BaseMath_ReadCallback(self) == -1) {
1467 return nullptr;
1468 }
1469
1470 negate_v4_v4(tquat, self->quat);
1471 return Quaternion_CreatePyObject(tquat, Py_TYPE(self));
1472}
1473
1475
1476/* -------------------------------------------------------------------- */
1479
1480static PySequenceMethods Quaternion_SeqMethods = {
1481 /*sq_length*/ (lenfunc)Quaternion_len,
1482 /*sq_concat*/ nullptr,
1483 /*sq_repeat*/ nullptr,
1484 /*sq_item*/ (ssizeargfunc)Quaternion_item,
1485 /*was_sq_slice*/ nullptr, /* DEPRECATED. */
1486 /*sq_ass_item*/ (ssizeobjargproc)Quaternion_ass_item,
1487 /*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
1488 /*sq_contains*/ nullptr,
1489 /*sq_inplace_concat*/ nullptr,
1490 /*sq_inplace_repeat*/ nullptr,
1491};
1492
1493static PyMappingMethods Quaternion_AsMapping = {
1494 /*mp_length*/ (lenfunc)Quaternion_len,
1495 /*mp_subscript*/ (binaryfunc)Quaternion_subscript,
1496 /*mp_ass_subscript*/ (objobjargproc)Quaternion_ass_subscript,
1497};
1498
1499static PyNumberMethods Quaternion_NumMethods = {
1500 /*nb_add*/ (binaryfunc)Quaternion_add,
1501 /*nb_subtract*/ (binaryfunc)Quaternion_sub,
1502 /*nb_multiply*/ (binaryfunc)Quaternion_mul,
1503 /*nb_remainder*/ nullptr,
1504 /*nb_divmod*/ nullptr,
1505 /*nb_power*/ nullptr,
1506 /*nb_negative*/ (unaryfunc)Quaternion_neg,
1507 /*nb_positive*/ (unaryfunc)Quaternion_copy,
1508 /*nb_absolute*/ nullptr,
1509 /*nb_bool*/ nullptr,
1510 /*nb_invert*/ nullptr,
1511 /*nb_lshift*/ nullptr,
1512 /*nb_rshift*/ nullptr,
1513 /*nb_and*/ nullptr,
1514 /*nb_xor*/ nullptr,
1515 /*nb_or*/ nullptr,
1516 /*nb_int*/ nullptr,
1517 /*nb_reserved*/ nullptr,
1518 /*nb_float*/ nullptr,
1519 /*nb_inplace_add*/ nullptr,
1520 /*nb_inplace_subtract*/ nullptr,
1521 /*nb_inplace_multiply*/ (binaryfunc)Quaternion_imul,
1522 /*nb_inplace_remainder*/ nullptr,
1523 /*nb_inplace_power*/ nullptr,
1524 /*nb_inplace_lshift*/ nullptr,
1525 /*nb_inplace_rshift*/ nullptr,
1526 /*nb_inplace_and*/ nullptr,
1527 /*nb_inplace_xor*/ nullptr,
1528 /*nb_inplace_or*/ nullptr,
1529 /*nb_floor_divide*/ nullptr,
1530 /*nb_true_divide*/ nullptr,
1531 /*nb_inplace_floor_divide*/ nullptr,
1532 /*nb_inplace_true_divide*/ nullptr,
1533 /*nb_index*/ nullptr,
1534 /*nb_matrix_multiply*/ (binaryfunc)Quaternion_matmul,
1535 /*nb_inplace_matrix_multiply*/ (binaryfunc)Quaternion_imatmul,
1536};
1537
1539
1540/* -------------------------------------------------------------------- */
1543
1545 /* Wrap. */
1546 Quaternion_axis_doc,
1547 "Quaternion axis value.\n"
1548 "\n"
1549 ":type: float\n");
1550static PyObject *Quaternion_axis_get(QuaternionObject *self, void *type)
1551{
1552 return Quaternion_item(self, POINTER_AS_INT(type));
1553}
1554
1555static int Quaternion_axis_set(QuaternionObject *self, PyObject *value, void *type)
1556{
1557 return Quaternion_ass_item(self, POINTER_AS_INT(type), value);
1558}
1559
1561 /* Wrap. */
1562 Quaternion_magnitude_doc,
1563 "Size of the quaternion (read-only).\n"
1564 "\n"
1565 ":type: float\n");
1566static PyObject *Quaternion_magnitude_get(QuaternionObject *self, void * /*closure*/)
1567{
1568 if (BaseMath_ReadCallback(self) == -1) {
1569 return nullptr;
1570 }
1571
1572 return PyFloat_FromDouble(sqrtf(dot_qtqt(self->quat, self->quat)));
1573}
1574
1576 /* Wrap. */
1577 Quaternion_angle_doc,
1578 "Angle of the quaternion.\n"
1579 "\n"
1580 ":type: float\n");
1581static PyObject *Quaternion_angle_get(QuaternionObject *self, void * /*closure*/)
1582{
1583 float tquat[4];
1584 float angle;
1585
1586 if (BaseMath_ReadCallback(self) == -1) {
1587 return nullptr;
1588 }
1589
1590 normalize_qt_qt(tquat, self->quat);
1591
1592 angle = 2.0f * safe_acosf(tquat[0]);
1593
1595
1596 return PyFloat_FromDouble(angle);
1597}
1598
1599static int Quaternion_angle_set(QuaternionObject *self, PyObject *value, void * /*closure*/)
1600{
1601 float tquat[4];
1602 float len;
1603
1604 float axis[3], angle_dummy;
1605 float angle;
1606
1608 return -1;
1609 }
1610
1611 len = normalize_qt_qt(tquat, self->quat);
1612 quat_to_axis_angle(axis, &angle_dummy, tquat);
1613
1614 angle = PyFloat_AsDouble(value);
1615
1616 if (angle == -1.0f && PyErr_Occurred()) { /* parsed item not a number */
1617 PyErr_SetString(PyExc_TypeError, "Quaternion.angle = value: float expected");
1618 return -1;
1619 }
1620
1622
1624
1625 axis_angle_to_quat(self->quat, axis, angle);
1626 mul_qt_fl(self->quat, len);
1627
1628 if (BaseMath_WriteCallback(self) == -1) {
1629 return -1;
1630 }
1631
1632 return 0;
1633}
1634
1636 /* Wrap. */
1637 Quaternion_axis_vector_doc,
1638 "Quaternion axis as a vector.\n"
1639 "\n"
1640 ":type: :class:`Vector`\n");
1641static PyObject *Quaternion_axis_vector_get(QuaternionObject *self, void * /*closure*/)
1642{
1643 float tquat[4];
1644
1645 float axis[3];
1646 float angle_dummy;
1647
1648 if (BaseMath_ReadCallback(self) == -1) {
1649 return nullptr;
1650 }
1651
1652 normalize_qt_qt(tquat, self->quat);
1653 quat_to_axis_angle(axis, &angle_dummy, tquat);
1654
1655 quat__axis_angle_sanitize(axis, nullptr);
1656
1657 return Vector_CreatePyObject(axis, 3, nullptr);
1658}
1659
1660static int Quaternion_axis_vector_set(QuaternionObject *self, PyObject *value, void * /*closure*/)
1661{
1662 float tquat[4];
1663 float len;
1664
1665 float axis[3];
1666 float angle;
1667
1669 return -1;
1670 }
1671
1672 len = normalize_qt_qt(tquat, self->quat);
1673 quat_to_axis_angle(axis, &angle, tquat); /* axis value is unused */
1674
1675 if (mathutils_array_parse(axis, 3, 3, value, "quat.axis = other") == -1) {
1676 return -1;
1677 }
1678
1680
1681 axis_angle_to_quat(self->quat, axis, angle);
1682 mul_qt_fl(self->quat, len);
1683
1684 if (BaseMath_WriteCallback(self) == -1) {
1685 return -1;
1686 }
1687
1688 return 0;
1689}
1690
1692
1693/* -------------------------------------------------------------------- */
1696
1697static PyGetSetDef Quaternion_getseters[] = {
1698 {"w",
1699 (getter)Quaternion_axis_get,
1700 (setter)Quaternion_axis_set,
1701 Quaternion_axis_doc,
1702 POINTER_FROM_INT(0)},
1703 {"x",
1704 (getter)Quaternion_axis_get,
1705 (setter)Quaternion_axis_set,
1706 Quaternion_axis_doc,
1707 POINTER_FROM_INT(1)},
1708 {"y",
1709 (getter)Quaternion_axis_get,
1710 (setter)Quaternion_axis_set,
1711 Quaternion_axis_doc,
1712 POINTER_FROM_INT(2)},
1713 {"z",
1714 (getter)Quaternion_axis_get,
1715 (setter)Quaternion_axis_set,
1716 Quaternion_axis_doc,
1717 POINTER_FROM_INT(3)},
1718 {"magnitude",
1720 (setter) nullptr,
1721 Quaternion_magnitude_doc,
1722 nullptr},
1723 {"angle",
1724 (getter)Quaternion_angle_get,
1725 (setter)Quaternion_angle_set,
1726 Quaternion_angle_doc,
1727 nullptr},
1728 {"axis",
1731 Quaternion_axis_vector_doc,
1732 nullptr},
1733 {"is_wrapped",
1735 (setter) nullptr,
1737 nullptr},
1738 {"is_frozen",
1740 (setter) nullptr,
1742 nullptr},
1743 {"is_valid",
1745 (setter) nullptr,
1747 nullptr},
1748 {"owner",
1750 (setter) nullptr,
1752 nullptr},
1753 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
1754};
1755
1757
1758/* -------------------------------------------------------------------- */
1761
1762#ifdef __GNUC__
1763# ifdef __clang__
1764# pragma clang diagnostic push
1765# pragma clang diagnostic ignored "-Wcast-function-type"
1766# else
1767# pragma GCC diagnostic push
1768# pragma GCC diagnostic ignored "-Wcast-function-type"
1769# endif
1770#endif
1771
1772static PyMethodDef Quaternion_methods[] = {
1773 /* In place only. */
1774 {"identity", (PyCFunction)Quaternion_identity, METH_NOARGS, Quaternion_identity_doc},
1775 {"negate", (PyCFunction)Quaternion_negate, METH_NOARGS, Quaternion_negate_doc},
1776
1777 /* Operate on original or copy. */
1778 {"conjugate", (PyCFunction)Quaternion_conjugate, METH_NOARGS, Quaternion_conjugate_doc},
1779 {"conjugated", (PyCFunction)Quaternion_conjugated, METH_NOARGS, Quaternion_conjugated_doc},
1780
1781 {"invert", (PyCFunction)Quaternion_invert, METH_NOARGS, Quaternion_invert_doc},
1782 {"inverted", (PyCFunction)Quaternion_inverted, METH_NOARGS, Quaternion_inverted_doc},
1783
1784 {"normalize", (PyCFunction)Quaternion_normalize, METH_NOARGS, Quaternion_normalize_doc},
1785 {"normalized", (PyCFunction)Quaternion_normalized, METH_NOARGS, Quaternion_normalized_doc},
1786
1787 /* Return converted representation. */
1788 {"to_euler", (PyCFunction)Quaternion_to_euler, METH_VARARGS, Quaternion_to_euler_doc},
1789 {"to_matrix", (PyCFunction)Quaternion_to_matrix, METH_NOARGS, Quaternion_to_matrix_doc},
1790 {"to_axis_angle",
1791 (PyCFunction)Quaternion_to_axis_angle,
1792 METH_NOARGS,
1793 Quaternion_to_axis_angle_doc},
1794 {"to_swing_twist",
1795 (PyCFunction)Quaternion_to_swing_twist,
1796 METH_O,
1797 Quaternion_to_swing_twist_doc},
1798 {"to_exponential_map",
1799 (PyCFunction)Quaternion_to_exponential_map,
1800 METH_NOARGS,
1801 Quaternion_to_exponential_map_doc},
1802
1803 /* Operation between 2 or more types. */
1804 {"cross", (PyCFunction)Quaternion_cross, METH_O, Quaternion_cross_doc},
1805 {"dot", (PyCFunction)Quaternion_dot, METH_O, Quaternion_dot_doc},
1806 {"rotation_difference",
1807 (PyCFunction)Quaternion_rotation_difference,
1808 METH_O,
1809 Quaternion_rotation_difference_doc},
1810 {"slerp", (PyCFunction)Quaternion_slerp, METH_VARARGS, Quaternion_slerp_doc},
1811 {"rotate", (PyCFunction)Quaternion_rotate, METH_O, Quaternion_rotate_doc},
1812 {"make_compatible",
1813 (PyCFunction)Quaternion_make_compatible,
1814 METH_O,
1815 Quaternion_make_compatible_doc},
1816
1817 /* Base-math methods. */
1818 {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc},
1819
1820 {"copy", (PyCFunction)Quaternion_copy, METH_NOARGS, Quaternion_copy_doc},
1821 {"__copy__", (PyCFunction)Quaternion_copy, METH_NOARGS, Quaternion_copy_doc},
1822 {"__deepcopy__", (PyCFunction)Quaternion_deepcopy, METH_VARARGS, Quaternion_copy_doc},
1823 {nullptr, nullptr, 0, nullptr},
1824};
1825
1826#ifdef __GNUC__
1827# ifdef __clang__
1828# pragma clang diagnostic pop
1829# else
1830# pragma GCC diagnostic pop
1831# endif
1832#endif
1833
1835
1836/* -------------------------------------------------------------------- */
1839
1840#ifdef MATH_STANDALONE
1841# define Quaternion_str nullptr
1842#endif
1843
1845 /* Wrap. */
1846 quaternion_doc,
1847 ".. class:: Quaternion(seq=(1.0, 0.0, 0.0, 0.0), angle=0.0, /)\n"
1848 "\n"
1849 " This object gives access to Quaternions in Blender.\n"
1850 "\n"
1851 " :arg seq: size 3 or 4\n"
1852 " :type seq: :class:`Vector`\n"
1853 " :arg angle: rotation angle, in radians\n"
1854 " :type angle: float\n"
1855 "\n"
1856 " The constructor takes arguments in various forms:\n"
1857 "\n"
1858 " (), *no args*\n"
1859 " Create an identity quaternion\n"
1860 " (*wxyz*)\n"
1861 " Create a quaternion from a ``(w, x, y, z)`` vector.\n"
1862 " (*exponential_map*)\n"
1863 " Create a quaternion from a 3d exponential map vector.\n"
1864 "\n"
1865 " .. seealso:: :meth:`to_exponential_map`\n"
1866 " (*axis, angle*)\n"
1867 " Create a quaternion representing a rotation of *angle* radians over *axis*.\n"
1868 "\n"
1869 " .. seealso:: :meth:`to_axis_angle`\n");
1870PyTypeObject quaternion_Type = {
1871 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
1872 /*tp_name*/ "Quaternion",
1873 /*tp_basicsize*/ sizeof(QuaternionObject),
1874 /*tp_itemsize*/ 0,
1875 /*tp_dealloc*/ (destructor)BaseMathObject_dealloc,
1876 /*tp_vectorcall_offset*/ 0,
1877 /*tp_getattr*/ nullptr,
1878 /*tp_setattr*/ nullptr,
1879 /*tp_as_async*/ nullptr,
1880 /*tp_repr*/ (reprfunc)Quaternion_repr,
1881 /*tp_as_number*/ &Quaternion_NumMethods,
1882 /*tp_as_sequence*/ &Quaternion_SeqMethods,
1883 /*tp_as_mapping*/ &Quaternion_AsMapping,
1884 /*tp_hash*/ (hashfunc)Quaternion_hash,
1885 /*tp_call*/ nullptr,
1886 /*tp_str*/ (reprfunc)Quaternion_str,
1887 /*tp_getattro*/ nullptr,
1888 /*tp_setattro*/ nullptr,
1889 /*tp_as_buffer*/ &Quaternion_as_buffer,
1890 /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
1891 /*tp_doc*/ quaternion_doc,
1892 /*tp_traverse*/ (traverseproc)BaseMathObject_traverse,
1893 /*tp_clear*/ (inquiry)BaseMathObject_clear,
1894 /*tp_richcompare*/ (richcmpfunc)Quaternion_richcmpr,
1895 /*tp_weaklistoffset*/ 0,
1896 /*tp_iter*/ nullptr,
1897 /*tp_iternext*/ nullptr,
1898 /*tp_methods*/ Quaternion_methods,
1899 /*tp_members*/ nullptr,
1900 /*tp_getset*/ Quaternion_getseters,
1901 /*tp_base*/ nullptr,
1902 /*tp_dict*/ nullptr,
1903 /*tp_descr_get*/ nullptr,
1904 /*tp_descr_set*/ nullptr,
1905 /*tp_dictoffset*/ 0,
1906 /*tp_init*/ nullptr,
1907 /*tp_alloc*/ nullptr,
1908 /*tp_new*/ Quaternion_new,
1909 /*tp_free*/ nullptr,
1910 /*tp_is_gc*/ (inquiry)BaseMathObject_is_gc,
1911 /*tp_bases*/ nullptr,
1912 /*tp_mro*/ nullptr,
1913 /*tp_cache*/ nullptr,
1914 /*tp_subclasses*/ nullptr,
1915 /*tp_weaklist*/ nullptr,
1916 /*tp_del*/ nullptr,
1917 /*tp_version_tag*/ 0,
1918 /*tp_finalize*/ nullptr,
1919 /*tp_vectorcall*/ Quaternion_vectorcall,
1920};
1921
1922#ifdef MATH_STANDALONE
1923# undef Quaternion_str
1924#endif
1925
1927
1928/* -------------------------------------------------------------------- */
1931
1932PyObject *Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type)
1933{
1935 float *quat_alloc;
1936
1937 quat_alloc = static_cast<float *>(PyMem_Malloc(QUAT_SIZE * sizeof(float)));
1938 if (UNLIKELY(quat_alloc == nullptr)) {
1939 PyErr_SetString(PyExc_MemoryError,
1940 "Quaternion(): "
1941 "problem allocating data");
1942 return nullptr;
1943 }
1944
1946 if (self) {
1947 self->quat = quat_alloc;
1948 /* init callbacks as nullptr */
1949 self->cb_user = nullptr;
1950 self->cb_type = self->cb_subtype = 0;
1951
1952 /* NEW */
1953 if (!quat) { /* new empty */
1954 unit_qt(self->quat);
1955 }
1956 else {
1957 copy_qt_qt(self->quat, quat);
1958 }
1960 }
1961 else {
1962 PyMem_Free(quat_alloc);
1963 }
1964
1965 return (PyObject *)self;
1966}
1967
1968PyObject *Quaternion_CreatePyObject_wrap(float quat[4], PyTypeObject *base_type)
1969{
1971
1973 if (self) {
1974 /* init callbacks as nullptr */
1975 self->cb_user = nullptr;
1976 self->cb_type = self->cb_subtype = 0;
1977
1978 /* WRAP */
1979 self->quat = quat;
1981 }
1982 return (PyObject *)self;
1983}
1984
1985PyObject *Quaternion_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar cb_subtype)
1986{
1988 if (self) {
1989 Py_INCREF(cb_user);
1990 self->cb_user = cb_user;
1991 self->cb_type = cb_type;
1992 self->cb_subtype = cb_subtype;
1993 BLI_assert(!PyObject_GC_IsTracked((PyObject *)self));
1994 PyObject_GC_Track(self);
1995 }
1996
1997 return (PyObject *)self;
1998}
1999
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
A dynamically sized string ADT.
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc: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.cc: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 AppView * view
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
iter begin(iter)
PyObject * self
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
nullptr float
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
float length(VecOp< float, D >) RET
int count
int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
Definition mathutils.cc:470
int BaseMathObject_is_gc(BaseMathObject *self)
Definition mathutils.cc:762
Py_hash_t mathutils_array_hash(const float *array, size_t array_len)
Definition mathutils.cc:68
void BaseMathObject_dealloc(BaseMathObject *self)
Definition mathutils.cc:740
int EXPP_VectorsAreEqual(const float *vecA, const float *vecB, int size, int floatSteps)
Definition mathutils.cc:489
int mathutils_array_parse(float *array, int array_num_min, int array_num_max, PyObject *value, const char *error_prefix)
Definition mathutils.cc:96
char BaseMathObject_is_valid_doc[]
Definition mathutils.cc:685
char BaseMathObject_is_wrapped_doc[]
Definition mathutils.cc:671
PyObject * BaseMathObject_owner_get(BaseMathObject *self, void *)
Definition mathutils.cc:665
char BaseMathObject_is_frozen_doc[]
Definition mathutils.cc:678
PyObject * mathutils_dynstr_to_py(DynStr *ds)
Definition mathutils.cc:501
int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix)
Definition mathutils.cc:408
PyObject * BaseMathObject_is_frozen_get(BaseMathObject *self, void *)
Definition mathutils.cc:680
PyObject * BaseMathObject_freeze(BaseMathObject *self)
Definition mathutils.cc:699
PyObject * BaseMathObject_is_wrapped_get(BaseMathObject *self, void *)
Definition mathutils.cc:673
char BaseMathObject_owner_doc[]
Definition mathutils.cc:664
char BaseMathObject_freeze_doc[]
Definition mathutils.cc:691
PyObject * BaseMathObject_is_valid_get(BaseMathObject *self, void *)
Definition mathutils.cc:686
int BaseMathObject_clear(BaseMathObject *self)
Definition mathutils.cc:722
int BaseMathObject_traverse(BaseMathObject *self, visitproc visit, void *arg)
Definition mathutils.cc:716
#define BaseMath_ReadCallback_ForWrite(_self)
Definition mathutils.hh:151
#define BaseMath_Prepare_ForBufferAccess(_self, _view, _flags)
Definition mathutils.hh:183
#define BaseMath_ReadIndexCallback(_self, _index)
Definition mathutils.hh:145
#define BaseMath_WriteCallback(_self)
Definition mathutils.hh:143
#define BASE_MATH_NEW(struct_name, root_type, base_type)
Definition mathutils.hh:27
#define BaseMathObject_Prepare_ForHash(_self)
Definition mathutils.hh:166
#define BASE_MATH_FLAG_DEFAULT
Definition mathutils.hh:52
#define BaseMath_Prepare_ForWrite(_self)
Definition mathutils.hh:161
@ BASE_MATH_FLAG_HAS_BUFFER_VIEW
Definition mathutils.hh:50
@ BASE_MATH_FLAG_IS_WRAP
Definition mathutils.hh:37
#define BaseMath_ReadCallback(_self)
Definition mathutils.hh:141
#define BaseMath_WriteIndexCallback(_self, _index)
Definition mathutils.hh:147
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 void Quaternion_releasebuffer(PyObject *, Py_buffer *view)
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)
static int Quaternion_getbuffer(PyObject *obj, Py_buffer *view, int flags)
static PyObject * Quaternion_magnitude_get(QuaternionObject *self, void *)
static PyObject * Quaternion_conjugated(QuaternionObject *self)
static PyBufferProcs Quaternion_as_buffer
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)
static PyObject * Quaternion_vectorcall(PyObject *type, PyObject *const *args, const size_t nargsf, PyObject *kwnames)
#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)
PyDoc_STRVAR(Quaternion_to_euler_doc, ".. method:: to_euler(order='XYZ', euler_compat=None, /)\n" "\n" " Return Euler representation of the quaternion.\n" "\n" " :arg order: Rotation order.\n" " :type order: Literal['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']\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")
#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)
Py_DECREF(oname)
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
#define sqrtf
i
Definition text_draw.cc:230
uint len