From 8b0dc8ca67f422dc8cde24a51c5a496e009d42b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 5 Dec 2023 20:56:25 +0100 Subject: [PATCH] python: expose new Quaternion reflection APIs. --- doc/python/magnum.math.rst | 4 ++++ doc/python/pages/changelog.rst | 2 ++ src/python/magnum/math.cpp | 9 +++++++++ src/python/magnum/test/test_math.py | 8 ++++++++ 4 files changed, 23 insertions(+) diff --git a/doc/python/magnum.math.rst b/doc/python/magnum.math.rst index 376789b..feeacb7 100644 --- a/doc/python/magnum.math.rst +++ b/doc/python/magnum.math.rst @@ -324,6 +324,10 @@ :raise ValueError: If :p:`normalized_axis` is not normalized .. py:function:: magnum.Quaterniond.rotation(angle: magnum.Rad, normalized_axis: magnum.Vector3d) :raise ValueError: If :p:`normalized_axis` is not normalized +.. py:function:: magnum.Quaternion.reflection + :raise ValueError: If :p:`normal` is not normalized +.. py:function:: magnum.Quaterniond.reflection + :raise ValueError: If :p:`normal` is not normalized .. py:function:: magnum.Quaternion.from_matrix :raise ValueError: If :p:`matrix` is not a rotation .. py:function:: magnum.Quaterniond.from_matrix diff --git a/doc/python/pages/changelog.rst b/doc/python/pages/changelog.rst index 192362f..59d546c 100644 --- a/doc/python/pages/changelog.rst +++ b/doc/python/pages/changelog.rst @@ -68,6 +68,8 @@ Changelog - Exposed :ref:`Color3.from_xyz()`, :ref:`Color3.from_linear_rgb_int()`, :ref:`Color3.to_xyz()`, :ref:`Color3.to_linear_rgb_int()` and equivalent APIs on :ref:`Color4` +- Exposed new :ref:`Quaternion.reflection()` and + :ref:`Quaternion.reflect_vector()` APIs - Exposed :ref:`gl.Context` and its platform-specific subclasses for EGL, WGL and GLX - Exposed :ref:`gl.Framebuffer.attach_texture()` and missing sRGB, depth diff --git a/src/python/magnum/math.cpp b/src/python/magnum/math.cpp index 36f4a46..ea40ebe 100644 --- a/src/python/magnum/math.cpp +++ b/src/python/magnum/math.cpp @@ -348,6 +348,13 @@ template void quaternion(py::module_& m, py::class_& c) { } return T::rotation(Math::Rad(angle), normalizedAxis); }, "Rotation quaternion", py::arg("angle"), py::arg("normalized_axis")) + .def_static("reflection", [](const Math::Vector3& normal) { + if(!normal.isNormalized()) { + PyErr_Format(PyExc_ValueError, "normal %S is not normalized", py::cast(normal).ptr()); + throw py::error_already_set{}; + } + return T::reflection(normal); + }, "Reflection quaternion", py::arg("normal")) .def_static("from_matrix", [](const Math::Matrix3x3& matrix) { /* Same as the check in fromMatrix() */ if(std::abs(matrix.determinant() - typename T::Type(1)) >= typename T::Type(3)*Math::TypeTraits::epsilon()) { @@ -459,6 +466,8 @@ template void quaternion(py::module_& m, py::class_& c) { } return self.transformVectorNormalized(vector); }, "Rotate a vector with a normalized quaternion", py::arg("vector")) + .def("reflect_vector", &T::reflectVector, + "Reflect a vector with a reflection quaternion", py::arg("vector")) /* Properties */ .def_property("vector", diff --git a/src/python/magnum/test/test_math.py b/src/python/magnum/test/test_math.py index d9e9072..0b55f15 100644 --- a/src/python/magnum/test/test_math.py +++ b/src/python/magnum/test/test_math.py @@ -1454,9 +1454,14 @@ class Quaternion_(unittest.TestCase): b = Quaternion.from_matrix(Matrix4.rotation_x(Deg(45.0)).rotation_scaling()) self.assertEqual(a, Quaternion((0.382683, 0.0, 0.0), 0.92388)) + c = Quaternion.reflection(Vector3.x_axis()) + self.assertEqual(c, Quaternion((1.0, 0.0, 0.0), 0.0)) + def test_static_methods_invalid(self): with self.assertRaisesRegex(ValueError, "axis Vector\\(2, 0, 1\\) is not normalized"): Quaternion.rotation(Deg(35.0), Vector3(2.0, 0.0, 1.0)) + with self.assertRaisesRegex(ValueError, "normal Vector\\(2, 0, 1\\) is not normalized"): + Quaternion.reflection(Vector3(2.0, 0.0, 1.0)) with self.assertRaisesRegex(ValueError, """the matrix is not a rotation: Matrix\\(2, 0, 0, 0, 2, 0, @@ -1483,6 +1488,9 @@ Matrix\\(2, 0, 0, self.assertEqual(a.transform_vector(Vector3.y_axis()), Vector3(0.0, 0.707107, 0.707107)) self.assertEqual(a.transform_vector_normalized(Vector3.y_axis()), Vector3(0.0, 0.707107, 0.707107)) + b = Quaternion.reflection(Vector3.x_axis()) + self.assertEqual(b.reflect_vector(Vector3.x_axis()), (-1.0, 0.0, 0.0)) + def test_methods_invalid(self): a = Quaternion.rotation(Deg(45.0), Vector3.x_axis())*3.0